2011-04-22 69 views
2

我正在嘗試在操作上設置'授權'過濾器,創建我自己的ActionFilterAttribute,在此處執行數據庫查找以確定用戶是否可以訪問某個資源。使用數據庫的MVC3操作過濾器(EF 4.1 DBContext,Ninject)

在我的繼承自ActionFilterAttribute的類上,我創建了Injected(Ninject)屬性來容納我用於數據庫訪問的服務。我有一個無參數的構造函數,所以我可以將它用作我的動作的屬性。在'OnActionExecuting'方法中,我能夠訪問Injected屬性(它不爲null),但它使用的基本DBCotext已關閉。

這工作正常,直到MVC3的RTM,其中,發行說明中指出:

Breaking Changes: In previous versions of ASP.NET MVC, action filters are create per request except in a few cases. This behavior was never a guaranteed behavior but merely an implementation detail and the contract for filters was to consider them stateless. In ASP.NET MVC 3, filters are cached more aggressively. Therefore, any custom action filters which improperly store instance state might be broken.

我第一次使用此過濾器,它工作正常,但如果我刷新頁面或其他用戶訪問此過濾器,我得到的錯誤:

The operation cannot be completed because the DbContext has been disposed.

這是我想我應該期待給予突破變化的筆記。

我的問題是這樣的,完成我需要做什麼的首選/推薦方式是什麼?如果這是在ActionFilterAttribute中,還是應該在其他地方完成這個「授權」?

+0

你有沒有想過這件事......有同樣的問題。 – 2012-10-01 00:58:38

回答

3

我想在Application_AuthenticateRequest中進行身份驗證並使用Thread.CurrentPrincipal在屬性中進行授權,但是您的方法也應該可以工作。你只需要考慮事實,即每個請求的DbContext將會不同,但你的屬性不會。這樣的事情應該做的伎倆(我假設你正在使用DependencyResolver):

public class MyMightyAttribute : ActionFilterAttribute 
{ 
    public override void OnActionExecuting(ActionExecutingContext filterContext) 
    { 
     var context = (DbContext)DependencyResolver.Current.GetService(typeof(DbContext)) 
     // authenticate, authorize, whatever 
     base.OnActionExecuting(filterContext); 
    } 
} 
+0

這就是我所使用的方法,但是使用這種方法無視使用「注入」屬性或構造函數的目的,不是嗎?每當我需要訪問我的某個服務時,是否使用DependencyResolver是一個好方法? – ncyankee 2011-04-22 19:38:05

+0

使用依賴注入的目的是,你可以動態地解決依賴關係 - 通常是整個實例,但在這種情況下只適用於一種方法 - 這就是我如何看待它(但我不是DI的專家,所以我可能是錯的)。 DependencyResolver是mvc特有的,所以在mvc上下文中使用它在我看來是有效的,但是如果你想要一些奇怪的映射'IServiceProvider'到你的'Kernel'實例並注入'IServiceProvider'到屬性並使用它來代替DependencyResolver。 – 2011-04-22 21:18:06

+0

我有同樣的問題,並最終使用DependencyResolver。據我所知,如果您的DI容器正在管理您的DbContext每個請求,構造函數注入將不起作用,因爲過濾器在請求之外被實例化。 (我正在使用Ninject和.InRequestScope()) – Pete 2015-02-27 18:33:10

0

我一直在這爭奪了一會兒,終於解決了我的問題。所以這裏是我的解決方案,希望它可以幫助別人。

設置: 1.我有一個MVC3項目,一個自定義操作過濾器,通過業務服務使用EF5訪問數據庫。 2.我使用Unity和unity.MVC來根據每個請求來解析我的依賴關係。 3.我使用屬性注入到我的自定義動作過濾器,因爲它有一個無參數的構造函數。

結果。 依賴注入對於動作使用的所有服務都正確工作,我的EF DbContext在每個請求結束時正確處理。

的問題 雖然我的財產關係在我的自定義操作過濾器解決,它包含了我的DbContext的陳舊實例(例如,它似乎已經從之前的請求緩存)

由於在先前的文章中提到, MVC3在過濾器緩存方面更具侵略性,並且不能依賴過濾器的狀態。所以建議解決OnActionExecuting方法中的依賴關係。所以我刪除了我的注入屬性,並在我的統一容器上做了那個叫做決心的事情。但是我仍然有一個DbContext的陳舊版本。在我的主要操作中正確查詢數據庫中的任何更改,但自定義操作篩選器沒有選擇它們。

解決方案。 Unity.MVC通過使用子容器並在每個請求的末尾處理它們來管理每個請求的生命週期。通過解析我的統一容器中的操作過濾器中的依賴關係,我正在從每個請求中未處理的父容器中解析出來。

因此,而不是

IoC.Instance.CurrentContainer.Resolve<IService>(); 

我用這個來獲得子容器,而不是父母的一個實例。

var childContainer = HttpContext.Current.Items["perRequestContainer"] as IUnityContainer; 
var service = childContainer.Resolve<IServcie>(); 

我確定必須有一個乾淨的方法來達到相同的結果,所以請添加建議。

好的稍微改進,以允許我的單元測試注入服務的模擬。 1.從OnActionexecuting中刪除依賴性解析並添加兩個構造函數。

public MyCustomActionfilter() : this(((IUnityContainer)HttpContext.Current.Items["perRequestContainer"].Resolve<IService>()) 

public MyCustomActionfilter(IService service) 
{ 
    this.service = service; 
} 

現在構造解決您服務,將其作爲私人只讀。這現在可以在OnActionExecutng函數中使用。單元測試現在可以調用第二個構造函數並注入一個模擬。