使用 StructureMap 在 ASP.NET MVC 3 RC2 中进行操作过滤器依赖注入

发布于 2024-10-08 00:56:39 字数 3746 浏览 0 评论 0原文

我一直在研究 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 Filter 提供程序注入 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;
    }
}

I've been playing with the DI support in ASP.NET MVC RC2.

I have implemented session per request for NHibernate and need to inject ISession into my "Unit of work" action filter.

If I reference the StructureMap container directly (ObjectFactory.GetInstance) or use DependencyResolver to get my session instance, everything works fine:

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

However if I attempt to use my StructureMap filter provider (inherits FilterAttributeFilterProvider) I have problems with committing the NHibernate transaction at the end of the request.

It is as if ISession objects are being shared between requests. I am seeing this frequently as all my images are loaded via an MVC controller so I get 20 or so NHibernate sessions created on a normal page load.

I added the following to my action filter:

    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 is injected using the StructureMap Filter provider.

I found that on a page with 20 images, "sessionsMatch" was false for 2-3 of the requests.

My StructureMap configuration for session management is as follows:

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

In global.asax I call the following at the end of each request:

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

Is this configuration thread safe? Previously I was injecting dependencies into the same filter using a custom IActionInvoker. This worked fine until MVC 3 RC2 when I started experiencing the problem above, which is why I thought I would try using a filter provider instead.

Any help would be appreciated.

I'm using NHibernate 3 RC and the latest version of StructureMap

Update:

Below are my implementations of DependencyResolver and FilterAttributeFilterProvider:

    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;
    }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

水中月 2024-10-15 00:56:41

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

Did you implement your own IDependencyResolver that uses StructureMap? It seems like you must not have, because you set the session to be HttpContext scoped and yet you are seeing separate sessions during the same request.

神经暖 2024-10-15 00:56:40

我想我会回来提供解决方案。

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

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

Thought I would come back and provide the solution.

As @Thomas pointed out above, Action Filters are now cached in MVC 3. This means that if you inject an object with an intended short life time (e.g. http request), it's going to be cached.

To fix, instead of injecting an ISession we inject a Func<ISession>. Then each time we need access to ISession we invoke the function. This ensures that even if the ActionFilter is cached, the ISession is scoped correctly.

I had to configure StructureMap like so to inject the "lazy" instance (unfortunately it doesn't inject a lazy instance automatically like it does with Ctor injection):

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

My updated ActionFilter is below:

[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();
        }
    }
}
长不大的小祸害 2024-10-15 00:56:40

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

I don't know if it would help but with MVC 3 action filters are now cached instead of being instantiated new at the beginning of every request. So if you're injecting dependencies something in the constructor it won't work well. Could you post your implementation of FilterAttributeFilterProvider ?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文