• .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中不变。

    结束!

  • 相关阅读:
    第十三章 类继承
    第十一章 使用类
    第十章 对象和类
    第九章 内存模型和名称空间
    第八章 函数幽探
    史上最详细得虚拟机安装过程,傻瓜式操作
    JVM内存结构图表展示
    主流消息队列rocketMq,rabbitMq比对使用
    springboot+druid+mybatis plus的多数据源配置
    linux服务器开放防火墙和端口,以及查询状态
  • 原文地址:https://www.cnblogs.com/Tmc-Blog/p/9912152.html
Copyright © 2020-2023  润新知