EF Core 5 中的 DbContextFactory

Intro

使用过 EF Core 大多都会遇到这样一个场景,希望能够并行查询,但是如果使用同一个 DbContext 实例进行并行操作的时候就会遇到一个 InvalidOperationException 的异常,在 EF Core 2.x/3.x 版本中, EF Core DbContext 的生命周期默认是 Scoped,如果要并行查询,需要创建多个 Scope,在子 Scope 中创建 DbContext 来进行操作,EF Core 5 中的 DbContextFactory 可以用来简化这样的操作,且看下文示例

DbContextFactory

DbContextFactory 就如同它的名字一样,就是一个 DbContext 的工厂,就是用来创建 DbContext

IDbContextFactory 接口定义如下,Github 源码 https://github.com/dotnet/efcore/blob/v5.0.0/src/EFCore/IDbContextFactory.cs

public interface IDbContextFactory<out TContext> where TContext : DbContext
{/// <summary>///     <para>///         Creates a new <see cref="DbContext" /> instance.///     </para>///     <para>///         The caller is responsible for disposing the context; it will not be disposed by the dependency injection container.///     </para>/// </summary>/// <returns> A new context instance. </returns>TContext CreateDbContext();
}

需要注意的是,如果使用 DbContextFactory 来创建 DbContext,需要自己来释放 DbContext,需要自己使用 using 或者 Dispose 来释放资源

另外 DbContextFactory 生命周期不同于 DbContext,默认的生命周期的 Singleton,也正是因为这样使得我们可以简化并行查询的代码,可以参考

https://github.com/dotnet/efcore/blob/v5.0.0/src/EFCore/Extensions/EntityFrameworkServiceCollectionExtensions.cs#L607

Sample

来看一个实际的示例,这是一个并行操作插入100条记录的简单示例,看一下如何使用 DbContextFactory 进行并行操作

var services = new ServiceCollection();
services.AddDbContextFactory<TestDbContext>(options =>
{options.UseInMemoryDatabase("Tests");
});
using var provider = services.BuildServiceProvider();
var contextFactory = provider.GetRequiredService<IDbContextFactory<TestDbContext>>();Enumerable.Range(1, 100).Select(async i =>{using (var dbContext = contextFactory.CreateDbContext()){dbContext.Posts.Add(new Post() { Id = i + 101, Author = $"author_{i}", Title = $"title_{i}" });return await dbContext.SaveChangesAsync();}}).WhenAll().Wait();using var context = contextFactory.CreateDbContext();
Console.WriteLine(context.Posts.Count());

实现源码

EF Core 的 DbContextFactory 的实现不算复杂,一起来看一下,首先看一下 DbContextFactory 的实现:

public class DbContextFactory<TContext> : IDbContextFactory<TContext> where TContext : DbContext
{private readonly IServiceProvider _serviceProvider;private readonly DbContextOptions<TContext> _options;private readonly Func<IServiceProvider, DbContextOptions<TContext>, TContext> _factory;public DbContextFactory([NotNull] IServiceProvider serviceProvider,[NotNull] DbContextOptions<TContext> options,[NotNull] IDbContextFactorySource<TContext> factorySource){Check.NotNull(serviceProvider, nameof(serviceProvider));Check.NotNull(options, nameof(options));Check.NotNull(factorySource, nameof(factorySource));_serviceProvider = serviceProvider;_options = options;_factory = factorySource.Factory;}public virtual TContext CreateDbContext()=> _factory(_serviceProvider, _options);
}

可以看到 DbContextFactory 的实现里用到了一个 IDbContextFactorySource,再来看一下 DbContextFactorySource 的实现,实现如下:

public class DbContextFactorySource<TContext> : IDbContextFactorySource<TContext> where TContext : DbContext
{public DbContextFactorySource()=> Factory = CreateActivator();public virtual Func<IServiceProvider, DbContextOptions<TContext>, TContext> Factory { get; }private static Func<IServiceProvider, DbContextOptions<TContext>, TContext> CreateActivator(){var constructors= typeof(TContext).GetTypeInfo().DeclaredConstructors.Where(c => !c.IsStatic && c.IsPublic).ToArray();if (constructors.Length == 1){var parameters = constructors[0].GetParameters();if (parameters.Length == 1){var isGeneric = parameters[0].ParameterType == typeof(DbContextOptions<TContext>);if (isGeneric|| parameters[0].ParameterType == typeof(DbContextOptions)){var optionsParam = Expression.Parameter(typeof(DbContextOptions<TContext>), "options");var providerParam = Expression.Parameter(typeof(IServiceProvider), "provider");return Expression.Lambda<Func<IServiceProvider, DbContextOptions<TContext>, TContext>>(Expression.New(constructors[0],isGeneric? optionsParam: (Expression)Expression.Convert(optionsParam, typeof(DbContextOptions))),providerParam, optionsParam).Compile();}}}var factory = ActivatorUtilities.CreateFactory(typeof(TContext), new Type[0]);return (p, _) => (TContext)factory(p, null);}
}

从上面的源码中可以看得出来, DbContextFactory 把工厂拆成了两部分,DbContextFactorySource 提供一个工厂方法,提供一个委托来创建 DbContext,而 DbContextFactory 则利用 DbContextFactorySource 提供的工厂方法来创建 DbContext.

More

DbContextFactory 可以使得在并行操作的时候会更加方便一些,但是注意要自己控制好 DbContext 生命周期,防止内存泄漏。

对于 EF Core  DbContextFactory 的实现,不得不说这样的实现灵活性更强一些,但是又感觉有一些多余,想要扩展 DbContextFactory 的实现,直接重写一个 DbContextFactory 的实现服务注册的时候注入就可以了,你觉得呢~~

