我遇到的新WCF WebApi面临的挑战之一是我无法干净地实现UnitOfWork模式。
作为一个快速背景,该模式的工作原理是在请求开始时开始工作单元,做一些工作,然后再回滚或提交工作单元。
使用HttpMessageHandler功能设置将“启动”工作单元的代码非常容易。并通过任务<>库,我可以写一个在处理请求后执行的继续。但是,我无法始终确定是否发生了故障,以便我可以回滚。
WCF对故障具有低级别支持,在传统的WCF服务端点中,您可以检查通道是否出现故障(例如,从IMessageInspector内部)。但WebApi似乎阻止了这种行为。
WebApi确实公开了一个HttpErrorHandler契约。但是由于您无权访问实例上下文或服务实例,所以这非常有限,因此我无法访问我的工作单元。
我想知道在这里使用了哪些其他方法来实现工作单元模式?
答案 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配置,允许在我的应用程序中使用单个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那样工作:)