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<User> Users { get; set; } = null!;
public DbSet<Department> Departments { get; set; } = null!; protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlite("Data Source=sample.db");
optionsBuilder.ReplaceService<IModelCacheKeyFactory, MyModelCacheFactory>(); 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<IActionResult> GetCompanies()
{
var res = await _container.Get("test1").DynamicSet(typeof(Company)).ToDynamicListAsync(); return Ok(res);
} [HttpGet]
public async Task<IActionResult> AddCompany()
{
var db = _container.Get("test1");
FormTypeBuilder.AddDynamicEntity("test1", "Companies", typeof(Company));
db.UpdateVersion(); return Ok();
}
}

Ef Core花里胡哨系列(10) 动态起来的 DbContext的更多相关文章

  1. 在EF Core里面如何使用以前EntityFramework的DbContext.Database.SqlQuery<SomeModel>自定义查询

    问: With Entity Framework Core removing dbData.Database.SqlQuery<SomeModel> I can't find a solu ...

  2. ASP.NET Boilerplate 学习 AspNet Core2 浏览器缓存使用 c#基础,单线程,跨线程访问和线程带参数 wpf 禁用启用webbroswer右键菜单 EF Core 2.0使用MsSql/MySql实现DB First和Code First ASP.NET Core部署到Windows IIS QRCode.js:使用 JavaScript 生成

    ASP.NET Boilerplate 学习   1.在http://www.aspnetboilerplate.com/Templates 网站下载ABP模版 2.解压后打开解决方案,解决方案目录: ...

  3. EF Core 2.0使用MsSql/Mysql实现DB First和Code First

    参考地址 EF官网 ASP.NET Core MVC 和 EF Core - 教程系列 环境 Visual Studio 2017 最新版本的.NET Core 2.0 SDK 最新版本的 Windo ...

  4. Asp.net core下利用EF core实现从数据实现多租户(2) : 按表分离

    前言 在上一篇文章中,我们介绍了如何根据不同的租户进行数据分离,分离的办法是一个租户一个数据库. 也提到了这种模式还是相对比较重,所以本文会介绍一种更加普遍使用的办法: 按表分离租户. 这样做的好处是 ...

  5. EF Core 2.0中Transaction事务会对DbContext底层创建和关闭数据库连接的行为有所影响

    数据库 我们先在SQL Server数据库中建立一个Book表: CREATE TABLE [dbo].[Book]( ,) NOT NULL, ) NULL, ) NULL, ) NULL, [Cr ...

  6. 9.10 翻译系列:EF数据注解特性之StringLength【EF 6 Code-First系列】

    原文链接:https://www.entityframeworktutorial.net/code-first/stringlength-dataannotations-attribute-in-co ...

  7. 9.翻译系列:EF 6以及EF Core中的数据注解特性(EF 6 Code-First系列)

    原文地址:http://www.entityframeworktutorial.net/code-first/dataannotation-in-code-first.aspx EF 6 Code-F ...

  8. 9.4 翻译系列:EF 6以及 EF Core中的NotMapped特性(EF 6 Code-First系列)

    原文链接:http://www.entityframeworktutorial.net/code-first/notmapped-dataannotations-attribute-in-code-f ...

  9. 10.翻译系列:EF 6中的Fluent API配置【EF 6 Code-First系列】

    原文链接:https://www.entityframeworktutorial.net/code-first/fluent-api-in-code-first.aspx EF 6 Code-Firs ...

  10. 10.1.翻译系列:EF 6中的实体映射【EF 6 Code-First系列】

    原文链接:https://www.entityframeworktutorial.net/code-first/configure-entity-mappings-using-fluent-api.a ...

随机推荐

  1. 斜率优化DP 学习笔记

    斜率优化 DP 适用情况 适用于求解最优解(最大.最小)问题. 上凸壳与下凸壳 求解步骤 对于任意状态转义方程,设 \(A_i\),\(B_i\),使状态转移方程转化为 \(f_i = \min(f_ ...

  2. 关于使用uniapp时Android 离线打包的注意事项

    Android 离线打包 文档地址: https://nativesupport.dcloud.net.cn/AppDocs/usesdk/android 注意事项: 添加权限,需要将 uniapp ...

  3. 算法打卡|Day4 链表part02

    Day4 链表part02 今日任务 ● 24. 两两交换链表中的节点 ● 19.删除链表的倒数第N个节点 ● 面试题 02.07. 链表相交 ● 142.环形链表II 目录 Day4 链表part0 ...

  4. 算法打卡|Day3 链表part01

    Day3 链表part01 今日任务 ● 链表理论基础 ● 203.移除链表元素 ● 707.设计链表 ● 206.反转链表 目录 Day3 链表part01 链表理论基础 Problem: 203. ...

  5. SQL函数union,union all整理

    SQL集合函数--并集union,union all 本次整理从4个方面展示union函数,union all函数的风采: 1.集合函数使用规则 2.集合函数作用 3.数据准备及函数效果展示 首先1. ...

  6. android开发阶段性技能

    一.初级 1. 拥有娴熟的Java基础,理解设计模式,比如OOP语言的工厂模式要懂得. 2. 掌握Android UI控件.Android Java层API相关使用. 迈向中级,最好再次更新下Java ...

  7. Android下音视频对讲演示程序(声学回音消除、噪音抑制、语音活动检测、自动增益控制、自适应抖动缓冲)(2023年07月13日更新)

    Android下音视频对讲演示程序 必读说明 简介   本软件根据<道德经>为核心思想而设计,实现了两个设备之间进行音视频对讲,一般可用于楼宇对讲.智能门铃对讲.企业员工对讲.智能对讲机. ...

  8. 【虹科干货】谈谈Redis Enterprise实时搜索的过人之处

    我们都知道,用户在使用应用程序时候,对于速度有着越来越高的要求,真可谓是"一秒也等不及".而开发团队又该怎样来满足这种对于实时性的期望呢? 文章速览: Redis Enterpri ...

  9. Net 高级调试之二:CLR和Windows加载器及应用程序域介绍

    一.简介 今天是 Net 高级调试的第二篇文章,第一篇文章记录了自己学习 Net 高级调试的第一步,认识一些调试工具,有了工具的倚仗,我们开始仗剑走天涯了,开始Net 高级调试正式的征程了.我先说一下 ...

  10. 【数字图像处理】Matlab实现-图像增强-灰度图像增强

    灰度图像增强的大致原理以及操作方法 面向作业编程 上图是大概对灰度图的增强处理 就是想让图片变得更好看 那么如何具体操作就看下面的代码了 这是一个简单的线性分段处理,相信在注释的帮助下应该能看懂. % ...