zoukankan      html  css  js  c++  java
  • .net core2.x 自动注入 Entity(实体对象到上下文)

    概要:有点老套,因为早在 .net frmework的时候(core还没出来),我们在使用 ef(4.。。。6)的时候就已经这么用,这里我在搭建框架,所以随手写下,让后来人直接拿去用用。

    1.使用前提

      使用前我们一定要明白的是,通过fluent api去映射实体关系和属性的,也就是说core里面,要实现IEntityTypeConfiguration<TEntity>接口对象,示例如下:

    public class UserRoleConfiguration : IEntityTypeConfiguration<UserRole>
        {
            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中如下方式注入:

    public class DbContextBase : DbContext, IDbContext
        {
            private readonly IEntityConfigurationFinder _configurationFinder;
            public DbContextBase(DbContextOptions options, IEntityConfigurationFinder configurationFinder)
                : base(options)
            {
                _configurationFinder = configurationFinder;
            }
    
            protected override void OnModelCreating(ModelBuilder modelBuilder)
            {
                Type contextType = GetType();
                IEntityRegister[] entityConfigures = _configurationFinder.GetEntityRegisters();
                foreach (var config in entityConfigures)
                {
                    config.Apply(modelBuilder);
                }
            }
        }
    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中不变。

    结束!

  • 相关阅读:
    sqlserver中判断表或临时表是否存在
    Delphi 简单方法搜索定位TreeView项
    hdu 2010 水仙花数
    hdu 1061 Rightmost Digit
    hdu 2041 超级楼梯
    hdu 2012 素数判定
    hdu 1425 sort
    hdu 1071 The area
    hdu 1005 Number Sequence
    hdu 1021 Fibonacci Again
  • 原文地址:https://www.cnblogs.com/Tmc-Blog/p/9912152.html
Copyright © 2011-2022 走看看