Ef Core花里胡哨系列(10) 动态起来的 DbContext
2024/1/4 18:02:36
本文主要是介绍Ef Core花里胡哨系列(10) 动态起来的 DbContext,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
Ef Core花里胡哨系列(10) 动态起来的 DbContext
我们知道,DbContext
有两种托管方式,一种是AddDbContext
和AddDbContextFactory
,但是呢他们各有优劣,例如工厂模式下性能更好呀等等。那么,我们能否自己托管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
取出我们对应AppId
的DbContext
进行操作就可以了:
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的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-12-26怎么使用nsenter命令进入容器?-icode9专业技术文章分享
- 2024-12-26导入文件提示存在乱码,请确定使用的是UTF-8编码怎么解决?-icode9专业技术文章分享
- 2024-12-26csv文件怎么设置编码?-icode9专业技术文章分享
- 2024-12-25TypeScript基础知识详解
- 2024-12-25安卓NDK 是什么?-icode9专业技术文章分享
- 2024-12-25caddy 可以定义日志到 文件吗?-icode9专业技术文章分享
- 2024-12-25wordfence如何设置密码规则?-icode9专业技术文章分享
- 2024-12-25有哪些方法可以实现 DLL 文件路径的管理?-icode9专业技术文章分享
- 2024-12-25错误信息 "At least one element in the source array could not be cast down to the destination array-icode9专业技术文章分享
- 2024-12-25'flutter' 不是内部或外部命令,也不是可运行的程序 或批处理文件。错误信息提示什么意思?-icode9专业技术文章分享