批量注册IEntityTypeConfiguration <>实体框架核心

tja*_*ams 17 c# entity-framework-core

好的,所以我使用点网络核心的实体框架和代码首次迁移.这不是问题,我只是想知道是否有人遇到过更好的方法.

目前我有很多实体类型配置,如此

public class ExampleEntityConfiguration : IEntityTypeConfiguration<ExampleEntity>
{
   public void Configure(EntityTypeBuilder<ExampleEntity> builder)
   {
      builder.Property(p => p.Id).ValueGeneratedNever();

      // more options here
   }
}
Run Code Online (Sandbox Code Playgroud)

我在我的dbcontext中注册它们就像这样

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

  modelBuilder.ApplyConfiguration(new ExampleEntityConfiguration());

  // lot's more configurations here
}
Run Code Online (Sandbox Code Playgroud)

有没有人遇到或知道注册所有IEntityTypeConfiguration接口的方法?

看起来很多重复的代码可以通过获取配置列表,循环它们并在上下文中应用来解决.我只是不知道从哪里开始获取IEntityTypeConfiguration特定命名空间中存在的类列表.

任何帮助/建议都会很棒.

Evk*_*Evk 18

它可以用这样的反射来完成:

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;
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 他们为此添加了重载,因此有两种方法匹配名称.我修改了第一行来完成这项工作:`var applyGenericMethod = typeof(ModelBuilder).GetMethods().其中​​(m => m.Name =="ApplyConfiguration"&& m.GetParameters().First().ParameterType.GetGenericTypeDefinition ()== typeof(IEntityTypeConfiguration <>).GetGenericTypeDefinition()).FirstOrDefault();` (3认同)

Jan*_*las 16

使用EF Core 2.2+,它变得更加简单:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
   Assembly assemblyWithConfigurations = GetType().Assembly; //get whatever assembly you want
   modelBuilder.ApplyConfigurationsFromAssembly(assemblyWithConfigurations);
}
Run Code Online (Sandbox Code Playgroud)

  • 这绝对是最简单,最好的解决方案。_但是,如果您有多个DbContext,则不会解决这种情况。在那种情况下,最好能够指定哪个名称空间,如@ paul-van-bladel的解决方案中所示 (2认同)
  • @JulienAmbos 尚未测试性能。然而,对我来说,它有一个很大的优点,即开发人员在向数据模型添加新实体时,不会忘记在“OnModelCreating”中注册新的“IEntityConfiguration”。 (2认同)

pau*_*del 12

来自@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;
                }
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

并可以这样称呼:

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);
    }
}
Run Code Online (Sandbox Code Playgroud)


mr1*_*100 6

来自@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;
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

并且,如果您的配置类与DbContext存储在同一个程序集中,则使用此方法,如下所示:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.ApplyAllConfigurationsFromAssembly(GetType().Assembly);           
    base.OnModelCreating(modelBuilder);
}
Run Code Online (Sandbox Code Playgroud)


Lak*_*mal 6

-- 数据库上下文

protected override void OnModelCreating(ModelBuilder builder)
{
    builder.ApplyConfigurationsFromAssembly(Assembly.GetExecutingAssembly());

    base.OnModelCreating(builder);
}
Run Code Online (Sandbox Code Playgroud)