使用WCF WebApi实现工作单元

3
我在使用新的WCF WebApi时遇到了一个挑战,即无法清晰地实现UnitOfWork模式。
简单来说,这种模式通过在请求开始时启动工作单元,执行一些工作,然后在之后回滚或提交工作单元来完成。设置“启动”工作单元的代码非常容易,可以利用HttpMessageHandler功能。通过Task<>库,我可以编写一个继续执行的代码,该代码在处理请求后执行。但是,我无法始终确定是否发生了故障,以便进行回滚。
WCF对故障有低级别的支持,在传统的WCF服务端点中,您可以检查通道是否已故障(例如从IMessageInspector内部)。但是,WebApi似乎阻止了此行为。
WebApi确实公开了HttpErrorHandler契约。但是,由于无法访问实例上下文或服务实例,因此此功能非常有限,因此我无法访问我的工作单元。
我想知道其他人在这里实现UnitOfWork模式时采用了哪些方法?
4个回答

4

Pedro和Darrel都提供了很好的建议。在我最终想出的解决方案中,我最终使用了一个消息处理程序:

public class UnitOfWorkHandler : DelegatingHandler 
{
    protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) 
    {
        var unitOfWork = new UnitOfWork();
        unitOfWork.Begin();
        return base.SendAsync(request, cancellationToken).ContinueWith(result => 
            {
                if (result.Result is HttpResponseMessage && ((HttpResponseMessage)result.Result).IsSuccessStatusCode) 
                {
                    unitOfWork.Commit();
                } 
                else 
                {
                    unitOfWork.Rollback();
                }
                return result.Result;
            });
    }
}

我本来会使用Darrel的建议,将UnitOfWork的引用存储在HttpRequestMessage属性集合中,但由于任务继续实现为闭包,因此我可以直接引用其外部范围中创建的工作单元。


2

HttpRequestMessage中有一个属性集合,你可以将你的工作单元对象放入其中,以便在返回路径上访问它。


非常适合跟踪对象。谢谢! - Joseph Daigle

2

1) 您可以使用操作处理程序或消息处理程序来设置和撤销工作单元(UoW) a) 消息处理程序具有端点范围,因此它们适用于所有操作。 b) 操作处理程序具有操作范围,如果仅某些操作需要UoW,则可能很有用。

2) 正如Darrel所述,有关HTTP请求的所有上下文信息都应添加到HttpRequestMessage属性包中。

3) 在Web API模型中,不存在故障(故障是SOAP构造)。 您可能应该依赖于HTTP响应状态来检查操作是否成功(2xx)或失败(4xx,5xx)。


0

我的场景:

  • 控制器(需要 repo)
  • 仓库(需要 IUnitOfWork
  • 操作过滤器(同样需要 IUnitOfWork

问题:

动作过滤器中的 DI 一直困扰着我。我喜欢 Joseph的方法,真的很喜欢。在每个请求之前创建(或启动)我的工作单元对我来说似乎很自然。由于操作过滤器可能会被缓存,它们不使用与控制器相同的依赖关系范围。因此,需要 Setter 注入。

解决方案:

像没有操作过滤器那样配置 structuremap。类似这样:

public class NHibernateRegistry : Registry
{
    public NHibernateRegistry()
    {
        For<ISessionFactory>).Singleton().Use(
            NhSessionFactoty.Instance.SessionFactory);
        For<IUnitOfWork>().HybridHttpOrThreadLocalScoped().Use<NhUnitOfWork>();
        For(typeof(IBaseRepository<,>)).Use(typeof(BaseRepository<,>));
    }
}

NhSessionFactory 封装了我的 Hibernate 配置,并允许获取单个 ISessionFactory 在应用程序中使用。 NhUnitOfWork 实现了我的 IUnitOfWork 接口,并处理会话和事务管理。

动作筛选器属性(改编自 Joseph's answer):

public class UnitOfWorkAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(HttpActionContext actionContext)
    {
        /* Check this line, it's a problem solver!! */
        var uow = (IUnitOfWork)actionContext.Request.GetDependencyScope()
            .GetService(typeof(IUnitOfWork));
        uow.Begin();
    }

    public override void OnActionExecuted(
        HttpActionExecutedContext actionExecutedContext)
    {
        var uow = (IUnitOfWork)actionExecutedContext.Request.GetDependencyScope()
            .GetService(typeof(IUnitOfWork));

        try
        {
            if (actionExecutedContext.Exception == null)
                uow.Commit();
            else
                uow.Rollback();
        }
        catch
        {
            uow.Rollback();
            throw;
        } 
        /*finally
        {
            uow.Dispose();
        }*/ // Resources are released in Application_EndRequest (Globals.cs)
    }
}

最后是我的基础控制器:

[UnitOfWork]
public class ControllerBaseUow : ApiController {}
/* Then, in my case, I inject Repositories via contructor */

它能够工作是因为在OnActionExecuting中,我们使用了与控制器相同的依赖范围。

然后像通常使用 DI 一样工作即可 :)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接