EF Core
does not have such a method, however you can extend ModelBuilder
to gain this functionality.
At one time, I needed to configure the properties of a particular interface. But as you might guess, builder.Entity<T>
does not accept Interface
as the first argument.
Then I implemented the following method in a static class
:
public static void Entities<T>(this ModelBuilder builder, DbContext instance, string methodName)
{
var method = instance.GetType().GetMethod(methodName, BindingFlags.Instance | BindingFlags.NonPublic);
var typesStatus = builder.Model.GetEntityTypes().Where(type => typeof(T).IsAssignableFrom(type.ClrType));
foreach (var type in typesStatus)
{
var builderType = typeof(EntityTypeBuilder<>).MakeGenericType(type.ClrType);
var buildMethod = method.MakeGenericMethod(type.ClrType);
var buildAction = typeof(Action<>).MakeGenericType(builderType);
var buildDelegate = Delegate.CreateDelegate(buildAction, instance, buildMethod);
var buildEntity = typeof(ModelBuilder).GetMethods()
.Single(m => m.Name == "Entity" && m.GetGenericArguments().Any() && m.GetParameters().Any())
.MakeGenericMethod(type.ClrType);
buildEntity.Invoke(builder, new[] { buildDelegate });
}
}
The call to this method was something like.:
protected override void OnModelCreating(ModelBuilder builder)
{
builder.Entities<IInterfaceA>(this, nameof(this.ModelInterfaceA));
builder.Entities<IInterfaceB>(this, nameof(this.ModelInterfaceB));
}
private void ModelInterfaceA<T>(EntityTypeBuilder<T> entity) where T : class, IInterfaceA
{
entity.HasQueryFilter(x => x.Ativo);
entity.Property(x => x.Data).HasColumnType("datetime2(2)");
}
private void ModelInterfaceB<T>(EntityTypeBuilder<T> entity) where T : class, IInterfaceB
{
entity.Property(x => x.Property).HasColumnType("...");
}
Modifying the extension to work with properties.
For your specific case, I will make an adaptation of the above mentioned method:
public static void Properties<T>(this ModelBuilder builder, Action<PropertyBuilder<T>> callback)
{
var types = builder.Model.GetEntityTypes();
foreach (var type in types)
{
var entityBuilderType = typeof(EntityTypeBuilder<>).MakeGenericType(type.ClrType);
var buildEntity = typeof(ModelBuilder).GetMethods()
.Single(m => m.Name == "Entity" && m.GetGenericArguments().Any() && !m.GetParameters().Any())
.MakeGenericMethod(type.ClrType);
var buildProperty = entityBuilderType.GetMethods()
.Single(m => m.Name == "Property" && m.GetGenericArguments().Any() && m.GetParameters().Any(p => p.ParameterType == typeof(string)))
.MakeGenericMethod(type.ClrType);
var entityTypeBuilder = buildEntity.Invoke(builder, null);
var properties = type.GetProperties().Where(x => x.GetType().Equals(typeof(T)));
foreach (var property in properties)
{
var propertyBuilder = buildProperty.Invoke(entityTypeBuilder, new[] { property.Name }) as PropertyBuilder<T>;
callback(propertyBuilder);
}
}
}
Using it is even simpler.:
protected override void OnModelCreating(ModelBuilder builder)
{
builder.Properties<string>(config => {
config.HasColumnType("varchar(50)");
})
}
The above method has not been tested, in any case, you can use the original algorithm to hone or correct the suggested fix.