Я ищу способ глобально обернуть вызовы БД в фоновом задании Hangfire в транзакции, аналогично реализации IAsyncActionFilter в AspNetCore.
Я пытался реализовать IServerFilter, но, похоже, я не получаю один и тот же экземпляр DbContext при выполнении моего запроса в задании, поэтому транзакция всегда равна нулю. (Откат по ошибке намеренно еще не реализован.)
public class DbContextTransactionFilter : IServerFilter
{
private readonly MyContext _dbContext;
public DbContextTransactionFilter(MyContext dbContext)
{
_dbContext = dbContext;
}
public void OnPerforming(PerformingContext filterContext)
{
Task.Run(async () => await _dbContext.BeginTransactionAsync());
}
public void OnPerformed(PerformedContext filterContext)
{
Task.Run(async () => await _dbContext.CommitTransactionAsync());
}
}
Каков предлагаемый способ сделать это?
Редактировать 1:
Так я реализовалIAsyncActionFilter и я собираюсь сделать что-то подобное для заданий Hangfire.
public class DbContextTransactionFilter : IAsyncActionFilter
{
private readonly MyContext _dbContext;
public DbContextTransactionFilter(MyContext dbContext)
{
_dbContext = dbContext;
}
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
try
{
await _dbContext.BeginTransactionAsync();
var actionExecuted = await next();
if (actionExecuted.Exception != null && !actionExecuted.ExceptionHandled)
{
_dbContext.RollbackTransaction();
}
else
{
await _dbContext.CommitTransactionAsync();
}
}
catch (Exception)
{
_dbContext.RollbackTransaction();
throw;
}
}
}
Редактировать 2: Вот так я регистрирую контекст БД и фильтр в классе запуска:
services.AddDbContext<MyContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("MyDatabase")));
services.AddScoped<DbContextTransactionFilter>();
services.AddHangfire((provider, configuration) => configuration
.UseFilter(provider.GetRequiredService<DbContextTransactionFilter>())
);
services.AddHangfireServer();