Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

EF 4.3 Code First: Table per Type (TPT) with Composite Primary Key and Foreign Key

So I'm trying to use Code First with Fluent to map a base class with one derived type where the tables schema is a Table-per-Type arrangement. Also, the derived type has a many-to-one relationship with another type that also has a composite foreign key. (The keys on these tables is unchangeable and the names match up exactly.)

Here is an example of what I'm trying to achieve in CSharp:

public class BaseType
{
    public int Id;
    public int TenantId;
    public int Name;
}

public class DerivedType : BaseType
{
    public int Active;
    public int OtherTypeId;
    public OtherType NavigationProperty; 
}

Here is the configuration for this in the configuration classes:

public BaseTypeConfiguration()
{
     ToTable("BaseTypes", "dbo");

     HasKey(f => new { f.Id, f.TenantId});

     Property(f => f.Id)
         .HasColumnName("BaseTypeId")
         .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
}

public DerivedTypeConfiguration()
{
    ToTable("DerivedTypes", "dbo");

    //OtherType has many DerivedTypes
    HasRequired(dt=> dt.OtherTypeNavigation)
        .WithMany(ot=> ot.DerivedTypes)
        .HasForeignKey(dt=> new { dt.OtherTypeId, dt.TenantId});
}

From what I can tell my mapping is set up correctly (as in, I followed many tutorials and examples that had this exact situation but with a single column identifier)

When I try to query these entities the exception I get is: The foreign key component 'TenantId' is not a declared property on type 'DerivedType'.

And when I try to explicitly declare these properties on the type using the new keyword I get an exception saying that duplicate properties exist.

Answer Response from EF Team

This is part of a more fundamental limitation where EF doesn't support having a property defined in a base type and then using it as a foreign key in a derived type. Unfortunately this is a limitation that would be very hard to remove from our code base. Given that we haven't seen a lot of requests for it, it's not something we are planning to address at this stage so we are closing this issue.

like image 807
MDADev Avatar asked Nov 01 '12 14:11

MDADev


1 Answers

I think this is what you are looking for:

[Table("BaseType")]
public class BaseType
{
    [Key, DatabaseGenerated(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.Identity)]
    public int Id {get;set;}
    [Key]
    public int TenantId { get; set; }
    public int Name { get; set; }
}

[Table("Derived1")]
public class DerivedType : BaseType
{
    public int Active { get; set; }
    public int OtherTypeId { get; set; }
    public virtual OtherType NavigationProperty {get;set;}
}

[ComplexType]
public class OtherType
{
    public string MyProperty { get; set; }

}


public class EFCodeFirstContext : DbContext
{
    public DbSet<BaseType> BaseTypes { get; set; }
    public DbSet<DerivedType> DerivedTypes { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<BaseType>().HasKey(p => new { p.Id, p.TenantId });
        base.OnModelCreating(modelBuilder);
    }
}

Code above results in:

like image 54
Simon Smeets Avatar answered Nov 01 '22 11:11

Simon Smeets