微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

有没有办法将已经打开的 MySQL 连接传递给 EF Core DbContext ?用于多线程目的

如何解决有没有办法将已经打开的 MySQL 连接传递给 EF Core DbContext ?用于多线程目的

我已经努力了 2 天,为我的简单场景找到合适的解决方案。

我想要完成的场景
在 .Net Core Parallel 循环中执行多个数据库访问,将在同一数据库中插入多个项目。

var actions = new List<Action>();

actions.Add(() => { new DbContext.Set<TEntity>().Add(entity); });
actions.Add(() => { new DbContext.Set<TEntity>().Add(entity); });
actions.Add(() => { new DbContext.Set<TEntity>().Add(entity); });
actions.Add(() => { new DbContext.Set<TEntity>().Add(entity); });

Parallel.ForEach(actions,new ParallelOptions { MaxDegreeOfParallelism = 2 },action =>
{
     action();
});

已知限制

  1. EF Core 的 DBContext 不是线程安全的(我们需要在每个 trhead 中重新创建它们)
  2. MysqL 服务器不接受使用同一个事务建立的 2 个连接
  3. 似乎我们无法将已经打开的连接传递给 DBContext
  4. 每次我们与数据库交互时,EF Core 都会在内部打开和关闭数据库的连接

例外

多个同时连接或不同连接 同一事务中的连接字符串当前不是 支持

