Last article on the concept of caching, the framework of understanding and views, this undertaking on my own cache module design practice.
Basic Cache Module Design
The most basic caching module must have a unified cachehelper, as follows:
Public interface Icachehelper
{
T get<t> (string key);
void set<t> (String key, T value);
void Remove (string key);
And then the business layer is called like this
Public User get (int id)
{
if (id <= 0)
throw new ArgumentNullException ("id");
The var key = string. Format (User_cache_key, id);
var user = _cachehelper.get<user> (key);
if (user!= null) return
user;
Return _repository. Get (ID);
}
There is nothing wrong with the above code, but the actual use of the time is questionable, because I always stressed that the cache to save "hot data", so "hot data" must have expired, we can not write another to set. So it's more appropriate to simply combine it with writing.
Public User GetV2 (int id)
{
if (id <= 0)
throw new ArgumentNullException ("id");
The var key = string. Format (User_cache_key, id);
var user = _cachehelper.get<user> (key);
if (user!= null) return
user;
user = _repository. Get (ID);
if (user!= null)
_cachehelper.set (key, user);
return user;
}
The code above is actually just adding a set, so the design, each time a get need to repeat the code is too much, then should be more streamlined? It is necessary to eat some C # grammar sugar at this time, the grammar sugar occasionally eat point to improve efficiency, why not?
Public User GetV3 (int id)
{
if (id <= 0)
throw new ArgumentNullException ("id");
The var key = string. Format (User_cache_key, id);
Return _cachehelperv2.get<user> (Key, () => _repository. Get (ID));
}
Icache get<t> Implementation public
T get<t> (string key, func<t> fetch = null)
{
t result = default (t );
var obj = cache.get (key);
if (obj is T)
{result
= (T) obj;
}
if (result = = null)
{result
= Fetch ();
if (result!= null)
Set (key, result);
}
return result;
}
Here I package the set method directly into the ICACHE.GET<T>, with the fetch Func. In this way, the common operations are abstracted together, simplifying the cache call, which is perfectly in line with my idea.
Cache Module Design Advanced
the Icache V3 in the previous section was almost the most streamlined, but after referring to Servicestack.redis, I found a much more abstract approach. Obviously, all the code in the previous section is manually managed key, for the usual object cache, does this key need to be manual? to the last improvement.
Public T get<t> (object ID, func<t> fetch = null)
{
var type = typeof (T);
The var key = string. Format (' urn:{1}:{2} ', type. Name, ID. ToString ());//Here is the key, directly using TypeName to act as key return get
(key, Fetch);
}
Public T get<t> (string key, func<t> fetch = null)
{
t result = default (t);
var obj = cache.get (key);
if (obj is T)
{result
= (T) obj;
}
if (result = = null)
{result
= Fetch ();
if (result!= null)
Set (key, result);
}
return result;
}
The Get method completely automates the management of the key, and then calls the way again to be streamlined.
Public User GetV4 (int id)
{
if (id <= 0)
throw new ArgumentNullException ("id");
return _cachehelperv3.get<user> (ID, () => _repository. Get (ID));
}
It's obvious that the most important set is missing, set, when this key gets to cost a little bit, the most need to solve is how to get the value of this primary key ID.
public class User
{
[PrimaryKey]//This attribute is the most important thing public
int UserId {get; set;}
public string UserName {get; set;}
public string Cellphone {get; set;}
}
public void set<t> (T obj)
{
//here should be cached to increase the efficiency of reflection
var type = typeof (T);
var PrimaryKey = type. GetProperties ()
. FirstOrDefault (t => t.getcustomattributes (FALSE)
. Any (c => c is primarykeyattribute);//This gets the value var keyvalue of ID by taking Primarykeyattribute
= Primarykey.getvalue ( obj, null);
The var key = string. Format (' Urn:{0}:{1} ', type. Name, keyvalue);
var dt = DateTime.UtcNow.AddDays (1);//Suppose the default cache is 1 days
var offset = new DateTimeOffset (DT);
Cache.set (key, obj, offset);
}
Here, I think of the final version of the Icache is done. It also needs to be explained that in fact PrimaryKey can be more flexible and changeable. Many times the PrimaryKey of an object is complex, and when you design a cache entity you can work with it:
public class usercacheentity
{
[PrimaryKey] public
int ID
{get
{return
string. Format ("{0}:{1}", UserId, UserName);
}
public int UserId {get; set;}
public string UserName {get; set;}
public string Cellphone {get; set;}
}
The above way can almost automatically manage the common data cache, the only trouble is to customize a cacheobject, which brings the problem of entity conversion, this time it depends on how to choose.
Again, I want the Icache design:
1. Always only cache heat data, which means that each key must have an expiration time
2. Icache Automatic Management Get/set, it is best to automatically manage key.
3. Icache streamlining while not losing flexibility.
Detailed code demo can refer to: Git
more flexible Implementation
Before I write this summary, I have been thinking about the cache should be put on what layer, normal three floor where to put? Where does DDD go when it's layered? Google the next, see some references.
Http://stackoverflow.com/questions/15340173/in-which-layer-implement-the-cache
I think this is more in line with my idea, cache should be global arbitrary, of course, the realization of course is INTERFACE+IOC, so that the more independent reference.
In addition to the cache more advanced use, AOP combined with Icache V4 such a design, is not better? Here I have not yet to implement the attributes of AOP, this is a big topic, the next time to achieve it.
The above is the entire content of this article, I hope to help you learn, but also hope that we support the cloud habitat community.