为了测试,我创建了一个新作业,它只是使用IRepository从数据库中读取数据。代码如下:
public class TestJob : BackgroundJob<string>, ITransientDependency
{
private readonly IRepository<Product, long> _productRepository;
private readonly IUnitOfWorkManager _unitOfWorkManager;
public TestJob(IRepository<Product, long> productRepository,
IUnitOfWorkManager unitOfWorkManager)
{
_productRepository = productRepository;
_unitOfWorkManager = unitOfWorkManager;
}
public override void Execute(string args)
{
var task = _productRepository.GetAll().ToListAsync();
var items = task.Result;
Debug.WriteLine("test db connection");
}
}
然后我创建一个新的应用程序服务来触发作业。代码段如下:
public async Task UowInJobTest()
{
await _backgroundJobManager.EnqueueAsync<TestJob, string>("aaaa");
}
当我测试作业时,执行 var task = _productRepository.GetAll()时会抛出以下异常.ToListAsync();
Cannot access a disposed object. A common cause of this error is disposing a context that was resolved from dependency injection and then later trying to use the same context instance elsewhere in your application. This may occur if you are calling Dispose() on the context, or wrapping the context in a using statement. If you are using dependency injection, you should let the dependency injection container take care of disposing context instances.Object name: 'AbpExampleDbContext'.
S1 :在execute方法上添加UnitOfWork属性。它可以解决这个问题。但对我的实际情况来说并不是更好。在我的实际场景中,作业是一项长时间的任务,并且有很多DB操作,如果启用UnitOfWork for Execute方法,它将长时间锁定db资源。所以这不是我的方案的解决方案。
[UnitOfWork]
public override void Execute(string args)
{
var task = _productRepository.GetAll().ToListAsync();
var items = task.Result;
Debug.WriteLine("test db connection");
}
S2:显式执行UnitOfWork中的DB操作。此外,这可以解决这个问题,但我认为这不是最佳做法。在我的示例中,只需从数据库中读取数据,不需要任何事务。即使问题得到解决,但我认为这不是正确的方法。
public override void Execute(string args)
{
using (var unitOfWork = _unitOfWorkManager.Begin())
{
var task = _productRepository.GetAll().ToListAsync();
var items = task.Result;
unitOfWork.Complete();
}
Debug.WriteLine("test db connection");
}
我的问题是在BackgroundJob中执行数据库操作的正确和最佳方法是什么?
还有另外一个问题,我创建了一个新的应用程序服务,并禁用了UnitOfWrok,但它运行正常。请参阅以下代码。为什么它在应用程序服务中工作正常,但在BackgroundJob中不起作用?
[UnitOfWork(IsDisabled =true)]
public async Task<GetAllProductsOutput> GetAllProducts()
{
var result = await _productRepository.GetAllListAsync();
var itemDtos = ObjectMapper.Map<List<ProductDto>>(result);
return new GetAllProductsOutput()
{
Items = itemDtos
};
}
答案 0 :(得分:3)
Background Jobs And Workers上的文档使用[UnitOfWork]
属性。
S1 :在execute方法上添加UnitOfWork属性。它可以解决这个问题。但对我的实际情况来说并不是更好。在我的实际场景中,作业是一项长时间的任务,并且有很多DB操作,如果启用UnitOfWork for Execute方法,它将长时间锁定db资源。所以这不是我的方案的解决方案。
后台作业在后台线程上同步运行,因此这种担忧是没有根据的。
S2 :显式执行UnitOfWork中的DB操作。此外,这可以解决这个问题,但我认为这不是最佳做法。在我的示例中,只需从数据库中读取数据,不需要任何事务。即使问题得到解决,但我认为这不是正确的方法。
您可以使用Non-Transactional Unit Of Work:
[UnitOfWork(isTransactional: false)]
public override void Execute(string args)
{
var task = _productRepository.GetAll().ToListAsync();
var items = task.Result;
}
您可以使用IUnitOfWorkManager
:
public override void Execute(string args)
{
using (var unitOfWork = _unitOfWorkManager.Begin(TransactionScopeOption.Suppress))
{
var task = _productRepository.GetAll().ToListAsync();
var items = task.Result;
unitOfWork.Complete();
}
}
您还可以使用AsyncHelper
:
[UnitOfWork(isTransactional: false)]
public override void Execute(string args)
{
var items = AsyncHelper.RunSync(() => _productRepository.GetAll().ToListAsync());
}
我创建了一个新的应用程序服务,并禁用UnitOfWork,但它工作正常 为什么它在应用程序服务中工作正常,但在BackgroundJob中不起作用?
[UnitOfWork(IsDisabled = true)] public async Task<GetAllProductsOutput> GetAllProducts() { var result = await _productRepository.GetAllListAsync(); var itemDtos = ObjectMapper.Map<List<ProductDto>>(result); return new GetAllProductsOutput { Items = itemDtos }; }
您使用的方法不同:GetAllListAsync()
vs GetAll().ToListAsync()
存储库方法为Conventional Unit Of Work Methods,但ToListAsync()
不是一个。
来自About IQueryable<T>
的文档:
当您在存储库方法之外调用
GetAll()
时,必须存在打开的数据库连接。这是因为IQueryable<T>
的延迟执行。除非您调用ToList()
方法或在IQueryable<T>
循环中使用foreach
(或以某种方式访问查询的项目),否则它不会执行数据库查询。因此,当您调用ToList()
方法时,数据库连接必须处于活动状态。