使用WCF WebApi实现工作单元

时间:2012-01-09 18:04:53

标签: wcf unit-of-work wcf-web-api

我遇到的新WCF WebApi面临的挑战之一是我无法干净地实现UnitOfWork模式。

作为一个快速背景,该模式的工作原理是在请求开始时开始工作单元,做一些工作,然后再回滚或提交工作单元。

使用HttpMessageHandler功能设置将“启动”工作单元的代码非常容易。并通过任务<>库,我可以写一个在处理请求后执行的继续。但是,我无法始终确定是否发生了故障,以便我可以回滚。

WCF对故障具有低级别支持,在传统的WCF服务端点中,您可以检查通道是否出现故障(例如,从IMessageInspector内部)。但WebApi似乎阻止了这种行为。

WebApi确实公开了一个HttpErrorHandler契约。但是由于您无权访问实例上下文或服务实例,所以这非常有限,因此我无法访问我的工作单元。

我想知道在这里使用了哪些其他方法来实现工作单元模式?

4 个答案:

答案 0 :(得分:4)

佩德罗和达雷尔都提出了很好的建议。在最终提出的解决方案中,我最终使用了一个消息处理程序:

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的suggestion在HttpRequestMessage属性集合上存储对UnitOfWork的引用,但是由于Task continuation是作为闭包实现的,我可以简单地引用我在其外部作用域中创建的工作单元

答案 1 :(得分:2)

有一个属性集合挂起HttpRequestMessage,您可以填充您的工作单元对象,以便您可以在返回路径上访问它。

答案 2 :(得分:2)

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

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

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

答案 3 :(得分:0)

我的情景:

  • 控制器(需要回购)
  • 回购(需要IUnitOfWork
  • 动作过滤器(还需要IUnitOfWork

问题:

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

解决方案:

配置结构图的方式与我没有操作过滤器的方式相同。像这样:

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

NhSessionFactoty封装了我的hibernate配置,允许在我的应用程序中使用单个ISessionFactoryNhUnitOfWork实现了我的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那样工作:)