浅析 EF Core 5 中的 DbContextFactory

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/306852.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

bcm943602cs蓝牙用不了_原来手机的蓝牙功能这么强大!除了连接耳机,还有这六大实用功能...

蓝牙是手机上的一个普通功能&#xff0c;基本上所有的手机里都有它&#xff0c;原先它的作用很有限&#xff0c;只是用来传输数据&#xff0c;但由于速度太慢&#xff0c;最后也是被软件所淘汰&#xff0c;慢慢的可以用来连接耳机&#xff0c;这也是博主用的比较多的一个功能&a…

c语言数字字母和字符串,C语言字符串用法之字符串和数字的转换

把字符串转换为数字1.考虑字符串除最后一个都是数字&#xff0c;如236\0int strToInt(const char *str){int temp 0;const char *ptr str; //ptr保存str字符串开头while(*str ! 0){temp temp * 10 (*str - 0);//根据ASCII码的数学关系把字符转换为数字str;}return temp;}2.…

数据结构——表达式求值(中序)

表达式求值&#xff08;中序&#xff09; 实验二 基于栈的中缀算术表达式求值 【实验目的】 1.掌握栈的基本操作算法的实现,包括栈初始化、进栈、出栈、取栈顶元素等。 2.掌握利用栈实现中缀表达式求值的算法。 【实验内容】 问题描述 输入一个中缀算术表达式,求解表达式的值。…

msdn画圆弧函数_画直线不简单!python-matplotlib告诉你为什么

1 说明&#xff1a;1.1 python的matplotlib画直线&#xff0c;看似简单&#xff0c;其实很难&#xff0c;从简单到复杂&#xff0c;逐步深入&#xff0c;小白秒懂。1.2 内容&#xff1a;画直线&#xff0c;画圆&#xff0c;画圆点&#xff0c;动画的单摆和圆套圆&#xff0c;好…

Wifi6网络

2020年是Wifi6设备全面爆发的一年&#xff0c;华为、小米、华硕、腾达、TP-LINK、360等多家厂商相继发布了Wifi6路由产品&#xff0c;掀起了一股更换路由器的热潮。首先&#xff0c;我们先来看几个常识1、Wifi6和IPv6两个没有必然联系&#xff0c;Wifi6是一种支持802.11ax的Wif…

c语言字符串中取最大字符串,使用C语言提取子字符串及判断对称子字符串最大长度...

先来看一个使用C语言从字符串中提取子字符串的基本方法总结&#xff1a;#include /*处理中文字符*//*遍历字符串&#xff0c;非ASCII字符读取2个字节&#xff0c;ASCII读取一个字节&#xff0c;获取字符串长度*/int StrLenU(const char* string){int len 0 ;const char* p st…

数据结构——用栈解决回文字符问题

回文 回文是指正读反读均相同的字符序列,如“abba”和“abdba”均是回文&#xff0c;但“good”不是回文。试写一个算法判定给定的字符序列是否为回文。&#xff08;提示&#xff1a;将一半字符入栈。&#xff09; 所需的知识前提&#xff1a;栈 以下是顺序栈的基本算法 结构…

aspose excel中文文档_除了VBA,还有哪些编程语言可以操作Excel文件?

Excel(Microsoft office)是现在最常用的办公软件&#xff0c;主要涉及电子表格制作、数据处理、报表输出展示以及更高端的还有金融建模等&#xff1b;我们知道&#xff0c;在需要批处理多个Excel工作表以及工作簿的时候&#xff0c;需要用到一个自动化的利器&#xff1a;VBAVBA…

关于.NET5在IIS中部署的几个问题总结

本来我的系列教程已经慢慢剥离开IIS了&#xff0c;毕竟有了Docker容器以后&#xff0c;配合Nginx使用真的很不错。但是还是有很多同学使用IIS的&#xff0c;这个不可否认IIS的重要性。随着.NET的发布&#xff0c;很多小伙伴已经开始升级了&#xff0c;我也就陆陆续续收到了一些…

上海市二级c语言软件环境,上海市计算机二级C语言复习资料 word整理版.doc

