Question

I have a legacy DB I'd like to writing an application for monitoring some stuff in it.

The DB has a set of tables which is repeated for each 'project' in it.

For example if I have 2 projects, I would have project_1_table and a similar project_2_table.

Even though the 2 tables serve the same purpose for each project, there are some minor differences.

I'm looking for a way to create a model that will have a base class holding all the common elements of those tables, and then 2 inherited classes which would map the actual content.

To clarify: I have no control over the DB, and cannot change it. Looking for the best way to use EF in this situation.

How can this be done (either in the Model Editor or via CodeFirst)?

DB Schema

 project_1_table                   project_2_table
   recordID                          recordID
   title                             title
   project1field                     project2field

Entity Model (What I Want)

 BaseEntity
   ProjectType
   RecordID
   Title

 (Mapped inherited Entities)
 Project1Entity: BaseEntity        Project2Entity: BaseEntity
   ProjectType = 1                   ProjectType = 2
   RecordID                          RecordID
   Title                             Title
   Project1Field                     Project2Field

I'm new to EF, and it seems to elude me.

Is this possible? If so, how?

Was it helpful?

Solution

Something similar along the lines of your requirement is possible by using Table per Concrete Type inheritance model.

Entities

public abstract class BaseEntity {
    public int RecordID { get; set; }
    public abstract int ProjectType { get; }
    public string Title { get; set; }
}

public class Project1Entity : BaseEntity {
    public override int ProjectType {
        get { return 1; }
    }

    public string Project1Field { get; set; }
}

public class Project2Entity : BaseEntity {
    public override int ProjectType {
        get { return 2; }
    }

    public string Project2Field { get; set; }
}

DBContext

public class DataContext : DbContext {
    public DbSet<Project1Entity> Project1Entities { get; set; }
    public DbSet<Project2Entity> Project2Entities { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder) {
        base.OnModelCreating(modelBuilder);

        modelBuilder.Entity<BaseEntity>().HasKey(o => o.RecordID);
        modelBuilder.Entity<Project1Entity>().Map(m => {
            m.MapInheritedProperties();
            m.ToTable("project_1_table");
        });

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

However there are several limitations

  • with this configuration EF isn't able to generate the RecordID for new records, so you have to provide value of the primary key yourself
  • it will work only if RecordID for items in project_1_table and project_2_table doesn't collide. If they do, you need to specify another primary key - e.g. add column ProjectType to the database and use the composite primary key {RecordID, ProjectType}

Version with composite key

Entities

public abstract class BaseEntity {
    public int RecordID { get; set; }
    public int ProjectType { get; set; }
    public string Title { get; set; }
}

public class Project1Entity : BaseEntity {
    public string Project1Field { get; set; }
}

public class Project2Entity : BaseEntity {
    public string Project2Field { get; set; }
}

DBContext

public class DataContext : DbContext {
    ...

    protected override void OnModelCreating(DbModelBuilder modelBuilder) {
        ...
        modelBuilder.Entity<BaseEntity>().HasKey(o => new { o.RecordID, o.ProjectType });
        ...
    }
}

OTHER TIPS

If you model it like this:

public abstract class BaseEntity
{
   [Key]
   public RecordID { get; set; }
   
   public string Title { get; set; }
}

public class Project1Entity: BaseEntity
{
   public string Project1Field { get; set; }
}

public class Project2Entity: BaseEntity
{
   public string Project2Field { get; set; }
}

Then entity framework will create a single table like this:

BaseEntity
   RecordID
   Title
   Project1Field                     
   Project2Field
   Discriminator

The "Discriminator" column means that entity framework knows what type of object to instantiate when retrieving from the database - and should make your ProjectType field redundant. (If you have a BaseEntity then you can test for type in C# using if(entity is Project1Entity) )

This strategy is known as Table Per Hierarchy inheritance .It is the default strategy for inheritance in EF and may suit your purposes better if your inheritance is not too complex.

If you don't like the single table and prefer the normalised form then you can change this by adding table attributes to the subclasses:

[Table("Project1")]
public class Project1Entity: BaseEntity
{
   public string Project1Field { get; set; }
}

[Table("Project2")]
public class Project2Entity: BaseEntity
{
   public string Project2Field { get; set; }
}

This will result in tables like this:

   BaseEntity
      RecordID
      Title

   Project1
      Project1Field       

   Project2
      Project2Field

So now it is normalised but requires an inner join everytime. This strategy is known as Table Per Type

The third option is table per concrete type. There is a full write up there if you really feel that suits you best and there are guidelines for choosing your strategy. Here are some quotes that I think are worth reading:

By default, choose TPH only for simple problems. For more complex cases (or when you’re overruled by a data modeler insisting on the importance of nullability constraints and normalization), you should consider the TPT strategy. But at that point, ask yourself whether it may not be better to remodel inheritance as delegation in the object model (delegation is a way of making composition as powerful for reuse as inheritance). Complex inheritance is often best avoided for all sorts of reasons unrelated to persistence or ORM

I recommend TPC (only) for the top level of your class hierarchy, where polymorphism isn’t usually required, and when modification of the base class in the future is unlikely

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top