When i am using Parallel.Foreach loop on the collection then it throws exception "A second operation started on this context before a previous operation completed. This is usually caused by different threads using the same instance of DbContext, however, instance members are not guaranteed to be thread-safe"
I have onion architecture using .netcore 3.1 and efcore 2.2. Using Generic repository with unitofwork design pattern
Created a factory for unitofwork and added a new method createFactoryContext.
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); } }`
Registered in startup.cs file
services.AddScoped<IUnitOfWorkFactory, UnitOfWorkFactory>();Injected in Service class file Created a object of unitofwork on each ` public class MyService { private readonly IUnitOfWorkFactory _unitOfWorkFactory;
public YourService(IUnitOfWorkFactory unitOfWorkFactory) { _unitOfWorkFactory = unitOfWorkFactory; } public void SendEmails(UserModel user) { using (var unitOfWork = _unitOfWorkFactory.CreateUnitOfWork()) { Templates emailTemplate; if (user.userType = 1) { emailTemplate = unitOfWork.Templates.GetAllWithNoTracking().Single(x => x.TemplateName= "abc"); } else { emailTemplate = unitOfWork.Templates.GetAllWithNoTracking().Single(x => x.TemplateName= "xyz"); } } }} `
Controller Code
Parallel.ForEach(result, obj => { _myService.SendEmail(obj); });
What could be the possible solution
Your
UnitOfWorkFactoryshould create a new dbContext wheneverCreateUnitOfWorkis called. Something likeThis way each call to
SendEmailswill use different DbContext objects, and therefore avoid concurrent usage of any context. The framework should pool connections to the database automatically, thus keeping creation of DbContext objects fairly cheap.However, running database queries in a parallel loop like this is usually not a great idea. If you can, you should usually do as much work as possible work in a single db query, i.e. add a method like
void SendEmails(IEnumerable<UserModel> users). Since the example is incomplete and does nothing, it is difficult to provide concrete recommendations.