Массовый регистр IEntityTypeConfiguration <> Сущность Framework core
Хорошо, поэтому я использую инфраструктуру сущностей с ядром и чистым ядром и первым переносом кода. Это не проблема как таковая, я просто задавался вопросом, нашел ли кто-нибудь лучший способ сделать это.
В настоящее время у меня много конфигураций типа объектов, таких как
public class ExampleEntityConfiguration : IEntityTypeConfiguration<ExampleEntity>
{
public void Configure(EntityTypeBuilder<ExampleEntity> builder)
{
builder.Property(p => p.Id).ValueGeneratedNever();
// more options here
}
}
и я зарегистрирую их в своем dbcontext так
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.ApplyConfiguration(new ExampleEntityConfiguration());
// lot more configurations here
}
Кто-нибудь сталкивался или знал способ зарегистрировать все интерфейсы IEntityTypeConfiguration
?
Это просто похоже на много повторяющийся код, который можно решить, получив список конфигураций, перейдя через них и применив их в контексте. Я просто не знаю, с чего начать с получения списка классов IEntityTypeConfiguration
, которые существуют в определенном пространстве имен.
Любая помощь/предложения будут замечательными.
Ответы
Ответ 1
Это можно сделать с отражением следующим образом:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
// get ApplyConfiguration method with reflection
var applyGenericMethod = typeof(ModelBuilder).GetMethod("ApplyConfiguration", BindingFlags.Instance | BindingFlags.Public);
// replace GetExecutingAssembly with assembly where your configurations are if necessary
foreach (var type in Assembly.GetExecutingAssembly().GetTypes()
.Where(c => c.IsClass && !c.IsAbstract && !c.ContainsGenericParameters))
{
// use type.Namespace to filter by namespace if necessary
foreach (var iface in type.GetInterfaces()) {
// if type implements interface IEntityTypeConfiguration<SomeEntity>
if (iface.IsConstructedGenericType && iface.GetGenericTypeDefinition() == typeof(IEntityTypeConfiguration<>)) {
// make concrete ApplyConfiguration<SomeEntity> method
var applyConcreteMethod = applyGenericMethod.MakeGenericMethod(iface.GenericTypeArguments[0]);
// and invoke that with fresh instance of your configuration type
applyConcreteMethod.Invoke(modelBuilder, new object[] {Activator.CreateInstance(type)});
break;
}
}
}
}
Ответ 2
Хорошая работа из @Evk kan будет дополнительно инкапсулирована в метод многократного использования:
public static class ModelBuilderExtensions
{
public static void ApplyAllConfigurationsFromCurrentAssembly(this ModelBuilder modelBuilder, Assembly assembly, string configNamespace = "")
{
var applyGenericMethods = typeof(ModelBuilder).GetMethods( BindingFlags.Instance | BindingFlags.Public | BindingFlags.FlattenHierarchy);
var applyGenericApplyConfigurationMethods = applyGenericMethods.Where(m => m.IsGenericMethod && m.Name.Equals("ApplyConfiguration", StringComparison.OrdinalIgnoreCase));
var applyGenericMethod = applyGenericApplyConfigurationMethods.Where(m=>m.GetParameters().FirstOrDefault().ParameterType.Name== "IEntityTypeConfiguration'1").FirstOrDefault();
var applicableTypes = assembly
.GetTypes()
.Where(c => c.IsClass && !c.IsAbstract && !c.ContainsGenericParameters);
if (!string.IsNullOrEmpty(configNamespace))
{
applicableTypes = applicableTypes.Where(c => c.Namespace == configNamespace);
}
foreach (var type in applicableTypes)
{
foreach (var iface in type.GetInterfaces())
{
// if type implements interface IEntityTypeConfiguration<SomeEntity>
if (iface.IsConstructedGenericType && iface.GetGenericTypeDefinition() == typeof(IEntityTypeConfiguration<>))
{
// make concrete ApplyConfiguration<SomeEntity> method
var applyConcreteMethod = applyGenericMethod.MakeGenericMethod(iface.GenericTypeArguments[0]);
// and invoke that with fresh instance of your configuration type
applyConcreteMethod.Invoke(modelBuilder, new object[] { Activator.CreateInstance(type) });
Console.WriteLine("applied model " + type.Name);
break;
}
}
}
}
}
И можно назвать таковым:
public class ApiDbContext : DbContext
{
public DbSet<Customer> Customers { get; set; }
public ApiDbContext(DbContextOptions<ApiDbContext> options) : base(options) { }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.ApplyAllConfigurationsFromCurrentAssembly("MyRoot.Api.Entities.Configuration");
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
base.OnConfiguring(optionsBuilder);
}
}
Ответ 3
Ответ от @Evk немного устарел и больше не работает, ответ от @paul van bladel - рабочий, хотя он содержит жестко закодированные строки, которые я очень ненавидел. Если кто-то разделяет мои чувства, я хотел бы предложить свое решение в форме создания статического метода расширения:
public static ModelBuilder ApplyAllConfigurationsFromAssembly(
this ModelBuilder modelBuilder, Assembly assembly)
{
var applyGenericMethod = typeof(ModelBuilder)
.GetMethods(BindingFlags.Instance | BindingFlags.Public)
.Single(m => m.Name == nameof(ModelBuilder.ApplyConfiguration)
&& m.GetParameters().Count() == 1
&& m.GetParameters().Single().ParameterType.GetGenericTypeDefinition() == typeof(IEntityTypeConfiguration<>));
foreach (var type in assembly.GetTypes()
.Where(c => c.IsClass && !c.IsAbstract && !c.ContainsGenericParameters))
{
foreach (var iface in type.GetInterfaces())
{
if (iface.IsConstructedGenericType && iface.GetGenericTypeDefinition() == typeof(IEntityTypeConfiguration<>))
{
var applyConcreteMethod = applyGenericMethod.MakeGenericMethod(iface.GenericTypeArguments[0]);
applyConcreteMethod.Invoke(modelBuilder, new object[] {Activator.CreateInstance(type)});
break;
}
}
}
}
И, если ваши классы конфигурации хранятся в той же сборке, что и ваш DbContext, вы используете этот метод следующим образом:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ApplyAllConfigurationsFromAssembly(GetType().Assembly);
base.OnModelCreating(modelBuilder);
}
Ответ 4
Используя EF Core 2. 2+, это стало намного проще:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
Assembly assemblyWithConfigurations = GetType().Assembly; //get whatever assembly you want
modelBuilder.ApplyConfigurationsFromAssembly(assemblyWithConfigurations);
}
Ответ 5
Мне очень нравится ответ @paul van bladel, предназначенный для перемещения кода в метод расширения. Мне также нужно вызвать это из других сборок, поэтому я создал перечисление и изменил применимые типы, которые будут установлены по-разному.
IEnumerable<Type> assemblyTypeList;
switch (pAssemblyMethodType)
{
case AssemblyMethodType.CallingAssembly:
assemblyTypeList = Assembly.GetCallingAssembly()
.GetTypes()
.Where(c => c.IsClass
&& !c.IsAbstract
&& !c.ContainsGenericParameters);
break;
case AssemblyMethodType.ExecutingAssembly:
assemblyTypeList = Assembly.GetExecutingAssembly()
.GetTypes()
.Where(c => c.IsClass
&& !c.IsAbstract
&& !c.ContainsGenericParameters);
break;
default:
throw new ArgumentOutOfRangeException(nameof(pAssemblyMethodType), pAssemblyMethodType, null);
}