在EF中跟踪SQL和缓存数据

2023/7/6 02:13:35

"在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-provider -wrapers

正是因为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语句输出位置:

<span style=color: blue;>public <span style=color: #2b91af;>TextWriter <span style=color: black;>Log { <span style=color: blue;>get<span style=color: black;>; <span style=color: blue;>set<span style=color: black;>; }

2、通过Cache属性指定EF上下文的缓存位置,这里配置的是全局设置:

<span style=color: blue;>public <span style=color: #2b91af;>ICache <span style=color: black;>Cache { <span style=color: blue;>get<span style=color: black;>; <span style=color: blue;>set<span style=color: black;>; }

可指定为AspNetCache(ASP.NET缓存)、InMemoryCache(当前内存缓存)和VelocityCache(微软Velocity分布式缓存),当然如果需要,你可继承ICache指定其它第三方缓存,比如:Memcached或者Redis缓存。 3、通过CachingPolicy属性指定缓存策略,可指定为CacheAll(缓存所有)和NoCaching(不缓存),也可通过继承CachingPolicy自定义缓存策略。

<span style=color: blue;>public <span style=color: #2b91af;>CachingPolicy <span style=color: black;>CachingPolicy { <span style=color: blue;>get<span style=color: black;>; <span style=color: blue;>set<span style=color: black;>; }

4、更高级别的跟踪,可通过提供的事件通知来进行,提供3个事件:命令执行前,命令执行完成和命令执行失败。

<span style=color: blue;>public event <span style=color: #2b91af;>EventHandler<span style=color: black;><<span style=color: #2b91af;>CommandExecutionEventArgs<span style=color: black;>> CommandExecuting

<span style=color: blue;>public event <span style=color: #2b91af;>EventHandler<span style=color: black;><<span style=color: #2b91af;>CommandExecutionEventArgs<span style=color: black;>> CommandFinished

<span style=color: blue;>public event <span style=color: #2b91af;>EventHandler<span style=color: black;><<span style=color: #2b91af;>CommandExecutionEventArgs<span style=color: black;>> CommandFailed

全局配置

上面的配置是基于EF上下文的局部配置,也可进行全局配置,全局配置将影响所有的EF上下文,局部配置优先级高于全局配置,全局配置通过EFTracingProviderConfiguration的属性进行设置,这些属性主要有:

1、是定是否需要将SQL语句信息打印到控制台中:

<span style=color: blue;>public static bool <span style=color: black;>LogToConsole { <span style=color: blue;>get<span style=color: black;>; <span style=color: blue;>set<span style=color: black;>; }

2、指定日志文件路径,可将SQL语句输出到指定的日志文件。

<span style=color: blue;>public static string <span style=color: black;>LogToFile { <span style=color: blue;>get<span style=color: black;>; <span style=color: blue;>set<span style=color: black;>; }

<span style=color: #2a2a2a;>3、指定日志记录Action行为,以便于灵活处理日志。

<span style=color: blue;>public static <span style=color: #2b91af;>Action<span style=color: black;><<span style=color: #2b91af;>CommandExecutionEventArgs<span style=color: black;>> LogAction { <span style=color: blue;>get<span style=color: black;>; <span style=color: blue;>set<span style=color: black;>; }

SQL跟踪示例

1、将EF执行的SQL语句记录到指定的sqllogfile.txt文件中。

<span style=color: blue;>using <span style=color: black;>(<span style=color: #2b91af;>TextWriter <span style=color: black;>logFile = <span style=color: #2b91af;>File<span style=color: black;>.CreateText(<span style=color: #a31515;>sqllogfile.txt<span style=color: black;>)) { <span style=color: blue;>using <span style=color: black;>(<span style=color: blue;>var <span style=color: black;>context = <span style=color: blue;>new <span style=color: #2b91af;>ExtendedNorthwindEntities<span style=color: black;>()) { context.Log = logFile;

    </span><span style=""color: green;"">//EF相关的操作
</span><span style=""color: black;"">}

}

<span style=color: #2a2a2a;>2、将EF执行的SQL语句打印到控制台中。

