Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Entity Framework : map multiple classes to one table

I think this is possible in nhiberate, but my question is about Entity Framework.

In my database model - which I cannot modify - I have redundant columns that I would like to store in different classes.

Example :

public class DateParams
{
    public DateTime CreationDate { get; set; }
    public DateTime ModificationDate { get; set; }

    // some methods
}

public class Localization
{
    public String EnglishLabel { get; set; }
    public String FrenchLabel { get; set; }

    // some methods
}

And then I would use them in some of my models :

public class Account // Localization && DateParams 
{
    public int ID { get; set;  }
    public String Name { get; set; }

    public Localization Localization { get; set; }
    public DateParams DateParams { get; set; }
}

public class Lead // DateParams only
{
    public int ID { get; set; }
    public String Name { get; set; }

    public DateParams DateParams { get; set; }
}

What I would like to achieve is having something like this

public class LocalizationMap : EntityTypeConfiguration<Localization>
{
   public LocalizationMap()
   { 
        Property(e => e.EnglishLabel).HasColumnName("en");
        Property(e => e.FrenchLabel).HasColumnName("fr");
   }
}

public class AccountMap : EntityTypeConfiguration<Account>
{
    public AccountMap()
    {            
        HasKey(x => x.ID);
        Property(e => e.Name).HasColumnName("Name");

        HasSubMapping(new LocalizationMap());
        HasSubMapping(new DateParamsMap());

        ToTable("Account");
    }
}

I could use inheritance to solve this, but C# does not allow multiple inheritance.

like image 861
Akli Avatar asked Apr 17 '16 17:04

Akli


People also ask

How do you map multiple entities on the same table?

Entity Mappings If you want to map the same database table to two entities, you should create a simple inheritance hierarchy. The superclass should be abstract and contain all attributes that are shared by both entities. You should map it as a mapped superclass so that it is not an entity itself.

What is table multiple classes mapping?

A class mapping describes how a class maps to the database. It typically controls the primary table for the class and how the class is linked to its superclass data, if any. For classes using datastore identity, the class mapping also manages the primary key column for the class.


2 Answers

You can achieve this by using complex types. These map to table columns named like complextypeName_propertyName but this behaviour can be changed by overwriting OnModelCreating(DbModelBuilder modelBuilder) in DbContext like described in Entity Framework - Reuse Complex Type

For your example:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.ComplexType<Localization>();

    modelBuilder.Entity<Account>().Property(x => x.Localization.EnglishLabel).HasColumnName("en");
    modelBuilder.Entity<Account>().Property(x => x.Localization.FrenchLabel).HasColumnName("fr");
    // et cetera
}
like image 109
ChriPf Avatar answered Sep 17 '22 15:09

ChriPf


I'm not going to make you happy.

There is an EF feature called Table Splitting. As the name suggests, this allows us to map (split) one database table to multiple classes in the conceptual model. In your case, the mappings for Account would look like this:

class AccountMap : EntityTypeConfiguration<Account>
{
    public AccountMap()
    {
        ToTable("Account");
        HasKey(x => x.ID);
        HasRequired(a => a.DateParams).WithRequiredPrincipal();
        HasRequired(a => a.Localization).WithRequiredPrincipal();
    }
}

class DateParamsMap : EntityTypeConfiguration<DateParams>
{
    public DateParamsMap()
    {
        ToTable("Account");
    }
}

class LocalizationMap : EntityTypeConfiguration<Localization>
{
    public LocalizationMap()
    {
        ToTable("Account");
    }
}

But this immediately shows the problem: the table name "Account" in the type configurations is hard coded. There's no way to reuse the satellite classes DateParams and Localization for multiple types. And, before you try, EF won't accept generics like DateParams<T>.

Which is sad, because all other options I can think of are ugly, or clunky at best:

  • Create subclasses of DateParams and Localization (and accompanying configurations) for any entity that needs them.
  • Just add the properties to all types and work with projections as much as possible (because I assume the whole point of this effort is to reduce the number of properties you're going to query).
  • Use one context hosting the main types without these properties and a second context hosting the satellite types (again, to help querying less properties easily). But unfortunately, you can only join the instances from both contexts in memory, i.e. LINQ to objects.
  • Create a third satellite class, combining both smaller classes, and use these three classes as base types.
like image 42
Gert Arnold Avatar answered Sep 16 '22 15:09

Gert Arnold