首页 > 解决方案 > EF 中的通用实体映射

问题描述

我想制作一个通用实体映射器,将所有实体映射到 dbContext 和 EF。目前,在我这样做之后Add-Migration init,我收到一个错误:

Cannot create an instance of Models.DataMapping.EntityMapper`1[TEntity] because Type.ContainsGenericParameters is true.

我不明白这个错误的确切含义以及为什么如果 typeContainsGenericParameters为 true 它会导致崩溃。有什么想法有什么问题,我该如何解决?
这是我的代码:

namespace Models.DataMapping
{
    public interface IEntityMapper
    {
        void Map(ModelBuilder modelBuilder);
    }
}

namespace Models.DataMapping
{
    public abstract class EntityMapper<TEntity> : 
        IEntityMapper where TEntity : class
    {
        public void Map(ModelBuilder modelBuilder)
        {
            EntityTypeBuilder<TEntity> entityTypeBuilder = modelBuilder.Entity<TEntity>();

            MapEntity(entityTypeBuilder);
        }

        protected virtual void MapEntity(EntityTypeBuilder<TEntity> entityTypeBuilder) 
        {    
        }
    }
}
namespace Models.DataMapping
{
    public static class EntityMappersProvider
    {
        public static IReadOnlyCollection<IEntityMapper> GetLocalEntityMappers() {
            return Assembly.GetExecutingAssembly()
                .GetTypes()
                .Where(t => typeof(IEntityMapper).IsAssignableFrom(t) && t.IsClass)
                .Select(t => (IEntityMapper)Activator.CreateInstance(t))
                .ToArray();
        }
    }
}

在 dbContext 中包含提供程序。

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

            IReadOnlyCollection<IEntityMapper> entityMappers = EntityMappersProvider.GetLocalEntityMappers();

            foreach (IEntityMapper mapper in entityMappers) {
                mapper.Map(modelBuilder);
            }
        }

实体关系实现示例。

namespace Models.DataMapping.EntitiesMapping
{
    public class CourseMapper : EntityMapper<Course>
    {
        protected override void MapEntity(EntityTypeBuilder<Course> entityTypeBuilder)
        {
            entityTypeBuilder.ToTable("Courses");
            entityTypeBuilder.HasKey(a => a.Id);
            //....
        }
    }
}

另外我注意到,如果我删除abstract关键字EntityMapper并添加空迁移,则会创建t.IsAbstract..EntityMappersProvided

标签: c#asp.net-core.net-core.net-assembly

解决方案


对我来说,您似乎正在尝试重新发明IEntityTypeConfiguration它允许将实体配置代码移动到单独的类:

public class BlogEntityTypeConfiguration : IEntityTypeConfiguration<Blog>
{
    public void Configure(EntityTypeBuilder<Blog> builder)
    {
        builder
            .Property(b => b.Url)
            .IsRequired();
    }
}

并提供了在程序集中查找所有此类配置的便捷方法,该方法从EF Core 2.2开始可用:

modelBuilder.ApplyConfigurationsFromAssembly(typeof(BlogEntityTypeConfiguration).Assembly);

至于你的错误 - 它发生是因为你的代码试图实例化一个开放的泛型类型(EntityMapper<TEntity>Activator.CreateInstance

public class OpenGeneric<T>{}
Activator.CreateInstance(typeof(OpenGeneric<>)); // throws Cannot create an instance of Generic`1[T] because Type.ContainsGenericParameters is true.

因此,您需要将其过滤掉,例如通过添加&& !t.IsAbstract && ! t.ContainsGenericParameters您的Where子句。


推荐阅读