如何在ABP应用程序的BackgroundJob中打开数据库连接 [英] How to open database connection in a BackgroundJob in ABP application

查看:643
本文介绍了如何在ABP应用程序的BackgroundJob中打开数据库连接的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

为了进行测试,我创建了一个新作业,它仅使用IRepository从数据库读取数据.代码如下:

For testing, I create a new job, it just use IRepository to read data from database. The code as below:

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");
    }
}

然后,我创建一个新的应用程序服务来触发作业.代码段如下:

Then I create a new application service to trigger the job. The code snippet as below:

public async Task UowInJobTest()
{
   await  _backgroundJobManager.EnqueueAsync<TestJob, string>("aaaa");
}

当我测试作业时,执行 var task = _productRepository.GetAll().ToListAsync();

When I test the job, It will throw following exception when execute 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属性.它可以解决该问题.但这对于我的实际情况并不更好.在我的实际情况中,该工作是一项长期的任务,并且具有大量的数据库操作,如果启用Execute方法的UnitOfWork,它将长时间锁定数据库资源.因此,这不是我的方案的解决方案.

Solution

S1: Add UnitOfWork attribute on execute method. It can address the issue. But it is not better for my actual scenario. In my actual scenario, the job is a long time task, and has much DB operatons, if enable UnitOfWork for Execute method, it will lock db resource for a long time. So this is not a solution for my scenario.

[UnitOfWork]
public override void Execute(string args)
{
    var task = _productRepository.GetAll().ToListAsync();
    var items = task.Result;
    Debug.WriteLine("test db connection");
}

S2 :在UnitOfWork中明确执行DB操作.另外,这可以解决问题,但我认为这不是最佳做法.在我的示例中,只需从数据库中读取数据,就不需要事务.即使该问题已解决,但我认为这不是正确的方法.

S2: Execute DB operation in UnitOfWork explicitly. Also, this can address the issue, but I don’t think this is a best practice. In my example,just read data from database, no transaction is required. Even-though the issue is addressed, but I don’t think it’s a correct way.

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中执行数据库操作的正确和最佳方法是什么?

Question

My question is what’s the correct and best way to execute a DB operation in BackgroundJob?

还有另一个问题,,我创建了一个新的应用程序服务,并禁用了UnitOfWrok,但是效果很好.请参见下面的代码.为什么它在应用程序服务中可以正常工作,但在BackgroundJob中却不能正常工作?

There is addtional another question, I create a new application service, and disable UnitOfWrok, but it works fine. Please see the code as below. Why It works fine in application service, but doesn’t work in 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
    };
}

推荐答案

背景工作和工人上的文档使用[UnitOfWork]属性.

S1 :在execute方法上添加UnitOfWork属性.它可以解决该问题.但这对于我的实际情况并不更好.在我的实际情况中,该工作是一项长期的任务,并且具有大量的数据库操作,如果启用Execute方法的UnitOfWork,它将长时间锁定数据库资源.因此,这不是我的方案的解决方案.

S1: Add UnitOfWork attribute on execute method. It can address the issue. But it is not better for my actual scenario. In my actual scenario, the job is a long time task, and has much DB operatons, if enable UnitOfWork for Execute method, it will lock db resource for a long time. So this is not a solution for my scenario.

后台作业在后台线程上同步运行,因此这种担心是没有根据的.

Background jobs are run synchronously on a background thread, so this concern is unfounded.

S2 :在UnitOfWork中明确执行DB操作.另外,这可以解决问题,但我认为这不是最佳做法.在我的示例中,只需从数据库中读取数据,就不需要事务.即使该问题已解决,但我认为这不是正确的方法.

S2: Execute DB operation in UnitOfWork explicitly. Also, this can address the issue, but I don’t think this is a best practice. In my example,just read data from database, no transaction is required. Even-though the issue is addressed, but I don’t think it’s a correct way.

您可以使用非交易性工作单位:

[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中却不能正常工作?

I create a new application service, and disable UnitOfWork, but it works fine.
Why it works fine in application service, but doesn’t work in 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()GetAll().ToListAsync()

You are using different methods: GetAllListAsync() vs GetAll().ToListAsync()

存储库方法为常规工作单元方法,但ToListAsync()不是一个.

Repository methods are Conventional Unit Of Work Methods, but ToListAsync() isn't one.

摘录自关于IQueryable<T> 的文档:

From the documentation on About IQueryable<T>:

在存储库方法之外调用GetAll()时,必须存在打开的数据库连接.这是因为IQueryable<T>的执行推迟了.除非您调用ToList()方法或在foreach循环中使用IQueryable<T>(或以某种方式访问​​所查询的项目),否则它不会执行数据库查询.因此,当您调用ToList()方法时,数据库连接必须处于活动状态.

When you call GetAll() outside of a repository method, there must be an open database connection. This is because of the deferred execution of IQueryable<T>. It does not perform a database query unless you call the ToList() method or use the IQueryable<T> in a foreach loop (or somehow access the queried items). So when you call the ToList() method, the database connection must be alive.

这篇关于如何在ABP应用程序的BackgroundJob中打开数据库连接的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