Parallel.Foreach 抛出异常 InvalidOperationException

问题描述 投票:0回答:1

当我在集合上使用 Parallel.Foreach 循环时,它会抛出异常 “在上一个操作完成之前,在此上下文上启动了第二个操作。这通常是由使用同一 DbContext 实例的不同线程引起的,但是,不能保证实例成员是线程安全的”

我有使用 .netcore 3.1 和 efcore 2.2 的洋葱架构。 使用具有工作单元设计模式的通用存储库

  • 为unitofwork创建了一个工厂并添加了一个新方法 创建FactoryContext。

     public interface IUnitOfWorkFactory { UnitOfWork CreateUnitOfWork(); }

     public class UnitOfWorkFactory : IUnitOfWorkFactory
     {
       private readonly YourDbContext _dbContext;
    
       public UnitOfWorkFactory(YourDbContext dbContext)
       {
          _dbContext = dbContext;
       }
    
       public UnitOfWork CreateUnitOfWork()
       {
          return new UnitOfWork(_dbContext);
       }
     }
    

    `

  • 注册到startup.cs文件中

    services.AddScoped<IUnitOfWorkFactory, UnitOfWorkFactory>();

  • 注入Service类文件中创建了一个unitofwork对象 每个 ` 公共类 MyService { 私有只读 IUnitOfWorkFactory _unitOfWorkFactory;

     public YourService(IUnitOfWorkFactory unitOfWorkFactory)
     {
         _unitOfWorkFactory = unitOfWorkFactory;
     }
    
     public void SendEmails(UserModel user)
     {
         using (var unitOfWork = _unitOfWorkFactory.CreateUnitOfWork())
         {
             var emailTemplate = unitOfWork.Templates.GetAllWithNoTracking().Single(x => x.TemplateName= "abc");
         }
     }
    

    } `

  • 控制器代码

    Parallel.ForEach(result, obj => { _myService.SendEmail(obj);                     });

可能的解决方案是什么

c# multithreading asp.net-core-webapi task-parallel-library ef-core-2.2
1个回答
2
投票

每当调用

UnitOfWorkFactory
时,您的
CreateUnitOfWork
都应该创建一个新的 dbContext。类似的东西

public class UnitOfWorkFactory : IUnitOfWorkFactory
 {
   private readonly string _connectionString;

   public UnitOfWorkFactory(string connectionString)
   {
      _connectionString= connectionString;
   }

   public UnitOfWork CreateUnitOfWork()
   {
      return new UnitOfWork(new YourDbContext (_connectionString));
   }
 }

这样,每次调用

SendEmails
将使用不同的 DbContext 对象,从而避免并发使用任何上下文。框架应该自动池化与数据库的连接,从而使 DbContext 对象的创建保持相当便宜。

但是,在像这样的并行循环中运行数据库查询通常不是一个好主意。如果可以的话,您通常应该在单个数据库查询中做尽可能多的工作,即添加像

void SendEmails(IEnumerable<UserModel> users)
这样的方法。由于示例不完整并且没有任何作用,因此很难提供具体的建议。

© www.soinside.com 2019 - 2024. All rights reserved.