I want to be able to maintain certain objects between application restarts.
To do that, I want to write specific cached items out to disk in Global.asax Application_End() function and re-load them back on Application_Start().
I currently have a cache helper class, which uses the following method to return the cached value:
return HttpContext.Current.Cache[key];
Problem: during Application_End(), HttpContext.Current is null since there is no web request (it's an automated cleanup procedure) - therefore, I cannot access .Cache[] to retrieve any of the items to save to disk.
Question: how can I access the cache items during Application_End()?
If you want to get access to cache object before it will be disposed, you need to use somethink like this to add object to cache:
Import namespace System.Web.Caching to your application where you are using adding objects to cache.
//Add callback method to delegate
var onRemove = new CacheItemRemovedCallback(RemovedCallback);
//Insert object to cache
HttpContext.Current.Cache.Insert("YourKey", YourValue, null, DateTime.Now.AddHours(12), Cache.NoSlidingExpiration, CacheItemPriority.NotRemovable, onRemove);
And when this object is going to be disposed will be called following method:
private void RemovedCallback(string key, object value, CacheItemRemovedReason reason)
{
//Use your logic here
//After this method cache object will be disposed
}
I strongly urge you to rethink your approach. You may want to describe specifics of what are you trying to do, so we might help you with that.
But if you are totally set on it, then you can simply save values on disk when you actually set them, i.e. your helper class would looks something like this:
public static class CacheHelper
{
public static void SetCache(string key, object value)
{
HttpContext.Current.Cache[key] = value;
if (key == "some special key")
WriteValueOnDisk(value);
}
}
You can access the cache through HttpRuntime.Cache when you don't have an HttpContext available. However, at Application_End, i believe the cache is already flushed.
The solution Dima Shmidt outlines would be the best approach to store your cached values. That is by adding your items to cache with a CacheItemRemovedCallback, and store the values to disk there.
As an alternative solution you could store the data in Application object (Application[key]) or simply create a static class and use it to keep your data within app - in this case the data would sill be available upon Application_End.
Related
I'm using .NET C# for a project.
I have a list of products which I want to cache as they're used company wide. If the products drop out of cache I already know how to lock the cache and rebuild it ok as per the patterns on various authority/blog sites.
In my pages/user controls etc, I might grab a reference to the cache, like this:
var myCacheInstance = cachedProducts
However, I might also want to do something like this:
myCacheInstance.Add(new product(...));
Which will also update the cache as it's the same object.
I have 2 queries.
If I have a reference to the cached object is it guaranteed to remain in cache for the lifetime of my variable?
In the scanario outlined above, how do I go about ensuring integrity? I'm only planning on adding in this instance, but suppose, I was updating and deleting objects as well?
1) If I have a reference to the cached object is it guaranteed to
remain in cache for the lifetime of my variable?
If I right interpret this question: responce is no.
cache.Add("key", new object()); // ADD KEY
var obj = cache["key"]; // GET REFERENCE TO CACHED OBJECT
cache.Remove("key"); // REMOVE OBJECT FROM CACHE
obj.DoSomething(..); //PERFECTLY VALID, STILL WORK ..
2) In the scanario outlined above, how do I go about ensuring
integrity? I'm only planning on adding in this instance, but suppose,
I was updating and deleting objects as well?
Can add bool property like, for example:
public bool IsValid
{
get; private set;
}
when object removed this property is set from the class to false. Just example, iff it really fits your need can tell us only you.
Do not pass around a reference to your cache!
Use an object for your cache and if a clients wants to have the cached items return a new list of your cached items, or a readonly collection.
If you want to add items to the cache, use a method on the cache object and in that method lock the cache and add the item. Same with remove.
question 1: If you pass around references you can not guarantee anything.
question 2: Use an object to cache all your items as I described above.
public class Cache
{
private List<Item> cachedItems = new List<Item>();
public void Add(Item item)
{
lock(cachedItems)
{
cachedItems.Add(item);
}
}
}
hello in order to ensure integrity, you must add key
Cache.Add("YourKey", yourValue)
here you can find helper for all operations
http://johnnycoder.com/blog/2008/12/10/c-cache-helper-class/
For duration or timeout you have this format, where you specify absoluteExpiration
public Object Add (string key, Object value, CacheDependency dependencies,
DateTime absoluteExpiration, TimeSpan slidingExpiration, CacheItemPriority
priority, CacheItemRemovedCallback onRemoveCallback)
I have looked at this question first: retain value after page refresh. But it seems like it doesn't work with collections because the ViewState only store one value. I need to use the collection because I do not know how many instances of object the user will create. Therefore, the ViewState is not an option. I also tried using static variables but it stores the values for longer than I need it (I found the explanation for this here: http://social.msdn.microsoft.com/Forums/en/csharpgeneral/thread/3664cd62-46c7-4098-88a7-c09647673096).
I would massively appreciate your help.
Often using Session state is better for lare objects, but you can store any serializable objects in the ViewState too ( http://msdn.microsoft.com/en-us/library/ms972976.aspx ).
Note: please avoid using storing data in static properties in ASP.Net as the values are shared across all requests. Note on note - HttpContext.Current is not really static objects (per-request object).
You could store the List<> instance in the client session on the server side. Then on each request you will have access to the list in the session.
However, you should still be able to use viewstate, as long as the items in your List<> are serializable.
You should consider the amount of data being exchanged between the browser and web server when using viewstate. If your items are not unreasonably large and you will not have too many of them in the list then viewstate might not be unreasonable.
The code might look something like this.
private ArrayList GlobalList
{
get
{
return ViewState["GlobalList"];
}
set
{
ViewState["GlobalList"] = value;
}
}
you can use class level variable which hold the value after post if you want to use List make it class level variable then it wont loose the value.
What is the best method of storing a shared object in asp.net? It will get called multiple times per request on every request. Ive been using these two methods but Id like to know if there is a better way. I refresh this object once an hour.
public static List<ResourceObject> SharedResources = new List<ResourceObject>()
//OR
public static List<ResourceObject> SharedResources
{
get
{
List<ResourceObject> _sharedResources = HttpContext.Current.Cache["RedirectRoutes"] as List<ResourceObject>;
if (_sharedResources == null)
{
_sharedResources = LoadNewSharedResource();
HttpContext.Current.Cache["RedirectRoutes"] = _sharedResources;
}
return _redirectRoutes;
}
set
{
HttpContext.Current.Cache["RedirectRoutes"] = value;
}
}
If your object is changing frequently (i.e. hourly as you mentioned) then you'll be best to use the cache as it will be able to take care of flushing for you (assuming you pass the correct parameters when adding the value to the cache). If you use a static value it will not be cleared out every hour automatically so you'd need to implement the check yourself.
If this is, as it seems, an object that needs to persist across requests, then this is a perfectly good and reasonable way to achieve it. You may want to put the cached version in a local variable if it is being accessed multiple times within one call, to save retrieving it from the cache each time.
Is there a specific issue with caching it like that that you are concerned about?
I am using webmethod which fetches data from database & storing it in a static variable so that next time it will not hit the database becoz the data is being accessed frequently. Creating static variable doesn't seem to be a proper solution what is an alernative to this scenario.
e.g
public static List<SupplierGridData> lstFullSupplierData;
public static List<SupplierGridData> GetProductData()
{
if (lstFullSupplierData == null)
{
//first time get data from database
lstFullSupplierData = new List<SupplierGridData>();
lstFullSupplierData = new POProcess().GetInquiryDetails();
}
else
{
//use lstFullSupplierData which holds data
}
}
What you want is caching:
http://msdn.microsoft.com/en-us/library/xsbfdd8c.aspx
More specifically, caching data:
http://msdn.microsoft.com/en-us/library/6hbbsfk6.aspx
There is also Session State:
http://msdn.microsoft.com/en-us/library/ms972429.aspx
This comes in the per-application and per-user guise. And you can also change the backing store of session state.
The easiest solution to this could be hitting the db once if(!this.IsPostBack) then burying that stuff in the viewstate of the page if it's not too much stuff (assuming you don't need to carry across other pages). Otherwise session or cache will do as other people are suggesting.
You need to store your data in session in order to not hit the database again.
http://msdn.microsoft.com/en-us/library/ms178581.aspx
im new to the Castle Active Record Pattern and Im trying to get my head around how to effectivley use cache.
So what im trying to do (or want to do) is when calling the GetAll, find out if I have called it before and check the cache, else load it, but I also want to pass a bool paramter that will force the cache to clear and requery the db.
So Im just looking for the final bits.
thanks
public static List<Model.Resource> GetAll(bool forceReload)
{
List<Model.Resource> resources = new List<Model.Resource>();
//Request to force reload
if (forceReload)
{
//need to specify to force a reload (how?)
XmlConfigurationSource source = new XmlConfigurationSource("appconfig.xml");
ActiveRecordStarter.Initialize(source, typeof(Model.Resource));
resources = Model.Resource.FindAll().ToList();
}
else
{
//Check the cache somehow and return the cache?
}
return resources;
}
public static List<Model.Resource> GetAll()
{
return GetAll(false);
}
Take a look at the caching pattern:
http://weblogs.asp.net/ssmith/archive/2003/06/20/9062.aspx
http://stevesmithblog.com/blog/cache-access-pattern-revised/
BTW you're initializing ActiveRecord each time you call GetAll. You have to initialize only once, when your application starts.
Also, it's not good practice generally to explicitly release the cache like that. Instead, use some sort of policy or dependency (see for example SqlDependency)
Also, NHibernate has a pluggable second-level cache.