【问题标题】:Action Filter Dependency Injection in ASP.NET MVC 3 RC2 with StructureMap带有 StructureMap 的 ASP.NET MVC 3 RC2 中的动作过滤器依赖注入
【发布时间】:2010-12-17 00:01:24
【问题描述】:

我一直在使用 ASP.NET MVC RC2 中的 DI 支持。

我已经为 NHibernate 的每个请求实现了会话,并且需要将 ISession 注入到我的“工作单元”操作过滤器中。

如果我直接引用 StructureMap 容器 (ObjectFactory.GetInstance) 或使用 DependencyResolver 来获取我的会话实例,一切正常:

    ISession Session {
        get { return DependencyResolver.Current.GetService<ISession>(); }
    }

但是,如果我尝试使用我的 StructureMap 过滤器提供程序(继承 FilterAttributeFilterProvider),我会在请求结束时提交 NHibernate 事务时遇到问题。

就好像ISession 对象在请求之间共享。我经常看到这种情况,因为我的所有图像都是通过 MVC 控制器加载的,所以我在正常页面加载时创建了 20 个左右的 NHibernate 会话。

我将以下内容添加到我的操作过滤器中:

    ISession Session {
        get { return DependencyResolver.Current.GetService<ISession>(); }
    }

    public ISession SessionTest { get; set; }

    public override void OnResultExecuted(System.Web.Mvc.ResultExecutedContext filterContext) {

        bool sessionsMatch = (this.Session == this.SessionTest);

SessionTest 是使用 StructureMap 过滤器提供程序注入的。

我发现在一个包含 20 张图片的页面上,“sessionsMatch”对于 2-3 个请求是错误的。

我的会话管理的 StructureMap 配置如下:

        For<ISessionFactory>().Singleton().Use(new NHibernateSessionFactory().GetSessionFactory());
        For<ISession>().HttpContextScoped().Use(ctx => ctx.GetInstance<ISessionFactory>().OpenSession());

在 global.asax 中,我在每个请求结束时调用以下代码:

    public Global() {
        EndRequest += (sender, e) => {
            ObjectFactory.ReleaseAndDisposeAllHttpScopedObjects();
        };
    }

这个配置线程安全吗?以前我使用自定义IActionInvoker 将依赖项注入到同一个过滤器中。当我开始遇到上述问题时,这一直很好,直到 MVC 3 RC2,这就是为什么我想我会尝试使用过滤器提供程序。

任何帮助将不胜感激。

我正在使用 NHibernate 3 RC 和最新版本的 StructureMap

更新:

以下是我对DependencyResolverFilterAttributeFilterProvider 的实现:

    public class StructureMapDependencyResolver : IDependencyResolver {
    private readonly IContainer container;

    public StructureMapDependencyResolver(IContainer container) {
        this.container = container;
    }

    public object GetService(Type serviceType) {
        var instance = container.TryGetInstance(serviceType);
        if (instance==null && !serviceType.IsAbstract){
            instance = AddTypeAndTryGetInstance(serviceType);
        }
        return instance;
    }

    private object AddTypeAndTryGetInstance(Type serviceType) {
        container.Configure(c=>c.AddType(serviceType,serviceType));
        return container.TryGetInstance(serviceType);
    }

    public IEnumerable<object> GetServices(Type serviceType) {
        return container.GetAllInstances(serviceType).Cast<object>();
    }
}
public class StructureMapFilterAttributeFilterProvider : FilterAttributeFilterProvider
{
    private readonly IContainer container;

    public StructureMapFilterAttributeFilterProvider(IContainer container) {
        this.container = container;
    }

    protected override IEnumerable<FilterAttribute> GetControllerAttributes(ControllerContext controllerContext, ActionDescriptor actionDescriptor) {
        return BuildUp(base.GetControllerAttributes(controllerContext, actionDescriptor));
    }

    protected override IEnumerable<FilterAttribute> GetActionAttributes(ControllerContext controllerContext, ActionDescriptor actionDescriptor) {
        return BuildUp(base.GetActionAttributes(controllerContext, actionDescriptor));
    }

    private IEnumerable<FilterAttribute> BuildUp(IEnumerable<FilterAttribute> attributes) {
        foreach (var attr in attributes)
            container.BuildUp(attr);
        return attributes;
    }
}

【问题讨论】:

  • 我发现了一个类似的问题。

标签: nhibernate asp.net-mvc-3 structuremap


【解决方案1】:

以为我会回来提供解决方案。

正如@Thomas 上面指出的,动作过滤器现在被缓存在 MVC 3 中。这意味着如果你注入一个预期生命周期短的对象(例如 http 请求),它将是缓存。

为了解决这个问题,我们不是注入ISession,而是注入Func&lt;ISession&gt;。然后,每次我们需要访问 ISession 时,我们都会调用该函数。这确保即使 ActionFilter 被缓存,ISession 的范围也正确。

我不得不像这样配置 StructureMap 来注入“惰性”实例(不幸的是,它不会像使用 Ctor 注入那样自动注入惰性实例):

            x.SetAllProperties(p => {
                p.OfType<Func<ISession>>();
            });

我更新的 ActionFilter 如下:

[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public class UnitOfWorkAttribute : ActionFilterAttribute {

    public Func<ISession> SessionFinder { get; set; }

    public override void OnActionExecuting(System.Web.Mvc.ActionExecutingContext filterContext) {
        var session = SessionFinder();
        session.BeginTransaction();
    }

    public override void OnResultExecuted(System.Web.Mvc.ResultExecutedContext filterContext) {         
        var session = SessionFinder();

        var txn = session.Transaction;

        if (txn == null || !txn.IsActive) return;

        if (filterContext.Exception == null || filterContext.ExceptionHandled)
        {
            session.Transaction.Commit();
        }
        else
        {
            session.Transaction.Rollback();
            session.Clear();
        }
    }
}

【讨论】:

  • 听起来不错。但是会话是如何设置的呢?您设置要注入的动作属性。但是注入的是哪个函数呢?
  • 这取决于您如何使用 StructureMap 配置依赖项。通常我们会做类似For&lt;ISession&gt;().HttpContextScoped().Use(ctx =&gt; ctx.GetInstance&lt;ISessionFactory&gt;().OpenSession())
【解决方案2】:

我不知道这是否会有所帮助,但使用 MVC 3 动作过滤器现在被缓存,而不是在每个请求开始时被实例化。因此,如果您在构造函数中注入依赖项,它将无法正常工作。你能发布你的 FilterAttributeFilterProvider 实现吗?

【讨论】:

    【解决方案3】:

    您是否实现了自己的使用 StructureMap 的 IDependencyResolver?看来您一定没有,因为您将会话设置为 HttpContext 范围,但在同一请求期间您会看到单独的会话。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多