<span style=color: blue;>using <span style=color: black;>(<span style=color: blue;>var <span style=color: black;>context = <span style=color: blue;>new <span style=color: #2b91af;>ExtendedNorthwindEntities<span style=color: black;>()) { context.Log = <span style=color: #2b91af;>Console<span style=color: black;>.Out; <span style=color: green;>// ... <span style=color: black;>}

<span style=color: #2a2a2a;>3、通过订阅执行前和执行后事件跟踪执行日志。

<span style=color: blue;>using <span style=color: black;>(<span style=color: blue;>var <span style=color: black;>context = <span style=color: blue;>new <span style=color: #2b91af;>ExtendedNorthwindEntities<span style=color: black;>()) { context.CommandExecuting += (sender, e) => { <span style=color: #2b91af;>Console<span style=color: black;>.WriteLine(<span style=color: #a31515;>Command is executing: {0}<span style=color: black;>, e.ToTraceString()); }; context.CommandFinished += (sender, e) => { <span style=color: #2b91af;>Console<span style=color: black;>.WriteLine(<span style=color: #a31515;>Command has finished: {0}<span style=color: black;>, e.ToTraceString()); }; <span style=color: green;>// ... <span style=color: black;>}

<span style=color: #2a2a2a;>4、当然,也允许你通过全局配置的方式指定默认的跟踪方式。

<span style=color: #2b91af;>EFTracingProviderConfiguration<span style=color: black;>.LogToConsole = <span style=color: blue;>true<span style=color: black;>; <span style=color: #2b91af;>EFTracingProviderConfiguration<span style=color: black;>.LogToFile = <span style=color: #a31515;>MyLogFile.txt<span style=color: black;>;

缓存示例

1、使用缓存,你首先需要指定缓存策略,下面代码指定内存缓存,并缓存所有数据。

<span style=color: #2b91af;>ICache <span style=color: black;>cache = <span style=color: blue;>new <span style=color: #2b91af;>InMemoryCache<span style=color: black;>(); <span style=color: #2b91af;>CachingPolicy <span style=color: black;>cachingPolicy = <span style=color: #2b91af;>CachingPolicy<span style=color: black;>.CacheAll;

<span style=color: #2a2a2a;>2、下面的代码演示使用Velocity分布式缓存。

<span style=color: blue;>private static <span style=color: #2b91af;>ICache <span style=color: black;>CreateVelocityCache(<span style=color: blue;>bool <span style=color: black;>useLocalCache) { <span style=color: #2b91af;>DataCacheServerEndpoint <span style=color: black;>endpoint = <span style=color: blue;>new <span style=color: #2b91af;>DataCacheServerEndpoint<span style=color: black;>(<span style=color: #a31515;>localhost<span style=color: black;>, 22233, <span style=color: #a31515;>DistributedCacheService<span style=color: black;>); <span style=color: #2b91af;>DataCacheFactory <span style=color: black;>fac = <span style=color: blue;>new <span style=color: #2b91af;>DataCacheFactory<span style=color: black;>(<span style=color: blue;>new <span style=color: #2b91af;>DataCacheServerEndpoint<span style=color: black;>[] , useLocalCache, useLocalCache);

</span><span style=""color: blue;"">return new </span><span style=""color: #2b91af;"">VelocityCache</span><span style=""color: black;"">(fac.GetCache(</span><span style=""color: #a31515;"">""Velocity""</span><span style=""color: black;"">));

}

<span style=color: #2a2a2a;>3、查询数据时可指定缓存策略。

<span style=color: blue;>using <span style=color: black;>(<span style=color: blue;>var <span style=color: black;>context = <span style=color: blue;>new <span style=color: #2b91af;>ExtendedNorthwindEntities<span style=color: black;>()) { context.Cache = cache; context.CachingPolicy = cachingPolicy; <span style=color: green;>// ... <span style=color: black;>}

配置文件

1、当然,我们不能忘记最重要的工作,那就是针对EFTracingProvider和EFCachingProvider的配置,必须在配置文件(app.config或者web.config)中指定,否则上面的代码无效,配置文件如下所示:

<span style=color: blue;><<span style=color: #a31515;>system.data<span style=color: blue;>> <<span style=color: #a31515;>DbProviderFactories<span style=color: blue;>> <<span style=color: #a31515;>add <span style=color: red;>name<span style=color: blue;>=<span style=color: black;><span style=color: blue;>EF Caching Data Provider<span style=color: black;> <span style=color: red;>invariant<span style=color: blue;>=<span style=color: black;><span style=color: blue;>EFCachingProvider<span style=color: black;> <span style=color: red;>description<span style=color: blue;>=<span style=color: black;><span style=color: blue;>Caching Provider Wrapper<span style=color: black;> <span style=color: red;>type<span style=color: blue;>=<span style=color: black;><span style=color: blue;>EFCachingProvider.EFCachingProviderFactory, EFCachingProvider, Version=1.0.0.0, Culture=neutral, PublicKeyToken=def642f226e0e59b<span style=color: black;> <span style=color: blue;>/> <<span style=color: #a31515;>add <span style=color: red;>name<span style=color: blue;>=<span style=color: black;><span style=color: blue;>EF Tracing Data Provider<span style=color: black;> <span style=color: red;>invariant<span style=color: blue;>=<span style=color: black;><span style=color: blue;>EFTracingProvider<span style=color: black;> <span style=color: red;>description<span style=color: blue;>=<span style=color: black;><span style=color: blue;>Tracing Provider Wrapper<span style=color: black;> <span style=color: red;>type<span style=color: blue;>=<span style=color: black;><span style=color: blue;>EFTracingProvider.EFTracingProviderFactory, EFTracingProvider, Version=1.0.0.0, Culture=neutral, PublicKeyToken=def642f226e0e59b<span style=color: black;> <span style=color: blue;>/> <<span style=color: #a31515;>add <span style=color: red;>name<span style=color: blue;>=<span style=color: black;><span style=color: blue;>EF Generic Provider Wrapper<span style=color: black;> <span style=color: red;>invariant<span style=color: blue;>=<span style=color: black;><span style=color: blue;>EFProviderWrapper<span style=color: black;> <span style=color: red;>description<span style=color: blue;>=<span style=color: black;><span style=color: blue;>Generic Provider Wrapper<span style=color: black;> <span style=color: red;>type<span style=color: blue;>=<span style=color: black;><span style=color: blue;>EFProviderWrapperToolkit.EFProviderWrapperFactory, EFProviderWrapperToolkit, Version=1.0.0.0, Culture=neutral, PublicKeyToken=def642f226e0e59b<span style=color: black;> <span style=color: blue;>/> </<span style=color: #a31515;>DbProviderFactories<span style=color: blue;>> </<span style=color: #a31515;>system.data<span style=color: blue;>>

2、正如上文中说明的那样,EFProviderWrappers的核心是对EntityConnection的封装,通过下面的代码封装EntityConnection,并将connection传入EF构造函数即可。

<span style=color: blue;>var <span style=color: black;>connection = <span style=color: #2b91af;>EntityConnectionWrapperUtils<span style=color: black;>.CreateEntityConnectionWithWrappers( connectionString, <span style=color: #a31515;>EFTracingProvider<span style=color: black;>, <span style=color: #a31515;>EFCachingProvider<span style=color: black;>);

3、当然我们也可以通过配置的方式设置SQL跟踪日志和缓存,配置方式如下所示。

<span style=color: blue;><<span style=color: #a31515;>appSettings<span style=color: blue;>> <<span style=color: #a31515;>add <span style=color: red;>key<span style=color: blue;>=<span style=color: black;><span style=color: blue;>EFTracingProvider.logToConsole<span style=color: black;> <span style=color: red;>value<span style=color: blue;>=<span style=color: black;><span style=color: blue;>true<span style=color: black;> <span style=color: blue;>/> <<span style=color: #a31515;>add <span style=color: red;>key<span style=color: blue;>=<span style=color: black;><span style=color: blue;>EFTracingProvider.logToFile<span style=color: black;> <span style=color: red;>value<span style=color: blue;>=<span style=color: black;><span style=color: blue;>sqllog.txt<span style=color: black;> <span style=color: blue;>/> </<span style=color: #a31515;>appSettings<span style=color: blue;>>

以上就是在EF中跟踪SQL和缓存数据的方法,其实EFProviderWrappers提供的配置很多,相当灵活,更多示例请参考开源包。

[官方下载](https://code.msdn.microsoft.com/EFProviderWrappers)"