RuntimeEntity提供了2种缓存, 用于释放程序员的编码工作.
一 , 对象缓存.
对象缓存即针对单个的对象逐个进行缓存.
RuntimeEntity的对象缓存是透明的,内置的.不需要编写任何代码, 就能享受到对象缓存的好处.
例如
Product p1=Product.LoadRow(1);
Product p2=Product.LoadRow(1);
这2行代码都去读取同一个Product.
第一行p1会去执行一条SQL语句. 对象会被缓存起来. 然后第二行p2就不用再重新执行SQL语句了.
但是RuntimeEntity有一个很好的feature,在上面例子中,
虽然p2是来自于缓存, 但是p1与p2引用的并不是同一个对象.
为什么会这样设计呢?
这是防止一段代码调用 p1.ProductName="Xxx" 修改对象中,提交修改之前,
另外一段代码取得 p2 , 并且执行 textbox.Text=p2.ProductName 可能发生的错误.
现在p1不等于p2, 无论p1怎样修改它的属性, 只要p1没有Save, 那么p1修改的值就不会影响到p2.
下面的代码,
Product p1=Product.LoadRow(1);
Product p2=Product.LoadRow(1);
p1.ProductName="NewName";
p1.Save();
在执行p1.Save()之后, p2.ProductName也会跟着变成"NewName".
很神奇吧! 目前其他的ORM并无法做到这一点!
RuntimeEntity会自动维护了缓存的对象的最新版本.
使缓存是高效的同时, 确保应用程序保持正确的行为.
如果一个对象更新了, 那么其他同一个ID的对象也会是最新的值.
查询例子代码:
Order[] orders1=RuntimeEntity.All<Order>().Where("ShipVia=1 AND EmployeeID<>1");
Order[] orders2=RuntimeEntity.All<Order>().Where("ShipVia=1");
在上述代码中, 使用了RuntimeEntityQuery来查询符合条件的Order.
orders1中并不包含EmployeeID为1的Order 而order2则包含.
因为某些对象已经在orders1被缓存, 所以查询orders2的时候,Employee<>1的部分将不会被加载!
这个设计最大化地应用了对象缓存,大大地提高了加载数据的性能!
无论是LoadRow,还是通过RuntimeEntityQuery一次过返回多个对象,
都会把对象加进缓存里面. 直到内存紧张时, 没有被引用的对象就会被释放.
二 , 查询缓存.
对象缓存只是节省了加载对象的过程. 但是如果使用RuntimeEntityQuery去查询对象,
即使所有符合条件的对象都已经在缓存中了, RuntimeEntityQuery依然会执行一句SQL.
为什么会这样的呢? 原因是RuntimeEntityQuery是分为2步的:
步骤1. SELECT OrderID FROM Orders WHERE ShipVia=1 AND EmployeeID<>1
这样,就得到了所有符合条件的Order的OrderID
RuntimeEntity在内存中,逐一寻找符合OrderID的对象缓存. 如果没有找到,那么就会执行语句:
步骤2. SELECT * FROM Orders WHERE OrderID IN (11111,11112,11113)
即使所有符合条件的Order都在对象缓存中, 步骤1 是必须执行的.
所以,下面代码,执行了3次SQL:
Order[] orders1=RuntimeEntity.All<Order>().Where("ShipVia=1");
Order[] orders2=RuntimeEntity.All<Order>().Where("ShipVia=1");
order1执行了步骤1和步骤2. 然后所有的对象都被缓存起来.
所以返回order2的时候, 仍然会执行步骤1,而跳过了步骤2..
是的, 只有对象缓存是不足够的. 因为上面说的步骤1,本身就是一个非常耗时间的WHERE语句.
为了更加省去步骤1, RuntimeEntityQuery增加了一个新方法 UseCache() .
(这个方法并不包含在可下载的RuntimeEntityPreview中)
只要把上面的代码改成:
Order[] orders1=RuntimeEntity.All<Order>().UseCache().Where("ShipVia=1");
Order[] orders2=RuntimeEntity.All<Order>().UseCache().Where("ShipVia=1");
就可以享受查询缓存的好处.
这个代码的不同之处,只是在中间增加了一个 UserCache() 方法的调用而已.
就这样, RuntimeEntityQuery就会帮你自动分析生成缓存的Key,
并且自动把查询得到的OrderID缓存起来.
这样. 上面的代码在返回orders2的过程, 是完全不需要执行SQL语句了!
默认的情况下,查询缓存是30秒过期. UseCache方法还可以指定缓存的过期时间:
Order[] orders2=RuntimeEntity.All<Order>().UseCache(TimeSpan.FromSeconds(10)).Where("ShipVia=1");
之所以默认不使用查询缓存. 是因为RuntimeEntity无法单独在内存中判断查询缓存的正确性.
所以默认情况下都会查询数据库, 返回最新的ID列表.
而在某些场合, 查询的结果并不需要太实时. 程序员可以根据具体的情况去显式调用UseCache()
这是一个一劳永逸的方案!
只要使用RuntimeEntity,就能自动应用对象缓存. 只要使用UseCache()函数, 就能应用查询缓存.
三 , 下面简单地说一下如何使用RuntimeEntity来做超高性能的分页!!
RuntimeEntityQuery提供一个GetRange方法, 用于返回符合页数的对象:
int pageIndex=int.Parse(Request.QueryString["Page"])-1;
int pageIndex=0;
int pageSize=20;
int recordStart=pageIndex*pageSize;//计算记录的开始位置
int recordCount=pageSize;//计算返回多少记录
int totalCount;//将获取所有的记录数!
//执行一句GetRange,就能返回COUNT(*)的同时,返回符合页数的记录.
//完全不需要手动编写什么分页存储过程!
Product[] products = RuntimeEntity.All<Product>().GetRange(recordStart,recordCount,out totalCount);
//计算页数:
int pageCount=(int)Math.Ceiling((double)totalCount/(double)pageSize);
//RuntimeEntity可以绑定到ASP.NET的控件中去!
DataGrid1.DataSource=products;
DataGrid1.DataBind();
//使用totalCount,pageCount,可以设置分页控件:
...
上面的代码, 描述了GetRange的用法. 程序员还可以加上UseCache()用来缓存搜索的结果.
是的. 这是也是一个一劳永逸的方案! 写一次代码, 就能适应绝大部分分页场合的性能要求!!
假如数据库有1千万的记录那怎么办???
RuntimeEntity并不会去读取所有记录. 而是通过对ID进行分页的方式, 读取某段的ID对应的记录!
但是无论如何, 尝试去读取1千万个ID也不是好事.
所以RuntimeEntityQuery增加了一个 LimitTo 方法去限制返回的记录数:
(其原理就是简单的 SELECT TOP N ..)
RuntimeEntityQuery<BlogTopic> query=RuntimeEntity.All<BlogTopic>().UseCache();
if(pageIndex<100)
query=query.LimitTo(pageSize*100);
query=query.OrderBySqlFieldDescending("TopicID");
BlogTopic[] topics=query.GetRange(recordStart,recordCount,out totalCount);
int pageCount=...
if(pageIndex<100&&pageCount>100)
moreLink.Visible=true;
上面的代码 , 模拟了一个超大型的社区网站的Blog首页代码.
该网站有超多的用户, 而文章也疯狂地增加.
这段代码中, 用pageIndex来判断是否需要限制加载的ID的数量.
当翻有人到第100页的时候, 才选择加载全部的ID.
这种方案既能让用户能看到所有的记录 , 也在大部分情况下拥有更高的性能.
.. 总结
简单, 总是程序员最希望的编程生活.
性能, 总是数据库开发中最让人头痛的事情.
RuntimeEntity不断尝试让常见的数据库操作变得更简单,同时,让程序拥有最佳的性能.
无论是高手 , 还是菜鸟 , 都能写出高质量的代码 !
希望体验RuntimeEntity的,请到http://www.cnblogs.com/lostinet/ 里寻找RuntimeEntityPreview .
(但是该版本, 并不包含上述提到的 UseCache() 和 LimitTo() 方法.)