question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

BackgroundJobManager can't insert record because DbContext is already used

See original GitHub issue

GitHub 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:

  1. if DB query and excel generation are executed into a 10 seconds time frame, the result is directly returned by the HttpResponse
  2. 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:closed
  • Created 2 years ago
  • Comments:5 (3 by maintainers)

github_iconTop GitHub Comments

1reaction
ismcagdascommented, Dec 7, 2021

@egrassinode I will test this but it might take some time.

0reactions
ismcagdascommented, Jan 31, 2022

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.

Read more comments on GitHub >

github_iconTop 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 >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found