问题
I have repository like
public interface IEmployeeRepository
{
Task<EmployeeSettings> GetEmployeeSettings(int employeeId);
Task<ICollection<DepartmentWorkPosition>> GetWorkPositions(int employeeId);
}
Constructor of repository (DbContext injection):
public EmployeeRepository(EmployeeDbContext dbContext)
{
_dbContext = dbContext;
}
And call it in EF Core 2.0 like
var settingsTask = _employeeRepository
.GetEmployeeSettings(employeeId.Value);
var workPositionsTask = _employeeRepository
.GetWorkPositions(employeeId.Value);
await Task.WhenAll(settingsTask, workPositionsTask);
// do other things...
Problem:
With EF Core 3.0 there is InvalidOperationException: a second operation started on this context before a previous operation completed...
DbContext is registered in ConfigureServices like
services.AddDbContext<EmployeeDbContext>(ServiceLifetime.Transient);
Tutorial says following: Entity Framework Core does not support multiple parallel operations being run on the same DbContext instance.
But! How to use it with repositories in async?
回答1:
How to use it with repositories in async?
You can only have one simultaneous asynchronous request per repository. If you need to have more than one at a time, then you need more than one repository. This may require you to inject a repository factory into your types.
回答2:
Use factory and explicity instantiate context.
Startup.cs
//classical dbcontext registration
services.AddDbContext<TestDB>(
options => options.UseSqlServer(
Configuration.GetConnectionString("Test")));
//factory
//in case we want parallellize more queries at the same request, we can't use the same connection. So, because dbcontext is instantiate at request time this would generate exception, so we need to use factory and explicit "using" to explicitly manage dbcontext lifetime
var optionsBuilder = new DbContextOptionsBuilder<TestDB>();
optionsBuilder.UseSqlServer(Configuration.GetConnectionString("Test"));
services.AddSingleton(s => new Func<TestDB>(() => new TestDB(optionsBuilder.Options)));
Service class
public class TestService
{
private readonly TestDB _testDb;
private readonly Func<TestDB> _testDbfunct;
public TestService(TestDB testDb, Func<TestDB> testDbfunct)
{
_testDb = testDb;
_testDbfunct = testDbfunct;
}
//mixed classical request dbcontext and factory approaches
public async Task<string> TestMultiple(int id, bool newConnection = false) //we need to add optional newConnection parameter and the end of other parameters
{
//use request connection (_testDb) if newconnection is false, otherwise instantiate a new connection using factory. null inside "using" means that "using" is not used
//use newconnection = true if you want run parallel queries, so you need different connection for each one
TestDB testDb = _testDb;
using (newConnection ? testDb = _testDbfunct() : null)
{
return await (from t in testDb.Table where t.id == id select t.code).FirstOrDefaultAsync();
}
}
}
Test class
//instantiate dbcontext for each call, so we can parallellize
[TestMethod]
public async Task TestMultiple()
{
//test1 and test2 starts in parallel without test2 that need to wait the end of test1. For each one a Task in returned
var test1 = _testService.TestMultiple(1,true);
var test2 = _testService.TestMultiple(2,true);
//wait test1 and test2 return
string code1 = await test1;
string code2 = await test2;
}
//use request dbcontext
[TestMethod]
public async Task TestClassic()
{
string code = await _testService.TestMultiple(3);
}
NB: in new .net core 5 you can use buildin AddDbContextFactory instead of create a custom factory as in my example
回答3:
just write:
var settings = await _employeeRepository.GetEmployeeSettings(employeeId.Value);
var workPositions = await _employeeRepository.GetWorkPositions(employeeId.Value);
Yeap. EF Core doesn't support multiple parallel operations being run on the same context instance. You should always wait for an operation to complete before beginning the next operation. This is typically done by using the await keyword on each async operation. Look at https://docs.microsoft.com/en-us/ef/core/querying/async
来源:https://stackoverflow.com/questions/58122227/how-to-make-parallel-async-queries-in-ef-core-3-0-with-repository-pattern