使用StructureMap在ASP.NET MVC 3 RC2中执行操作筛选依赖项注入

Ben*_*ter 10 structuremap nhibernate asp.net-mvc-3

我一直在玩ASP.NET MVC RC2中的DI支持.

我已经为NHibernate的每个请求实现了会话,并且需要注入ISession我的"工作单元"动作过滤器.

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

    ISession Session {
        get { return DependencyResolver.Current.GetService<ISession>(); }
    }
Run Code Online (Sandbox Code Playgroud)

但是,如果我尝试使用我的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);
Run Code Online (Sandbox Code Playgroud)

使用StructureMap过滤器提供程序注入SessionTest.

我发现在一个有20个图像的页面上,"sessionsMatch"对于2-3个请求是假的.

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

        For<ISessionFactory>().Singleton().Use(new NHibernateSessionFactory().GetSessionFactory());
        For<ISession>().HttpContextScoped().Use(ctx => ctx.GetInstance<ISessionFactory>().OpenSession());
Run Code Online (Sandbox Code Playgroud)

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

    public Global() {
        EndRequest += (sender, e) => {
            ObjectFactory.ReleaseAndDisposeAllHttpScopedObjects();
        };
    }
Run Code Online (Sandbox Code Playgroud)

这个配置线程安全吗?以前我使用自定义将依赖项注入到同一个过滤器中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;
    }
}
Run Code Online (Sandbox Code Playgroud)

Ben*_*ter 6

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

正如@Thomas上面指出的那样,Action Filters现在缓存在MVC 3中.这意味着如果你注入一个预期寿命较短的对象(例如http请求),它将被缓存.

要修复,而不是注入ISession我们注入Func<ISession>.然后每次我们需要访问ISession时,我们都会调用该函数.这样可以确保即使缓存了ActionFilter,也可以正确确定ISession的范围.

我不得不像这样配置StructureMap来注入"懒惰"实例(不幸的是它不像Ctor注入那样自动注入一个惰性实例):

            x.SetAllProperties(p => {
                p.OfType<Func<ISession>>();
            });
Run Code Online (Sandbox Code Playgroud)

我更新的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();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)