2012-07-16 111 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