一、前言
今天我们来谈谈EF的缓存问题。
缓存对于一个系统来说至关重要,但是是EF到版本6了仍然没有见到有支持查询结果缓存机制的迹象。EF4开始会把查询语句编译成存储过程缓存在Sql Server中,据说EF6中对此做了改进,会把Linq To Entities 的查询条件直接编译缓存在EF中。但是这些都是只是对查询条件做了缓存,而不是缓存查询的结果集(DbSet.Find(object key)那个虽然走了DbSet.Local数据集,但也仅支持通过主键查找单个实体的情况,很有局限性),没有达到我们想要的效果。
EF不加缓存功能,可能也有另外的考虑吧,这里不去猜测。虽然EF团队没有在EF中加入缓存功能,但已经给出的缓存功能的扩展,这就是Community Entity Framework Provider Wrappers,这个扩展的工作原理由下图可以清晰的了解:
该扩展提供了跟踪SQL运行日志与SQJ结果集缓存的功能,这里,我们只用到它的缓存功能来为EF建立二级缓存的支持。
二、缓存设计
(一) 引用EFProviderWrappers
如下图,在NuGet中只提供了Entity Framework Provider Wrapper Toolkit(基础类库)与Entity Framework Tracing Provider(日志跟踪)的下载,很遗憾的并没有提供 Entity Framework Caching Provider(缓存)。
我们只能自己动手来引用了,这里提供几种思路:
我是觉得两种思路都挺麻烦的,这个扩展的代码貌似已经不更新了(3/18/2011),而且在GMF.Component.Data中额外的引用两个程序集也是个麻烦事,于是我用下面的方法来引用:
在GMF.Component.Data项目中新建两个文件夹,把以上源代码中的两个工程以文件夹的形式包含到项目中。 |
这样,似乎更干净利落,如图:
(二) 缓存代码分析及整合
1. 关键代码简介
在EFCachingProvider中,我们要用到的核心类有三个:
2. 应用缓存扩展
EF的DbContext上下文类有一个重载
public DbContext(DbConnection existingConnection, bool contextOwnsConnection) { } |
需要的是DbConnection参数,而EFCachingConnection正好是派生自DbConnection的,我们只需要构建一个EFCachingConnection对象作为参数去构造DbContext派生类的对象,即可完成缓存功能的注入(如本篇第一张图所示)。这里,缓存专用的DbContext派生类只需要派生自原项目中定义的EFDbContext类。
- namespace GMF.Component.Data
- {
- ///
- /// 启用缓存的自定义EntityFramework数据访问上下文
- ///
- [Export("EFCaching", typeof (DbContext))]
- public class EFCachingDbContext : EFDbContext
- {
- private static readonly InMemoryCache InMemoryCache = new InMemoryCache();
- public EFCachingDbContext()
- : base(CreateConnectionWrapper("default")) { }
- public EFCachingDbContext(string connectionStringName)
- : base(CreateConnectionWrapper(connectionStringName)) { }
- ///
- /// 由数据库连接串名称创建连接对象
- ///
- /// 数据库连接串名称
- ///
- private static DbConnection CreateConnectionWrapper(string connectionStringName)
- {
- PublicHelper.CheckArgument(connectionStringName, "connectionStringName");
- string providerInvariantName = "System.Data.SqlClient";
- string connectionString = null;
- ConnectionStringSettings connectionStringSetting = ConfigurationManager.ConnectionStrings[connectionStringName];
- if (connectionStringSetting != null)
- {
- providerInvariantName = connectionStringSetting.ProviderName;
- connectionString = connectionStringSetting.ConnectionString;
- }
- if (connectionString == null)
- {
- throw PublicHelper.ThrowComponentException("名称为“" + connectionStringName + "”数据库连接串的ConnectionString值为空。");
- }
- string wrappedConnectionString = "wrappedProvider=" + providerInvariantName + ";" + connectionString;
- EFCachingConnection connection = new EFCachingConnection
- {
- ConnectionString = wrappedConnectionString,
- CachingPolicy = CachingPolicy.CacheAll,
- Cache = InMemoryCache
- };
- return connection;
- }
- }
- }
这里缓存策略使用了缓存所有数据(CacheAllPolicy)的策略,在实际项目中,最好自定义缓存策略,而不要使用这个策略,以免服务器内存被撑爆。
#p#
我们在应用程序配置(Web.Config或App.Config)中,添加一个名为“EntityFrameworkCachingEnabled”的AppSettings节点,用来进行启用/禁用缓存的开关配置。
- ...
- ...
另外,缓存扩展还需要我们在配置文件中添加如下节点的配置:
再来看看,怎样使用“EntityFrameworkCachingEnabled”配置来控制缓存功能的开关。我们的设计中,DbContext对象的注入点为如下所示的Context属性:
所以,我们只需要在UnitOfWorkContextBase的派生类中读取 EntityFrameworkCachingEnabled 进行切换即可。
- namespace GMF.Component.Data
- {
- ///
- /// 数据单元操作类
- ///
- [Export(typeof (IUnitOfWork))]
- public class EFRepositoryContext : UnitOfWorkContextBase
- {
- ///
- /// 获取 当前使用的数据访问上下文对象
- ///
- protected override DbContext Context
- {
- get
- {
- bool secondCachingEnabled = ConfigurationManager.AppSettings["EntityFrameworkCachingEnabled"].CastTo(false);
- return secondCachingEnabled ? EFCachingDbContext.Value : EFDbContext.Value;
- }
- }
- [Import("EF", typeof (DbContext))]
- private Lazy
EFDbContext { get; set; } - [Import("EFCaching", typeof(DbContext))]
- private Lazy
EFCachingDbContext { get; set; } - }
- }
注意,因为EFDbContext与EFCachingDbContext两个属性只能同时用到其中之一,导入需要使用Lazy<>类型来包装,这样没用到的属性就不会实例化了。
下面,我们来测试一下缓存功能是否生效,就用上篇的那个翻页列表吧。判断标准为SQL Server Profiler是否有SQL语句执行。为方便演示,这里在列表的下方显示当前的时间,以便与SQL Server Profiler中的时间进行匹配。
第1页不计。
点击第2页,执行了查询:
点击第3页,执行了查询:
再回到第2页,没有执行查询:
点击第4页,执行了查询:
结论:重复第2页的时候,数据已经缓存了,没有读数据库查询数据,说明缓存已经生效了。
最后要提示的一点:
带缓存的上下文不能担当生成数据库的职责,因此在第一次运行生成数据库的时候,必须关闭缓存。 |
三、源码获取
为了让大家能第一时间获取到本架构的最新代码,也为了方便我对代码的管理,本系列的源码已加入微软的开源项目网站 http://www.codeplex.com,地址为:
https://gmframework.codeplex.com/
原文链接:http://www.cnblogs.com/guomingfeng/p/mvc-ef-caching.html
本文名称:EFCodeFirst:二级缓存
浏览路径:http://www.shufengxianlan.com/qtweb/news41/469391.html
网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等
声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联