使用 ASP.NET 样板对 IDomainService 进行单元测试

Unit testing for IDomainService with ASP.NET Boilerplate

我正在对 ABP 进行单元测试,但出现以下错误:

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: 'XXXDbContext'.

以下是我的详细步骤:

  1. 应用服务

    public async Task<ProductDto> CreateProduct(CreateProductInput input)
    {
        var existing = // await _productManager.ProductRepository.FirstOrDefaultAsync(p => p.Name == input.Name);
                          await _productManager.Products.Where(p => p.Name == input.Name).FirstOrDefaultAsync();
        if (existing != null) throw new UserFriendlyException(L("ExistedRepeatedAd"));
    
        var newAdEntity = ObjectMapper.Map<Product>(input);
        // Rest of the code
    }
    
  2. 产品经理

    public class ProductManager : IDomainService
    {
        private readonly IRepository<Product, long> _ProductRepository;
        private readonly IUnitOfWorkManager _unitOfWorkManager;
    
        public ProductsManager(
            IRepository<Product, long> ProductRepository,
            IUnitOfWorkManager unitOfWorkManager)
        {
            _ProductRepository = ProductRepository;
            _unitOfWorkManager = unitOfWorkManager;
        }
    
        #region Products
    
        public IRepository<Product, long> ProductRepository
        {
            get { return _ProductRepository; }
        }
    
        public IQueryable<Product> Products
        {
            get { return _ProductRepository.GetAll(); }
        }
    
        public async Task<Product> CreateProduct(Product input)
        {
            var result = await _ProductRepository.InsertAsync(input);
            await _unitOfWorkManager.Current.SaveChangesAsync();
            return result;
        }
    
        #endregion
    }
    

这行会抛出错误:

await _adManager.Ads.Where(p => p.Name == input.Name).FirstOrDefaultAsync();

但如果我改用它,它会起作用:

await _adManager.AdRepository.FirstOrDefaultAsync(p => p.Name == input.Name);

此外,我在上面的代码中得到_unitOfWorkManager.Current作为null
有什么建议吗?

UnitOfWork 属性

添加 [UnitOfWork] 属性并使其成为 virtual 方法:

[UnitOfWork]
public virtual async Task<ProductDto> CreateProduct(CreateProductInput input)
{
    var existing = await _productManager.Products
        .Where(p => p.Name == input.Name)
        .FirstOrDefaultAsync();

    // ...
}
[UnitOfWork]
public virtual async Task<Product> CreateProduct(Product input)
{
    var result = await _ProductRepository.InsertAsync(input);
    await _unitOfWorkManager.Current.SaveChangesAsync();
    return result;
}

参见:UnitOfWork Attribute Restrictions

You can use UnitOfWork attribute for:

  • All public or public virtual methods for classes that are used over an interface (Like an application service used over a service interface).
  • All public virtual methods for self-injected classes (Like MVC Controllers and Web API Controllers).
  • All protected virtual methods.

IUnitOfWorkManager

您可以注入 IUnitOfWorkManager 以显式开始 UnitOfWork

public async Task<Product> CreateProduct(Product input)
{
    using (var uow = _unitOfWorkManager.Begin())
    {
        var result = await _ProductRepository.InsertAsync(input);
        await _unitOfWorkManager.Current.SaveChangesAsync();

        await uow.CompleteAsync();

        return result;
    }
}

我的问题已通过为我的应用服务创建接口然后在我的测试项目中解决此接口而得到解决。

感谢 aaron 的建议,但在我的每个应用程序服务中使用 uniofwork 会很复杂。