i create an app with .NET 6.0 and Blazor Server. Sometimes i got an error in my application with message A second operation was started on this context instance before a previous operation completed.
I use normally pattern repository. I have repository for every table and unitofwork to save and other. In my previous question was explained me, that i use DbContext bad. Because it's shared dbcontext over repository and unit of work. I read many articles and usual recommendations was use only custom DbContext. I think it's not problem for me. I need to some changes in my framework but np. But i don't understand one thing. How i can do my custom generic queries for every dbset? Was there many articles about use repository with context factory and without unity of work. I don't really like the fact that all the operations are separate and access to database many times.
What is your recommendation and your expirience? Do you have any articles or tutorials about that?
I'm really just looking for how to make it the "best" and also usable for me. Thank you very much :)
CodePudding user response:
DbContext
's are designed to be short-lived. Create a new instance of your context for each operation to resolve your issue.
Your DbContext would be something like this to make it easy to construct.
public class SomeDbContext : DbContext
{
private readonly IConfiguration configuration;
public SomeDbContext(IConfiguration configuration)
{
this.configuration = configuration;
// this.Database.Migrate(); <- optional
}
DbSet<SomeValue> SomeValues { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
string connectionString =
this.configuration.GetConnectionString("DefaultConnection");
optionsBuilder.UseSqlServer(connectionString);
}
public async ValueTask<SomeValue> InsertSomeValueAsync(SomeValue someValue)
{
using var someDbContext = new SomeDbContext(this.configuration);
EntityEntry<SomeValue> entityEntry =
await someDbContext.SomeValues.AddAsync(entity: someValue);
await someDbContext.SaveChangesAsync();
return entityEntry.Entity;
}
}
The InsertSomeValueAsync method could easily be made generic.
public async ValueTask<T> InsertSomeValueAsync<T>(T someValue)
where T : class
{
using var someDbContext = new SomeDbContext(this.configuration);
EntityEntry<T> entityEntry =
await someDbContext.AddAsync(someValue);
await someDbContext.SaveChangesAsync();
return entityEntry.Entity;
}
CodePudding user response:
One option is to use a DBContextFactory
to manage the contexts.
Here's some example code from one of my applications.
Program
var dbConnectionString = builder.Configuration.GetValue<string>("MyConfiguration:ConnectionString");
builder.Services.AddDbContextFactory<MySqlServerDbContext>(options => options.UseSqlServer(dbConnectionString), ServiceLifetime.Singleton);
And using it in a (generic) data broker:
public class ServerDataBroker
: IDataBroker
{
private IDbContextFactory<MySqlServerDbContext> _dbContextFactory;
public ServerDataBroker(IDbContextFactory<MySqlServerDbContext> factory)
=> _dbContextFactory = factory;
//.....
public async ValueTask<int> GetRecordCountAsync<TRecord>() where TRecord : class, new()
{
using var context = _dbContextFactory.CreateDbContext();
var dbSet = context.Set<TRecord>();
return dbSet is not null
? await dbSet.CountAsync()
: 0;
}
//.....
There's an MS-Docs article about it here - https://docs.microsoft.com/en-us/ef/core/dbcontext-configuration/.
On testing, I use EF InMemory - you can use the Factory with it. Ask if you want to be pointed to some code.