在 foreach 循环中应用实体配置

Applying entity configurations in a foreach loop

我想知道是否有人知道如何动态应用实体配置。我不想重复让我们说 builder.ApplyConfiguration(new PersonConfiguration());对于我 have/might 将来拥有的每个实体。

我正在尝试使用以下代码,为了简单起见,我将所有内容都放在一个文件中:

namespace WebApplication1.Data
{
    public class ApplicationDbContext : IdentityDbContext
    {
        public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
            : base(options)
        {
        }

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

            var entityTypes = AppDomain.CurrentDomain.GetAssemblies()
                .SelectMany(p => p.GetTypes())
                .Where(p => typeof(IEntity).IsAssignableFrom(p) && p != typeof(IEntity));

            foreach (var entityType in entityTypes)
            {
                // this doesn't work
                //builder.ApplyConfiguration(new BaseEntityConfiguration<entityType>());
            }

            // this works, but I don't want to basically repeat it for each entity individually
            //builder.ApplyConfiguration(new BaseEntityConfiguration<Person>());
            //builder.ApplyConfiguration(new PersonConfiguration());
        }

        public virtual DbSet<Person> People { get; set; }
    }

    public interface IEntity
    {
        int Id { get; set; }
        string RowModifyUser { get; set; }
        DateTime RowModifyDate { get; set; }
        byte[] RowVersion { get; set; }
    }

    public class Person : IEntity
    {
        public int Id { get; set; }
        public string RowModifyUser { get; set; }
        public DateTime RowModifyDate { get; set; }
        public byte[] RowVersion { get; set; }

        public string FirstName { get; set; }
        public string LastName { get; set; }
    }

    public class BaseEntityConfiguration<TEntity> : IEntityTypeConfiguration<TEntity> where TEntity : class, IEntity
    {
        public void Configure(EntityTypeBuilder<TEntity> builder)
        {
            builder.HasKey(m => m.Id);
            builder.Property(m => m.RowModifyDate).IsRequired();
            builder.Property(m => m.RowModifyUser).IsRequired();
            builder.Property(m => m.RowVersion).IsRequired().IsConcurrencyToken();
        }
    }

    public class PersonConfiguration : IEntityTypeConfiguration<Person>
    {
        public void Configure(EntityTypeBuilder<Person> builder)
        {
            builder.Property(m => m.FirstName).IsRequired();
            builder.Property(m => m.LastName).IsRequired();
        }
    }
}

未经测试,但您可以尝试以下操作:

foreach (Type entityType in entityTypes)
{
     Type openConfigType = typeof(BaseEntityConfiguration<>);
     Type genericConfigType = openConfigType.MakeGenericType(entityType);
     builder.ApplyConfiguration(Activator.CreateInstance(genericConfigType));           
}

除了按照另一个答案中的建议创建 generic BaseEntityConfiguration<TEntity> class 之外,您还需要调用 generic ModelBuilder.ApplyConfiguration<TEntity>(IEntityTypeConfiguration<TEntity> configuration) 通过反射的方法。

类似这样的东西(需要 using System.Reflection;):

// Can be moved to a static readonly field of the class
var applyConfigurationMethodDefinition = typeof(ModelBuilder)
    .GetTypeInfo()
    .DeclaredMethods
    .Single(m => m.Name == "ApplyConfiguration" &&
        m.IsGenericMethodDefinition &&
        m.GetParameters().Length == 1 &&
        m.GetParameters()[0].ParameterType.IsGenericType &&
        m.GetParameters()[0].ParameterType.GetGenericTypeDefinition() == typeof(IEntityTypeConfiguration<>));


foreach (var entityType in entityTypes)
{
    var configurationType = typeof(BaseEntityConfiguration<>).MakeGenericType(entityType);
    var configuration = Activator.CreateIntance(configurationType);

    var applyConfigurationMethod = applyConfigurationMethodDefinition.MakeGenericMethod(entityType);
    applyConfigurationMethod.Invoke(builder, new object[] { configuration });
}

请注意,在 EF Core 2.1 ModelBuilder class 中有 2 个 ApplyConfiguration 方法重载,它们仅在参数类型上有所不同,这就是查找方法包括所有检查的原因。