I have read lots of information about page caching and partial page caching in a MVC application. However, I would like to know how you would cache data.
In my scenario I will be using LINQ to Entities (entity framework). On the first call to GetNames (or whatever the method is) I want to grab the data from the database. I want to save the results in cache and on the second call to use the cached version if it exists.
Can anyone show an example of how this would work, where this should be implemented (model?) and if it would work.
I have seen this done in traditional ASP.NET apps , typically for very static data.
Here's a nice and simple cache helper class/service I use:
using System.Runtime.Caching;
public class InMemoryCache: ICacheService
{
public T GetOrSet<T>(string cacheKey, Func<T> getItemCallback) where T : class
{
T item = MemoryCache.Default.Get(cacheKey) as T;
if (item == null)
{
item = getItemCallback();
MemoryCache.Default.Add(cacheKey, item, DateTime.Now.AddMinutes(10));
}
return item;
}
}
interface ICacheService
{
T GetOrSet<T>(string cacheKey, Func<T> getItemCallback) where T : class;
}
Usage:
cacheProvider.GetOrSet("cache key", (delegate method if cache is empty));
Cache provider will check if there's anything by the name of "cache id" in the cache, and if there's not, it will call a delegate method to fetch data and store it in cache.
Example:
var products=cacheService.GetOrSet("catalog.products", ()=>productRepository.GetAll())
Reference the System.Web dll in your model and use System.Web.Caching.Cache
public string[] GetNames()
{
string[] names = Cache["names"] as string[];
if(names == null) //not in cache
{
names = DB.GetNames();
Cache["names"] = names;
}
return names;
}
A bit simplified but I guess that would work. This is not MVC specific and I have always used this method for caching data.
I'm referring to TT's post and suggest the following approach:
Reference the System.Web dll in your model and use System.Web.Caching.Cache
public string[] GetNames()
{
var noms = Cache["names"];
if(noms == null)
{
noms = DB.GetNames();
Cache["names"] = noms;
}
return ((string[])noms);
}
You should not return a value re-read from the cache, since you'll never know if at that specific moment it is still in the cache. Even if you inserted it in the statement before, it might already be gone or has never been added to the cache - you just don't know.
So you add the data read from the database and return it directly, not re-reading from the cache.
For .NET 4.5+ framework
add reference: System.Runtime.Caching
add using statement:
using System.Runtime.Caching;
public string[] GetNames()
{
var noms = System.Runtime.Caching.MemoryCache.Default["names"];
if(noms == null)
{
noms = DB.GetNames();
System.Runtime.Caching.MemoryCache.Default["names"] = noms;
}
return ((string[])noms);
}
In the .NET Framework 3.5 and earlier versions, ASP.NET provided an in-memory cache implementation in the System.Web.Caching namespace. In previous versions of the .NET Framework, caching was available only in the System.Web namespace and therefore required a dependency on ASP.NET classes. In the .NET Framework 4, the System.Runtime.Caching namespace contains APIs that are designed for both Web and non-Web applications.
More info:
https://msdn.microsoft.com/en-us/library/dd997357(v=vs.110).aspx
https://learn.microsoft.com/en-us/dotnet/framework/performance/caching-in-net-framework-applications
Steve Smith did two great blog posts which demonstrate how to use his CachedRepository pattern in ASP.NET MVC. It uses the repository pattern effectively and allows you to get caching without having to change your existing code.
http://ardalis.com/Introducing-the-CachedRepository-Pattern
http://ardalis.com/building-a-cachedrepository-via-strategy-pattern
In these two posts he shows you how to set up this pattern and also explains why it is useful. By using this pattern you get caching without your existing code seeing any of the caching logic. Essentially you use the cached repository as if it were any other repository.
I have used it in this way and it works for me.
https://msdn.microsoft.com/en-us/library/system.web.caching.cache.add(v=vs.110).aspx
parameters info for system.web.caching.cache.add.
public string GetInfo()
{
string name = string.Empty;
if(System.Web.HttpContext.Current.Cache["KeyName"] == null)
{
name = GetNameMethod();
System.Web.HttpContext.Current.Cache.Add("KeyName", name, null, DateTime.Noew.AddMinutes(5), Cache.NoSlidingExpiration, CacheitemPriority.AboveNormal, null);
}
else
{
name = System.Web.HttpContext.Current.Cache["KeyName"] as string;
}
return name;
}
AppFabric Caching is distributed and an in-memory caching technic that stores data in key-value pairs using physical memory across multiple servers. AppFabric provides performance and scalability improvements for .NET Framework applications. Concepts and Architecture
Extending #Hrvoje Hudo's answer...
Code:
using System;
using System.Runtime.Caching;
public class InMemoryCache : ICacheService
{
public TValue Get<TValue>(string cacheKey, int durationInMinutes, Func<TValue> getItemCallback) where TValue : class
{
TValue item = MemoryCache.Default.Get(cacheKey) as TValue;
if (item == null)
{
item = getItemCallback();
MemoryCache.Default.Add(cacheKey, item, DateTime.Now.AddMinutes(durationInMinutes));
}
return item;
}
public TValue Get<TValue, TId>(string cacheKeyFormat, TId id, int durationInMinutes, Func<TId, TValue> getItemCallback) where TValue : class
{
string cacheKey = string.Format(cacheKeyFormat, id);
TValue item = MemoryCache.Default.Get(cacheKey) as TValue;
if (item == null)
{
item = getItemCallback(id);
MemoryCache.Default.Add(cacheKey, item, DateTime.Now.AddMinutes(durationInMinutes));
}
return item;
}
}
interface ICacheService
{
TValue Get<TValue>(string cacheKey, Func<TValue> getItemCallback) where TValue : class;
TValue Get<TValue, TId>(string cacheKeyFormat, TId id, Func<TId, TValue> getItemCallback) where TValue : class;
}
Examples
Single item caching (when each item is cached based on its ID because caching the entire catalog for the item type would be too intensive).
Product product = cache.Get("product_{0}", productId, 10, productData.getProductById);
Caching all of something
IEnumerable<Categories> categories = cache.Get("categories", 20, categoryData.getCategories);
Why TId
The second helper is especially nice because most data keys are not composite. Additional methods could be added if you use composite keys often. In this way you avoid doing all sorts of string concatenation or string.Formats to get the key to pass to the cache helper. It also makes passing the data access method easier because you don't have to pass the ID into the wrapper method... the whole thing becomes very terse and consistant for the majority of use cases.
Here's an improvement to Hrvoje Hudo's answer. This implementation has a couple of key improvements:
Cache keys are created automatically based on the function to update data and the object passed in that specifies dependencies
Pass in time span for any cache duration
Uses a lock for thread safety
Note that this has a dependency on Newtonsoft.Json to serialize the dependsOn object, but that can be easily swapped out for any other serialization method.
ICache.cs
public interface ICache
{
T GetOrSet<T>(Func<T> getItemCallback, object dependsOn, TimeSpan duration) where T : class;
}
InMemoryCache.cs
using System;
using System.Reflection;
using System.Runtime.Caching;
using Newtonsoft.Json;
public class InMemoryCache : ICache
{
private static readonly object CacheLockObject = new object();
public T GetOrSet<T>(Func<T> getItemCallback, object dependsOn, TimeSpan duration) where T : class
{
string cacheKey = GetCacheKey(getItemCallback, dependsOn);
T item = MemoryCache.Default.Get(cacheKey) as T;
if (item == null)
{
lock (CacheLockObject)
{
item = getItemCallback();
MemoryCache.Default.Add(cacheKey, item, DateTime.Now.Add(duration));
}
}
return item;
}
private string GetCacheKey<T>(Func<T> itemCallback, object dependsOn) where T: class
{
var serializedDependants = JsonConvert.SerializeObject(dependsOn);
var methodType = itemCallback.GetType();
return methodType.FullName + serializedDependants;
}
}
Usage:
var order = _cache.GetOrSet(
() => _session.Set<Order>().SingleOrDefault(o => o.Id == orderId)
, new { id = orderId }
, new TimeSpan(0, 10, 0)
);
public sealed class CacheManager
{
private static volatile CacheManager instance;
private static object syncRoot = new Object();
private ObjectCache cache = null;
private CacheItemPolicy defaultCacheItemPolicy = null;
private CacheEntryRemovedCallback callback = null;
private bool allowCache = true;
private CacheManager()
{
cache = MemoryCache.Default;
callback = new CacheEntryRemovedCallback(this.CachedItemRemovedCallback);
defaultCacheItemPolicy = new CacheItemPolicy();
defaultCacheItemPolicy.AbsoluteExpiration = DateTime.Now.AddHours(1.0);
defaultCacheItemPolicy.RemovedCallback = callback;
allowCache = StringUtils.Str2Bool(ConfigurationManager.AppSettings["AllowCache"]); ;
}
public static CacheManager Instance
{
get
{
if (instance == null)
{
lock (syncRoot)
{
if (instance == null)
{
instance = new CacheManager();
}
}
}
return instance;
}
}
public IEnumerable GetCache(String Key)
{
if (Key == null || !allowCache)
{
return null;
}
try
{
String Key_ = Key;
if (cache.Contains(Key_))
{
return (IEnumerable)cache.Get(Key_);
}
else
{
return null;
}
}
catch (Exception)
{
return null;
}
}
public void ClearCache(string key)
{
AddCache(key, null);
}
public bool AddCache(String Key, IEnumerable data, CacheItemPolicy cacheItemPolicy = null)
{
if (!allowCache) return true;
try
{
if (Key == null)
{
return false;
}
if (cacheItemPolicy == null)
{
cacheItemPolicy = defaultCacheItemPolicy;
}
String Key_ = Key;
lock (Key_)
{
return cache.Add(Key_, data, cacheItemPolicy);
}
}
catch (Exception)
{
return false;
}
}
private void CachedItemRemovedCallback(CacheEntryRemovedArguments arguments)
{
String strLog = String.Concat("Reason: ", arguments.RemovedReason.ToString(), " | Key-Name: ", arguments.CacheItem.Key, " | Value-Object: ", arguments.CacheItem.Value.ToString());
LogManager.Instance.Info(strLog);
}
}
I use two classes. First one the cache core object:
public class Cacher<TValue>
where TValue : class
{
#region Properties
private Func<TValue> _init;
public string Key { get; private set; }
public TValue Value
{
get
{
var item = HttpRuntime.Cache.Get(Key) as TValue;
if (item == null)
{
item = _init();
HttpContext.Current.Cache.Insert(Key, item);
}
return item;
}
}
#endregion
#region Constructor
public Cacher(string key, Func<TValue> init)
{
Key = key;
_init = init;
}
#endregion
#region Methods
public void Refresh()
{
HttpRuntime.Cache.Remove(Key);
}
#endregion
}
Second one is list of cache objects:
public static class Caches
{
static Caches()
{
Languages = new Cacher<IEnumerable<Language>>("Languages", () =>
{
using (var context = new WordsContext())
{
return context.Languages.ToList();
}
});
}
public static Cacher<IEnumerable<Language>> Languages { get; private set; }
}
I will say implementing Singleton on this persisting data issue can be a solution for this matter in case you find previous solutions much complicated
public class GPDataDictionary
{
private Dictionary<string, object> configDictionary = new Dictionary<string, object>();
/// <summary>
/// Configuration values dictionary
/// </summary>
public Dictionary<string, object> ConfigDictionary
{
get { return configDictionary; }
}
private static GPDataDictionary instance;
public static GPDataDictionary Instance
{
get
{
if (instance == null)
{
instance = new GPDataDictionary();
}
return instance;
}
}
// private constructor
private GPDataDictionary() { }
} // singleton
HttpContext.Current.Cache.Insert("subjectlist", subjectlist);
You can also try and use the caching built into ASP MVC:
Add the following attribute to the controller method you'd like to cache:
[OutputCache(Duration=10)]
In this case the ActionResult of this will be cached for 10 seconds.
More on this here
Related
I am using the code below to cache items. It's pretty basic.
The issue I have is that every time it caches an item, section of the code locks. So with roughly a million items arriving every hour or so, this is a problem.
I've tried creating a dictionary of static lock objects per cacheKey, so that locking is granular, but that in itself becomes an issue with managing expiration of them, etc...
Is there a better way to implement minimal locking?
private static readonly object cacheLock = new object();
public static T GetFromCache<T>(string cacheKey, Func<T> GetData) where T : class {
// Returns null if the string does not exist, prevents a race condition
// where the cache invalidates between the contains check and the retrieval.
T cachedData = MemoryCache.Default.Get(cacheKey) as T;
if (cachedData != null) {
return cachedData;
}
lock (cacheLock) {
// Check to see if anyone wrote to the cache while we where
// waiting our turn to write the new value.
cachedData = MemoryCache.Default.Get(cacheKey) as T;
if (cachedData != null) {
return cachedData;
}
// The value still did not exist so we now write it in to the cache.
cachedData = GetData();
MemoryCache.Default.Set(cacheKey, cachedData, new CacheItemPolicy(...));
return cachedData;
}
}
You may want to consider using ReaderWriterLockSlim, which you can obtain write lock only when needed.
Using cacheLock.EnterReadLock(); and cacheLock.EnterWriteLock(); should greatly improve the performance.
That link I gave even have an example of a cache, exactly what you need, I copy here:
public class SynchronizedCache
{
private ReaderWriterLockSlim cacheLock = new ReaderWriterLockSlim();
private Dictionary<int, string> innerCache = new Dictionary<int, string>();
public int Count
{ get { return innerCache.Count; } }
public string Read(int key)
{
cacheLock.EnterReadLock();
try
{
return innerCache[key];
}
finally
{
cacheLock.ExitReadLock();
}
}
public void Add(int key, string value)
{
cacheLock.EnterWriteLock();
try
{
innerCache.Add(key, value);
}
finally
{
cacheLock.ExitWriteLock();
}
}
public bool AddWithTimeout(int key, string value, int timeout)
{
if (cacheLock.TryEnterWriteLock(timeout))
{
try
{
innerCache.Add(key, value);
}
finally
{
cacheLock.ExitWriteLock();
}
return true;
}
else
{
return false;
}
}
public AddOrUpdateStatus AddOrUpdate(int key, string value)
{
cacheLock.EnterUpgradeableReadLock();
try
{
string result = null;
if (innerCache.TryGetValue(key, out result))
{
if (result == value)
{
return AddOrUpdateStatus.Unchanged;
}
else
{
cacheLock.EnterWriteLock();
try
{
innerCache[key] = value;
}
finally
{
cacheLock.ExitWriteLock();
}
return AddOrUpdateStatus.Updated;
}
}
else
{
cacheLock.EnterWriteLock();
try
{
innerCache.Add(key, value);
}
finally
{
cacheLock.ExitWriteLock();
}
return AddOrUpdateStatus.Added;
}
}
finally
{
cacheLock.ExitUpgradeableReadLock();
}
}
public void Delete(int key)
{
cacheLock.EnterWriteLock();
try
{
innerCache.Remove(key);
}
finally
{
cacheLock.ExitWriteLock();
}
}
public enum AddOrUpdateStatus
{
Added,
Updated,
Unchanged
};
~SynchronizedCache()
{
if (cacheLock != null) cacheLock.Dispose();
}
}
I don't know how MemoryCache.Default is implemented, or whether or not you have control over it.
But in general, prefer using ConcurrentDictionary over Dictionary with lock in a multi threaded environment.
GetFromCache would just become
ConcurrentDictionary<string, T> cache = new ConcurrentDictionary<string, T>();
...
cache.GetOrAdd("someKey", (key) =>
{
var data = PullDataFromDatabase(key);
return data;
});
There are two more things to take care about.
Expiry
Instead of saving T as the value of the dictionary, you can define a type
struct CacheItem<T>
{
public T Item { get; set; }
public DateTime Expiry { get; set; }
}
And store the cache as a CacheItem with a defined expiry.
cache.GetOrAdd("someKey", (key) =>
{
var data = PullDataFromDatabase(key);
return new CacheItem<T>() { Item = data, Expiry = DateTime.UtcNow.Add(TimeSpan.FromHours(1)) };
});
Now you can implement expiration in an asynchronous thread.
Timer expirationTimer = new Timer(ExpireCache, null, 60000, 60000);
...
void ExpireCache(object state)
{
var needToExpire = cache.Where(c => DateTime.UtcNow >= c.Value.Expiry).Select(c => c.Key);
foreach (var key in needToExpire)
{
cache.TryRemove(key, out CacheItem<T> _);
}
}
Once a minute, you search for all cache entries that need to be expired, and remove them.
"Locking"
Using ConcurrentDictionary guarantees that simultaneous read/writes won't corrupt the dictionary or throw an exception.
But, you can still end up with a situation where two simultaneous reads cause you to fetch the data from the database twice.
One neat trick to solve this is to wrap the value of the dictionary with Lazy
ConcurrentDictionary<string, Lazy<CacheItem<T>>> cache = new ConcurrentDictionary<string, Lazy<CacheItem<T>>>();
...
var data = cache.GetOrData("someKey", key => new Lazy<CacheItem<T>>(() =>
{
var data = PullDataFromDatabase(key);
return new CacheItem<T>() { Item = data, Expiry = DateTime.UtcNow.Add(TimeSpan.FromHours(1)) };
})).Value;
Explanation
with GetOrAdd you might end up invoking the "get from database if not in cache" delegate multiple times in the case of simultaneous requests.
However, GetOrAdd will end up using only one of the values that the delegate returned, and by returning a Lazy, you guaranty that only one Lazy will get invoked.
I am trying to implement caching in one of our projects.
I have no experience with this part of the framework, so am likely doing something very wrong
Using code I found over at Code Review (https://codereview.stackexchange.com/questions/48148/generic-thread-safe-memorycache-manager-for-c) I came up with, more or less, the same - added the ability to add lists to the cache.
I have this (just showing the code i am using):
private CacheItemPolicy _defaultCacheItemPolicy = new CacheItemPolicy()
{
SlidingExpiration = new TimeSpan(0, 15, 0)
};
public CacheUtil(string cacheName)
: base(cacheName) { }
public void Set(string cacheKey, Func<T> getData)
{
this.Set(cacheKey, getData(), _defaultCacheItemPolicy);
}
public bool TryGetAndSet(string cacheKey, Func<List<T>> getData, out List<T> returnData)
{
if (TryGet(cacheKey, out returnData))
{
return true;
}
returnData = getData();
this.Set(cacheKey, returnData, _defaultCacheItemPolicy);
return true;
}
public bool TryGet(string cacheKey, out List<T> returnItem)
{
returnItem = (List<T>)this[cacheKey];
return returnItem != null;
}
I can then call this by doing this:
public override List<T> GetAll()
{
string keyName = typeof(T).ToString();
List<T> t;
_cache.TryGetAndSet(keyName, () => base.GetAll(), out t);
return t;
}
base.GetAll() is a function in a repository class that fetches data, via EF.
If i call my GetAll() twice it sets the list into the cache again - returnItem = (List<T>)this[cacheKey]; is coming up null every time.
What am i doing wrong?
I have a .net web application that for all intents and purposes of this question is CRUD with many different domain objects.
A common theme across theses objects is the need to know which value properties have been modified as well as child domain model properties. Currently we have two different systems in place for this.
The value properties is the one I am trying to sort out with this question.
Right now the models all inherit from the PersistableModel base that has these fields and methods of note:
private readonly List<string> _modifiedProperties = new List<string>();
public virtual ModelState State { get; set; }
public IEnumerable<string> ModifiedProperties { get { return _modifiedProperties; } }
protected bool HasModifiedProperties { get { return 0 < _modifiedProperties.Count; } }
public bool WasModified(string propertyName)
{
return _modifiedProperties.Contains(propertyName);
}
public void WasModified(string propertyName, bool modified)
{
if (modified)
{
if (!WasModified(propertyName)) _modifiedProperties.Add(propertyName);
}
else
{
_modifiedProperties.Remove(propertyName);
}
}
Then within each individual model whenever a property is set we also need to call WasModified with a string of the property name and a boolean value.
Obviously this is very tedious and error prone, what I want to do is redesign this base class to automatically add entries to the dictionary when a derived class's property is set.
In my research the closest I've been able to get is to use PostSharp which is out of the question.
While working on a different project I came up with a solution that gets most of the way towards my original goal.
Note that this solution is reliant upon the Dev Express ViewModelBase as its base class, but it wouldn't be hard to make a new base class with the features being used for non Dev Express projects:
Edit: I found an issue with the reset state logic, this update removes that issue.
public abstract class UpdateableModel : ViewModelBase
{
private static readonly MethodInfo GetPropertyMethod;
private static readonly MethodInfo SetPropertyMethod;
private readonly bool _trackingEnabled;
private readonly Dictionary<string, Tuple<Expression, object>> _originalValues;
private readonly List<string> _differingFields;
static UpdateableModel()
{
GetPropertyMethod = typeof(UpdateableModel).GetMethod("GetProperty", BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic);
SetPropertyMethod = typeof(UpdateableModel).GetMethod("SetProperty");
}
protected UpdateableModel(bool isNewModel)
{
_originalValues = new Dictionary<string, Tuple<Expression, object>>();
_differingFields = new List<string>();
if (isNewModel) return;
State = ModelState.Unmodified;
_trackingEnabled = true;
}
public ModelState State
{
get { return GetProperty(() => State); }
set { base.SetProperty(() => State, value); }
}
public new bool SetProperty<T>(Expression<Func<T>> expression, T value)
{
var wasUpdated = base.SetProperty(expression, value);
if (_trackingEnabled && wasUpdated)
{
UpdateState(expression, value);
}
return wasUpdated;
}
/// <summary>
/// Reset State is meant to be called when discarding changes, it will reset the State value to Unmodified and set all modified values back to their original value.
/// </summary>
public void ResetState()
{
if (!_trackingEnabled) return;
foreach (var differingField in _differingFields)
{
var type = GetFuncType(_originalValues[differingField].Item1);
var genericPropertySetter = SetPropertyMethod.MakeGenericMethod(type);
genericPropertySetter.Invoke(this, new[]{_originalValues[differingField].Item2});
}
}
/// <summary>
/// Update State is meant to be called after changes have been persisted, it will reset the State value to Unmodified and update the original values to the new values.
/// </summary>
public void UpdateState()
{
if (!_trackingEnabled) return;
foreach (var differingField in _differingFields)
{
var type = GetFuncType(_originalValues[differingField].Item1);
var genericPropertySetter = GetPropertyMethod.MakeGenericMethod(type);
var value = genericPropertySetter.Invoke(this, new object[] { _originalValues[differingField].Item1 });
var newValue = new Tuple<Expression, object>(_originalValues[differingField].Item1,value);
_originalValues[differingField] = newValue;
}
_differingFields.Clear();
State = ModelState.Unmodified;
}
private static Type GetFuncType(Expression expr)
{
var lambda = expr as LambdaExpression;
if (lambda == null)
{
return null;
}
var member = lambda.Body as MemberExpression;
return member != null ? member.Type : null;
}
private void UpdateState<T>(Expression<Func<T>> expression, T value)
{
var propertyName = GetPropertyName(expression);
if (!_originalValues.ContainsKey(propertyName))
{
_originalValues.Add(propertyName, new Tuple<Expression,object>(expression, value));
}
else
{
if (!Compare(_originalValues[propertyName].Item2, value))
{
_differingFields.Add(propertyName);
}
else if (_differingFields.Contains(propertyName))
{
_differingFields.Remove(propertyName);
}
State = _differingFields.Count == 0
? ModelState.Unmodified
: ModelState.Modified;
}
}
private static bool Compare<T>(T x, T y)
{
return EqualityComparer<T>.Default.Equals(x, y);
}
Another quick note, the IsNewModel constructor flag serves to differentiate objects create on the UI level which don't need any kind of state tracking, and objects generated from the data access level which will need the state tracking.
Im new to generics in c#, and I'm trying to create a storage that other parts of my program can ask for models objects.
The idea was that if my cache class has the object, it checks its date and returns it if the object is not older then 10 min.
If it is older then 10 min it downloads a updated model from the server online.
It it does not have the object is downloads it and returns it.
But I'm having some problems pairing my objects with a DateTime, makeing it all generic.
// model
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
class Program
{
static void Main(string[] args)
{
Person p = new Person();
Cache c = new Cache();
p = c.Get<Person>(p);
}
}
public class Cache
{
struct DatedObject<T>
{
public DateTime Time { get; set; }
public T Obj { get; set; }
}
List<DatedObject<T>> objects;
public Cache()
{
objects = new List<DatedObject<T>>();
}
public T Get<T>(T obj)
{
bool found = false;
// search to see if the object is stored
foreach(var elem in objects)
if( elem.ToString().Equals(obj.ToString() ) )
{
// the object is found
found = true;
// check to see if it is fresh
TimeSpan sp = DateTime.Now - elem.Time;
if( sp.TotalMinutes <= 10 )
return elem;
}
// object was not found or out of date
// download object from server
var ret = JsonConvert.DeserializeObject<T>("DOWNLOADED JSON STRING");
if( found )
{
// redate the object and replace it in list
foreach(var elem in objects)
if( elem.Obj.ToString().Equals(obj.ToString() ) )
{
elem.Obj = ret;
elem.Time = DateTime.Now;
}
}
else
{
// add the object to the list
objects.Add( new DatedObject<T>() { Time = DateTime.Now, Obj = ret });
}
return ret;
}
}
Check out the memory cache class available as part of the .NET framework http://msdn.microsoft.com/en-us/library/system.runtime.caching.memorycache.aspx
You'll need to add the System.RunTime.Caching assembly as a reference to your application. The following is a helper class to add items and remove them from cache.
using System;
using System.Runtime.Caching;
public static class CacheHelper
{
public static void SaveTocache(string cacheKey, object savedItem, DateTime absoluteExpiration)
{
MemoryCache.Default.Add(cacheKey, savedItem, absoluteExpiration);
}
public static T GetFromCache<T>(string cacheKey) where T : class
{
return MemoryCache.Default[cacheKey] as T;
}
public static void RemoveFromCache(string cacheKey)
{
MemoryCache.Default.Remove(cacheKey);
}
public static bool IsIncache(string cacheKey)
{
return MemoryCache.Default[cacheKey] != null;
}
}
The nice thing about this is that it's thread safe, and it takes care of expiring the cache automatically for you. So basically all you have to do is check if getting an item from MemoryCache is null or not. Note however that MemoryCache is only available in .NET 4.0+
If your application is a web application then use System.Web.Caching rather than MemoryCache. System.Web.Caching has been available since .NET 1.1 and there's no additional references you have to add to your project. Heres the same helper class for web.
using System.Web;
public static class CacheHelper
{
public static void SaveTocache(string cacheKey, object savedItem, DateTime absoluteExpiration)
{
if (IsIncache(cacheKey))
{
HttpContext.Current.Cache.Remove(cacheKey);
}
HttpContext.Current.Cache.Add(cacheKey, savedItem, null, System.Web.Caching.Cache.NoAbsoluteExpiration, new TimeSpan(0, 10, 0), System.Web.Caching.CacheItemPriority.Default, null);
}
public static T GetFromCache<T>(string cacheKey) where T : class
{
return HttpContext.Current.Cache[cacheKey] as T;
}
public static void RemoveFromCache(string cacheKey)
{
HttpContext.Current.Cache.Remove(cacheKey);
}
public static bool IsIncache(string cacheKey)
{
return HttpContext.Current.Cache[cacheKey] != null;
}
}
There are other cache expiration policies that you can use for both of these patterns, for instance cache based on a file path(s) so that when a file changes the cache automatically expires, SQL cache dependency (does periodic polling of the SQL server for changes), sliding expiration or you could build your own. They come in really handy.
I have problem with cache in my asp.net mvc3 application.
My code
using System.Web.Caching;
...
class RegularCacheProvider : ICacheProvider
{
Cache cache ;
public object Get(string name)
{
return cache[name];
}
public void Set(string name, object value)
{
cache.Insert(name, value);
}
public void Unset(string name)
{
cache.Remove(name);
}
}
And I use singleton for give value for it :
School schoolSettings = (School)CacheProviderFactory.Cache.Get("SchoolSettings");
if (schoolSettings == null)
{
CacheProviderFactory.Cache.Set("SchoolSettings", someObject);
}
So in first use it does not work and give me an error cache[name] is null.
What I'm doing wrong?
Any help would be appreciated.
At no point have you given cache a value... and note that the regular web cache probably isn't your best bet if you want it separate; perhaps
MemoryCache cache = new MemoryCache();
What about using the HttpRuntime.Cache, this example would cache for an hour?
HttpRuntime.Cache.Add("SchoolSettings", someObject, null, DateTime.Now.AddHours(1),
System.Web.Caching.Cache.NoSlidingExpiration,
System.Web.Caching.CacheItemPriority.Normal, null);
Try the following code. it works fine for my project
using System.Runtime.Caching;
public class RegularCacheProvider : ICacheProvider
{
private ObjectCache Cache { get { return MemoryCache.Default; } }
object ICacheProvider.Get(string key)
{
return Cache[key];
}
void ICacheProvider.Set(string key, object data, int cacheTime = 30)
{
var policy = new CacheItemPolicy {AbsoluteExpiration = DateTime.Now + TimeSpan.FromMinutes(cacheTime)};
Cache.Add(new CacheItem(key, data), policy);
}
void ICacheProvider.Unset(string key)
{
Cache.Remove(key);
}
}
Change the code where you check for the value as follow:
School schoolSettings = CacheProviderFactory.Cache.Get("SchoolSettings") as (School);
Notice that I am using "as" rather than casting the object. Cast will blow up if the value is null while "as" will just give you a null value which is what you expect.