.net core2.1 自動注入 Entity(實體物件到上下文)
概要:有點老套,因為早在 .net frmework的時候(core還沒出來),我們在使用 ef(4.。。。6)的時候就已經這麼用,這裡我在搭建框架,所以隨手寫下,讓後來人直接拿去用用。
1.使用前提
使用前我們一定要明白的是,通過fluent api去對映實體關係和屬性的,也就是說core裡面,要實現IEntityTypeConfiguration<TEntity>介面物件,示例如下:
public class UserRoleConfiguration : IEntityTypeConfiguration<UserRole> {View Codepublic override void Configure(EntityTypeBuilder<UserRole> builder) { builder.HasMany(x => x.UserRolePermissionCollection).WithOne(x => x.UserRole).HasForeignKey(x => x.UserRoleID).IsRequired(); builder.HasDataRole(); } }
這時候我們可以在 DBContext的 onModelCreating中如下方式注入:
protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.ApplyConfiguration(new RoleEntityTypeConfiguration()); //.....其他實體對映注入。。。 }View Code
這是其中一個實體的對映方式,假設我們有十幾或幾十個,那麼我們需要在這些十幾或者幾十遍,累得慌吧,累就對了,所以換個方式實現:
我們在定義一個IEntityRegister物件,所有的 所有實體對映類都需要實現這個介面物件,介面如下:
public interface IEntityRegister { void Apply(ModelBuilder builder); }View Code
同時修改上面的 roleEntityTypeConfiguration
public class UserRoleConfiguration : IEntityTypeConfiguration<UserRole>,IEntityRegister { public override void Configure(EntityTypeBuilder<UserRole> builder) { builder.HasMany(x => x.UserRolePermissionCollection).WithOne(x => x.UserRole).HasForeignKey(x => x.UserRoleID).IsRequired(); builder.HasDataRole(); } public void Apply(ModelBuilder modelBuilder){ modelBuilder.ApplyConfiguration(this); } }View Code
這時候我們其他的幾十個 實體的配置物件,依舊按照如上寫法即可,現在我們要做的就是找到所有實現了IEntityRegister介面的物件,也就是實體的對映物件。
2.查詢實體配置物件
之前我們在上一篇說 dependencyInjection物件的時候,有寫過一個類,其中查詢程式及物件的方法,這裡我們就又用到了,再貼一次完整的:
介面實現:
/// <summary> /// 查詢應用程式中的程式集物件 /// </summary> public interface IAppAssemblyFinder { /// <summary> /// 查詢所有程式集物件 /// </summary> /// <param name="filterAssembly">是否排除非業務程式集物件</param> /// <returns></returns> Assembly[] FindAllAssembly(bool filterAssembly = true); /// <summary> /// 獲取指定型別的物件集合 /// </summary> /// <typeparam name="ItemType">指定的型別</typeparam> /// <param name="expression"> /// 過濾表示式: /// 查詢介面(type=>typeof(ItemType).IsAssignableFrom(type)); /// 查詢實體:type => type.IsDeriveClassFrom<ItemType>() /// </param> /// <param name="fromCache">是否從快取查詢</param> /// <returns></returns> Type[] FindTypes<ItemType>(Func<Type, bool> expression, bool fromCache = true) where ItemType : class; }View Code
對應實現類:
public class AppAssemblyFinder : IAppAssemblyFinder { private List<Assembly> _assemblies = new List<Assembly>(); public Assembly[] FindAllAssembly(bool filterAssembly = true) { var filter = new string[]{ "System", "Microsoft", "netstandard", "dotnet", "Window", "mscorlib", "Newtonsoft", "Remotion.Linq" }; //core中獲取依賴物件的方式 DependencyContext context = DependencyContext.Default; if (context != null) { List<string> names = new List<string>(); string[] dllNames = context.CompileLibraries.SelectMany(m => m.Assemblies).Distinct().Select(m => m.Replace(".dll", "")).ToArray(); if (dllNames.Length > 0) { names = (from name in dllNames let index = name.LastIndexOf('/') + 1 select name.Substring(index)) .Distinct() .WhereIf(name => !filter.Any(name.StartsWith), filterAssembly) .ToList(); } return LoadFromFiles(names); } //傳統方式 string pathBase = AppDomain.CurrentDomain.BaseDirectory; string[] files = Directory.GetFiles(pathBase, "*.dll", SearchOption.TopDirectoryOnly) .Concat(Directory.GetFiles(pathBase, ".exe", SearchOption.TopDirectoryOnly)) .ToArray(); if (filterAssembly) { files = files.WhereIf(f => !filter.Any(n => f.StartsWith(n, StringComparison.OrdinalIgnoreCase)), filterAssembly).Distinct().ToArray(); } _assemblies = files.Select(Assembly.LoadFrom).ToList(); return _assemblies.ToArray(); } /// <summary> /// 獲取指定型別的物件集合 /// </summary> /// <typeparam name="ItemType">指定的型別</typeparam> /// <param name="expression"> 過濾表示式: 查詢介面(type=>typeof(ItemType).IsAssignableFrom(type)); 查詢實體:type => type.IsDeriveClassFrom<ItemType>()</param> /// <param name="fromCache">是否從快取查詢</param> /// <returns></returns> public Type[] FindTypes<ItemType>(Func<Type, bool> expression, bool fromCache = true) where ItemType : class { List<Assembly> assemblies; if (fromCache) assemblies = _assemblies; if (_assemblies == null || _assemblies.Count() == 0) assemblies = this.FindAllAssembly().ToList(); Type[] types = _assemblies.SelectMany(a => a.GetTypes()) .Where(expression).Distinct().ToArray(); return types; } /// <summary> /// 從檔案載入程式集物件 /// </summary> /// <param name="files">檔案(名稱集合)</param> /// <returns></returns> private static Assembly[] LoadFromFiles(List<string> files) { List<Assembly> assemblies = new List<Assembly>(); files?.ToList().ForEach(f => { AssemblyName name = new AssemblyName(f); try { Assembly assembly = Assembly.Load(name); assemblies.Add(assembly); } catch { } }); return assemblies.ToArray(); } }View Code
需要注意的是,這個介面以及實現類,需要註冊為 singleton物件,保證生命週期和應用程式一致,否則,引數的fromCache無效,效能也會急劇下降。
查詢IEntityRegister物件:
public class EntityConfigFinder : IEntityConfigFinder { public EntityConfigFinder(IAppAssemblyFinder assemblyFinder) { _assemblyFinder = assemblyFinder; } private readonly IAppAssemblyFinder _assemblyFinder; public IEntityRegister[] EntityRegisters() { var baseType = typeof(IEntityRegister); var types = _assemblyFinder.FindTypes<IEntityRegister>(type => baseType.IsAssignableFrom(type)); var entityRegisters = types.Select(t => (IEntityRegister)Activator.CreateInstance(t))?.ToArray(); return entityRegisters; } }View Code
這時候我們就可以很簡單的使用了:
3.使用
public class DbContextBase : DbContext, IDbContext { public DbContextBase(DbContextOptions options, IEntityConfigFinder entityFinder) : base(options) { _entityConfigFinder = entityFinder; } private readonly IEntityConfigFinder _entityConfigFinder; protected override void OnModelCreating(ModelBuilder modelBuilder) { var dbContextType = GetType(); IEntityRegister[] entityRegisters = _entityConfigFinder.EntityRegisters(); foreach (var entityConfig in entityRegisters) { entityConfig.RegistTo(modelBuilder); Console.WriteLine($"成功註冊實體:{entityConfig.EntityType}"); } Console.WriteLine($"成功註冊實體:{entityRegisters.Length}個"); } } }View Code
4.其他
在 ef(6.x)中我們使用EntityTypeConfiguration的時候,可以直接使用該物件,但是core中沒有了,所以我們可以再封裝一個實現類:
public abstract class EntityTypeConfigurationBase<TEntity, TKey> : IEntityTypeConfiguration<TEntity>, IEntityRegister where TEntity : class, IEntity<TKey> { /// <summary> /// 將當前實體類對映物件註冊到資料上下文模型構建器中 /// </summary> /// <param name="modelBuilder">上下文模型構建器</param> public void Apply(ModelBuilder modelBuilder) { modelBuilder.ApplyConfiguration(this); } /// <summary> /// 重寫以實現實體型別各個屬性的資料庫配置 /// </summary> /// <param name="builder">實體型別建立器</param> public abstract void Configure(EntityTypeBuilder<TEntity> builder); }View Code
這時候,我們的實體的配置類只需要繼承該類,並實現其方法就可以了,比如:
public class UserRoleConfiguration : EntityTypeConfigurationBase<UserRole, Guid> { public override void Configure(EntityTypeBuilder<UserRole> builder) { builder.HasMany(x => x.UserRolePermissionCollection).WithOne(x => x.UserRole).HasForeignKey(x => x.UserRoleID).IsRequired(); builder.HasDataRole(); } }View Code
DbContext的 OnModelCreating中不變。
結束!