Entity Framework 4 und der 2nd-Level Cache

Von Haus aus bringt das Entity Framework keine erweiterte Cache-Funktionalität mit. Wer für Projekte solche Szenarien benötigt, kann aber auf die EFProviderWrappers zurückgreifen. In früheren Beiträgen habe ich aus diesem Toolkit der Möglichkeiten des EFTracingProviders beschrieben. Vor allem gefällt mir hier, dass ich das generierte SQL an spezifische Vorstellungen anpassen kann. Es gibt DBA's die sind in dieser Hinsicht wenig kompromissbereit.

Es lassen sich durch dieses Toolkit auch eigene Provider realisieren.

Mit dem EFCachingProvider besteht die Möglichkeit, dem Entity Framework einen 2nd-Level Cache zu verpassen. So kann es auch weiter zu NHibernate aufschliessen.

Cachevarianten die zur Verfügung stehen sind:

  • InMemoryCache (einfache Variante, die nicht sehr leistungsfähig ist)
  • Der ASP.NET Cache
  • und Velocity (AppFabric)

Gerade mit Velocity steht dann ein sehr mächtiges Caching zur Verfügung. Haufig werden jedoch Probleme mit dem Caching berichtet, wenn View-Generation benutzt wird, da dass Caching mit den Hashes nicht umgehen kann. Dieses Phänomen konnte ich nicht reproduzieren, es liegt evtl. daran, dass ich diese Views mittels T4 und dem zur Verfügungen stehenden EntityViewGenerator erstellen lasse.

Mit der CachingPolicy besteht zudem die Möglichkeit, dass ich definieren kann, ob einzelne Entitäten oder alle zwischengespeichert werden sollen, es ist jedoch nicht möglich, eine Art "partitioniertes" Caching auf einzelne Entitäten eines Sets zu definieren.

Damit das Caching funktioniert, muss der entsprechende Wrapper um die normale Verbindung "gewickelt" werden, das geschieht beim Konstruktor mit folgender Helperklasse:

Provider-Wrapper C#
public ORMSamplesEntities()
            : base(EntityConnectionWrapperUtils.CreateEntityConnectionWithWrappers(
                        ConnectionString,
                        "EFTracingProvider",
                        "EFCachingProvider"
                ), ContainerName)
        {
            Initialize();
        }

In diesem Szenario wird die Verbindung mit dem Tracing- und Caching-Provider umhüllt. In meinem Context habe ich zudem 2 statische Eigenschaften angelegt, die den Zugriff auf die Einstellungen an die Basisklasse weiterleiten:

Eigenschaften für die Cache-Initialisierung C#
public static ICache DefaultCache
{
  get
  {
    return EFCachingProviderConfiguration.DefaultCache;
  }
    		
  set
  {
    EFCachingProviderConfiguration.DefaultCache = value;
  }
}    	
    	
public static CachingPolicy DefaultCachingPolicy
{
  get
  {
    return EFCachingProviderConfiguration.DefaultCachingPolicy;
  }
    		
  set
  {
    EFCachingProviderConfiguration.DefaultCachingPolicy = value;
  }
}

Ein interessantes Detail, diese Eigenschaften müssen vor der Erstellung einer Instanz des Kontexts gesetzt werden, da mit diesen Angaben die Wrapper in den Konstruktoren gefüttert werden.

Machen wir ein kleines Testbeispiel mithilfe des SQL-Profilers und einem Unit-Test. Es soll 4-mal die gleiche Order geladen werden. Mit den Standardmöglichkeiten des Entity Frameworks wird dann die gleiche Abfrage 4-mal an den Datenbankserver gesendet.

Test ohne Cache C#
    [TestMethod]
    public void NonCachingProviderTest()
    {
      // Arrange
      var ctx = new ORMSamplesEntities();

      // Act
      var result = (from o in ctx.Orders.Include("OrderItems")
                   where o.Id == 1
                   select o).ToList();
      
      var result2 = (from o in ctx.Orders.Include("OrderItems")
                     where o.Id == 1
                     select o).ToList();

      var result3 = (from o in ctx.Orders.Include("OrderItems")
                     where o.Id == 1
                     select o).ToList();

      var result4 = (from o in ctx.Orders.Include("OrderItems")
                     where o.Id == 1
                     select o).ToList();
    
      // Assert
      // see Sql-Profiler log ;-)
    }

