在EF4和EF5中需要跟踪执行SQL和缓存数据,微软官方有一个名为EFProviderWrappers的扩展示例非常值得学习,EFProviderWrappers包含EFTracingProvider和EFCachingProvider,前者用于跟踪EF中增删改查的SQL语句,后者用于将EF查询的结果自动进行缓存,缓存策略过期时间可由开发者自己指定,目前这两个扩展只支持EF4和EF5,因为EF6中微软已提供拦截器,开发者可自行开发拦截扩展,此示例演示如何通过ADO.NET提供程序接口扩展EF框架。
EF有一个公共提供程序模型,这样的公共模型允许开发者使用Oracle、MySQL和PostreSQL等第三方数据库,针对不同数据库EF提供相同的API接口,每当你通过ObjectContext使用LINQ查询数据时,查询通过一系列的层次被传递,所有的查询都通过EntityConnection执行,EntityConnection根据不同的连接字符串驱动不同数据库,比如:SqlClient、SqlServerCE和OralceClient,从而支持多数据库,层次关系如下图所示。
正是因为EF提供这样的层次体系,所有SQL都经过EntityConnection执行,所以我们可以拦截经过EntityConnection的所有命令,从而进行SQL跟踪和数据缓存,上面提到的EFTracingProvider和EFCachingProvider正是通过封装EntityConnection实现拦截的。
EFTracingProvider拦截ExecuteReader()、ExecuteScalar()和ExecuteNonQuery(),并将SQL语句和参数打印出来。EFCachingProvider相对来说比较复杂,它使用外部缓存实现,缓存所有通过DbCommand.ExecuteReader()执行的查询的结果,以便于在后期查询中获得更好的性能。
1、通过零度提供的链接下载开源代码包,并在Visual Studio中生成解决方案。
2、在你的项目中分别引用:EFCachingProvider.dll、EFTracingProvider.dll 和EFProviderWrapperToolkit.dll。
3、将开源代码包中的EFProviderWrapperDemo\ExtendedNorthwindEntities.cs复制到你的项目中,可重命名为适当的名称。
4、将ExtendedNorthwindEntities所继承的基类NorthwindEntities修改为自己的EF上下文类。
5、将ExtendedNorthwindEntities构造函数中的字符串名称修改为自己在配置文件中设置的名称。
接下来,针对EF的所有查询,我们都通过上文中封装的ExtendedNorthwindEntities进行,ExtendedNorthwindEntities提供一些接口可供使用。
1、可通过Log属性指定SQL语句输出位置:
public TextWriter Log { get; set; }
2、通过Cache属性指定EF上下文的缓存位置,这里配置的是全局设置:
public ICache Cache { get; set; }
可指定为AspNetCache(ASP.NET缓存)、InMemoryCache(当前内存缓存)和VelocityCache(微软Velocity分布式缓存),当然如果需要,你可继承ICache指定其它第三方缓存,比如:Memcached或者Redis缓存。 3、通过CachingPolicy属性指定缓存策略,可指定为CacheAll(缓存所有)和NoCaching(不缓存),也可通过继承CachingPolicy自定义缓存策略。
public CachingPolicy CachingPolicy { get; set; }
4、更高级别的跟踪,可通过提供的事件通知来进行,提供3个事件:命令执行前,命令执行完成和命令执行失败。
public event EventHandler<CommandExecutionEventArgs> CommandExecutingpublic event EventHandler<CommandExecutionEventArgs> CommandFinishedpublic event EventHandler<CommandExecutionEventArgs> CommandFailed
上面的配置是基于EF上下文的局部配置,也可进行全局配置,全局配置将影响所有的EF上下文,局部配置优先级高于全局配置,全局配置通过EFTracingProviderConfiguration的属性进行设置,这些属性主要有:
1、是定是否需要将SQL语句信息打印到控制台中:
public static bool LogToConsole { get; set; }
2、指定日志文件路径,可将SQL语句输出到指定的日志文件。
public static string LogToFile { get; set; }
3、指定日志记录Action行为,以便于灵活处理日志。
public static Action<CommandExecutionEventArgs> LogAction { get; set; }
1、将EF执行的SQL语句记录到指定的sqllogfile.txt文件中。
using (TextWriter logFile = File.CreateText("sqllogfile.txt"))
{ using (var context = new ExtendedNorthwindEntities())
{
context.Log = logFile;
//EF相关的操作 }
}
2、将EF执行的SQL语句打印到控制台中。
using (var context = new ExtendedNorthwindEntities())
{
context.Log = Console.Out; // ...
}
3、通过订阅执行前和执行后事件跟踪执行日志。
using (var context = new ExtendedNorthwindEntities())
{
context.CommandExecuting += (sender, e) =>
{ Console.WriteLine("Command is executing: {0}", e.ToTraceString());
};
context.CommandFinished += (sender, e) =>
{ Console.WriteLine("Command has finished: {0}", e.ToTraceString());
}; // ...
}
4、当然,也允许你通过全局配置的方式指定默认的跟踪方式。
EFTracingProviderConfiguration.LogToConsole = true;EFTracingProviderConfiguration.LogToFile = "MyLogFile.txt";
1、使用缓存,你首先需要指定缓存策略,下面代码指定内存缓存,并缓存所有数据。
ICache cache = new InMemoryCache();CachingPolicy cachingPolicy = CachingPolicy.CacheAll;
2、下面的代码演示使用Velocity分布式缓存。
private static ICache CreateVelocityCache(bool useLocalCache)
{ DataCacheServerEndpoint endpoint = new DataCacheServerEndpoint("localhost", 22233, "DistributedCacheService"); DataCacheFactory fac = new DataCacheFactory(new DataCacheServerEndpoint[] { endpoint }, useLocalCache, useLocalCache); return new VelocityCache(fac.GetCache("Velocity"));
}
3、查询数据时可指定缓存策略。
using (var context = new ExtendedNorthwindEntities())
{
context.Cache = cache;
context.CachingPolicy = cachingPolicy;
// ...
}
1、当然,我们不能忘记最重要的工作,那就是针对EFTracingProvider和EFCachingProvider的配置,必须在配置文件(app.config或者web.config)中指定,否则上面的代码无效,配置文件如下所示:
<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>
2、正如上文中说明的那样,EFProviderWrappers的核心是对EntityConnection的封装,通过下面的代码封装EntityConnection,并将connection传入EF构造函数即可。
var connection = EntityConnectionWrapperUtils.CreateEntityConnectionWithWrappers(
connectionString, "EFTracingProvider", "EFCachingProvider");
3、当然我们也可以通过配置的方式设置SQL跟踪日志和缓存,配置方式如下所示。
<appSettings>
<add key="EFTracingProvider.logToConsole" value="true" />
<add key="EFTracingProvider.logToFile" value="sqllog.txt" />
</appSettings>
以上就是在EF中跟踪SQL和缓存数据的方法,其实EFProviderWrappers提供的配置很多,相当灵活,更多示例请参考开源包。
下载地址:https://code.msdn.microsoft.com/EFProviderWrappers