Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Controlling column mapping with a custom convention in Entity Framework 6

I have a class TypeEntity that will act as the base class for several dozen entities. I'm using TPC, so I need to map all the properties on the base class to the table with the name of the concrete class, and set the Key field to be database generated.

Currently I'm doing this with an EntityTypeConfiguration for each entity type that looks like this:

class LineItemType : EntityTypeConfiguration<Models.LineItemType>
{
    public LineItemType()
    {
        this.Property(e => e.Key)
            .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);

        this.Map(e => e.MapInheritedProperties()
                       .ToTable(nameof(LineItemType)));
    }
}

This works fine, but is very repetitive. I have to remember to create a configuration class for every type that inherits from TypeEntity, set the key, and map the inherited properties. This seems like an ideal case for a custom Convention.


I created a TypeEntityTpcConvention Convention as follows:

class TypeEntityTpcConvention : Convention
{
    public TypeEntityTpcConvention()
    {
        this.Types<TypeEntity>()
            .Configure(e => e.Property(p => p.Key)
                             .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity));
    }
}

Which works to set Key as database generated, but I can't find any way to access the table mappings for properties from inside a convention.


Ideally, I'd expect something like this:

this.Types<TypeEntity>()
    .Configure(e => e.MapInheritedProperties()
    .ToTable(e.ClrType.Name));

Or even a call like this for each property that needs to be mapped:

this.Types<TypeEntity>()
    .Configure(e => e.Property(p=>p.Key)
                     .ToTable(e.ClrType.Name));

Neither of which seems to exist. Is there any way for me to control the mapping of properties from inside a Convention?


After some additional research, it looks like there are more advanced convention options available as IStoreModelConvention and IConceptualModelConvention, but useful documentation for how to use these is severely lacking. After several hours poking through them with breakpoints and watch windows, I haven't figured out how to control column mapping using these interfaces either.


My current solution is to use reflection to find all types that inherit from TypeEntity in OnModelCreating, and map the properties to the correct table. This works, but I would prefer to use a convention if possible, as this really seems like the type of thing conventions were made for. I feel like I am missing something obvious.

like image 832
Bradley Uffner Avatar asked Sep 06 '17 02:09

Bradley Uffner


1 Answers

As far as I can see, you could just write the type configuration in the OnModelCreating method of your DbContext and it is very similar to the code you already mentioned in the question.

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

    modelBuilder.Types<TypeEntity>().Configure(c =>
    {
        c.HasKey(p => p.Key);
        // probably not needed, but won't do any harm
        c.Property(p => p.Key).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
        c.ToTable(c.ClrType.Name);
    });
}

If there is any problem with this approach, let me know and I'll re-visit the topic.

Edit:

The exact same principle can be applied with conventions:

class TypeEntityConvention : Convention
{
    public TypeEntityConvention()
    {
        this.Types<TypeEntity>().Configure(c =>
        {
            c.HasKey(p => p.Key);
            c.Property(p => p.Key).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
            c.ToTable(c.ClrType.Name);
        });
    }
}

and

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

    modelBuilder.Conventions.Add<TypeEntityConvention>();
}
like image 65
grek40 Avatar answered Nov 19 '22 16:11

grek40