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

时间:2010-12-17 00:01:25

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

我一直在玩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);

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

我发现在包含20张图片的网页上,“sessionsMatch”在2-3次请求中均为false。

我的会话管理的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;
    }
}

3 个答案:

答案 0 :(得分:6)

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

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

要修复,我们会注入ISession而不是注入Func<ISession>。然后每次我们需要访问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();
        }
    }
}

答案 1 :(得分:2)

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

答案 2 :(得分:0)

您是否实现了自己的使用StructureMap的IDependencyResolver?看起来你一定不能拥有,因为你将会话设置为HttpContext范围,但你在同一个请求中看到了不同的会话。