fix: SqlSugar 与 EF Outbox 连接冲突时回落短事务
All checks were successful
Build and Deploy / deploy (push) Successful in 59s
All checks were successful
Build and Deploy / deploy (push) Successful in 59s
发布集成事件前先挂 Outbox 上下文;连接不匹配时 RequiresNew 写 Outbox,避免预报等按钮报 transaction not associated。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
6c3a094126
commit
fa9777bd08
@ -1,3 +1,4 @@
|
|||||||
|
using System.Data;
|
||||||
using Fake.EntityFrameworkCore;
|
using Fake.EntityFrameworkCore;
|
||||||
using Fake.EventBus.Distributed;
|
using Fake.EventBus.Distributed;
|
||||||
using Fake.EventBus.IntegrationEventLog;
|
using Fake.EventBus.IntegrationEventLog;
|
||||||
@ -9,6 +10,7 @@ namespace Oms.Application.Messaging;
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 可靠发布 IntegrationEvent:事务 UoW 内写入 Outbox(由 Fake.EventBus.IntegrationEventLog 投递)。
|
/// 可靠发布 IntegrationEvent:事务 UoW 内写入 Outbox(由 Fake.EventBus.IntegrationEventLog 投递)。
|
||||||
|
/// SqlSugar 与 EF 默认各持连接;若环境事务已由 SqlSugar 占用,则改用 RequiresNew 短事务只写 Outbox。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static class OmsIntegrationEventPublisher
|
internal static class OmsIntegrationEventPublisher
|
||||||
{
|
{
|
||||||
@ -21,6 +23,9 @@ internal static class OmsIntegrationEventPublisher
|
|||||||
{
|
{
|
||||||
if (OmsOutboxUnitOfWork.IsAmbientTransactional(unitOfWorkManager))
|
if (OmsOutboxUnitOfWork.IsAmbientTransactional(unitOfWorkManager))
|
||||||
{
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await EnsureOutboxDbContextAsync(unitOfWorkManager);
|
||||||
await eventBus.PublishAsync(@event, cancellationToken);
|
await eventBus.PublishAsync(@event, cancellationToken);
|
||||||
logger.LogDebug(
|
logger.LogDebug(
|
||||||
"Outbox enqueued {EventName} Id={EventId} (ambient TX)",
|
"Outbox enqueued {EventName} Id={EventId} (ambient TX)",
|
||||||
@ -28,17 +33,31 @@ internal static class OmsIntegrationEventPublisher
|
|||||||
@event.Id);
|
@event.Id);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
catch (Exception ex) when (IsTxConnectionMismatch(ex))
|
||||||
// 无环境事务时开启短事务只写 Outbox,并告警:业务落库与事件非原子。
|
{
|
||||||
|
// SqlSugar 已在另一条连接上开事务时,EF 无法征用该 DbTransaction。
|
||||||
|
logger.LogWarning(
|
||||||
|
ex,
|
||||||
|
"Ambient UoW TX incompatible with EF Outbox; using short outbox TX. Event={EventName} Id={EventId}",
|
||||||
|
@event.GetType().Name,
|
||||||
|
@event.Id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
logger.LogWarning(
|
logger.LogWarning(
|
||||||
"Publish {EventName} Id={EventId} without ambient transactional UoW; opening short outbox TX",
|
"Publish {EventName} Id={EventId} without ambient transactional UoW; opening short outbox TX",
|
||||||
@event.GetType().Name,
|
@event.GetType().Name,
|
||||||
@event.Id);
|
@event.Id);
|
||||||
|
}
|
||||||
|
|
||||||
using var uow = unitOfWorkManager.Begin(OmsOutboxUnitOfWork.Transactional());
|
using var uow = unitOfWorkManager.Begin(new UnitOfWorkAttribute
|
||||||
// 先打开 Outbox DbContext 以产生可征用的 DbTransaction
|
{
|
||||||
var dbProvider = uow.ServiceProvider.GetRequiredService<IEfDbContextProvider<IntegrationEventLogContext>>();
|
IsTransactional = true,
|
||||||
await dbProvider.GetDbContextAsync();
|
RequiresNew = true,
|
||||||
|
IsolationLevel = IsolationLevel.ReadCommitted
|
||||||
|
});
|
||||||
|
await EnsureOutboxDbContextAsync(unitOfWorkManager);
|
||||||
await eventBus.PublishAsync(@event, cancellationToken);
|
await eventBus.PublishAsync(@event, cancellationToken);
|
||||||
await uow.CompleteAsync(cancellationToken);
|
await uow.CompleteAsync(cancellationToken);
|
||||||
}
|
}
|
||||||
@ -65,4 +84,30 @@ internal static class OmsIntegrationEventPublisher
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 业务写库前先打开 Outbox DbContext,尽量让 EF 先占住环境事务连接,供 SqlSugar 征用。
|
||||||
|
/// </summary>
|
||||||
|
public static Task EnsureOutboxDbContextAsync(IUnitOfWorkManager unitOfWorkManager)
|
||||||
|
{
|
||||||
|
var current = unitOfWorkManager.Current
|
||||||
|
?? throw new InvalidOperationException("No ambient unit of work");
|
||||||
|
var dbProvider = current.ServiceProvider
|
||||||
|
.GetRequiredService<IEfDbContextProvider<IntegrationEventLogContext>>();
|
||||||
|
return dbProvider.GetDbContextAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsTxConnectionMismatch(Exception ex)
|
||||||
|
{
|
||||||
|
for (var e = ex; e != null; e = e.InnerException!)
|
||||||
|
{
|
||||||
|
if (e.Message.Contains("not associated with the current connection", StringComparison.OrdinalIgnoreCase)
|
||||||
|
|| e.Message.Contains("associated with the current connection", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
using Fake.Application;
|
using Fake.Application;
|
||||||
using Fake.DependencyInjection;
|
using Fake.DependencyInjection;
|
||||||
|
using Fake.UnitOfWork;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
@ -30,6 +31,7 @@ public sealed class OrderBatchJobCoordinator(
|
|||||||
IObjectStorage fileStorage,
|
IObjectStorage fileStorage,
|
||||||
IOrderBatchJobPublisher publisher,
|
IOrderBatchJobPublisher publisher,
|
||||||
OrderBatchJobProcessor batchJobProcessor,
|
OrderBatchJobProcessor batchJobProcessor,
|
||||||
|
IUnitOfWorkManager unitOfWorkManager,
|
||||||
IOptions<OrderBatchRabbitMqOptions> batchOptions,
|
IOptions<OrderBatchRabbitMqOptions> batchOptions,
|
||||||
ILogger<OrderBatchJobCoordinator> logger) : IScopedDependency
|
ILogger<OrderBatchJobCoordinator> logger) : IScopedDependency
|
||||||
{
|
{
|
||||||
@ -76,6 +78,12 @@ public sealed class OrderBatchJobCoordinator(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 先打开 EF Outbox 上下文,尽量让环境事务由 EF 占连接,避免 SqlSugar 先开事务后 Outbox 征用失败。
|
||||||
|
if (OmsOutboxUnitOfWork.IsAmbientTransactional(unitOfWorkManager))
|
||||||
|
{
|
||||||
|
await OmsIntegrationEventPublisher.EnsureOutboxDbContextAsync(unitOfWorkManager);
|
||||||
|
}
|
||||||
|
|
||||||
var now = DateTime.Now;
|
var now = DateTime.Now;
|
||||||
var job = new OrderBatchJob
|
var job = new OrderBatchJob
|
||||||
{
|
{
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
using System.Data;
|
||||||
using Fake.Application;
|
using Fake.Application;
|
||||||
using Fake.Security.Claims;
|
using Fake.Security.Claims;
|
||||||
using Fake.UnitOfWork;
|
using Fake.UnitOfWork;
|
||||||
@ -22,7 +23,7 @@ public partial class OrderService
|
|||||||
/// 创建订单批量异步任务并入队 RabbitMQ。
|
/// 创建订单批量异步任务并入队 RabbitMQ。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[HttpPost("/order/batch/jobs")]
|
[HttpPost("/order/batch/jobs")]
|
||||||
[UnitOfWork(IsTransactional = true)]
|
[UnitOfWork(IsTransactional = true, IsolationLevel = IsolationLevel.ReadCommitted)]
|
||||||
public Task<OrderBatchJobOutput> CreateBatchJobAsync([FromBody] OrderBatchJobCreateInput input)
|
public Task<OrderBatchJobOutput> CreateBatchJobAsync([FromBody] OrderBatchJobCreateInput input)
|
||||||
{
|
{
|
||||||
return BatchJobCoordinator.CreateAndEnqueueAsync(input, CurrentUser.GetId<int>());
|
return BatchJobCoordinator.CreateAndEnqueueAsync(input, CurrentUser.GetId<int>());
|
||||||
@ -75,7 +76,7 @@ public partial class OrderService
|
|||||||
/// 异步批量创建轨迹订单(替代前端逐条调用)。
|
/// 异步批量创建轨迹订单(替代前端逐条调用)。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[HttpPost("/order/batch/track-upsert")]
|
[HttpPost("/order/batch/track-upsert")]
|
||||||
[UnitOfWork(IsTransactional = true)]
|
[UnitOfWork(IsTransactional = true, IsolationLevel = IsolationLevel.ReadCommitted)]
|
||||||
public Task<OrderBatchJobOutput> BatchTrackUpsertAsync([FromBody] OrderBatchJobCreateInput input)
|
public Task<OrderBatchJobOutput> BatchTrackUpsertAsync([FromBody] OrderBatchJobCreateInput input)
|
||||||
{
|
{
|
||||||
input.Action = OrderBatchActionCodes.TrackUpsertSmallBag;
|
input.Action = OrderBatchActionCodes.TrackUpsertSmallBag;
|
||||||
@ -97,7 +98,7 @@ public partial class OrderService
|
|||||||
/// 异步批量预报下游订单。
|
/// 异步批量预报下游订单。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[HttpPost("/order/batch/downstream-forward")]
|
[HttpPost("/order/batch/downstream-forward")]
|
||||||
[UnitOfWork(IsTransactional = true)]
|
[UnitOfWork(IsTransactional = true, IsolationLevel = IsolationLevel.ReadCommitted)]
|
||||||
public Task<OrderBatchJobOutput> BatchDownstreamForwardAsync([FromBody] OrderBatchJobCreateInput input)
|
public Task<OrderBatchJobOutput> BatchDownstreamForwardAsync([FromBody] OrderBatchJobCreateInput input)
|
||||||
{
|
{
|
||||||
input.Action = OrderBatchActionCodes.DownstreamForwardCreateOne;
|
input.Action = OrderBatchActionCodes.DownstreamForwardCreateOne;
|
||||||
@ -115,7 +116,7 @@ public partial class OrderService
|
|||||||
string.IsNullOrWhiteSpace(authorization) ? null : authorization);
|
string.IsNullOrWhiteSpace(authorization) ? null : authorization);
|
||||||
}
|
}
|
||||||
|
|
||||||
[UnitOfWork(IsTransactional = true)]
|
[UnitOfWork(IsTransactional = true, IsolationLevel = IsolationLevel.ReadCommitted)]
|
||||||
private Task<OrderBatchJobOutput> EnqueueOrderBatchAsync(string action, OrderBatchJobCreateInput input)
|
private Task<OrderBatchJobOutput> EnqueueOrderBatchAsync(string action, OrderBatchJobCreateInput input)
|
||||||
{
|
{
|
||||||
input.Action = action;
|
input.Action = action;
|
||||||
@ -126,7 +127,7 @@ public partial class OrderService
|
|||||||
/// 异步批量支付订单。
|
/// 异步批量支付订单。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[HttpPost("/order/batch/pay")]
|
[HttpPost("/order/batch/pay")]
|
||||||
[UnitOfWork(IsTransactional = true)]
|
[UnitOfWork(IsTransactional = true, IsolationLevel = IsolationLevel.ReadCommitted)]
|
||||||
public Task<OrderBatchJobOutput> BatchPayAsync([FromBody] OrderBatchJobCreateInput input) =>
|
public Task<OrderBatchJobOutput> BatchPayAsync([FromBody] OrderBatchJobCreateInput input) =>
|
||||||
EnqueueOrderBatchAsync(OrderBatchActionCodes.Pay, input);
|
EnqueueOrderBatchAsync(OrderBatchActionCodes.Pay, input);
|
||||||
|
|
||||||
@ -134,7 +135,7 @@ public partial class OrderService
|
|||||||
/// 异步批量预报订单。
|
/// 异步批量预报订单。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[HttpPost("/order/batch/pre-declare")]
|
[HttpPost("/order/batch/pre-declare")]
|
||||||
[UnitOfWork(IsTransactional = true)]
|
[UnitOfWork(IsTransactional = true, IsolationLevel = IsolationLevel.ReadCommitted)]
|
||||||
public Task<OrderBatchJobOutput> BatchPreDeclareAsync([FromBody] OrderBatchJobCreateInput input) =>
|
public Task<OrderBatchJobOutput> BatchPreDeclareAsync([FromBody] OrderBatchJobCreateInput input) =>
|
||||||
EnqueueOrderBatchAsync(OrderBatchActionCodes.PreDeclare, input);
|
EnqueueOrderBatchAsync(OrderBatchActionCodes.PreDeclare, input);
|
||||||
|
|
||||||
@ -142,7 +143,7 @@ public partial class OrderService
|
|||||||
/// 异步批量拦截订单。
|
/// 异步批量拦截订单。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[HttpPost("/order/batch/intercept")]
|
[HttpPost("/order/batch/intercept")]
|
||||||
[UnitOfWork(IsTransactional = true)]
|
[UnitOfWork(IsTransactional = true, IsolationLevel = IsolationLevel.ReadCommitted)]
|
||||||
public Task<OrderBatchJobOutput> BatchInterceptAsync([FromBody] OrderBatchJobCreateInput input) =>
|
public Task<OrderBatchJobOutput> BatchInterceptAsync([FromBody] OrderBatchJobCreateInput input) =>
|
||||||
EnqueueOrderBatchAsync(OrderBatchActionCodes.Intercept, input);
|
EnqueueOrderBatchAsync(OrderBatchActionCodes.Intercept, input);
|
||||||
|
|
||||||
@ -150,7 +151,7 @@ public partial class OrderService
|
|||||||
/// 异步批量取消拦截。
|
/// 异步批量取消拦截。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[HttpPost("/order/batch/cancel-intercept")]
|
[HttpPost("/order/batch/cancel-intercept")]
|
||||||
[UnitOfWork(IsTransactional = true)]
|
[UnitOfWork(IsTransactional = true, IsolationLevel = IsolationLevel.ReadCommitted)]
|
||||||
public Task<OrderBatchJobOutput> BatchCancelInterceptAsync([FromBody] OrderBatchJobCreateInput input) =>
|
public Task<OrderBatchJobOutput> BatchCancelInterceptAsync([FromBody] OrderBatchJobCreateInput input) =>
|
||||||
EnqueueOrderBatchAsync(OrderBatchActionCodes.CancelIntercept, input);
|
EnqueueOrderBatchAsync(OrderBatchActionCodes.CancelIntercept, input);
|
||||||
|
|
||||||
@ -158,7 +159,7 @@ public partial class OrderService
|
|||||||
/// 异步批量删除订单。
|
/// 异步批量删除订单。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[HttpPost("/order/batch/delete")]
|
[HttpPost("/order/batch/delete")]
|
||||||
[UnitOfWork(IsTransactional = true)]
|
[UnitOfWork(IsTransactional = true, IsolationLevel = IsolationLevel.ReadCommitted)]
|
||||||
public Task<OrderBatchJobOutput> BatchDeleteAsync([FromBody] OrderBatchJobCreateInput input) =>
|
public Task<OrderBatchJobOutput> BatchDeleteAsync([FromBody] OrderBatchJobCreateInput input) =>
|
||||||
EnqueueOrderBatchAsync(OrderBatchActionCodes.Delete, input);
|
EnqueueOrderBatchAsync(OrderBatchActionCodes.Delete, input);
|
||||||
|
|
||||||
@ -166,7 +167,7 @@ public partial class OrderService
|
|||||||
/// 异步批量打印面单。
|
/// 异步批量打印面单。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[HttpPost("/order/batch/print")]
|
[HttpPost("/order/batch/print")]
|
||||||
[UnitOfWork(IsTransactional = true)]
|
[UnitOfWork(IsTransactional = true, IsolationLevel = IsolationLevel.ReadCommitted)]
|
||||||
public Task<OrderBatchJobOutput> BatchPrintAsync([FromBody] OrderBatchJobCreateInput input) =>
|
public Task<OrderBatchJobOutput> BatchPrintAsync([FromBody] OrderBatchJobCreateInput input) =>
|
||||||
EnqueueOrderBatchAsync(OrderBatchActionCodes.Print, input);
|
EnqueueOrderBatchAsync(OrderBatchActionCodes.Print, input);
|
||||||
|
|
||||||
@ -174,7 +175,7 @@ public partial class OrderService
|
|||||||
/// 异步批量标记问题件。
|
/// 异步批量标记问题件。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[HttpPost("/order/batch/mark-problematic")]
|
[HttpPost("/order/batch/mark-problematic")]
|
||||||
[UnitOfWork(IsTransactional = true)]
|
[UnitOfWork(IsTransactional = true, IsolationLevel = IsolationLevel.ReadCommitted)]
|
||||||
public Task<OrderBatchJobOutput> BatchMarkProblematicAsync([FromBody] OrderBatchJobCreateInput input) =>
|
public Task<OrderBatchJobOutput> BatchMarkProblematicAsync([FromBody] OrderBatchJobCreateInput input) =>
|
||||||
EnqueueOrderBatchAsync(OrderBatchActionCodes.MarkProblematic, input);
|
EnqueueOrderBatchAsync(OrderBatchActionCodes.MarkProblematic, input);
|
||||||
|
|
||||||
@ -182,7 +183,7 @@ public partial class OrderService
|
|||||||
/// 异步批量重新排货。
|
/// 异步批量重新排货。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[HttpPost("/order/batch/reallocate")]
|
[HttpPost("/order/batch/reallocate")]
|
||||||
[UnitOfWork(IsTransactional = true)]
|
[UnitOfWork(IsTransactional = true, IsolationLevel = IsolationLevel.ReadCommitted)]
|
||||||
public Task<OrderBatchJobOutput> BatchReallocateAsync([FromBody] OrderBatchJobCreateInput input) =>
|
public Task<OrderBatchJobOutput> BatchReallocateAsync([FromBody] OrderBatchJobCreateInput input) =>
|
||||||
EnqueueOrderBatchAsync(OrderBatchActionCodes.Reallocate, input);
|
EnqueueOrderBatchAsync(OrderBatchActionCodes.Reallocate, input);
|
||||||
|
|
||||||
@ -190,7 +191,7 @@ public partial class OrderService
|
|||||||
/// 异步批量标记已认领。
|
/// 异步批量标记已认领。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[HttpPost("/order/batch/mark-claimed")]
|
[HttpPost("/order/batch/mark-claimed")]
|
||||||
[UnitOfWork(IsTransactional = true)]
|
[UnitOfWork(IsTransactional = true, IsolationLevel = IsolationLevel.ReadCommitted)]
|
||||||
public Task<OrderBatchJobOutput> BatchMarkClaimedAsync([FromBody] OrderBatchJobCreateInput input) =>
|
public Task<OrderBatchJobOutput> BatchMarkClaimedAsync([FromBody] OrderBatchJobCreateInput input) =>
|
||||||
EnqueueOrderBatchAsync(OrderBatchActionCodes.MarkClaimed, input);
|
EnqueueOrderBatchAsync(OrderBatchActionCodes.MarkClaimed, input);
|
||||||
|
|
||||||
@ -198,7 +199,7 @@ public partial class OrderService
|
|||||||
/// 异步批量更新处理状态。
|
/// 异步批量更新处理状态。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[HttpPost("/order/batch/handle-status-update")]
|
[HttpPost("/order/batch/handle-status-update")]
|
||||||
[UnitOfWork(IsTransactional = true)]
|
[UnitOfWork(IsTransactional = true, IsolationLevel = IsolationLevel.ReadCommitted)]
|
||||||
public Task<OrderBatchJobOutput> BatchHandleStatusUpdateAsync([FromBody] OrderBatchJobCreateInput input) =>
|
public Task<OrderBatchJobOutput> BatchHandleStatusUpdateAsync([FromBody] OrderBatchJobCreateInput input) =>
|
||||||
EnqueueOrderBatchAsync(OrderBatchActionCodes.HandleStatusUpdate, input);
|
EnqueueOrderBatchAsync(OrderBatchActionCodes.HandleStatusUpdate, input);
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
using Fake.Application;
|
using Fake.Application;
|
||||||
using Fake.ObjectStorage;
|
using Fake.ObjectStorage;
|
||||||
|
using Fake.UnitOfWork;
|
||||||
using Microsoft.Extensions.Logging.Abstractions;
|
using Microsoft.Extensions.Logging.Abstractions;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
using Moq;
|
using Moq;
|
||||||
@ -36,6 +37,8 @@ public class OrderBatchJobCoordinatorGovernanceTests
|
|||||||
actionExecutor.Object,
|
actionExecutor.Object,
|
||||||
batchOptions,
|
batchOptions,
|
||||||
NullLogger<OrderBatchJobProcessor>.Instance);
|
NullLogger<OrderBatchJobProcessor>.Instance);
|
||||||
|
var unitOfWorkManager = new Mock<IUnitOfWorkManager>(MockBehavior.Loose);
|
||||||
|
unitOfWorkManager.SetupGet(x => x.Current).Returns((IUnitOfWork?)null);
|
||||||
_coordinator = new OrderBatchJobCoordinator(
|
_coordinator = new OrderBatchJobCoordinator(
|
||||||
_batchJobRepository.Object,
|
_batchJobRepository.Object,
|
||||||
_orderRepository.Object,
|
_orderRepository.Object,
|
||||||
@ -44,6 +47,7 @@ public class OrderBatchJobCoordinatorGovernanceTests
|
|||||||
_fileStorage.Object,
|
_fileStorage.Object,
|
||||||
_publisher.Object,
|
_publisher.Object,
|
||||||
processor,
|
processor,
|
||||||
|
unitOfWorkManager.Object,
|
||||||
batchOptions,
|
batchOptions,
|
||||||
NullLogger<OrderBatchJobCoordinator>.Instance);
|
NullLogger<OrderBatchJobCoordinator>.Instance);
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user