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. 支持JDK19虚拟线程的web框架,之四:看源码,了解quarkus如何支持虚拟线程

    欢迎访问我的GitHub 这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos 本篇概览 本篇是<支持JDK19虚拟线程的web ...

  2. 千呼万唤始出来 JDK 21 LTS, 久等了

    平地起惊雷!!! 目录 英雄的迟暮 大人时代变了 JDK 21 LTS 前 JAVA并发编程模型 JDK 21 LTS 中的 JAVA 并发编程模型 虚拟线程 VS 线程池 The Last 你可以称 ...

  3. 【ASP.NET Core】在 Mini-API 中注入服务

    经过版本更新,Mini API 的功能逐步完善,早期支持得不太好的 mini API 现在许多特性都可以用了,比如灰常重要的依赖注入. 咱们先来个相当简单的注入测试.来,定义一个服务类,为了偷懒,老周 ...

  4. python第2~5章 code

    02基本语法 print('he\aaa\aaa') # 这是一个打印语句,请你看见了不要慌张# 这是一个注释# 注释会被解释器所忽略# print(123+456) 这行代码被注释了,将不会执行pr ...

  5. python3 gui 计时器

    # -*- coding: utf-8 -*- # @Time : 2023/4/4 21:53 # @File : 定时器gui.py # @Software: PyCharm Community ...

  6. ciscn_2019_c_1 题解

    main函数如下: int __cdecl main(int argc, const char **argv, const char **envp) { int v4; // [rsp+Ch] [rb ...

  7. 关于Halcon中variation_model模型的快速解读。

    十一期间在家用期间研读了下Halcon的variation_model模型,基本上全系复现了他的所有技术要求和细节,这里做个记录. 其实这个模型的所有原理都不是很复杂的,而且Halcon中的帮助文档也 ...

  8. 虹科案例 | 丝芙兰xDomo:全球美妆巨头商业智能新玩法

    全球美妆行业的佼佼者丝芙兰,其走向成功绝非仅依靠品牌知名度和营销手段.身为数据驱动型企业,2018年以来,丝芙兰就率先在行业内采用虹科提供的Domo商业智能进行数据分析和决策,并首先享受了运营优化.效 ...

  9. Wonder8.promotion营销规则引擎,轻松搞掂千变万化的营销玩法

    超过10年没有更新过内容了,不知道现在园子的氛围这类文章还适不适合放首页 想着整点内容,也是支持园子! 旺德發.营销 引擎 概述 为了广泛支持营销活动的复杂与灵活,Wonder8.promotion( ...

  10. .Net Core 3.1 服务端配置跨域

    一.在Startup.cs 的 ConfigureServices 配置好跨域策略 原文链接:https://www.jianshu.com/p/534b9a6a6ed5 public void Co ...