I use Entity Framework 6 with Table-Per-Type inheritance approach.
Tables look like this (just an example):
ConfigurationKeys
ID - int, not null, auto increment, primary
ConfigurationKeyType - int, not null
Key - varchar(50), not null
StringConfigurationKeys
ID - int, not null, primary (foreign key to ConfigurationKey.ID)
StringValue - varchar(50), not null
IntConfigurationKeys
ID - int, not null, primary (foreign key to ConfigurationKey.ID)
IntValue - int, not null
and the following classes structure:
public enum ConfigurationKeyType
{
StringConfigurationKey = 0,
IntConfigurationKey = 1
}
[Table("ConfigurationKeys")]
public class ConfigurationKey
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
public ConfigurationKeyType ConfigurationKeyType { get; set; }
public string Key { get; set; }
}
[Table("StringConfigurationKeys")]
public class StringConfigurationKey : ConfigurationKey
{
public string StringValue { get; set; }
}
[Table("IntConfigurationKeys")]
public class IntConfigurationKey : ConfigurationKey
{
public int IntValue { get; set; }
}
No other configurations and model builder mappings are made.
Now, when I do the following query:
var keys = context.ConfigurationKeys.ToArray();
Entity Framework returns an array of derived types.
I.e., keys[0]
may be IntConfigurationKey
and keys[1]
may be of type StringConfigurationKey
. I can safely cast it and access properties of derived types.
It is a great feature and I love it, but I would like to know how it works in order to maintain this functionality in future.
Does it use enum or does it look for an entity with the same ID across all tables?
Does it use enum or does it look for an entity with the same ID across all tables?
With TPT strategy it does use LEFT OUTER JOIN
s to all related tables for determining the derived type (and data). No discriminator column or your enum is used.
You can see that by looking at the generated SQL by either turning on the EF logging or using query.ToString()
. So both
var sql = context.ConfigurationKeys.ToString();
or
context.Database.Log = Console.WriteLine;
var keys = context.ConfigurationKeys.ToArray();
will show something like this:
SELECT
CASE WHEN (( NOT (([Project1].[C1] = 1) AND ([Project1].[C1] IS NOT NULL))) AND ( NOT (([Project2].[C1] = 1) AND ([Project2].[C1] IS NOT NULL)))) THEN '0X' WHEN (([Project1].[C1] = 1) AND ([Project1].[C1] IS NOT NULL)) THEN '0X0X' ELSE '0X1X' END AS [C1],
[Extent1].[ID] AS [ID],
[Extent1].[ConfigurationKeyType] AS [ConfigurationKeyType],
[Extent1].[Key] AS [Key],
CASE WHEN (( NOT (([Project1].[C1] = 1) AND ([Project1].[C1] IS NOT NULL))) AND ( NOT (([Project2].[C1] = 1) AND ([Project2].[C1] IS NOT NULL)))) THEN CAST(NULL AS int) WHEN (([Project1].[C1] = 1) AND ([Project1].[C1] IS NOT NULL)) THEN [Project1].[IntValue] END AS [C2],
CASE WHEN (( NOT (([Project1].[C1] = 1) AND ([Project1].[C1] IS NOT NULL))) AND ( NOT (([Project2].[C1] = 1) AND ([Project2].[C1] IS NOT NULL)))) THEN CAST(NULL AS varchar(1)) WHEN (([Project1].[C1] = 1) AND ([Project1].[C1] IS NOT NULL)) THEN CAST(NULL AS varchar(1)) ELSE [Project2].[StringValue] END AS [C3]
FROM [dbo].[ConfigurationKeys] AS [Extent1]
LEFT OUTER JOIN (SELECT
[Extent2].[ID] AS [ID],
[Extent2].[IntValue] AS [IntValue],
cast(1 as bit) AS [C1]
FROM [dbo].[IntConfigurationKeys] AS [Extent2] ) AS [Project1] ON [Extent1].[ID] = [Project1].[ID]
LEFT OUTER JOIN (SELECT
[Extent3].[ID] AS [ID],
[Extent3].[StringValue] AS [StringValue],
cast(1 as bit) AS [C1]
FROM [dbo].[StringConfigurationKeys] AS [Extent3] ) AS [Project2] ON [Extent1].[ID] = [Project2].[ID]
Detail explanation can be found in Inheritance with EF Code First: Part 2 – Table per Type (TPT).