MysqL.Data.MysqLClient.MysqLConnection.open() 在 Microsoft.EntityFrameworkCore.Storage.RelationalConnection.OpenDbConnection(Boolean 错误预期)在 Microsoft.EntityFrameworkCore.Storage.RelationalConnection.Open(Boolean 错误预期)在 Microsoft.EntityFrameworkCore.Query.Internal.QueryingEnumerable1.Enumerator.BufferlessMoveNext(DbContext _,Boolean buffer) at Microsoft.EntityFrameworkCore.Query.Internal.QueryingEnumerable1.Enumerator.MoveNext() 在 Microsoft.EntityFrameworkCore.Query.Internal.LinqOperatorProvider._TrackEntities[TOut,TIn](IEnumerable1 results,QueryContext queryContext,IList1 entityTrackingInfos,IList1 entityAccessors)+MoveNext() at Microsoft.EntityFrameworkCore.Query.Internal.LinqOperatorProvider.ExceptionInterceptor1.EnumeratorExceptionInterceptor.MoveNext() 在 System.Collections.Generic.List1.AddEnumerable(IEnumerable1 enumerable) 在 System.Linq.Enumerable.ToList[TSource](IEnumerable`1 来源)在 CloseTheMonth.Backend.Data.Repositories.AccountUserRightRepository.ListAccounts(Guid 用户 ID)在 C:\Work\GitHub\CloseTheMonth\Backend\CloseTheMonth.Data\Repositories\AccountUserRightRepository.cs:line 44 在 CloseTheMonth.Backend.Services.AccountUserRightService.ListAccounts(Guid 用户 ID)在 C:\Work\GitHub\CloseTheMonth\Backend\CloseTheMonth.Services\AccountUserRightService.cs:line 53 在 CloseTheMonth.Backend.Controllers.AppController.Init(String 授权,AppInitRequest 请求)中 C:\Work\GitHub\CloseTheMonth\Backend\CloseTheMonth.Backend\Controllers\AppController.cs:line 101

反思...
如果我可以像这样全局打开一个连接,并将它传递给我的 DBContexts,那就可以了。但是我查看了 EF Core 和 Pomelo 源代码,并没有找到实现类似功能方法

也许除了 Pomelo 之外的其他一些 EF Core MysqL 驱动程序可以做到这一点?

var actions = new List<Action>();

using (var conn = new MysqLConnection())
{
   actions.Add(() => { new DbContext(conn).Set<TEntity>().Add(entity); });
   actions.Add(() => { new DbContext(conn).Set<TEntity>().Add(entity); });
   actions.Add(() => { new DbContext(conn).Set<TEntity>().Add(entity); });
   actions.Add(() => { new DbContext(conn).Set<TEntity>().Add(entity); });

   Parallel.ForEach(actions,action =>
   {
        action();
   });
}

我正在使用带有 Pomelo.EntityFrameworkCore.MysqL (2.1.4) 的 MysqL 服务器 (8.0.22)

解决方法

如果代码不是线程安全的,您必须在每个线程中使用单独的 MySQL 连接。

MySQL 协议是有状态的,所以如果查询-响应周期的一部分与不同查询的不同查询-响应周期交错,那么响应就会变得混乱。你不会喜欢这个结果。

设计使用数据库的多线程代码的唯一明智方法是让每个线程打开自己的连接。

,

现在一切都清楚了,让我继续简单:

  1. 无法使用 EF Core 和 MySQL 进行多线程写入操作
  2. 可以使用 EF Core 和 MySQL 进行多线程读取操作

一、写操作

  • DBContext 不是线程安全的,每个线程需要一个上下文
  • 每次 Context.SaveChanges 都会打开和关闭一个连接
  • MySQL 拒绝在一个事务中打开多个连接
  • 不能在同一个数据库连接上执行多线程操作

由于您不能多线程插入/更新/删除,您当然可以通过避免每次触摸实体时调用 SaveChanges 并等到提交事务前才调用它来优化它。

首先,只要您不 SaveChanges,它将允许 EF 留在内存中。

其次,如果 EF Core 必须完成任何优化以喜欢、批量插入或任何可能完成的事情,它将能够完成,因为您将所有数据库作业保留到最后(因此 EF 知道要完成的工作量)。

二、读操作

  • DBContext 不是线程安全的,每个线程需要一个上下文
  • 你需要在每个线程下创建一个作用域
  • 依赖注入将为每个线程作用域创建一个新的 UnitOfWork(因为您在 Startup 类中将其定义为 Scoped)
  • 您同时执行所有线程,EF Core 将处理多个连接

对于读取操作,由于我希望代码看起来干净,所以我做了以下操作:

public class Multithreader : IDisposable
{
    private List<Action> _actions = new List<Action>();

    public Multithreader(int maxThreads)
    {
        this._maxThreads = maxThreads;
    }

    public void Enqueue(Action action)
    {
        this._actions.Add(action);
    }

    public void Dispose()
    {
        Parallel.ForEach(this._actions,new ParallelOptions { MaxDegreeOfParallelism = 8 },action =>
        {
            action();
        });
    }
}

我还在 BaseController 中创建了一个辅助函数来获取 Scoped Services 类(其中包含对我的服务的引用):

public class BaseController : ControllerBase
{
    private readonly IServiceProvider _serviceProvider;

    public BaseController(IServiceProvider serviceProvider)
    {
        this._serviceProvider = serviceProvider;
    }

    protected IServices GetScopedServices()
    {
        var scope = _serviceProvider.CreateScope();

        return scope.ServiceProvider.GetService<IServices>();
    }
}

然后我只是非常巧妙地将所有东西排入队列以获得我想要的东西:

using (var threader = new Multithreader())
{
    threader.Enqueue(() => { using (var services = this.GetScopedServices()) { entity.Value1 = services.Accounts.GetValue1(); } });
    threader.Enqueue(() => { using (var services = this.GetScopedServices()) { entity.Value2 = services.Accounts.GetValue2(); } });
    threader.Enqueue(() => { using (var services = this.GetScopedServices()) { entity.Value3 = services.Accounts.GetValue3(); } });
    threader.Enqueue(() => { using (var services = this.GetScopedServices()) { entity.Value4 = services.Accounts.GetValue4(); } });
    threader.Enqueue(() => { using (var services = this.GetScopedServices()) { entity.Value5 = services.Accounts.GetValue5(); } });
    threader.Enqueue(() => { using (var services = this.GetScopedServices()) { entity.Value6 = services.Accounts.GetValue6(); } });
    threader.Enqueue(() => { using (var services = this.GetScopedServices()) { entity.Value7 = services.Accounts.GetValue7(); } });
    threader.Enqueue(() => { using (var services = this.GetScopedServices()) { entity.Value8 = services.Accounts.GetValue8(); } });
    threader.Enqueue(() => { using (var services = this.GetScopedServices()) { entity.Value9 = services.Accounts.GetValue9(); } });
}

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?