BackgroundJobManager can't insert record because DbContext is already used
See original GitHub issueGitHub Issues
Informations:
- Abp package version: 5.14.0
- Your base framework: .Net Core.
- Exception message and stack trace if available:
InvalidOperationException: 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. For more information on how to avoid threading issues with DbContext, see https://go.microsoft.com/fwlink/?linkid=2097913.
STACK TRACE: at Microsoft.EntityFrameworkCore.Internal.ConcurrencyDetector.EnterCriticalSection()
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChangesAsync(IList`1 entriesToSave, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChangesAsync(DbContext _, Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerExecutionStrategy.ExecuteAsync[TState,TResult](TState state, Func`4 operation, Func`4 verifySucceeded, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.DbContext.SaveChangesAsync(Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken)
at Abp.EntityFrameworkCore.AbpDbContext.SaveChangesAsync(CancellationToken cancellationToken)
at Abp.Zero.EntityFrameworkCore.AbpZeroCommonDbContext`3.SaveChangesAsync(CancellationToken cancellationToken)
at Abp.EntityFrameworkCore.Uow.EfCoreUnitOfWork.SaveChangesInDbContextAsync(DbContext dbContext)
at Abp.EntityFrameworkCore.Uow.EfCoreUnitOfWork.SaveChangesAsync()
at Abp.BackgroundJobs.BackgroundJobManager.EnqueueAsync[TJob,TArgs](TArgs args, BackgroundJobPriority priority, Nullable`1 delay)
at Abp.Domain.Uow.UnitOfWorkInterceptor.InternalInterceptAsynchronous[TResult](IInvocation invocation)
at BilancioSociale.Report.ReportAppService.DownloadBilancioReport(PagedReportRequestDto input) in C:\\GitHub\\Platform\\5.8.1\\aspnet-core\\src\\Application\\Report\\ReportAppService.cs:line 134
at lambda_method(Closure , Object )
at Microsoft.Extensions.Internal.ObjectMethodExecutorAwaitable.Awaiter.GetResult()
at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.AwaitableObjectResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeActionMethodAsync>g__Awaited|12_0(ControllerActionInvoker invoker, ValueTask`1 actionResultValueTask)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeNextActionFilterAsync>g__Awaited|10_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeInnerFilterAsync>g__Awaited|13_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextExceptionFilterAsync>g__Awaited|25_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
- Steps needed to reproduce the problem.
I’ve implemented a functionality that should generate a Excel File within a certains input data in two ways basically:
- if DB query and excel generation are executed into a 10 seconds time frame, the result is directly returned by the HttpResponse
- if the execution lasts more than 10 seconds, i enqueue the job using BackgroundJobManager as the guide explains.
but i’m getting the exception i reported above.
AppService method:
ReportAppService.cs
[HttpGet]
public async Task<ReportResultDto> DownloadBilancioReport(PagedReportRequestDto input)
{
var tokenSource = new CancellationTokenSource();
var token = tokenSource.Token;
Task<byte[]> exportTask = Task.Run(async () =>
{
var registration = token.Register(() =>
{
try { token.ThrowIfCancellationRequested(); } catch { }
});
using (var unitOfWork = _unitOfWorkManager.Begin(new UnitOfWorkOptions() { AsyncFlowOption = TransactionScopeAsyncFlowOption.Enabled, IsolationLevel = IsolationLevel.Serializable, Scope = TransactionScopeOption.RequiresNew, Timeout = TimeSpan.FromMilliseconds(CancellationExportTaskTime) }))
{
var fileBytes = await _exportationManager.ExportAsync(input.Anno, input.RegioneId, input.StrutturaId);
unitOfWork.Complete();
return fileBytes;
}
}, cancellationToken: token);
if (!exportTask.Wait(CancellationExportTaskTime))
{
var jobId = await _backgroundJobManager.EnqueueAsync<QueryAndCreateReportJob, ReportJobArg>(new ReportJobArg(input.Anno, input.RegioneId, input.StrutturaId));
try
{
tokenSource.Cancel();
}
catch (OperationCanceledException oce)
{
Logger.InfoFormat("ReportAppService.DownloadBilancioReport synchronous task was aborted because it exceeded the maximum execution time limit.");
}
return new ReportResultDto
{
Message = "L'elaborazione dell'export richesto richiede più tempo del previsto. Riceverai una email con il report in allegato al termine dell'operazione",
File = null
};
}
else
{
var byteArray = await exportTask;
return new ReportResultDto
{
Message = null,
File = new FileDto
{
FileName = $"EsportazioneBilanci{DateTime.Now.Date:yyyyMMdd}",
FileData = byteArray,
MimeType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
}
};
}
}
QueryAndCreateReportJob.cs
using Abp.BackgroundJobs;
using Abp.Dependency;
using Abp.Domain.Uow;
using Abp.Net.Mail;
using System.IO;
using System.Threading.Tasks;
using System.Transactions;
namespace BilancioSociale.Report
{
public class QueryAndCreateReportJob : AsyncBackgroundJob<ReportJobArg>, ITransientDependency
{
private readonly ExportationManager _exportationManager;
private readonly IEmailSender _emailSender;
public QueryAndCreateReportJob(ExportationManager exportationManager, IEmailSender emailSender)
{
_unitOfWorkManager = unitOfWorkManager;
_emailSender = emailSender;
}
protected override async Task ExecuteAsync(ReportJobArg arg)
{
Logger.Info("QueryAndCreateReportJob.ExecuteAsync started..");
byte[] result = await _exportationManager.ExportAsync(arg.Anno, arg.RegioneId, arg.StrutturaId);
File.WriteAllBytes(@"C:\Temp\Test.xlsx", result);
Logger.Info("QueryAndCreateReportJob.ExecuteAsync completed!!!");
}
}
}
As i can see, EntityFramework DbContext is registered as Transient but seems it’s not.
The first execution is cancelled from the CancellationToken
, used into the Task.Run
.
Issue Analytics
- State:
- Created 2 years ago
- Comments:5 (3 by maintainers)
Top Results From Across the Web
c# - "The operation cannot be completed because ...
I am new at Entity Framework Code first and I am building a small app to get used to it.When the site runs...
Read more >DbContext Lifetime, Configuration, and Initialization
This article shows basic patterns for initialization and configuration of a DbContext instance. The DbContext lifetime. The lifetime of a ...
Read more >What's New in EF Core 7.0
EF7 simplifies the SQL in this case by using OUTPUT INSERTED . While this simplification is not valid for many other cases, it...
Read more >Adding data via the DbContext
The key methods for adding/inserting entities via the DbContext are. Add<TEntity>(TEntity entity); Add(object entity) ...
Read more >Best Practices in Using the DbContext in EF Core
DbContext controls its lifespan; after your data access request is complete, DbContext will automatically close the database connection for you.
Read more >
Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free
Top Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
@egrassinode I will test this but it might take some time.
Hi @egrassinode sorry for my late reply. I have investigated the problem and it seems like current DbContext creation logic doesn’t support this usage, see https://github.com/aspnetboilerplate/aspnetboilerplate/issues/1903#issuecomment-283577173. Sorry that I forgot this and kept you waiting.