2011-12-12 59 views
3

背景/問題:C#類庫 - Singleton設計模式

我是相當新的Singleton設計模式。我已經在Web應用程序中使用一次(與SO社會的幫助下):

public static AppGlobal Instance 
{ 
    get 
    { 
     if (HttpContext.Current.Session != null) 
     { 
      HttpSessionState session = HttpContext.Current.Session; 

      if (session["AppGlobalInstance"] == null) 
      { 
       session["AppGlobalInstance"] = new AppGlobal(); 
      } 

      return (AppGlobal)session["AppGlobalInstance"]; 
     } 
     else 
     { 
      return null; 
     } 
    } 
} 

上述實施對我來說很有意義,因爲AppGlobal的實例存儲在會話中。當會話消失時,AppGlobal死亡。如果我在由Web應用程序調用的類庫中使用相同的設計模式,會發生什麼情況?例如,用戶請求一個調用不知道會話的DLL中的方法的頁面。存儲在單例實例中的數據是否會通過多次調用持續存在?

private static readonly Singleton instance = new Singleton(); 
private Singleton() { } 

public static Singleton Instance 
{ 
    get 
    { 
     return instance; 
    } 
} 

附加信息:

這裏就是我要完成的:我有一個要接收來自第三方應用XML請求的Web應用程序。這個XML將告訴我的Web應用程序執行三件事中的一件(或全部三件)。我想有一個類的單例實例存儲可以被多個類訪問的數據。我希望每個請求後單例實例到DIE。如果上述內容沒有完成這個,那麼完成它的最好方法是什麼?

注意:此Web應用程序在單個服務器上運行,並且從不在服務器場上運行。

編輯1:

基於下面的建議下,我用System.Web.HttpContext.Current.Session來存儲我的類的實例。這看起來像是對每個會話都是獨一無二的單身人士的正確方法嗎(記得我在一個班級圖書館裏)?

public static Ariba Instance 
    { 
     get 
     { 
      if (HttpContext.Current.Session != null) 
      { 
       HttpSessionState session = HttpContext.Current.Session; 

       if (session["AribaInstance"] == null) 
       { 
        session["AribaInstance"] = new Ariba(); 
       } 

       return (Ariba)session["AribaInstance"]; 
      } 
      else 
      { 
       return null; 
      } 
     } 
    } 
+0

你提到你希望在每個**請求**後單身死亡。如果是這樣的話,你不需要在會話中存儲任何東西。相反,您可以將對象存儲在HttpContext.Current.Items [「AribaInstance」]中。我會補充一點,如果你使用HttpContext.Current,你將會遇到一些單元測試你的類庫的困難,因爲HttpContext.Current不會被填充到ASP.NET之外(Session也是如此)。如果單元測試對你很重要,你需要包裝上下文和會話對象。 –

+0

@AndyWilson,謝謝你的領導,併爲答案! –

回答

4

它將通過多次調用持續,但有一個警告。靜態變量的作用域爲AppDomain,所以任何時候IIS工作進程被回收時,存儲在靜態變量中的任何數據都將丟失。會話數據也是如此,如果您將它存儲在「proc」中。

如果您想要一個僅在HTTP請求期間存在的對象,則可以使用HttpContext.Items屬性。

0

因爲單是靜態的,您的數據將可用於在Web應用程序的所有請求,所以它不會只爲會議提供。

但是在ASP.NET應用程序中,您應該避免使用單例。相反,你應該使用Application對象。主要的原因是,如果你將使用一個網絡農場,那麼你的單例不再是應用程序範圍的singelton,而只是在機器上。

+0

你在農場中使用應用程序狀態的想法是錯誤的,你的意思是會話狀態? – felickz

+0

_Scalability_ - **應用程序狀態不在服務於同一應用程序的多個服務器之間共享,例如在Web場中,或在Web服務器中爲同一臺服務器上的同一應用程序提供服務的多個工作進程之間共享。因此,您的應用程序不能依賴包含不同服務器或進程中應用程序狀態相同數據的應用程序狀態。如果您的應用程序將在多處理器或多服務器環境中運行,請考慮使用更具可擴展性的選項(例如數據庫),以獲取必須保持整個應用程序保真度的數據。** – felickz

+0

[MSDN ASP.NET應用程序狀態概述]( http://msdn.microsoft.com/en-us/library/ms178594.aspx) – felickz

0

哦! 如果您想使用PER REQUEST實例,爲什麼不將它作爲參數傳遞給您正在調用的方法,或者將它作爲需要xml的類的構造函數參數傳遞。我認爲這將是最好的設計方法。

+0

我想過這個,但我不想添加8個參數到我創建的每個方法的簽名。 –

+1

在這種情況下,就該開始考慮使用依賴注入了。大多數DI框架允許您使用「每個Web請求」生活方式配置對象,並將它們注入依賴它的類型中。這使您不必將它傳遞給調用堆棧中的所有方法(稱爲方法注入)。 – Steven

+0

好的。假設你不想將它們添加到構造函數中。您可以擁有一個擁有這8個依賴關係的類,並且只能通過簽名中的那個類。使用這種方法可以很容易地對它進行單元測試。如果您想繼續粘貼某種「靜態」方式來獲取它們,您需要將對象放在HttpContext.Current.Items集合中:public static MyObject {get {return(MyObject)HttpContext.Current.Items [ 「MyObject」];} set {HttpContext.Current.Items.Add(「MyObject」,value); }} HttpContext.Current.Items是一個只存在於請求中的字典。 – ivowiblo