在Kooboo中使用了Entity Framework作为持久化框架,但由于EF1.0并没有提供完整缓存解决方案,一直以来都在为数据缓存而烦脑,在没有找到合适解决方案的情况下,采取了临时的解决办法:直接缓存实体。但是由于Entity实体都是带状态的,并且都与ObjectContext有间接的反向引用,缓存带状态的实体,会造成对象上下文混乱和连接资源的无法被正确释放。因此缓存的Entity实体,首先必须被分离或者重新定义POCO实体来代替Entity实体作为缓存对象。这样一来,所有的缓存实体的关联关系都会失效,造成使用上的麻烦和整个软件框架存在严重的不足。
再说说EF的SQL日志问题。在之前的LINQ TO SQL的项目中,有一个可视化的调试器,可以查看查询表达式生成对应的SQL语句,这种可以大大方便开发人员的调试工作。可以在EF1.0中,却一直也找不到类似可用的工具。因此,我的做法是通过SQL Profile来查看EF生成和执行的SQL语句。虽然可行,但还是很不方便。
现在,EF团队终于推出一套比较完整的缓存和SQL执行日志的解决方案,EFProviderWrappers。他们的做法是在原来的EF Provider之上,再加一层包装,通过这层包装拦截,进行数据缓存和日志监控。这里缓存的数据是数据库查询后返回的原生数据,并不是Entity实体对象,这样就可以避免Entity实体状态对缓存造成的的极端负面影响。并且这样的缓存对上层的数据查询本身是透明,在同一个封闭区间内,缓存数据所依赖的实体类型在被更新后(对应的表有发生CURD操作),缓存并会被自动清空。对于日志的监控,经过这层包装后就可以非常容易得到处理。
上面的图虽然是说明对SqlClient有效,但由于这层包装并不涉及具体的SQL操作,因此对不同的数据的Provider应该都是有效。下面通过一个自带的实例简单介绍一下如何使用。
在下载的EFProviderWrappers解决方案中,EFProviderWrapperToolkit,EFCachingProvider,EFTracingProvider这三个工程是真正干事的,其它的工程都是示例工程。在EFProviderWrapperDemo工程,我们可以找到我们所要的例子。
第一步:在配置文件中添加如下配置:
隐藏行号 复制代码 ?
<system.data>
<DbProviderFactories>
<add name="EF Caching Data Provider" invariant="EFCachingProvider" description="Caching Provider Wrapper" type="EFCachingProvider.EFCachingProviderFactory, EFCachingProvider, Version=1.0.0.0, Culture=neutral, PublicKeyToken=def642f226e0e59b" />
<add name="EF Tracing Data Provider" invariant="EFTracingProvider" description="Tracing Provider Wrapper" type="EFTracingProvider.EFTracingProviderFactory, EFTracingProvider, Version=1.0.0.0, Culture=neutral, PublicKeyToken=def642f226e0e59b" />
<add name="EF Generic Provider Wrapper" invariant="EFProviderWrapper" description="Generic Provider Wrapper" type="EFProviderWrapperToolkit.EFProviderWrapperFactory, EFProviderWrapperToolkit, Version=1.0.0.0, Culture=neutral, PublicKeyToken=def642f226e0e59b" />
</DbProviderFactories>
</system.data>
<style type="text/css">
.src_container{background-color:#e7e5dc; width:99%; overflow:hidden; margin:12px 0 12px 0 !important; padding:0px 3px 3px 0px}
.src_container .titlebar{ background-color:#d4dfff; border:1px solid #4f81bd; border-bottom:0; padding:3px 24px; margin:0; width:auto; line-height:120%; overflow:hidden; text-align:left; font-size:12px}
.src_container .toolbar{ display:inline; font-weight:normal; font-size:100%; float:right; cursor:hand; color:#00f; text-align:left; overflow:hidden}
.toolbar span.button{ display:inline; font-weight:normal; font-size:100%; cursor:hand; color:#00f; text-align:left; overflow:hidden; cursor:pointer;}
.src_container div.clientarea{ background-color:white; border:1px solid #4f81bd; margin:0; width:auto !important; width:100%; height:auto; overflow:auto; text-align:left; font-size:12px; font-family: "Courier New","Consolas","Fixedsys",courier,monospace,serif}
.src_container ol.mainarea{ padding:0 0 0 52px; margin:0; background-color:#f7f7ff !important}
.number_show{ padding-left:52px !important; list-style:decimal outside !important}
.number_show li{ list-style:decimal outside !important; border-left:1px dotted #4f81bd}
.number_hide{ padding-left:0px !important; list-style-type:none !important}
.number_hide li{ list-style-type:none !important; border-left:0px}
ol.mainarea li{ display:list-item !important; font-size:12px !important; margin:0 !important; line-height:18px !important; padding:0 0 0 0px !important; background-color:#f7f7ff !important; color:#4f81bd}
ol.mainarea li pre{color:black; line-height:18px; padding:0 0 0 12px !important; margin:0em; background-color:#fff !important}
.linewrap ol.mainarea li pre{white-space:pre-wrap; white-space:-moz-pre-wrapwhite-space:-pre-wrap; white-space:-o-pre-wrap; word-wrap:break-word}
ol.mainarea li pre.alt{ background-color:#f7f7ff !important}
</style><script language="javascript">
function CopyCode(key){var codeElement=null;var trElements=document.all.tags("ol");var i;for(i=0;i<trelements.length;++i){if(key.parentelement.parentelement.parentelement==trelements[i].parentelement.parentelement){codeelement=trelements[i];break}}if(codeelement!=null){var content="codeElement.innerText;if(window.clipboardData=http://www.mamicode.com/=null){window.alert("您的浏览器不支持脚本复制,请尝试手动复制。")}else{window.clipboardData.setData("Text",content);window.alert("源代码已经复制到剪贴板上。")}}}function" LineNumberVisible(key){var codeelement="null;var" trelements="document.all.tags("ol");var" i;for( i="0;i<trElements.length;++i){if(key.parentElement.parentElement.parentElement==trElements[i].parentElement.parentElement){codeElement=trElements[i];break}}if(codeElement!=null){if(codeElement.className=="mainarea" number_hide"){codeElement. classname="mainarea number_show" ;key. innertext="隐藏行号" }else{codeElement. classname="mainarea number_hide" ;key. innertext="显示行号" }}}function ChangeIcon(key,isHover){if(isHover)key.style. color=‘red‘ ;else key.style. color=‘blue‘ }function CopyCode_CheckKey(key){if(window.event. keycode="=13)CopyCode(key)}function" AboutMe(){window.alert("本代码框由 CodePaste for Windows Live Writer 生成。\r\n\r\nAuthor: 范传根\r\nEmail: chuangen@live.cn\r\nWebsite: http://chuangen.name\r\nBlog: http://blog.csdn.net/chuangen");}
</script>
第二步:从生成的ObjectContext中继承一个扩展的ObjectContext,定义所需的扩展属性,其中重点是重新定义构造器,生成包装后的EntityConnection对象。注意 name 是指web.config 中数据库连接的名称
隐藏行号 复制代码 ?
public class ExtendedNorthwindEntities : NorthwindEntities
{
private TextWriter logOutput;
public ExtendedNorthwindEntities()
: this("name=NorthwindEntities")
{
}
public ExtendedNorthwindEntities(string connectionString)
: base(EntityConnectionWrapperUtils.CreateEntityConnectionWithWrappers(
connectionString,
"EFTracingProvider",
"EFCachingProvider"
))
{
}
<style type="text/css">
.src_container{background-color:#e7e5dc; width:99%; overflow:hidden; margin:12px 0 12px 0 !important; padding:0px 3px 3px 0px}
.src_container .titlebar{ background-color:#d4dfff; border:1px solid #4f81bd; border-bottom:0; padding:3px 24px; margin:0; width:auto; line-height:120%; overflow:hidden; text-align:left; font-size:12px}
.src_container .toolbar{ display:inline; font-weight:normal; font-size:100%; float:right; cursor:hand; color:#00f; text-align:left; overflow:hidden}
.toolbar span.button{ display:inline; font-weight:normal; font-size:100%; cursor:hand; color:#00f; text-align:left; overflow:hidden; cursor:pointer;}
.src_container div.clientarea{ background-color:white; border:1px solid #4f81bd; margin:0; width:auto !important; width:100%; height:auto; overflow:auto; text-align:left; font-size:12px; font-family: "Courier New","Consolas","Fixedsys",courier,monospace,serif}
.src_container ol.mainarea{ padding:0 0 0 52px; margin:0; background-color:#f7f7ff !important}
.number_show{ padding-left:52px !important; list-style:decimal outside !important}
.number_show li{ list-style:decimal outside !important; border-left:1px dotted #4f81bd}
.number_hide{ padding-left:0px !important; list-style-type:none !important}
.number_hide li{ list-style-type:none !important; border-left:0px}
ol.mainarea li{ display:list-item !important; font-size:12px !important; margin:0 !important; line-height:18px !important; padding:0 0 0 0px !important; background-color:#f7f7ff !important; color:#4f81bd}
ol.mainarea li pre{color:black; line-height:18px; padding:0 0 0 12px !important; margin:0em; background-color:#fff !important}
.linewrap ol.mainarea li pre{white-space:pre-wrap; white-space:-moz-pre-wrapwhite-space:-pre-wrap; white-space:-o-pre-wrap; word-wrap:break-word}
ol.mainarea li pre.alt{ background-color:#f7f7ff !important}
</style><script language="javascript">
function CopyCode(key){var codeElement=null;var trElements=document.all.tags("ol");var i;for(i=0;i<trelements.length;++i){if(key.parentelement.parentelement.parentelement==trelements[i].parentelement.parentelement){codeelement=trelements[i];break}}if(codeelement!=null){var content="codeElement.innerText;if(window.clipboardData=http://www.mamicode.com/=null){window.alert("您的浏览器不支持脚本复制,请尝试手动复制。")}else{window.clipboardData.setData("Text",content);window.alert("源代码已经复制到剪贴板上。")}}}function" LineNumberVisible(key){var codeelement="null;var" trelements="document.all.tags("ol");var" i;for( i="0;i<trElements.length;++i){if(key.parentElement.parentElement.parentElement==trElements[i].parentElement.parentElement){codeElement=trElements[i];break}}if(codeElement!=null){if(codeElement.className=="mainarea" number_hide"){codeElement. classname="mainarea number_show" ;key. innertext="隐藏行号" }else{codeElement. classname="mainarea number_hide" ;key. innertext="显示行号" }}}function ChangeIcon(key,isHover){if(isHover)key.style. color=‘red‘ ;else key.style. color=‘blue‘ }function CopyCode_CheckKey(key){if(window.event. keycode="=13)CopyCode(key)}function" AboutMe(){window.alert("本代码框由 CodePaste for Windows Live Writer 生成。\r\n\r\nAuthor: 范传根\r\nEmail: chuangen@live.cn\r\nWebsite: http://chuangen.name\r\nBlog: http://blog.csdn.net/chuangen");}
</script>
第三步:指定缓存管理器和缓存策略,缓存管理器是可扩展的。默认提供两种缓存管理器的实现,InMemoryCache和AspNetCache,另外还提供了一种分布式缓存NVelocity适配器的实现:VelocityCache。对于日志监控,可以通过EFTracingProviderConfiguration设置是否输出到控制台或输出到文件,当然通过扩展的ObjectContext还可以将日志输出到指定的TextWriter:
隐藏行号 复制代码 ?
void Application_Start(object sender, EventArgs e)
{
EFTracingProviderConfiguration.LogToFile = Server.MapPath("/Logs/" + string.Format("{0:yyyy-MM-dd}", DateTime.Now) + ".txt");
EFCachingProviderConfiguration.DefaultCache = new AspNetCache();
EFCachingProviderConfiguration.DefaultCachingPolicy = CachingPolicy.CacheAll;
}
<style type="text/css">
.src_container{background-color:#e7e5dc; width:99%; overflow:hidden; margin:12px 0 12px 0 !important; padding:0px 3px 3px 0px}
.src_container .titlebar{ background-color:#d4dfff; border:1px solid #4f81bd; border-bottom:0; padding:3px 24px; margin:0; width:auto; line-height:120%; overflow:hidden; text-align:left; font-size:12px}
.src_container .toolbar{ display:inline; font-weight:normal; font-size:100%; float:right; cursor:hand; color:#00f; text-align:left; overflow:hidden}
.toolbar span.button{ display:inline; font-weight:normal; font-size:100%; cursor:hand; color:#00f; text-align:left; overflow:hidden; cursor:pointer;}
.src_container div.clientarea{ background-color:white; border:1px solid #4f81bd; margin:0; width:auto !important; width:100%; height:auto; overflow:auto; text-align:left; font-size:12px; font-family: "Courier New","Consolas","Fixedsys",courier,monospace,serif}
.src_container ol.mainarea{ padding:0 0 0 52px; margin:0; background-color:#f7f7ff !important}
.number_show{ padding-left:52px !important; list-style:decimal outside !important}
.number_show li{ list-style:decimal outside !important; border-left:1px dotted #4f81bd}
.number_hide{ padding-left:0px !important; list-style-type:none !important}
.number_hide li{ list-style-type:none !important; border-left:0px}
ol.mainarea li{ display:list-item !important; font-size:12px !important; margin:0 !important; line-height:18px !important; padding:0 0 0 0px !important; background-color:#f7f7ff !important; color:#4f81bd}
ol.mainarea li pre{color:black; line-height:18px; padding:0 0 0 12px !important; margin:0em; background-color:#fff !important}
.linewrap ol.mainarea li pre{white-space:pre-wrap; white-space:-moz-pre-wrapwhite-space:-pre-wrap; white-space:-o-pre-wrap; word-wrap:break-word}
ol.mainarea li pre.alt{ background-color:#f7f7ff !important}
</style><script language="javascript">
function CopyCode(key){var codeElement=null;var trElements=document.all.tags("ol");var i;for(i=0;i<trElements.length;++i){if(key.parentElement.parentElement.parentElement==trElements[i].parentElement.parentElement){codeElement=trElements[i];break}}if(codeElement!=null){var content=codeElement.innerText;if(window.clipboardData=http://www.mamicode.com/=null){window.alert("您的浏览器不支持脚本复制,请尝试手动复制。")}else{window.clipboardData.setData("Text",content);window.alert("源代码已经复制到剪贴板上。")}}}function LineNumberVisible(key){var codeElement=null;var trElements=document.all.tags("ol");var i;for(i=0;i<trElements.length;++i){if(key.parentElement.parentElement.parentElement==trElements[i].parentElement.parentElement){codeElement=trElements[i];break}}if(codeElement!=null){if(codeElement.className=="mainarea number_hide"){codeElement.className="mainarea number_show";key.innerText="隐藏行号"}else{codeElement.className="mainarea number_hide";key.innerText="显示行号"}}}function ChangeIcon(key,isHover){if(isHover)key.style.color=‘red‘;else key.style.color=‘blue‘}function CopyCode_CheckKey(key){if(window.event.keyCode==13)CopyCode(key)}function AboutMe(){window.alert("本代码框由 CodePaste for Windows Live Writer 生成。\r\n\r\nAuthor: 范传根\r\nEmail: chuangen@live.cn\r\nWebsite: http://chuangen.name\r\nBlog: http://blog.csdn.net/chuangen");}
</script>
接下来直接就可以使用,注意不同的ObjectContext实例都要指向同一个ICache实例,并且即使你的创建的ObjectCotnext不需要缓存操作,也应该指定ICache实例,并且设置缓存策略为NoCaching,这样在当你的ObjectContext调用了SaveChanges后将会自动清空被更新的实体类型的缓存数据。