上海市高等学校计算机等级考试二级(C程序设计)03年上海市高等学校计算机等级考试试卷二级 (C程序设计)(本试卷答卷时间为120分钟)试题一(28分&#xff0c;每小题4分)解答下列各小题&#xff0c;把正确的解答写在答卷纸的对应栏内。设有变量说明&#xff1a;int a5,b4,c3,r1,r2;…

数据结构——括号匹配问题

括号匹配 给定一个字符串&#xff0c;其中的字符只包含三种括号&#xff1a;花括号{ }、中括号[ ]、圆括号( )&#xff0c;即它仅由 “( ) [ ] { }” 这六个字符组成。设计算法&#xff0c;判断该字符串是否有效&#xff0c;即字符串中括号是否匹配。括号匹配要求括号必须以正…

wordpress多站点主站调用分站最新文章_企业网站SEO最新的7个优化步骤!

如果你是一个企业主&#xff0c;你有建立企业官方网站的经验&#xff0c;在2-3年的运作中&#xff0c;我相信你至少修改了一个网站&#xff0c;甚至做了一个重大的SEO策略调整。当我们开始建立一个公司的时候&#xff0c;很多时候就是认为只要我们有一个公司的网站&#xff0c;…

c语言sizeof和strlen哪个大,C语言的sizeof和strlen区别与联系

sizeof指的是占有空间的大小&#xff0c;包括字符串结束的\0。strlen是计算字符串长度&#xff0c;以\0作为结束标志&#xff0c;并且\0不计入数值。#include #include using namespace std;void fun(char a[100]){cout << sizeof(a) << endl;// 参数里的数组也是按…

数据结构——进制转换(10—n)

进制转换&#xff08;10进制——n进制&#xff09; 所需知识&#xff1a;栈 #include<stdio.h> #include<bits/stdc.h> using namespace std; #include<malloc.h> #include<string>typedef int Status; #define OK 1 #define ERROR 0 #define TRUE 1…

Azure 静态 web 应用集成 Azure 函数 API

前几次我们演示了如何通过Azure静态web应用功能发布vue跟blazor的项目(使用 Azure静态web应用Github全自动部署VUE站点、使用Azure静态Web应用部署Blazor Webassembly应用)。但是一个真正的web应用&#xff0c;总是免不了需要后台api服务为前端提供数据或者处理数据的能力。同样…

c++ new一个结构体_C语言结构体,又一个纸老虎,纯干货讲解(附代码)

来源&#xff1a;网络&#xff0c;排版整理&#xff1a;晓宇微信公众号&#xff1a;芯片之家(ID&#xff1a;chiphome-dy)结构体的定义结构体(struct)是由一系列具有相同类型或不同类型的数据构成的数据集合&#xff0c;也叫结构。结构体和其他类型基础数据类型一样&#xff0c…

c语言next的用法,C语言strchr使用之Next查找和截断想要的字符串

#include #include #include #include #include #include #include #include static char pstring[] "Hello\n"; // 这里不能是char*int main(){char tmp ;char* pstr strchr(pstring,‘l‘);printf("pstr is %s %p\n",pstr,pstr);#if 0// 搜后面的pstr …

problem b: 十进制整数转二进制_二进制的科学计数法?白话谈谈计算机如何存储与理解小数:IEEE 754...

浮点数的计算机表示(IEEE 754)&#xff0c;由 UCB 数学教授 William Kahan 主要起草。后者也因其卓越贡献于1989年获得图灵奖。计算机组成原理与汇编语言这两门课均对该内容有所讲解。与课程中直接抛出公式与概念不同&#xff0c;我想首先与各位探讨"科学计数法"这个…

分享我的写作经验

大家好&#xff0c;我是Z哥。最近一段时间&#xff0c;我面基了几个在微信上聊得不错的小伙伴。和其中的两位有聊到关于写作的事情。概括地说就是他们也想写写博客、公众号&#xff0c;但是感觉无从下手。我和他们分享了我的一些经验&#xff0c;在这里做一下总结和补充&#x…

数据结构——二叉树的最小深度算法

给定一个二叉树&#xff0c;找出其最小深度。 最小深度是从根节点到最近叶子节点的最短路径上的节点数量。 说明&#xff1a;叶子节点是指没有子节点的节点。 输入&#xff1a;root [3,9,20,null,null,15,7] 输出&#xff1a;2 示例 2&#xff1a; 输入&#xff1a;root …