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是我的新作,希望大家喜欢
  • 相关阅读:
    Educational Codeforces Round 83 --- F. AND Segments
    Educational Codeforces Round 83 --- G. Autocompletion
    SEERC 2019 A.Max or Min
    2019-2020 ICPC Southwestern European Regional Programming Contest(Gym 102501)
    Educational Codeforces Round 78 --- F. Cards
    今天我学习了一门全新的语言
    codeforces 1323D 题解(数学)
    Educational Codeforces Round 80 (Div. 2) 题解 1288A 1288B 1288C 1288D 1288E
    Educational Codeforces Round 81 (Div. 2) 题解 1295A 1295B 1295C 1295D 1295E 1295F
    Codeforces Round #617 (Div. 3) 题解 1296C 1296D 1296E 1296F
  • 原文地址:https://www.cnblogs.com/CoolJie/p/2751492.html
Copyright © 2011-2022 走看看