2

So I've implement Table Per concrete Class to deal with an inheritance hierarchy, but I'm having trouble with Navigation properties.

My Model is structure as follows:

I have an abstract BaseEntity class, with Multiple derived classes, so:

public abstract class BaseEntity
{
    public virtual ICollection<Comment> Comments { get; set; }
    public EntitytType EntitytType { get; set; }
}

public class FirstDerivedEntity : BaseEntity
{
    //EntitytType == First;
}

public class SecondDerivedEntity : BaseEntity
{
    //EntitytType == Second;
}

public class Comment
{
    public long BaseEntityId { get; set; }
    public EntitytType BaseEntityType { get; set; }
}

public enum EntitytType
{
    First,
    Second
}

The Comments navigation property here doesn't work because each derived(Concrete) class has it's own set of Ids. In my mind the EntityType column in each Table would serve as some sort of Discriminator, but I don't know how to tell EF to use it as such.

Any help would be greatly appreciated!

ecairncross
  • 41
  • 1
  • 5

1 Answers1

0

The solution in order for TPC to work would be the Ids in the derived classes to be unique not only in their table but in both tables.

You can use a database solution like an auto increment primary key with different initial seeds or GUID keys like the ones in SQL server.

Another approach would be to generate unique GUID keys in you application code. You can see same sample code of how to model the entities below :

namespace tpc_so
{
    public class tpc_soContext : DbContext
    {
        public tpc_soContext()
        {
        }
        public DbSet<BaseEntity> BaseEntities { get; set; }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Entity<BaseEntity>().HasKey(b => b.BaseEntityId);
            modelBuilder.Entity<BaseEntity>()
           .Property(b => b.BaseEntityId)
           .HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);

            modelBuilder.Entity<FirstDerivedEntity>().Map(m =>
            {
                m.MapInheritedProperties();
                m.ToTable("FirstDerivedEntities");
            });

            modelBuilder.Entity<SecondDerivedEntity>().Map(m =>
            {
                m.MapInheritedProperties();
                m.ToTable("SecondDerivedEntities");
            });


            modelBuilder.Entity<Comment>().ToTable("Comments");

            base.OnModelCreating(modelBuilder);
        }

    }
    public abstract class BaseEntity
    {
        public Guid BaseEntityId { get; set; }
        public virtual ICollection<Comment> Comments { get; set; }
    }

    public class FirstDerivedEntity : BaseEntity{}
    public class SecondDerivedEntity : BaseEntity{ }

    public class Comment
    {
        public long CommentId { get; set; }
        public Guid BaseEntityId { get; set; }
        public string Text { get; set; }
    }

}

And to create some entities use the code below :

using (var ctx = new tpc_soContext())
{
    ctx.BaseEntities.Add(new FirstDerivedEntity()
    {
        BaseEntityId = Guid.NewGuid(),
        Comments = new List<Comment>() { new Comment() { Text = "First Derived Comment" } }
    });

    ctx.BaseEntities.Add(new SecondDerivedEntity()
    {
        BaseEntityId = Guid.NewGuid(),
        Comments = new List<Comment>() { new Comment() { Text = "Second-Derived Comment" } }
    });

    ctx.SaveChanges();
}

Some good sources for TPC would be here :

[Inheritance with EF Code First: Part 3 – Table per Concrete Type (TPC)]

[Entity Framework - Table Per Concrete]

gpanagakis
  • 519
  • 5
  • 7