Mentioning cache policy using an attribute at each model classes make it a flexible and easy to use approach.
In entity framework this can be implemented using GenericRepository and custom attributes.
So first, lets create an enum to represent the cache policies to be used in our application:
public enum WA1CachePoliciesEnum
{
None,
SlidingExpiration10Minutes,
AbsoluteExpiration1Hour,
AbsoluteExpiration1Day,
NeverExpire
}
Now we need to define the attribute
[AttributeUsage(AttributeTargets.Class,
AllowMultiple = false)]
public class CachePolicyAttribute : Attribute
{
public WA1CachePoliciesEnum policy;
public CachePolicyAttribute(WA1CachePoliciesEnum policy)
{
this.policy = policy;
}
}
The atrribute class merely saves CachedItemPolicy object passed to it in a public property. This attribute can be added to any class, but we intend to use it for our model classes only. Lets use the attribute in a model.
[CachePolicy(WA1CachePoliciesEnum.AbsoluteExpiration1Hour)]
public class Students
{
[Key]
public int
StudentId { get; set; }
public string Name { get; set; }
[ForeignKey("Class")]
[Column("ClassId")]
public int ClassId
{ get; set; }
public virtual Class
Class { get; set; }
}
Now we need to modify the Generic repository class to use the attribute
public class GenericRepository<TEntity> :
IGenericRepository<TEntity> where TEntity : class
{
internal CustomContext context;
internal DbSet<TEntity> dbSet;
internal DbContext dbcontext;
protected ObjectCache cache = MemoryCache.Default;
public GenericRepository(CustomContext context)
{
this.context = context;
this.dbSet = context.Set<TEntity>();
}
public GenericRepository(DbContext dbcontext)
{
this.dbcontext = dbcontext;
this.dbSet = context.Set<TEntity>();
}
public virtual
IEnumerable<TEntity> Get()
{
Type entityType = typeof(TEntity);
object[] policyAttributes = entityType.GetCustomAttributes(typeof(CustomCachePolicyAttribute), false);
var result = new
List<TEntity>();
if (policyAttributes != null && policyAttributes.Length > 0)
{
var policy =
CustomCacheHelper.GetPolicy(((CustomCachePolicyAttribute)policyAttributes[0]).policy);
result =
(List<TEntity>)cache.Get(entityType.Name);
if (result == null)
{
result = GetQuery().ToList();
cache.Add(new CacheItem(entityType.Name, result),
policy);
}
}
else
{
result = GetQuery().ToList();
}
return result;
}
public virtual
IQueryable<TEntity> GetQuery()
{
IQueryable<TEntity> query =
dbSet;
return query;
}
public virtual
IQueryable<TEntity> GetByIdAndIncludeRelated(object id, List<string> relatedEntities)
{
IQueryable<TEntity> query =
dbSet.AsQueryable();
if (relatedEntities != null)
{
query =
relatedEntities.Aggregate(query,
(current, include)
=> current.Include(include));
}
// TEntity
entity = dbSet.Find(id);
// var
entityKey = GetEntityKey(context, entity)
// object res =
entityKey.EntityKeyValues.FirstOrDefault().Value;
//string key =
entityKey.EntityKeyValues.FirstOrDefault().Key;
return query;
}
public virtual
IQueryable<TEntity> GetAndIncludeRelated(List<string> relatedEntities)
{
IQueryable<TEntity> query =
dbSet.AsQueryable();
if (relatedEntities != null)
{
query =
relatedEntities.Aggregate(query,
(current, include)
=> current.Include(include));
}
return query;
}
public virtual
EntityKey GetEntityKey<T>(DbContext
context, T entity)
where T : class
{
var oc = ((IObjectContextAdapter)context).ObjectContext;
ObjectStateEntry ose;
if (null !=
entity && oc.ObjectStateManager
.TryGetObjectStateEntry(entity, out ose))
{
return ose.EntityKey;
}
return null;
}
public virtual
EntityKey GetEntityKey<T>(DbContext
context
, DbEntityEntry<T> dbEntityEntry)
where T : class
{
if (dbEntityEntry != null)
{
return GetEntityKey(context, dbEntityEntry.Entity);
}
return null;
}
public virtual TEntity
GetByID(object id)
{
return dbSet.Find(id);
}
public virtual void Insert(TEntity entity)
{
dbSet.Add(entity);
}
public virtual void Delete(object id)
{
TEntity entityToDelete =
dbSet.Find(id);
Delete(entityToDelete);
}
public virtual void Delete(TEntity entityToDelete)
{
if (context.Entry(entityToDelete).State == EntityState.Detached)
{
dbSet.Attach(entityToDelete);
}
dbSet.Remove(entityToDelete);
}
public virtual void Update(TEntity entityToUpdate)
{
dbSet.Attach(entityToUpdate);
context.Entry(entityToUpdate).State
= EntityState.Modified;
}
}
In the code above, I have highlighted the Get() method which uses our custom cache attribute values. Every time we call get method on an entity, it checks if cache is enabled and if the entity is present in the cache before fetching data from database.
public class CustomCacheHelper
{
public static CacheItemPolicy
GetPolicy(CustomCachePoliciesEnum cmCachePolicy)
{
var result = new
CacheItemPolicy { AbsoluteExpiration = DateTime.MinValue };
switch (cmCachePolicy)
{
case CustomCachePoliciesEnum.None:
break;
case CustomCachePoliciesEnum.SlidingExpiration10Minutes:
result = new CacheItemPolicy { SlidingExpiration
= new TimeSpan(0, 10, 0) };
break;
case CustomCachePoliciesEnum.AbsoluteExpiration1Hour:
result = new CacheItemPolicy {
AbsoluteExpiration = DateTime.Now.AddHours(1) };
break;
case CustomCachePoliciesEnum.AbsoluteExpiration1Day:
result = new CacheItemPolicy {
AbsoluteExpiration = DateTime.Now.AddDays(1) };
break;
case CustomCachePoliciesEnum.NeverExpire:
result = new CacheItemPolicy {
AbsoluteExpiration = DateTime.MaxValue };
break;
default:
break;
}
return result;
}
}
This helper class creates cache item policies based on settings passed to it.