自定义动作过滤器 unity 依赖注入 web api 2
Custom action filter unity dependency injection web api 2
我遵循了这个 article 并且除了依赖注入(部分)之外一切正常。在我的项目中,我正在使用 unity,我正在尝试创建一个自定义事务属性,其目的是在执行操作之前启动 NHibernate 事务,并在方法执行之后 commit/rollback 启动事务。
这是我的属性定义:-
public class TransactionAttribute : Attribute
{
}
以下是我的 TransactionFilter 的定义
public class TransactionFilter : IActionFilter
{
private readonly IUnitOfWork _unitOfWork;
public TransactionFilter(IUnitOfWork uow) {
_unitOfWork = uow;
}
public Task<HttpResponseMessage> ExecuteActionFilterAsync(HttpActionContext actionContext, CancellationToken cancellationToken, Func<Task<HttpResponseMessage>> continuation) {
var transAttribute = actionContext.ActionDescriptor.GetCustomAttributes<TransactionAttribute>().SingleOrDefault();
if (transAttribute == null) {
return continuation();
}
var transaction = uow.BeginTransaction();
return continuation().ContinueWith(t =>
{
try{
transaction.Commit();
return t.Result;
}
catch(Exception e)
{
transaction.Rollback();
return new ExceptionResult(ex, actionContext.ControllerContext.Controller as ApiController).ExecuteAsync(cancellationToken).Result;
}
}
}
}
并且我创建了一个自定义过滤器提供程序,它使用统一来构造此过滤器。
public class UnityActionFilterProvider
: ActionDescriptorFilterProvider,
IFilterProvider
{
private readonly IUnityContainer container;
public UnityActionFilterProvider(IUnityContainer container)
{
this.container = container;
}
public new IEnumerable<FilterInfo> GetFilters(HttpConfiguration configuration, HttpActionDescriptor actionDescriptor)
{
foreach (IActionFilter actionFilter in container.ResolveAll<IActionFilter>())
{
// TODO: Determine correct FilterScope
yield return new FilterInfo(actionFilter, FilterScope.Global);
}
}
}
我在UnityWebApiActivator中注册了UnityActionFilterProvider(我使用的是Unity.AspNet.WebApi包)如下
public static void Start()
{
var container = UnityConfig.GetConfiguredContainer();
var resolver = new UnityDependencyResolver(container);
var config = GlobalConfiguration.Configuration;
config.DependencyResolver = resolver;
var providers = config.Services.GetFilterProviders();
var defaultProvider = providers.Single(i => i is ActionDescriptorFilterProvider);
config.Services.Remove(typeof(IFilterProvider), defaultProvider);
config.Services.Add(typeof(IFilterProvider), new UnityActionFilterProvider(container));
}
问题是对于任何操作的第一个请求一切正常,但对同一操作的后续请求不会重新创建 TransactionFilter,这意味着它不会调用构造函数来分配新的 UOW。我不认为我可以禁用操作过滤器缓存。
我现在唯一的选择是使用服务定位器模式并使用 ExecuteActionFilterAsync 中的容器获取 UOW 实例,在我看来这会破坏此目的,我最好实现自定义 ActionFilterAttribute。
有什么建议吗?
据我多年来所知,Web 应用程序启动代码中发生的事情本质上具有单例生命周期。该代码只运行一次。
这意味着您的每个过滤器只有一个实例。这对性能有好处,但不适合您的场景。
该问题的最简单解决方案是注入一个抽象工厂而不是依赖项本身:
public class TransactionFilter : IActionFilter
{
private readonly IFactory<IUnitOfWork> _unitOfWorkFactory;
public TransactionFilter(IFactory<IUnitOfWork> uowFactory) {
_unitOfWorkFactory = uowFactory;
}
// etc...
然后在ExecuteActionFilterAsync
方法中使用工厂:
var transaction = _unitOfWorkFactory.Create().BeginTransaction();
在我看来,一个更优雅的解决方案是使用适应 TransactionFilter
的 Decoraptor,但上面的答案可能更容易理解。
我遵循了这个 article 并且除了依赖注入(部分)之外一切正常。在我的项目中,我正在使用 unity,我正在尝试创建一个自定义事务属性,其目的是在执行操作之前启动 NHibernate 事务,并在方法执行之后 commit/rollback 启动事务。
这是我的属性定义:-
public class TransactionAttribute : Attribute
{
}
以下是我的 TransactionFilter 的定义
public class TransactionFilter : IActionFilter
{
private readonly IUnitOfWork _unitOfWork;
public TransactionFilter(IUnitOfWork uow) {
_unitOfWork = uow;
}
public Task<HttpResponseMessage> ExecuteActionFilterAsync(HttpActionContext actionContext, CancellationToken cancellationToken, Func<Task<HttpResponseMessage>> continuation) {
var transAttribute = actionContext.ActionDescriptor.GetCustomAttributes<TransactionAttribute>().SingleOrDefault();
if (transAttribute == null) {
return continuation();
}
var transaction = uow.BeginTransaction();
return continuation().ContinueWith(t =>
{
try{
transaction.Commit();
return t.Result;
}
catch(Exception e)
{
transaction.Rollback();
return new ExceptionResult(ex, actionContext.ControllerContext.Controller as ApiController).ExecuteAsync(cancellationToken).Result;
}
}
}
}
并且我创建了一个自定义过滤器提供程序,它使用统一来构造此过滤器。
public class UnityActionFilterProvider
: ActionDescriptorFilterProvider,
IFilterProvider
{
private readonly IUnityContainer container;
public UnityActionFilterProvider(IUnityContainer container)
{
this.container = container;
}
public new IEnumerable<FilterInfo> GetFilters(HttpConfiguration configuration, HttpActionDescriptor actionDescriptor)
{
foreach (IActionFilter actionFilter in container.ResolveAll<IActionFilter>())
{
// TODO: Determine correct FilterScope
yield return new FilterInfo(actionFilter, FilterScope.Global);
}
}
}
我在UnityWebApiActivator中注册了UnityActionFilterProvider(我使用的是Unity.AspNet.WebApi包)如下
public static void Start()
{
var container = UnityConfig.GetConfiguredContainer();
var resolver = new UnityDependencyResolver(container);
var config = GlobalConfiguration.Configuration;
config.DependencyResolver = resolver;
var providers = config.Services.GetFilterProviders();
var defaultProvider = providers.Single(i => i is ActionDescriptorFilterProvider);
config.Services.Remove(typeof(IFilterProvider), defaultProvider);
config.Services.Add(typeof(IFilterProvider), new UnityActionFilterProvider(container));
}
问题是对于任何操作的第一个请求一切正常,但对同一操作的后续请求不会重新创建 TransactionFilter,这意味着它不会调用构造函数来分配新的 UOW。我不认为我可以禁用操作过滤器缓存。
我现在唯一的选择是使用服务定位器模式并使用 ExecuteActionFilterAsync 中的容器获取 UOW 实例,在我看来这会破坏此目的,我最好实现自定义 ActionFilterAttribute。
有什么建议吗?
据我多年来所知,Web 应用程序启动代码中发生的事情本质上具有单例生命周期。该代码只运行一次。
这意味着您的每个过滤器只有一个实例。这对性能有好处,但不适合您的场景。
该问题的最简单解决方案是注入一个抽象工厂而不是依赖项本身:
public class TransactionFilter : IActionFilter
{
private readonly IFactory<IUnitOfWork> _unitOfWorkFactory;
public TransactionFilter(IFactory<IUnitOfWork> uowFactory) {
_unitOfWorkFactory = uowFactory;
}
// etc...
然后在ExecuteActionFilterAsync
方法中使用工厂:
var transaction = _unitOfWorkFactory.Create().BeginTransaction();
在我看来,一个更优雅的解决方案是使用适应 TransactionFilter
的 Decoraptor,但上面的答案可能更容易理解。