2010-12-17 33 views
10

我一直在使用ASP.NET MVC RC2中的DI支持。動作在ASP.NET MVC 3 RC2中使用StructureMap過濾依賴注入

我已經爲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個圖像的頁面上,對於2-3個請求,「sessionsMatch」爲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; 
    } 
} 
+1

我在找一個類似的問題。 – Craig 2010-12-17 03:46:22

回答

6

想到我會回來並提供解決方案。

正如@Thomas指出的那樣,動作過濾器現在被緩存在MVC 3中。這意味着如果您注入的對象的生命期很短(例如http請求),它將被緩存。

要修復,而不是注入ISession我們注入Func<ISession>。然後,每次我們需要訪問ISession時,都會調用該函數。這確保即使ActionFilter被緩存,ISession的範圍也是正確的。

我不得不像這樣配置StructureMap注入「懶」的實例(遺憾的是它不注射懶實例會自動喜歡它與男星注射一樣):

  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(); 
     } 
    } 
} 
+0

聽起來不錯。但是會議如何確定呢?您設置要注入的動作屬性。但是哪個功能被注入? – Joel 2013-08-22 18:40:01

+1

這將取決於您如何使用StructureMap配置依賴關係。通常情況下,我們會這樣做'對於().HttpContextScoped()。使用(ctx => ctx.GetInstance ()。OpenSession())' – 2013-08-23 07:50:43

+0

我會檢查 – Joel 2013-08-23 09:52:04

0

你實現自己的使用StructureMap的IDependencyResolver?看起來你不可能擁有,因爲你將會話設置爲HttpContext的作用域,但在同一個請求中你看到單獨的會話。

+0

是的,我有。我上面發佈了我的實現。 – 2010-12-23 09:35:33

2

我不知道這是否會幫助,但與MVC 3行動過濾器現在緩存,而不是在每個請求開始時實例化新。所以如果你在構造函數中注入依賴關係,它將無法正常工作。你可以發佈你的FilterAttributeFilterProvider實現嗎?