Reputation: 685
Net core. I am using EF core, Unit Of Work and Repository pattern. In my code I am releasing my main thread using await Task.Delay(150); Then I am trying to get DB Context but exception says Dbcontext is disposed. So Is there any way I can get DBContext again after the main thread disposes? below few are my code snippets,
services.AddDbContext<MyContext>(options =>
options.UseSqlServer(this.Configuration["AzureSQLConnectionString"]));
services.AddScoped<IUnitOfWork, UnitOfWork>();
Below is my method
public Class MyTestBusinessLogic : IMyTestBusinessLogic
{
public MyTestBusinessLogic(IUnitOfWork unitOfWork)
{
UnitOfWork = unitOfWork ?? throw new ArgumentNullException(nameof(unitOfWork));
MyTestRepository= UnitOfWork.GetRepository<MyTest>();
}
private IDbRepository<MyTest> MyTestRepository{ get; set; }
public async Task MyMethod()
{
await MyTestRepository.GetFirstOrDefaultAsync(x => x.Id == Id); // works here
await Task.Delay(150);
// here after my context disposes
await MyTestRepository.GetFirstOrDefaultAsync(x => x.Id == Id); //this wont work and It will throw error inside GetFirstOrDefaultAsync method.
}
}
GetFirstOrDefaultAsync method
public class DbRepository<T> : BaseRepository<T>, IDbRepository<T> where T : class
{
private readonly DbContext dbContext;
private readonly DbSet<T> dbSet;
public DbRepository(DbContext dbContext)
{
this.dbContext = dbContext;
this.dbSet = dbContext?.Set<T>();
}
public async Task<T> GetFirstOrDefaultAsync(Expression<Func<T, bool>> filter = null, Func<IQueryable<T>, IOrderedQueryable<T>> orderBy = null, params Expression<Func<T, object>>[] includes)
{
IQueryable<T> query = this.dbSet; // throws exception saying cannot use disposed object
foreach (Expression<Func<T, object>> include in includes)
{
query = query.Include(include);
}
if (filter != null)
{
query = query.Where(filter);
}
if (orderBy != null)
{
query = orderBy(query);
}
return await query.FirstOrDefaultAsync().ConfigureAwait(false);
}
}
Can someone help me to fix this? Any help would be appreciated. Thanks
Upvotes: 1
Views: 590
Reputation: 457057
I am calling without await
This is your problem. So-called "fire and forget" is dangerous on ASP.NET, and is only appropriate when it doesn't matter whether the code runs or not. In your case, since you need the code to run, omitting the await
is inappropriate.
The correct solution is to call MyMethod
with await
.
If you absolutely, must return early, then the next-best solution is to introduce a reliable queue (e.g., Azure Queue / Amazon SMS) and a background processor (e.g., Azure Function / Amazon Lambda / .NET Core BackgroundService) that processes messages on that queue. Once you have these, when you have a situation where you need to return early and do the work later, then that code will place a message into the queue and then return a result. When the background processor retrieves the message from the queue, it will then do the work.
Upvotes: 2
Reputation: 12629
I presume that you are running MyMethod
without await
ing it. So while main thread completes execution it will destroy dbcontext
or any scoped
objects injected from DI
.
To fulfill your requirement you can use IServiceScopeFactory
which is singleton
object and added by .net core
framework. Using it you can create scope
using serviceScopeFactory.CreateScope()
and create new instance of required dependencies
using scope.ServiceProvider.GetRequiredService<MyDbContext>();
.
So in such scenario you can use like below. Please refer comment for understanding. I don't know exact structure of you application so I added IServiceScopeFactory
to your MyTest
for example/test. But as per your code you can move it elsewhere I guess in UnitOfWork
.
public Class MyTestBusinessLogic : IMyTestBusinessLogic
{
// Add serviceScopeFactory
private readonly IServiceScopeFactory serviceScopeFactory;
// Inject IServiceScopeFactory and assign to class object.
public MyTestBusinessLogic(IServiceScopeFactory serviceScopeFactory)
{
this.serviceScopeFactory = serviceScopeFactory;
// Create scope with IServiceScopeFactory.CreateScope()
var scope = serviceScopeFactory.CreateScope();
// GetRequiredService unitOfWork
UnitOfWork = scope.ServiceProvider.GetRequiredService<IUnitOfWork>();
// UnitOfWork = unitOfWork ?? throw new ArgumentNullException(nameof(unitOfWork));
MyTestRepository= UnitOfWork.GetRepository<MyTest>();
}
private IDbRepository<MyTest> MyTestRepository{ get; set; }
public async Task MyMethod()
{
await MyTestRepository.GetFirstOrDefaultAsync(x => x.Id == Id); // works here
await Task.Delay(150);
// here after my context disposes
await MyTestRepository.GetFirstOrDefaultAsync(x => x.Id == Id); //this wont work and It will throw error inside GetFirstOrDefaultAsync method.
}
}
Upvotes: 1