【问题标题】:How can I share a 'unit of work' between multiple service methods?如何在多种服务方法之间共享“工作单元”?
【发布时间】:2015-07-23 08:58:07
【问题描述】:

我在作为原型的一部分构建的应用程序服务中实现工作单元模式时遇到问题。我想我是:

a) autofac 的功能中缺少一些我不知道的东西 它可以做到

b) 完全滥用工作单元模式,需要重构我的 服务和/或存储库。

基本上我的问题源于我的服务中的代码共享。具体来说,我有一个名为CreateCustomerAsync(…) 的服务方法,在其中我构建了一个工作单元(包装一个数据库连接并开始一个数据库事务)并使用存储库插入几个数据库表。这工作正常,直到我想从该方法(并且在 UOW 范围内)调用另一个名为 AddCustomerToGroupAsync(…) 的服务方法,以便(在同一个 UOW 内)将客户添加到组(将行添加到链接表)。 AddCustomerToGroupAsync 本身在内部使用自己的工作单元,以确保其存储库操作也在数据库事务中发生。

目前我无法在同一个 UOW 中完成所有这些工作 - 事实上,使用这样的代码它实际上根本不起作用,因为最里面的 UOW 运行在不同的连接上,它看不到客户那已经被插入到外部事务中了!我可以重新排序代码,以便 AddCustomerToGroupAsync 调用在父 UOW 之外,但随后我失去了数据库完整性。

所以 - 我大致(这在语法上不正确 - 但代表我面临的问题)是这样的:

public async Task<int> CreateCustomerAsync(string name, int groupid)
{
    // do some validation etc..

    // NOTE: UnitOfWork and CustomerRepository are scoped to InstancePerMatchingLifetimeScope for 'tx'
    using(var scope = this.Container.BeginLifetimeScope("tx"))
    using(var uow = scope.Resolve<UnitOfWork>())
    {
        // NOTE: ResolveRepository is an extension method - the repo is having the uow injected into it
        var customerrepository = uow.ResolveRepository<CustomerRepository>();

        // multiple repository calls all within the same UOW/db transaction
        int newid = await customerrepository.CreateAsync(name);
        await customerrepository.ActivateAsync(newid);

        // here we invoke our seperate service method... and which I would *like* to execute within
        // this same UOW - so if it fails then all of the db statements executed so far get rolled back
        await this.AddCustomerToGroupAsync(newid, groupid);

        uow.Commit();
    }
}

public async Task<bool> AddCustomerToGroupAsync(int customerId, int groupId)
{
    // really here I'd LIKE to resolve the same lifetime scope that was constructed in the parent if it doesnt
    // exist with the tag specified already...
    // if i could do that then I would be able to resolve the *same* unit of work which would be a step forward
    using(var scope = this.Container.BeginLifetimeScope("tx"))
    using (var uow = scope.Resolve<UnitOfWork>())
    {
        var grouprepository = uow.ResolveRepository<GroupRepository>();

        // two repository calls that need to be wrapped in the same UOW/TX
        int linkid = await grouprepository.CreateLinkAsync(customerId, groupId);
        await grouprepository.ActivateAsync(linkid);

        uow.Commit();
    }
}

有任何尝试实现这一目标的建议,还是我的方法从根本上被误导了?

干杯。

【问题讨论】:

  • 我会写一个重载的AddCustomerToGroupAsync,它接受一个范围和uow作为参数。这种方法会完成繁重的工作(添加角色 - 但调用uow.Commit())。只接受 customerid 和 groupid 的方法将构造范围 & uow,并处理 uow.Commit()。 CreateCustomerAsync 只需调用新方法,传递范围和工作单元。
  • 这绝对是一种有效的方法——但如果你明白我的意思,我会觉得“不对”吗?它似乎暴露了调用的内部结构,并且是额外的样板,毫无疑问会随着时间的推移而增加。您认为有什么方法可以使用 DI 实现这一目标吗?
  • 您不必创建新的LifetimescopeLifetimeScope 应该在工作开始时创建(例如在 HTTP 请求开始时)。对于 MVC 或 Web API 项目,Autofac 集成包会自动创建一个新范围。
  • @KieranBenton 我认为像AddCustomerToGroupAsync 这样的方法应该总是采用一个工作单元——而不是实现细节。这些方法假设它们应该立即将更改持久化到数据库中;这根本不是真的。更大的功能,例如CreateCustomerAsync - 创建用户并添加角色应该非常关注工作单元和何时保存。通过它自己的实现,很明显必须使用角色创建客户,或者根本不创建。
  • 你不能只使用每个请求模式的上下文吗?那处理一切。上下文随处可见。

标签: c# transactions repository-pattern autofac unit-of-work


【解决方案1】:

考虑为 AddCustomerToGroupAsync 编写一个私有方法来完成大部分工作。

   private async Task<bool> AddCustomerToGroupInternalAsync(int customerId, int groupId, UnitOfWork uow)
   { ../* All the code in the AddCustomerToGroupAsync inside the unitOfWork */. }

对于现有的 AddCustomerToGroupAsync 方法,您可以打开一个范围,解析一个 unitOfWork 并将其传递给 AddCustomerToGroupInternalAsync 方法。同样,对于现有的 CreateCustomerAsync 方法,您可以将在该方法中解析的 UnitOfWork 传递给 AddCustomerToGroupInternalAsync 方法。

在这两种情况下,您都可以在调用 AddCustomerToGroupInternalAsync 之后调用 uow.Commit。

【讨论】:

    猜你喜欢
    • 2011-11-30
    • 2017-12-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-25
    • 1970-01-01
    • 2012-06-02
    • 1970-01-01
    相关资源
    最近更新 更多