Search code examples
c#entity-frameworktable-per-type

How does EF6 know derived type?


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?


Solution

  • 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 JOINs 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).