在EF中跟踪SQL和缓存数据

administrator / 2019/5/12 13:42 / 0 阅读 7500

在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语句输出位置:

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> CommandExecuting

public event EventHandler<CommandExecutionEventArgs> CommandFinished

public 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; }

SQL跟踪示例

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提供的配置很多,相当灵活,更多示例请参考开源包。

官方下载

年入30万+的.NET Core学习路线?



发送评论 请先登录再发布评论