I have a huge XML document which I have to parse it to generate domain objects.
Because the document is huge, i don't want to parse it every time a user requests it, but only first time, then saving all the objects into cache.
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;
}
How is the best way i can make this function working in singleton and be thread-safe?
Fixed the saving an object into cache method (was a copy-paste mistake)
Create a Lazy static and keep in memory for the lifetime of the application. And don't forget the "true" part, that's what makes it thread safe.
public static readonly Lazy<List<Product>> _product = new Lazy<List<Products>>(() => GetProducts(), true);
To add this to your model, just make it private and return _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; } }
}
To create a singleton using Lazy<>, use this pattern.
public MyClass
{
private static readonly Lazy<MyClass> _myClass = new Lazy<MyClass>(() => new MyClass(), true);
private MyClass(){}
public static MyClass Instance { get { return _myClass.Value; } }
}
Update/Edit:
Another lazy pattern for use within a context (i.e. Session)
Some Model that is saved in Session:
public MyModel
{
private List<Product> _currentProducts = null;
public List<Product> CurrentProducts
{
get
{
return this._currentProducts ?? (_currentProducts = ProductDataLayer.GetProducts(this.CurrentCustomer));
}
}
}
For the record - a lazy static (Chris Gessler's answer, which gets a +1 from me) is a good solution; in this case because you always want the data in memory. This answer looks specifically at the use of Cache
(addressing your somewhat confusing code) and bringing another way to initialise a website.
The traditional way to do this is in an Application_Start
handler in Global.asax(.cs). However I'm going to show another nice way:
Add the WebActivator package to your website with Nuget.
Then add the following code to a new .cs file in a new folder you create in your project, called 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);
}
}
}
}
Note - the correct use of Cache.Insert, and a using
for the stream reader. Your original code has other strange logic and semantic errors - such trying to assign to the result of a function and returning the value from the cache if it's null.
Note also you need to bring some namespaces in the above code - and look out for *your_namespace*
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With