自動註冊實體類到EntityFramework Core上下文,並適配ABP及ABP VNext

吉祥興旺 發表於 2022-11-25

繼上篇文章(EF Core懶人小技巧之拒絕DbSet)之後,最近筆者把這個小功能單獨封裝成一個擴充套件方法並開源,歡迎交流和Star~

GitHub: EntityFrameworkCore.Extension.AutoMapping

Nuget:EntityFrameworkCore.Extension.AutoMapping

             EntityFrameworkCore.Extension.AutoMapping.Abp

             EntityFrameworkCore.Extension.AutoMapping.AbpVNext

如何使用

在DbContext.cs中重寫OnModelCreating方法:

using EntityFrameworkCore.Extension;
... //此處省略其它程式碼
public class XmateDbContext:DbContext
{
  ... //此處省略其它程式碼
  protected override void OnModelCreating(ModelBuilder modelBuilder)
  {
    var modelAssemblyName = "XMate.Models";//實體類所在類庫的名稱,不包含副檔名(.dll)
    modelBuilder.AutoMappingEntityTypes<IEntity>(modelAssemblyName);//泛型IEntity為所有實體類的規約型別
    base.OnModelCreating(modelBuilder);//這個必須加,否則報錯
    
    ...//此處省略其它程式碼
  }
}

這樣,我們就可以不用寫滿屏的DbSet了。
但是,在有的第三方框架中可能就會誕生新的問題。。。
比如在ABP或者VNext框架中,用過ABP框架的都應該知道,ABP是透過掃描DbContext中的DbSet來實現將實體類的倉儲自動註冊到IOC容器中的,下面我們就需要自己動手來實現:

    public static class AutoRegisterEntityRepositoryExtensions
    {
        /// <summary>
        /// 將資料表實體型別對應的倉儲注入到IOC容器
        /// </summary>
        /// <param name="iocManager"></param>
        public static void RegisterDbEntityRepositories<TDbContext>(this IIocManager iocManager, string modelAssemblyName) where TDbContext : DbContext
        {
            foreach (var entityType in GetDbEntityType(typeof(IEntity<>), modelAssemblyName))
            {
                var keyType = entityType.GetInterfaces().Where(t => t.IsGenericType && t.GetGenericTypeDefinition() == typeof(IEntity<>)).SelectMany(t => t.GetGenericArguments()).First();
                var genericRepositoryType = typeof(IRepository<,>).MakeGenericType(entityType, keyType);
                var impType = typeof(EfCoreRepositoryBase<,,>).MakeGenericType(typeof(TDbContext), entityType, keyType);
                iocManager.RegisterIfNot(genericRepositoryType, impType, lifeStyle: DependencyLifeStyle.Transient);
            }
        }

        /// <summary>
        /// 獲取資料表實體型別列表
        /// </summary>
        /// <param name="constraintType">實體定義約束型別</param>
        /// <param name="modelAssemblyName">實體類所在dll名稱,不包含字尾名(.dll)</param>
        /// <returns></returns>
        private static List<Type> GetDbEntityType(Type constraintType, string modelAssemblyName)
        {
            var all = AppDomain.CurrentDomain.GetAssemblies();
            var types = all.WhereIf(!modelAssemblyName.IsNullOrWhiteSpace(), a => a.FullName.Contains(modelAssemblyName))
                .SelectMany(m => m.GetTypes().Where(t => t.IsClass && !t.IsAbstract && (t.IsImplement(constraintType) || t.IsSubclass(constraintType))).ToList())
                .Distinct()
                .ToList();
            return types.Where(t => !t.GetCustomAttributes<NotMappedAttribute>().Any()).ToList();
        }
    }

注:以上程式碼摘自:AutoRegisterEntityRepositoryExtensions.cs

在ABP VNext中的實現思路也是如此,這裡就不貼程式碼了,感興趣的可以查閱原始碼

在Abp中實現自動注入實體類對應的Repository

using EntityFrameworkCore.Extension.AutoMapping.Abp;
... //此處省略其它程式碼
public class XmateModule:AbpModule
{
  ... //此處省略其它程式碼
  //重寫Initialize方法
  public override void Initialize()
  {
      ... //此處省略其它程式碼
      var modelAssemblyName = "XMate.Models";//實體類所在類庫的名稱,不包含副檔名(.dll)
      IocManager.RegisterDbEntityRepositories(modelAssemblyName);
  }
}

在Abp VNext中實現自動注入實體類對應的Repository

using EntityFrameworkCore.Extension.AutoMapping.AbpVNext;
... //此處省略其它程式碼
public class XmateModule:AbpModule
{
  ... //此處省略其它程式碼
  //重寫ConfigureServices方法
  public override void ConfigureServices(ServiceConfigurationContext context)
  {
      ... //此處省略其它程式碼
      var modelAssemblyName = "XMate.Models";//實體類所在類庫的名稱,不包含副檔名(.dll)
      context.Services.RegisterDbEntityRepositories(modelAssemblyName);
  }
}