博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
浅析 EF Core 5 中的 DbContextFactory
阅读量:4032 次
发布时间:2019-05-24

本文共 5096 字,大约阅读时间需要 16 分钟。

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
 where TContext : DbContext{    /// 
    ///     
    ///         Creates a new 
 instance.    ///     
    ///     
    ///         The caller is responsible for disposing the context; it will not be disposed by the dependency injection container.    ///     
    /// 
    /// 
 A new context instance. 
    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
(options =>{    options.UseInMemoryDatabase("Tests")        ;});using var provider = services.BuildServiceProvider();var contextFactory = provider.GetRequiredService
>();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
 : IDbContextFactory
 where TContext : DbContext{    private readonly IServiceProvider _serviceProvider;    private readonly DbContextOptions
 _options;    private readonly Func
, TContext> _factory;    public DbContextFactory(        [NotNull] IServiceProvider serviceProvider,        [NotNull] DbContextOptions
 options,        [NotNull] IDbContextFactorySource
 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
 : IDbContextFactorySource
 where TContext : DbContext{    public DbContextFactorySource()        => Factory = CreateActivator();    public virtual Func
, TContext> Factory { get; }    private static Func
, 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
);                if (isGeneric                    || parameters[0].ParameterType == typeof(DbContextOptions))                {                    var optionsParam = Expression.Parameter(typeof(DbContextOptions
), "options");                    var providerParam = Expression.Parameter(typeof(IServiceProvider), "provider");                    return Expression.Lambda
, 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://pakdi.baihongyu.com/

你可能感兴趣的文章
SQL 多表联合查询
查看>>
Visual Studio 2010:C++0x新特性
查看>>
drwtsn32.exe和adplus.vbs进行dump文件抓取
查看>>
cppcheck c++静态代码检查
查看>>
在C++中使用Lua
查看>>
在Dll中调用自身的位图资源
查看>>
IP校验和详解
查看>>
C++中使用Mongo执行count和distinct运算
查看>>
一些socket的编程经验
查看>>
socket编程中select的使用
查看>>
C++获取文件大小常用技巧分享
查看>>
关于AIS编码解码的两个小问题
查看>>
GitHub 万星推荐:黑客成长技术清单
查看>>
可以在线C++编译的工具站点
查看>>
关于无人驾驶的过去、现在以及未来,看这篇文章就够了!
查看>>
所谓的进步和提升,就是完成认知升级
查看>>
昨夜今晨最大八卦终于坐实——人类首次直接探测到了引力波
查看>>
如何优雅、机智地和新公司谈薪水?
查看>>
为什么读了很多书,却学不到什么东西?
查看>>
长文干货:如何轻松应对工作中最棘手的13种场景?
查看>>