Ef Core花里胡哨系列(10) 動(dòng)態(tài)起來的 DbContext
我們知道,DbContext
有兩種托管方式,一種是AddDbContext
和AddDbContextFactory
,但是呢他們各有優(yōu)劣,例如工廠模式下性能更好呀等等。那么,我們能否自己托管DbContext
呢?
Github Demo:動(dòng)態(tài)起來的 DbContext
場景:
結(jié)合我們之前的文章 [Ef Core花里胡哨系列(5) 動(dòng)態(tài)修改追蹤的實(shí)體、動(dòng)態(tài)查詢] 假設(shè)一個(gè)應(yīng)用內(nèi)有很多的子應(yīng)用,且都需要更新追蹤的動(dòng)態(tài)實(shí)體,那么很多表在重置OnModelCreating
的時(shí)候?qū)?huì)非常的慢。主要體現(xiàn)在modelBuilder.Model.AddEntityType(type)
,每個(gè)實(shí)體都需要花費(fèi)一小段時(shí)間,幾百個(gè)實(shí)體就會(huì)按分鐘計(jì)算了,而且還會(huì)數(shù)據(jù)庫操作產(chǎn)生一定的影響。
我們先實(shí)現(xiàn)一個(gè)基礎(chǔ)的DbContext
用來添加一些通用的實(shí)體以及處理動(dòng)態(tài)實(shí)體的邏輯,每次需要重置DbContext的時(shí)候,都會(huì)獲取最新的動(dòng)態(tài)實(shí)體進(jìn)行更新:
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);
}
}
然后實(shí)現(xiàn)一個(gè)動(dòng)態(tài)DbContext
的生成器,用于針對(duì)不同的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;
}
}
然后我們需要實(shí)現(xiàn)一個(gè)DbContext
的容器用于管理我們生成的DbContext
,以及負(fù)責(zé)初始化:
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
的生命周期,因?yàn)?code>DbContext的緩存是共享的,所以我們也不用擔(dān)心一些性能問題。文章來源:http://www.zghlxwxcb.cn/news/detail-776481.html
使用時(shí)也非常簡單,我們只需要在DbContextContainer
取出我們對(duì)應(yīng)AppId
的DbContext
進(jìn)行操作就可以了:文章來源地址http://www.zghlxwxcb.cn/news/detail-776481.html
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();
}
}
到了這里,關(guān)于Ef Core花里胡哨系列(10) 動(dòng)態(tài)起來的 DbContext的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!