Im SQL-Profiler wurden während diesem Test 4 Abfragen mit dem exakt gleichen Inhalt protokolliert.

Abbildung 1
Abbildung 1 Protokoll der Abfragen die bei der Ausführung der Unit-Tests verursacht worden sind.

Führen wir den Test erneut aus, diesmal jedoch mit aktivierten 2nd-Level Cache und dem InMemory-Cache.

Test mit Cache C#
    [TestMethod]
    public void CachingProviderInMemoryTest()
    {
      ORMSamplesEntities.DefaultCache = new InMemoryCache();
      ORMSamplesEntities.DefaultCachingPolicy = CachingPolicy.CacheAll;

      // Arrange
      var ctx = new ORMSamplesEntities();

      // Act
      var result = (from o in ctx.Orders.Include("OrderItems")
                    where o.Id == 1
                    select o).ToList();

      var result2 = (from o in ctx.Orders.Include("OrderItems")
                     where o.Id == 1
                     select o).ToList();

      var result3 = (from o in ctx.Orders.Include("OrderItems")
                     where o.Id == 1
                     select o).ToList();

      var result4 = (from o in ctx.Orders.Include("OrderItems")
                     where o.Id == 1
                     select o).ToList();

      InMemoryCache cache = ORMSamplesEntities.DefaultCache as InMemoryCache;
      // Assert
      Assert.AreEqual(3, cache.CacheHits);
      Assert.AreEqual(1, cache.CacheMisses);
      Assert.AreEqual(0, cache.CacheItemInvalidations);
      Assert.AreEqual(1, cache.CacheItemAdds);

      // see Sql-Profiler log
    }

Der SQL-Profiler hat nun während der Ausführung nur eine Abfrage protokolliert. Beim 2., 3. und 4. Aufruf wurden die Linq-Abfragen aus dem Cache bedient.

Abbildung 2
Abbildung 2 Log SQL Server Profiler während des Test. Die 4 Linq-Abfragen führen nur noch zu einer Abfrage an den Datenbankserver.

Die Provider-Wrapper sind nicht nur ein "nettes Spielzeug", sondern auch sehr hilfreich, da diese fehlende Funktionalitäten für das Entity Framework bereitstellen.

Ich gehe stark davon aus, das mit der nächsten Version - ich rede hier jetzt nicht von der EF CTP 4 - diese Funktionalitäten fester Bestandteil des Entity Frameworks werden.

  •  
  • 0 Kommentar(e)
  •  

Mein Kommentar

Über jeden weiteren Kommentar in diesem Post benachrichtigen.

Zurück

Translate this page

Kategorien

  • [-].NET Development (215)
  • [-]Datenbank (26)
  • HTML (1)
  • Konfiguration (12)
  • Mind Map (10)
  • Off-topic (9)
  • Open Source (3)
  • Qualität (7)
  • Sharepoint (6)
  • Sicherheit (2)

Archiv

Social Bookmarking

Bookmark bei: Mr. Wong Bookmark bei: Webnews Bookmark bei: Icio Bookmark bei: Oneview Bookmark bei: Linkarena Bookmark bei: Favoriten Bookmark bei: Seekxl Bookmark bei: Favit Bookmark bei: Social Bookmarking Tool Bookmark bei: Power Oldie Bookmark bei: Bookmarks.cc Bookmark bei: Newskick Bookmark bei: Newsider Bookmark bei: Linksilo Bookmark bei: Readster Bookmark bei: Folkd Bookmark bei: Yigg Bookmark bei: Digg Bookmark bei: Del.icio.us Bookmark bei: Reddit Bookmark bei: Simpy Bookmark bei: StumbleUpon Bookmark bei: Slashdot Bookmark bei: Netscape Bookmark bei: Furl Bookmark bei: Yahoo Bookmark bei: Spurl Bookmark bei: Google Bookmark bei: Blinklist Bookmark bei: Blogmarks Bookmark bei: Diigo Bookmark bei: Technorati Bookmark bei: Newsvine Bookmark bei: Blinkbits Bookmark bei: Ma.Gnolia Bookmark bei: Smarking Bookmark bei: Netvouz Information