• Ef Core花里胡哨系列(10) 动态起来的 DbContext


    Ef Core花里胡哨系列(10) 动态起来的 DbContext

    我们知道,DbContext有两种托管方式,一种是AddDbContextAddDbContextFactory,但是呢他们各有优劣,例如工厂模式下性能更好呀等等。那么,我们能否自己托管DbContext呢?

    Github Demo:动态起来的 DbContext

    场景:
    结合我们之前的文章 [Ef Core花里胡哨系列(5) 动态修改追踪的实体、动态查询] 假设一个应用内有很多的子应用,且都需要更新追踪的动态实体,那么很多表在重置OnModelCreating的时候将会非常的慢。主要体现在modelBuilder.Model.AddEntityType(type),每个实体都需要花费一小段时间,几百个实体就会按分钟计算了,而且还会数据库操作产生一定的影响。

    我们先实现一个基础的DbContext用来添加一些通用的实体以及处理动态实体的逻辑,每次需要重置DbContext的时候,都会获取最新的动态实体进行更新:

    public class DbContextBase : DbContext
    {
        public DbSet Users { get; set; } = null!;
        public DbSet Departments { get; set; } = null!;
    
        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder.UseSqlite("Data Source=sample.db");
            optionsBuilder.ReplaceService();
    
            base.OnConfiguring(optionsBuilder);
        }
    
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            var name = GetType().Name.Split("_");
            if (name.Length > 1)
            {
                foreach (var item in FormTypeBuilder.GetAppTypes(name[0]).Where(item => modelBuilder.Model.FindEntityType(item.Value) is null))
                {
                    modelBuilder.Model.AddEntityType(item.Value);
                }
            }
    
            base.OnModelCreating(modelBuilder);
        }
    }
    

    然后实现一个动态DbContext的生成器,用于针对不同的AppId生成不同的DbContext

    public class DbContextGenerator
    {
        private readonly ConcurrentDictionary<string, Type> _contextTypes = new()
        {
        };
    
        public Type GetOrCreate(string appId)
        {
            if (!_contextTypes.TryGetValue(appId, out var value))
            {
                value = GeneratorDbContext(appId);
                _contextTypes.TryAdd(appId, value);
            }
    
            return value;
        }
    
        public Type GeneratorDbContext(string appId)
        {
            var assemblyName = new AssemblyName("__RuntimeDynamicDbContexts");
            var assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
            var moduleBuilder = assemblyBuilder.DefineDynamicModule("__RuntimeDynamicModule");
            var typeBuilder = moduleBuilder.DefineType($"{appId.ToLower()}_DbContext", TypeAttributes.Public | TypeAttributes.Class, typeof(DbContextBase));
            var constructorBuilder = typeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, new Type[] { });
            var ilGenerator = constructorBuilder.GetILGenerator();
            ilGenerator.Emit(OpCodes.Ldarg_0);
            ilGenerator.Emit(OpCodes.Call, typeof(DbContextBase).GetConstructor(Type.EmptyTypes));
            ilGenerator.Emit(OpCodes.Ret);
            typeBuilder.CreateType();
            var dbContextType = assemblyBuilder.GetType($"{appId.ToLower()}_DbContext");
            return dbContextType;
        }
    }
    

    然后我们需要实现一个DbContext的容器用于管理我们生成的DbContext,以及负责初始化:

    public class DbContextContainer : IDisposable
    {
        private readonly DbContextGenerator _generator;
        private readonly Dictionary<string, DbContext> _contexts = new();
    
        public DbContextContainer(DbContextGenerator generator)
        {
            _generator = generator;
        }
    
        public DbContext Get(string appId)
        {
            if (!_contexts.TryGetValue(appId, out var context))
            {
                context = (DbContext)Activator.CreateInstance(_generator.GetOrCreate(appId))!;
                _contexts[appId] = context;
            }
    
            return context;
        }
    
        public void Dispose()
        {
            _contexts.Clear();
        }
    }
    

    DbContextContainer的生命周期即DbContext的生命周期,因为DbContext的缓存是共享的,所以我们也不用担心一些性能问题。

    使用时也非常简单,我们只需要在DbContextContainer取出我们对应AppIdDbContext进行操作就可以了:

    public class DynamicController : ApiControllerBase
    {
        private readonly DbContextContainer _container;
    
        public DynamicController(DbContextContainer container)
        {
            _container = container;
        }
    
        [HttpGet]
        public async Task GetCompanies()
        {
            var res = await _container.Get("test1").DynamicSet(typeof(Company)).ToDynamicListAsync();
    
            return Ok(res);
        }
    
        [HttpGet]
        public async Task AddCompany()
        {
            var db = _container.Get("test1");
            FormTypeBuilder.AddDynamicEntity("test1", "Companies", typeof(Company));
            db.UpdateVersion();
    
            return Ok();
        }
    }
    
  • 相关阅读:
    OFD查验~看OFD 用超阅
    WinForm简洁有效的项目框架结构
    极致性能优化之道之消除伪共享
    什么是跨站请求伪造(CSRF)攻击?如何防止它?
    KubeGems 启用 Nacos 配置中心
    李呈祥:bilibili在湖仓一体查询加速上的实践与探索
    1.rk3588的yolov5运行:pt_onnx_rknn转换及rknn在rk3588系统python运行
    量体裁衣方得最优解:聊聊页面静态化架构和二级CDN建设
    golang runc NewSockPair
    数据可视化实验-多维数据平行坐标可视化(HTML语言)
  • 原文地址:https://www.cnblogs.com/donpangpang/p/17944918