Reference

  • https://github.com/dotnet/efcore/blob/v5.0.0/src/EFCore/IDbContextFactory.cs

  • https://github.com/dotnet/efcore/blob/v5.0.0/src/EFCore/Extensions/EntityFrameworkServiceCollectionExtensions.cs#L607

  • https://github.com/dotnet/efcore/blob/v5.0.0/src/EFCore/Internal/DbContextFactory.cs

  • https://github.com/dotnet/efcore/blob/v5.0.0/src/EFCore/Internal/DbContextFactorySource.cs

  • https://github.com/WeihanLi/SamplesInPractice/blob/master/EF5Samples/DbContextFactoryTest.cs

浅析 EF Core 5 中的 DbContextFactory相关推荐

  1. EF CORE 7 中的新功能:使用 ExecuteDelete 和 ExecuteUpdate 进行批量操作

    原文链接:https://timdeschryver.dev/blog/new-in-entity-framework-7-bulk-operations-with-executedelete-and ...

  2. 万字长文,带你彻底理解EF Core5的运行机制,让你成为团队中的EF Core专家

    在EF Core 5中,有很多方式可以窥察工作流程中发生的事情,并与该信息进行交互.这些功能点包括日志记录,拦截,事件处理程序和一些超酷的最新出现的调试功能.EF团队甚至从Entity Framewo ...

  3. 万字长文,带你彻底理解EF Core 5的运行机制,让你成为团队中的EF Core专家

    目录 1.将EF的ToTraceString移植为EF Core的ToQueryString 2.从EF Core记录详细信息 2.1. 简单的日志记录 2.2.响应EF Core 事件 2.3.使用 ...

  4. Entity Framework Core 5中实现批量更新、删除

    本文介绍了一个在EntityFramework Core 5中不需要预先加载数据而使用一句SQL语句批量更新.删除数据的开发包,并且分析了其实现原理,并且与其他实现方案做了比较. 一.背景 随着微软全 ...

  5. EF Core 3.0查询

    随着.NET Core 3.0的发布,EF Core 3.0也随之正式发布,关于这一块最近一段时间也没太多去关注,陆续会去对比之前版本有什么变化没有,本节我们来看下两个查询. 分组 我们知道在EF C ...

  6. EF Core 3 的 40 个中断性变更

    为了修复 Entify Framework Core 中许多已发现的缺陷,微软在 EF Core 3 中引入了 40 个中断性变更.我们可以在微软文档中查看完整的中断性变更列表,本文仅列举几个主要的点 ...

  7. EF Core 2.1路线图:视图、GROUP BY和惰性加载

    Entity Framework Core一直追随着初始Entity Framework的发展,并不断推陈出新.它首先推出的是对视图的支持,这听起来有些耸人听闻.在即将推出的EF Core 2.1之前 ...

  8. 查缺补漏系统学习 EF Core 6 - 原始 SQL 查询

    推荐关注「码侠江湖」加星标,时刻不忘江湖事 这是 EF Core 系列的第五篇文章,上一篇文章盘点了 EF Core 中的几种数据查询方式. 但是有有时候,我们可能无法用标准的 LINQ 方法完成查询 ...

  9. Blazor服务器应用程序中使用EF Core的多租户

    目录 工厂生命周期 一种方法 把事情放在上下文中 依赖的生命周期 transient事件 性能说明 许多业务应用程序旨在与多个客户合作.保护数据安全很重要,这样客户数据就不会被其他客户和潜在竞争对手泄 ...

最新文章

  1. 在推荐系统中,我还有隐私吗?联邦学习:你可以有
  2. Caused by: java.lang.ClassNotFoundException: javax.persistence.Entity
  3. 求最大公约数的设计与C语言实现
  4. 通信 / CRC 校验
  5. CSS box-shadow 盒子阴影属性
  6. 同一个容器实例可以同时运行在多个宿主机_从零开始学K8s: 3.什么是容器
  7. [机器学习-实践篇]学习之线性回归、岭回归、Lasso回归,tensorflow实现的线性回归
  8. TensorFlow 教程 --进阶指南--3.6增加一个新 Op
  9. 我是如何在六个月内学会 Python 的?
  10. TypeScript学习(二):任意类型及推论
  11. 第十三次CCFCSP认证(2018年3月)真题碰撞的小球
  12. ClassNotFoundException: com.lowagie.text.pdf.PdfGState
  13. 【STM32】PS2遥控手柄使用和程序移植
  14. mac自带工具的抠图方法
  15. 服务器解析xml文件报错:org.dom4j.DocumentException: Error on line -1 of document
  16. EPICS记录参考--模拟输出记录(ao)
  17. 怎么样防止服务器被入侵
  18. TKinterDesigner界面背景和操作界面调试,TKinterDesigner教程(3)
  19. idea中Toggle Offline Mode是什么意思
  20. oracle 导出secquence,oracle等待事件7——事務上的等待事件

热门文章

  1. bzoj1143/2718 祭祀river(最大独立集)
  2. 深入浅出Mybatis系列(八)---mapper映射文件配置之select、resultMap[转]
  3. c语言双引号和单引号的区别_Python中的单引号和双引号有什么区别?
  4. 文件下载至storage_如何防止Storage Sense在Windows 10上删除下载的文件
  5. 如何发现假库存照片(并将合适的人归于属性)
  6. ios 启用 证书_如何在iOS 10中启用就寝提醒,轻柔的唤醒和睡眠跟踪
  7. 二 面向对象三大特性
  8. Android渠道包自动化验证
  9. 《手机测试Robotium实战教程》——导读
  10. OC之非ARC环境下循环retain问题