2012-07-16 52 views
4

我有一個巨大的XML文檔,我必須解析它來生成域對象。ASP.net緩存+單例模式

由於文檔很大,我不想每次用戶請求時都解析它,但只是第一次,然後將所有對象保存到緩存中。

public List<Product> GetXMLProducts() 
{ 
    if (HttpRuntime.Cache.Get("ProductsXML") != null) 
    { 
     return (List<Product>)(HttpRuntime.Cache.Get("ProductsXML")); 
    } 

    string xmlPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Content\\Products.xml"); 
    XmlReader reader = XmlReader.Create(xmlPath); 
    XDocument doc = XDocument.Load(reader); 

    List<Product> productsList = new List<Product>(); 
    // Parsing the products element 

    HttpRuntime.Cache.Insert("ProductsXML", productsList); 
    return productsList; 
} 

我如何才能使這個函數在singleton中工作並且是線程安全的?

修正了保存對象到緩存方法(被複制 - 粘貼錯誤)

+0

如果您從使用會話的頁面調用它,而不是從任何其他線程調用該會話,則會話會鎖定所有讀/寫並使其安全。 – Aristos 2012-07-16 10:44:01

回答

9

創建一個懶惰的靜態和保存在內存中的應用程序的生命週期。不要忘記「真正」的部分,這是什麼使它線程安全。

public static readonly Lazy<List<Product>> _product = new Lazy<List<Products>>(() => GetProducts(), true); 

要將此添加到您的模型,只需將其設置爲private並返回_product.Value;

public MyModel 
{ 
    ... bunch of methods/properties 

    private static readonly Lazy<List<Product>> _products = new Lazy<List<Products>>(() => GetProducts(), true); 

    private static List<Product> GetProducts() 
    { 
     return DsLayer.GetProducts(); 

    } 

    public List<Product> Products { get { return _products.Value; } } 
} 

要使用Lazy <>創建單身人士,請使用此模式。

public MyClass 
{ 
    private static readonly Lazy<MyClass> _myClass = new Lazy<MyClass>(() => new MyClass(), true); 

    private MyClass(){} 

    public static MyClass Instance { get { return _myClass.Value; } } 
} 

更新/編輯:

另一個懶惰模式使用的範圍內(即會話)

一些模型,保存在會話:

public MyModel 
{ 
    private List<Product> _currentProducts = null; 
    public List<Product> CurrentProducts 
    { 
     get 
     { 
     return this._currentProducts ?? (_currentProducts = ProductDataLayer.GetProducts(this.CurrentCustomer)); 
     } 
    } 
} 
+0

永遠不要以爲我會在應用程序中使用Lazy <>類:)這看起來像我需要的東西。只有一個問題:我可以在「Dictionary like」情況下使用懶惰模式嗎?例如,我有多個用戶的多個XML文件,我想分別爲每個用戶緩存列表。 – Catalin 2012-07-16 11:08:34

+0

當然可以,但你可能不想在這一點上有一個靜態。您可以延遲加載每個用戶的產品並將其保留在會話中。 – 2012-07-16 11:13:18

2

記錄 - 一個懶惰的靜態(克里斯蓋斯勒的答案,從我得到一個+1)是一個很好的解決方案;在這種情況下,因爲你總是想要內存中的數據。這個答案看起來特別是使用Cache(解決你的一些令人困惑的代碼)並帶來另一種方式來初始化一個網站。

執行此操作的傳統方法是在Global.asax(.cs)中的Application_Start處理程序中。不過,我將展示另一種不錯的方式:

將Nutil添加到您的網站的WebActivator包。

然後將下面的代碼添加到一個新的.cs文件在您的項目中創建一個新的文件夾,名爲App_Start

[assembly: WebActivator.PreApplicationStartMethod(typeof(*your_namespace*.MyInit), 
    "Start", Order = 0)] 
namespace *your_namespace* 
{ 

    public static class MyInit { 
    public static void Start() { 
     string xmlPath = HostingEnvironment.MapPath("~/Content/Products.xml"); 
     using(var reader = XmlReader.Create(xmlPath)) { 
     XDocument doc = XDocument.Load(reader); 

     List<Product> productsList = new List<Product>(); 
     // Parsing the products element 

     HttpRuntime.Cache.Insert("ProductsXML", productsList); 
     } 
    } 
    } 
} 

注 - 正確使用Cache.Insert的,和using爲流讀者。你的原始代碼有其他奇怪的邏輯和語義錯誤 - 例如試圖分配給函數的結果,如果它爲空,則從緩存中返回值。

請注意,您還需要在上面的代碼中帶一些名稱空間 - 並注意尋找*your_namespace*

+0

甚至不知道WebActivator存在。謝謝!不要忘記提及Cache Dependency,非常酷的東西,以防文件在中間流更改,我認爲自動重新加載... – 2012-07-16 10:56:13

+0

這段代碼僅在應用程序啓動時第一次執行一次?這是一個非常好的方法,我可以在很多站點找到它!唯一的缺點是,即使沒有用戶訪問該數據,它也會讀取並緩存所有內容。 – Catalin 2012-07-16 11:12:04

+0

@RaraituL - 這不是一個缺點 - 數據已加載並準備就緒,因此用戶不必等待。這只是一種折衷,現在加載或稍後加載。 – 2012-07-16 11:27:20