Hi,
Thks @oguzhanagir ! Didn't see that one. I've tested and it's working :)
It is a dependency issue because I need to include Hangfire core package to get access to JobStorage :
using System.Linq;
using Microsoft.EntityFrameworkCore;
using Abp.Collections.Extensions;
using Abp.BackgroundJobs;
using Abp.Dependency;
using MyApp.FileReferences.Generating;
using MyApp.Notifications;
using Abp.Domain.Uow;
using System;
using Abp.Extensions;
using MyApp.Events.Search;
using System.Linq.Dynamic.Core;
using System.Threading.Tasks;
using Hangfire;
namespace MyApp.Events.Exporting
{
public class FileDocIoGeneratorJob : AsyncBackgroundJob<FileDocIoGeneratorJobArgs>, ITransientDependency
{
private readonly IFileDocIoGenerator _fileDocIoGenerator;
private readonly IUnitOfWorkManager _unitOfWorkManager;
private readonly IAppNotifier _appNotifier;
private readonly IEventQueryManager _eventQueryManager;
public FileDocIoGeneratorJob(IFileDocIoGenerator fileDocIoGenerator,
IUnitOfWorkManager unitOfWorkManager,
IAppNotifier appNotifier,
IEventQueryManager eventQueryManager)
{
_fileDocIoGenerator = fileDocIoGenerator;
_appNotifier = appNotifier;
_unitOfWorkManager = unitOfWorkManager;
_eventQueryManager = eventQueryManager;
}
public override async Task ExecuteAsync(FileDocIoGeneratorEventForecastsJobArgs args)
{
await _unitOfWorkManager.WithUnitOfWorkAsync(async () =>
{
using (UnitOfWorkManager.Current.SetTenantId(args.TenantId))
{
try
{
var events = await _eventQueryManager.GetEventsQueryable().Take(maxExportCount).ToListAsync();
if (events.IsNullOrEmpty())
return;
file = await _fileDocIoGenerator.GenerateFileAsync(events);
await _appNotifier.FileGenerationJobSucceedAsync(file, "FileGenerationJobForSucceed", args.UserIdentifier);
}
catch (Exception ex)
{
var retry = JobStorage.Current.GetConnection().GetJobParameter("jobId", "RetryCount");
Logger.Error("File generation job for event forecasts failed", ex);
if(retry == "10")
await _appNotifier.FileGenerationJobFailedAsync("FileGenerationJobForFailed", args.UserIdentifier);
}
}
});
}
}
}
Since ABP backgroundjob abstracts Hangfire, I don't know how to proceed to get retryCount parameter
Hi,
Yes for sure ! I know your documentation for a while now ;) Have a look to my question please. Are you able to retreive "retryCount" Hangfire property with ABP ?
Tks
Hi,
Is there a way to get Hangfire job context with ABP 9.3 ? I need to get "retryCount" value on job execution.
This should be possible with following Hangfire command : JobStorage.Current.GetConnection().GetJobParameter(jobId, "RetryCount")
I tried it by adding Hangfire nuget package directly but I'm getting a dependency error :
'MyJob' is waiting for the following dependencies:
- Service 'Hangfire.Server.PerformContext' which was not registered.
Great ! Thank you
Hi @m.aliozkaya,
Thanks for this feedback. I also use it in other parts of the code without any issue.
Did you try doing it from a background job like I did ?
Hi support team,
I'm facing an issue with abp 9.3.0. We have a bacground job that updates some tenant data. Data is being saved successfuly.
I wanted to avoid loosing LastModifierUserId in this case.
Thus, I'm using UnitOfWorkManager.Current.DisableAuditing(AbpAuditFields.LastModifierUserId, AbpAuditFields.LastModificationTime)
But this is only preventing LastModificationTime to be updated. LastModifierUserId is being saved as null value and I don't understand why.
Here is the code :
public class UpdateStatusJob : AsyncBackgroundJob<UpdateStatusJobArgs>, ITransientDependency
{
private readonly IDataManager _dataManager;
private readonly IUnitOfWorkManager _unitOfWorkManager;
private readonly IInvoiceManager _invoiceManager;
public UpdateStatusJob(IDataManager dataManager,
IInvoiceManager invoiceManager,
IUnitOfWorkManager unitOfWorkManager)
{
_unitOfWorkManager = unitOfWorkManager;
_dataManager = dataManager;
_invoiceManager = invoiceManager;
}
public override async Task ExecuteAsync(UpdateStatusJobArgs args)
{
await _unitOfWorkManager.WithUnitOfWorkAsync(async () =>
{
using (UnitOfWorkManager.Current.SetTenantId(args.TenantId))
{
using (UnitOfWorkManager.Current.DisableAuditing(AbpAuditFields.LastModifierUserId, AbpAuditFields.LastModificationTime))
{
if (args.InvoiceId.HasValue)
{
//Initialize job
var invoiceReport = await _invoiceManager.GetInvoice(args.InvoiceId.Value);
var updateResult = await _dataManager.UpdateStatus(invoiceReport, args.TenantId);
if (updateResult.Success == false)
{
new Exception("Updating invoice status failed !");
}
}
}
}
});
}
}
I can see in debug that LastModifierUserId is disabled in current UOW. When looking at the logs, I can see following SQL request :
UPDATE [AbpTenants] SET [AdditionalCreditAmount] = @p0
OUTPUT 1
WHERE [Id] = @p1;
UPDATE [LInvoices] SET [Status] = @p2, [IsCreditUsed] = @p3, [LastModifierUserId] = @p4
OUTPUT 1
WHERE [Id] = @p5;
I've tested several ways to begin UOW but the issue is always the same.
Do you see something wrong here ? Is it a bug on the framework ?
@ismcagdas, I have more information, the error reported on server side is :
StatusCode cannot be set because the response has already started
here are the server logs :
WARN 2024-06-07 13:57:50,467 [110 ] e.Diagnostics.ExceptionHandlerMiddleware - The response has already started, the error handler will not be executed.
ERROR 2024-06-07 13:57:50,468 [110 ] AspNetCore.Server.IIS.Core.IISHttpServer - Connection ID "11024811889413598794", Request ID "80002efa-0000-9900-b63f-84710c7967bb": An unhandled exception was thrown by the application.
System.InvalidOperationException: StatusCode cannot be set because the response has already started.
at Microsoft.AspNetCore.Server.IIS.Core.IISHttpContext.ThrowResponseAlreadyStartedException(String name)
at Microsoft.AspNetCore.Server.IIS.Core.IISHttpContext.Microsoft.AspNetCore.Http.Features.IHttpResponseFeature.set_StatusCode(Int32 value)
at Abp.AspNetCore.Mvc.ExceptionHandling.AbpExceptionFilter.HandleAndWrapException(ExceptionContext context, WrapResultAttribute wrapResultAttribute)
at Abp.AspNetCore.Mvc.ExceptionHandling.AbpExceptionFilter.OnException(ExceptionContext context)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResourceFilter>g__Awaited|25_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed context)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeFilterPipelineAsync>g__Awaited|20_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
at Microsoft.AspNetCore.Localization.RequestLocalizationMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
at Abp.AspNetZeroCore.Web.Authentication.JwtBearer.JwtTokenMiddleware.<>c__DisplayClass0_0.<<UseJwtTokenMiddleware>b__0>d.MoveNext()
--- End of stack trace from previous location ---
at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
at MyApp.Web.Startup.Startup.<>c.<<Configure>b__5_1>d.MoveNext() in D:\a\1\s\aspnet-core\src\MyApp.Web.Host\Startup\Startup.cs:line 193
--- End of stack trace from previous location ---
at Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddlewareImpl.<Invoke>g__Awaited|8_0(ExceptionHandlerMiddlewareImpl middleware, HttpContext context, Task task)
at Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddlewareImpl.HandleException(HttpContext context, ExceptionDispatchInfo edi)
at Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddlewareImpl.<Invoke>g__Awaited|8_0(ExceptionHandlerMiddlewareImpl middleware, HttpContext context, Task task)
at Microsoft.AspNetCore.Diagnostics.StatusCodePagesMiddleware.Invoke(HttpContext context)
at Abp.AspNetCore.Security.AbpSecurityHeadersMiddleware.Invoke(HttpContext httpContext)
at Microsoft.AspNetCore.Server.IIS.Core.IISHttpContextOfT`1.ProcessRequestAsync()
The problem is occuring in the startup.cs file, here is the code line :
app.Use(async (context, next) => { await next(); if (context.Response.StatusCode == 404 && !Path.HasExtension(context.Request.Path.Value)) { context.Request.Path = "/index.html"; await next(); } });
Only POST requests are firing this exception ; If the user waits for 5/10 min, the problem disappears. Seems to be linked with JwtTokenMiddleware... maybe with a worker in charge of jwt tokens
Hi @ismcagdas,
Sure, I've just sent you an email.
Hi @Ismcagdas,
No we don't have any error message logged in server side. This seems to be linked with Chrome browsers. The same exact request sent with edge or Firefox is working fine. Here is the message displayed :
It is not something we can reproduce easily, it only happens for some users. We noticed only on Chrome with Windows 11.
In Kudu detailed logs, I found this :
Any idea ?
We really have to fix this as it is impacting several users right now. Tks