Reputation: 966
using EF 6.1+ there were times where we need to add or remove existing conentions. The code looks more or less like:
public class MyContext : DbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.AddFromAssembly(Assembly.GetExecutingAssembly());
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
modelBuilder.Conventions.Remove<ManyToManyCascadeDeleteConvention>();
base.OnModelCreating(modelBuilder);
}
}
how do the same in EF core? Modelbuilder has no Conventions property :(
Upvotes: 27
Views: 17433
Reputation: 2072
In order to set singular table names in EF Core 5 you can do the following
modelBuilder.Model.GetEntityTypes()
.Configure(et => et.SetTableName(et.DisplayName()));
but then if you have value objects in you domain they will all be treated as entity types and created as tables in the database. If your value objects all inherit from a base type like ValueObject
you can use the following:
modelBuilder.Model.GetEntityTypes()
.Where(x => !x.ClrType.IsSubclassOf(typeof(ValueObject)))
.Configure(et => et.SetTableName(et.DisplayName()));
Another drawback in EF Core 5 is that when you use inheritance in your entity model, setting tables names changes from Table-per-Hierarchy (TPH) to Table-per-Type (TPT)
You could use the following alternative (assuming your entities derive from BaseEntity
)
modelBuilder.Model.GetEntityTypes()
.Where(x => x.ClrType.BaseType == typeof(BaseEntity))
.Configure(et => et.SetTableName(et.DisplayName()));
where Configure is defined as an extension method:
public static class ModelBuilderExtensions
{
public static IEnumerable<IMutableEntityType> EntityTypes(this ModelBuilder builder)
{
return builder.Model.GetEntityTypes();
}
public static IEnumerable<IMutableProperty> Properties(this ModelBuilder builder)
{
return builder.EntityTypes().SelectMany(entityType => entityType.GetProperties());
}
public static IEnumerable<IMutableProperty> Properties<T>(this ModelBuilder builder)
{
return builder.EntityTypes().SelectMany(entityType => entityType.GetProperties().Where(x => x.ClrType == typeof(T)));
}
public static void Configure(this IEnumerable<IMutableEntityType> entityTypes, Action<IMutableEntityType> convention)
{
foreach (var entityType in entityTypes)
{
convention(entityType);
}
}
public static void Configure(this IEnumerable<IMutableProperty> propertyTypes, Action<IMutableProperty> convention)
{
foreach (var propertyType in propertyTypes)
{
convention(propertyType);
}
}
}
Upvotes: 1
Reputation: 8156
I'm porting some code from EF to EF Core 2.1+ and can't wait for EF Core 3.0 so wrote a few extension methods which help a bit..
public static IEnumerable<IMutableEntityType> EntityTypes(this ModelBuilder builder)
{
return builder.Model.GetEntityTypes();
}
public static IEnumerable<IMutableProperty> Properties(this ModelBuilder builder)
{
return builder.EntityTypes().SelectMany(entityType => entityType.GetProperties());
}
public static IEnumerable<IMutableProperty> Properties<T>(this ModelBuilder builder)
{
return builder.EntityTypes().SelectMany(entityType => entityType.GetProperties().Where(x => x.ClrType == typeof(T)));
}
public static void Configure(this IEnumerable<IMutableEntityType> entityTypes, Action<IMutableEntityType> convention)
{
foreach (var entityType in entityTypes)
{
convention(entityType);
}
}
public static void Configure(this IEnumerable<IMutableProperty> propertyTypes, Action<IMutableProperty> convention)
{
foreach (var propertyType in propertyTypes)
{
convention(propertyType);
}
}
with these you can write conventions similar to those in EF 6.1.x, for example.
// equivalent of modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.EntityTypes()
.Configure(et => et.Relational().TableName = et.DisplayName());
// Put the table name on the primary key
modelBuilder.Properties()
.Where(x => x.Name == "Id")
.Configure(p => p.Relational().ColumnName = p.DeclaringEntityType.Name + "Id");
// Mark timestamp columns as concurrency tokens
modelBuilder.Properties()
.Where(x => x.Name == "Timestamp")
.Configure(p => p.IsConcurrencyToken = true);
For EF Core 3.0 the metamodel methods have changed slightly so you need
modelBuilder.EntityTypes()
.Configure(et => et.SetTableName(et.DisplayName()));
modelBuilder.Properties()
.Where(x => x.Name == "Id")
.Configure(p => p.SetColumnName(BaseName(p.DeclaringEntityType.Name) + "Id"));
Haven't checked this for efficiency but unless your model is huge it shouldn't pose a problem
This can be extended with other helpers for foreign keys, indexes etc
Upvotes: 19
Reputation: 2540
Looks like it's still not in EF Core 2.0. So here's one way of achieving it. I had done this to apply consistent behaviour to certain attributes, but to address the examples in your question you could try this:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// equivalent of modelBuilder.Conventions.AddFromAssembly(Assembly.GetExecutingAssembly());
// look at this answer: https://stackoverflow.com/a/43075152/3419825
// for the other conventions, we do a metadata model loop
foreach (var entityType in modelBuilder.Model.GetEntityTypes())
{
// equivalent of modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
entityType.Relational().TableName = entityType.DisplayName();
// equivalent of modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
// and modelBuilder.Conventions.Remove<ManyToManyCascadeDeleteConvention>();
entityType.GetForeignKeys()
.Where(fk => !fk.IsOwnership && fk.DeleteBehavior == DeleteBehavior.Cascade)
.ToList()
.ForEach(fk => fk.DeleteBehavior = DeleteBehavior.Restrict);
}
base.OnModelCreating(modelBuilder);
}
You should be able to do a lot of things with entityType
Upvotes: 13