zoukankan      html  css  js  c++  java
  • (转)Entity Framework 缓存处理与日志监控,(非常重要的技术)

    编写Entity Framework项目的过程中,经常遇到性能问题,但是由于Entity Framework没有很好的调试手段观察编写的LINQ语句产生的SQL语句,导致调试优化性能非常困难,下面的文章能够很容易的解决该问题。

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

     第二步:从生成的ObjectContext中继承一个扩展的ObjectContext,定义所需的扩展属性,其中重点是重新定义构造器,生成包装后的EntityConnection对象。

     1 public ExtendedNorthwindEntities()
     2 : this("name=NorthwindEntities")
     3 {
     4 }
     5 
     6 public ExtendedNorthwindEntities(string connectionString)
     7 : base(EntityConnectionWrapperUtils.CreateEntityConnectionWithWrappers(
     8         connectionString,
     9         "EFTracingProvider",
    10         "EFCachingProvider"
    11 ))
    12 {
    13 }

     第三步:指定缓存管理器和缓存策略,缓存管理器是可扩展的。默认提供两种缓存管理器的实现,InMemoryCache和AspNetCache,另外还提供了一种分布式缓存NVelocity适配器的实现:VelocityCache。对于日志监控,可以通过EFTracingProviderConfiguration设置是否输出到控制台或输出到文件,当然通过扩展的ObjectContext还可以将日志输出到指定的TextWriter:

    1 ICache cache = new InMemoryCache();
    2 CachingPolicy cachingPolicy = CachingPolicy.CacheAll;
    3 
    4 // log SQL from all connections to the console
    5 EFTracingProviderConfiguration.LogToConsole = true;

     接下来直接就可以使用,注意不同的ObjectContext实例都要指向同一个ICache实例,并且即使你的创建的ObjectCotnext不需要缓存操作,也应该指定ICache实例,并且设置缓存策略为NoCaching,这样在当你的ObjectContext调用了SaveChanges后将会自动清空被更新的实体类型的缓存数据。下面一段简单代码演示了数据缓存后的SQL执行情况:

     1 private static void CacheInvalidationDemo()
     2 {
     3     var cache = new InMemoryCache();
     4 
     5     // log SQL from all connections to the console
     6     EFTracingProviderConfiguration.LogToConsole = true;
     7 
     8     for (int i = 0; i < 3; ++i)
     9     {
    10         Console.WriteLine();
    11         Console.WriteLine("*** Pass #{0}...", i);
    12         Console.WriteLine();
    13         using (var context = new ExtendedNorthwindEntities())
    14         {
    15             // set up caching
    16             context.Cache = cache;
    17             context.CachingPolicy = CachingPolicy.CacheAll;
    18 
    19             Console.WriteLine("Loading customer...");
    20             var cust = context.Customers.First(c => c.CustomerID == "ALFKI");
    21             Console.WriteLine("Customer name: {0}", cust.ContactName);
    22             cust.ContactName = "Change" + Environment.TickCount;
    23             Console.WriteLine("Loading orders...");
    24             cust.Orders.Load();
    25             Console.WriteLine("Order count: {0}", cust.Orders.Count);
    26             context.SaveChanges();
    27         }
    28     }
    29 
    30     Console.WriteLine();
    31 }

    在结果控制台中你会看第一次执行了SQL,后面两次都是直接返回结果,并没有访问数据库。

      最后,还想提醒一下,目前下载的这个版本在真正使用时,可能还会存在一些小问题,其中最常见的就是存在大量的throw new NotSupportedException(); 不知出于什么目的并没有提供这些方法的默认实现,其实只是调用被包装对象的相应方法而已。

      更多的资源:

      http://blogs.msdn.com/jkowalski/archive/tags/EFProviderWrappers/default.aspx

      http://code.msdn.microsoft.com/EFProviderWrappers

     

     

     

    Pop Jungle是我的新作,希望大家喜欢
  • 相关阅读:
    JS学习专辑(3) DOM
    JS学习专辑(4) 变量作用域和语句
    JS学习专辑(6) 函数
    JS学习专辑(2) BOM
    WPF Adorner学习(1)
    C# 递归
    IEnumerable和IEnumerator
    C# 索引器
    JS学习专辑(5) 对象和数组
    JS学习专辑(1) 入门
  • 原文地址:https://www.cnblogs.com/CoolJie/p/2751492.html
Copyright © 2011-2022 走看看