5

我在改變我的Asp.Net MVC3項目中使用Autofac服務注入到我的控制器的過程。迄今爲止,這非常簡單。我的服務都有一個Telerik OpenAccess數據庫屬性,我通過構造函數注入(在服務基類中)。而且我的控制器都具有可以注入的服務的構造函數屬性。Autofac財產注射

我有一個叫AuditInfo類,它封裝了控制器的可審計性:

public class AuditInfo 
{  
    public string RemoteAddress { get; set; } 

    public string XForwardedFor { get; set; } 

    public Guid UserId { get; set; } 

    public string UserName { get; set; } 
} 

在我的服務類我的OpenAccess數據庫屬性需要有這個類中注入它的一個實例,以便爲使用審計各種數據庫調用中的信息。

問題是,這不是一個可以在Application_Start實例化的類,因爲它至少有兩個屬性,RemoteAddress和XForwardedFor在OnActionExecuting的最早階段(即一旦存在Request變量)被填充。

因此,我在BaseController類的OnActionExecuting方法實例化這個這樣:

protected override void OnActionExecuting(ActionExecutingContext filterContext) 
{ 
    base.OnActionExecuting(filterContext); 
    db.AuditInfo = AuditInfo;          
} 

public AuditInfo AuditInfo 
{ 
    get 
    { 
     return new AuditInfo() 
     { 
      RemoteAddress = this.Request.ServerVariables["REMOTE_ADDR"], 
      XForwardedFor = this.Request.ServerVariables["X_FORWARDED_FOR"], 
      UserId = this.UserId, 
      UserName = this.UserName 
     }; 
    } 
} 

所以 - 我的問題/問題是:

  1. 我不喜歡在這直接觸及分配給OnActionExecuting中的OpenAccess數據庫屬性。
  2. 我想這AuditInfo基本上在被注入到任何AuditInfo財產的任何地方
  3. 我不認爲我可以使用構造函數注射AuditInfo因爲服務依賴於數據庫 - 控制器取決於服務 - 分貝取決於AuditInfo BUT直到控制器實例化並收到其第一個請求後,AuditInfo纔可用。 =>循環依賴...

我該如何設置autofac來將AuditInfo注入到任何具有它作爲屬性的類中?還是有避免循環依賴和使用某種形式的lambda/lazy構造函數屬性的更好方法?

是它在所有關於該AuditInfo被在即使大量的請求可以是相同會話的一部分,而不是有不同的IP地址/用戶信息的每個請求重新初始化潛在的不必要的?

謝謝

+0

我的問題/問題不同意3 - 正如史蒂芬指出,你可以使用'HttpContext.Current'。所以'AuditInfo'不依賴於控制器,所以沒有循環依賴,所以如果你願意,你可以構造器注入'AuditInfo'。 –

+0

嗯 - 我認爲這是因爲建議的解決方案是使用全局靜態變量來引用Request對象,而不是初始化Request對象自然存在的控制器內的AuditInfo對象。我認爲服務器變量在Application_Start中甚至不存在,因爲必須首先調用一個動作,否則會發生注入。 – t316

回答

2

原來Autofac's MVC Integration can resolve an HttpRequestBase for you。所以你不需要直接引用HttpContext.Current.Request

Autofac的實現uses HttpContext.Current幕後。這工作,因爲MVC框架設置HttpContext.Current你的代碼(或Autofac的)運行之前。所以沒有循環依賴 - 請求「自然存在」在HttpContext.Current.Request上,就像你的控制器一樣。 (This question種解釋如何)

所以當史蒂芬建議,但要求在其構造的HttpRequestBase而不是使用HttpContext.Current如果它讓你覺得不引用靜態變量更好,你可以做一個IAuditInfoFactory

而且,沒有循環依賴,如果你願意,你可以構造函數注入AuditInfo

builder.Register(c => c.Resolve<IAuditInfoFactory>().CreateNew()) 
    .As<AuditInfo>() 
    .InstancePerHttpRequest(); 
+0

Steven在構建正確答案的基礎方面做得很好,但缺乏HttpRequestBase注入和default.kramer添加了這一點使得他的答案更加完整。不知道 - 我如何被處理在這樣的情況下處理正確答案的公平標記 - 請隨時讓我知道。現在我將default.kramer的答案標記爲正確。謝謝 – t316

2

答案是:使用工廠。

注入的IAuditInfoFactory到需要它的類型,創建這樣一個實現:

public class HttpRequestAuditInfoFactory : IAuditInfoFactory 
{ 
    // Service for requesting information about the current user. 
    private readonly ICurrentUserServices user; 

    public HttpRequestAuditInfoFactory(ICurrentUserServices user) 
    { 
     this.user = user; 
    } 

    AuditInfo IAuditInfoFactory.CreateNew() 
    { 
     var req = HttpContext.Current.Request; 

     return new AuditInfo() 
     { 
      RemoteAddress = req.ServerVariables["REMOTE_ADDR"], 
      XForwardedFor = req.ServerVariables["X_FORWARDED_FOR"], 
      UserId = this.user.UserId, 
      UserName = this.user.UserName 
     }; 
    } 
} 

如下您可以註冊類:

builder.RegisterType<HttpRequestAuditInfoFactory>() 
    .As<IAuditInfoFactory>() 
    .SingleInstance(); 

現在,你可以注入

+0

感謝您的快速響應。這應該可行,但這種解決方案實際上不依賴於引用HttpContext.Current.Request並在外部環境中讀取它的變量,而不是在HttpContext.Current.Request的自然生命週期中存儲必要的變量,並將這些變量注入到國外背景鬆耦合?含義 - IAuditInfoFactory實現需要引用System.Web,並直接瞭解有關HttpContext.Current.Request的信息... – t316

+0

這個特殊的'HttpRequestAuditInfoFactory'實現的確確實實實在在地依賴於'HttpContext'。在這個意義上說,它是平臺意識。這不是問題,但出於這個原因,它不是(或者不應該)是你的應用程序的一部分,但應該是我們所說的[Composition Root]的一部分(http://blog.ploeh.dk/2011/ 07/28/CompositionRoot.aspx)(CR)。這是應用程序的啓動路徑。除了CR之外,其他任何部分都不應該意識到這個'HttpRequestAuditInfoFactory'的存在。該應用程序只知道'IAuditInfoFactory'。 – Steven

+0

因爲應用程序的其餘部分知道「IAuditInfoFactory」而不知道「HttpRequestAuditInfoFactory」,所以它可以很容易地將您的應用程序遷移到(例如)Windows服務。您需要Windows服務特定的'IAuditInfoFactory'實現並將其連接起來,而不是Windows Service的CR('main'方法)中的'HttpRequestAuditInfoFactory'。 – Steven