使用了EF4 + MVC3架构,因为数据库的数据变化不是很频繁而且数据量不是很大,在服务端缓存数据库的数据我觉得很有必要,本着简洁、有效的原则,于是写了个具有缓存性质的代码:
1 public class DataService 2 { 3 public DataService() { } 4 5 private T Get<T>(string cacheId, Func<T> getItemCallback, int secondsToCache) where T : class 6 { 7 T item = HttpRuntime.Cache.Get(cacheId) as T; 8 if (item == null) 9 { 10 item = getItemCallback(); 11 HttpRuntime.Cache.Insert(cacheId, 12 item, 13 null, 14 System.Web.Caching.Cache.NoAbsoluteExpiration, 15 new TimeSpan(0, 0, secondsToCache), 16 CacheItemPriority.Normal, 17 null); 18 } 19 return item; 20 } 21 22 /// <summary> 23 /// 使用谓词从数据库或数据缓存中获取对应的实体集合。 24 /// </summary> 25 /// <typeparam name="T">实体类型</typeparam> 26 /// <param name="predicate">谓词表达式以进行筛选,若获取全部数据,可置为null,默认为null</param> 27 /// <param name="fromDB">是否从数据库获取即时数据,默认为false</param> 28 /// <returns>满足要求的数据集合</returns> 29 public List<T> Get<T>(Expression<Func<T, bool>> predicate = null, bool fromDB = false, string path="") where T : class,new() 30 { 31 if (!fromDB) 32 { 33 if (predicate == null) 34 return this.Get<List<T>>(typeof(T).Name, FecthFromDatabase<T>, 3600); 35 return this.Get<List<T>>(typeof(T).Name, FecthFromDatabase<T>, 3600).AsQueryable().Where<T>(predicate).ToList(); 36 } 37 else 38 { 39 this.Delete(typeof(T).Name); 40 if (predicate == null) 41 return new ServiceBase<T>().GetAll(path); 42 else 43 return new ServiceBase<T>().GetMuch(predicate, path); 44 } 45 } 46 47 private List<T> FecthFromDatabase<T>() where T : class,new() 48 { 49 return new ServiceBase<T>().GetAll(); 50 } 51 52 private void Delete(string cacheId) 53 { 54 HttpRuntime.Cache.Remove(cacheId); 55 } 56 57 /// <summary> 58 /// 删除对应的实体,并更新该实体集合的缓存。 59 /// </summary> 60 /// <typeparam name="T"></typeparam> 61 /// <param name="t">要删除的实体</param> 62 public void Delete<T>(T t) where T : class,new() 63 { 64 new ServiceBase<T>().Delete(t); 65 this.Delete(t.GetType().Name); 66 } 67 68 69 /// <summary> 70 /// 删除满足谓词条件的实体集合,并更新该实体集合的缓存。 71 /// </summary> 72 /// <typeparam name="T"></typeparam> 73 /// <param name="predicate">谓词表达式</param> 74 public void Delete<T>(Expression<Func<T, bool>> predicate) where T : class,new() 75 { 76 new ServiceBase<T>().Delete(predicate); 77 this.Delete(typeof(T).Name); 78 } 79 80 /// <summary> 81 /// 更新对应的实体,并更新该实体集合的缓存。 82 /// </summary> 83 /// <typeparam name="T"></typeparam> 84 /// <param name="t"></param> 85 /// <returns>更新后的实体</returns> 86 public T Update<T>(T t) where T : class,new() 87 { 88 T t2 = new ServiceBase<T>().Update(t); 89 this.Delete(t.GetType().Name); 90 return t2; 91 } 92 93 /// <summary> 94 /// 插入一个实体,并更新该实体集合的缓存。 95 /// </summary> 96 /// <typeparam name="T"></typeparam> 97 /// <param name="t"></param> 98 /// <returns>插入后的实体</returns> 99 public T Insert<T>(T t) where T : class,new() 100 { 101 T t2 = new ServiceBase<T>().Insert(t); 102 this.Delete(t.GetType().Name); 103 return t2; 104 } 105 106 }
代码中ServiceBase是EF操作的一层封装,没贴出来。使用具体对象时只采取如下方法即可:
1 DataService db = new DataService(); 2 //这里会自动缓存,或从缓存中得到数据 3 List<Order> orders = db.Get<Order>(); 4 5 Order order = orders.First(); 6 order.Price = 2.03; 7 //缓存失效,下次会自动获取 8 db.Update<Order>(order);
对EF的理解不是很深刻,缓存到服务端的数据关系保存,比如Order里Product导航属性,使用缓存时Order的Product是空的,要想取到Product,只能通过Order的ProductId在Product集合中查找。
问题是:
1. 关于缓存,这样做是否科学?有没有更好的实践? 当然,这建立在一个前提下:数据量不大,数据更新不频繁。
2. 如何把Order的Product属性缓存下来,不至于使用那么复杂的方法来获取呢?
还有一个方法是基于Sql Server 2008的Broken,可以发通知到客户端,但经过试验,只要加入了一条新的数据到数据库,再打开页面速度那是相当的慢。
看来只能这么着结贴了。
缓存其实是个业务层处理的事情。
当读频率远大于写频率的时候,缓存其实很起作用。但是这个不是必需的准则