Zur Zeit wird gefiltert nach: Code First
Filter zurücksetzen

Entity Framework 4.3 - Building Blocks oder wie steuer ich die Datenbankversion

Gelegentlich kommt es vor, dass die Datenbankversionen pro Umgebung unterschiedlich sein können. Bei meinen ersten versuchen ist mir das mit den SQL-Server Versionen 2005 und 2008 so ergangen.

Sofort ersichtlich wurde das, wenn die folgende Fehlermeldung im Log zu sehen war:

System.ArgumentException: The version of SQL Server in use does not support datatype 'datetime2'

Um dies in den Griff zu bekommen gibt es das Attribut "ProviderManifestToken" in der EDMX-Datei. Mit dem ERM-Ansatz lassen sich, neben unterschiedlen Datenbanken, auch unterschiedliche Versionen einer Datenbank verwalten und mappen. Ein sehr interessanter Ansatz bei der Realisierung von Standardsoftware.

Nun stellt sich die Frage, wie mache ich das in Code First mit Schema Migrations?

Der Schlüssel liegt dabei in der Klasse DbModelBuilder. Beim erstellen kann damit die Datenbankversion definiert werden.


// default
var model = builder.Build(connection);

// with version
var model = builder.Build(new DbProviderInfo("System.Data.SqlClient", "2005"));

Gerade mit der nächsten Version von SQL Server 2012 wird es sicherlich ähnliche Probleme geben können, wenn die Systemumgebungen trotz ITIL nicht ganz so synchron sind. Bei der erstmaligen Erstellung des Models wird das sicherlich nicht von Bedeutung sein, wohl aber bei Schema Migrations.

Im Blogbeitrag von Arthur Vickers finden sich noch andere interessante Punkte die interessant sind, so auch die Aussage das Pluggable Conventions für den Durchschnittsentwickler frühstens mit EF6 nutzbar werden, wenn überhaupt.

Weitere Informationen zum Thema:

Entity Framework 4.3 Mapping Szenarien

Vor ein paar Wochen wurde über Twitter gefachsimpelt, welche Vererbungsstrategie Standard bei Code First ist. Es kam sehr schnell die Antwort TPT. Da ich diesen Bereich mit der Version 4.1 durchgetestet habe und eigentlich TPH der Standard ist, wollte ich nun überprüfen, ob sich etwas zur Version 4.3 geändert hat.

Mit Partick Weibel hatte ich vor 2 Jahren im Bereich der Mapping Strategien eine Präsentation zum Thema "Übersicht zwischen NHibernate und dem Entity Framework" bei der .NET User Group Bern gehalten. Der Schwerpunkt lag dabei auf Business Value, weniger auf Religion.

Diese Beispiele habe ich auch für den Code First Ansatz mit dem Entity Framework nachgeführt, um die Unterschiede zwischen den Vorgehensweisen Bottom Up (DB First), Middle Out (Model First) und Top Down (Code First) zu visualisieren. Herausgekommen ist dabei folgende Übersicht:

Abbildung 1 Mapping Szenarioen (mb = Fluent API)
Abbildung 1

Diese Beispiele habe ich nun auf die Version 4.3-1 aktualisiert und die Standardvererbung überprüft. Es ist immer noch die Strategie TPH. Meine Übersicht ist auch mit der Version 4.3 noch aktuell. ;-)

Im Bereich der Vererbungshierarchie soll es bei TPC Verbesserungen gegeben haben. Bisher war es so, dass nur einfache TPC-Szenarien möglich waren. Daran ändert sich auch nichts. Folgender Code lässt sich auch nicht mit der Version 4.3 mappen:


  public abstract class Product
  {
    public int Id { get; set; }

    [StringLength(50)]
    [Required]
    public string Name { get; set; }

    [StringLength(400)]
    public string Description { get; set; }
  }

  public class Book : Product
  {
    [StringLength(10)]
    [Required]
    public string ISBN10 { get; set; }

    [StringLength(13)]
    [Required]
    public string ISBN13 { get; set; }

    public int LanguageCD { get; set; }

    [Required]
    public int Pages { get; set; }
  }

  public class EBook : Book
  {
    [Required]
    public string Filename { get; set; }
  }

  public class Hardcover : Book
  {
    [StringLength(20)]
    [Required]
    public string Size { get; set; }

    [Required]
    public double Weight { get; set; }
  }

Im Designer ist dieser Variante auch nur über manuelle Anpassungen im EDMX-File möglich, aber es funktioniert.

Die Mapping Szenarien für Code First stehen auf der Website der .NET User Group Bern zur Verfügung, ein Update auf 4.3.1 muss aber noch gemacht werden. ;-) Bei mir sind die paar Tests auch mit EF 4.3.1 im grünen Bereich.

Abbildung 2 Mapping-Szenarien Tests mit EF 4.3.1
Abbildung 2

Entity Framework - Ein neuer Workaround für 2nd Level Cache mit dem DbContext

Bisher habe ich den 2nd Level Cache nur über einen Umweg über den ObjectContext zum laufen gebracht. Pawel Kadluczka vom EF-Team hat Ende März einen weiteren Ansatz in seinen Blog veröffentlicht, der ohne den ObjectContext auskommt.

Dieser kann auch in Verbindung mit SchemaMigrations zusammen arbeiten, wenn die Tricks und Kniffe im Beitrag beachtet werden.

Wenn das EF-Team an solchen Workarounds rumbastelt, kann davon ausgegangen werden, dass der 2nd Level Cache weiterhin kein Bestandteil des Kerns von EF werden wird. Zum Beitrag geht es hier lang.

Weitere Informationen zum Thema

Entity Framework 4.3 - Schema Migrations für Code First (DbContext)

Eine Anwendung lebt bekanntlich von Änderungen. Nicht selten haben diese Änderungen auch Auswirkungen auf das Datenbankmodell. Das Entity Framework unterstützt mittlerweile DB First, Model First und Code First. Für DB First und Model First ist es kein grösseres Problem Änderungen auf der Datenbank nachzuführen, mit ein paar Tricks und Tools sind sogar Roundtrips zwischen Datenbank und Modell möglich.

Die möglichen Vorgehensweisen sind in der Präsentation Tipps und Tricks Entity Framework ersichtlich.

Bei Code First war dieser Ansatz bisher ein Ding der Unmöglichkeit. Lediglich mit Zusatztools wie SQL Delta und einer ITIL-konformen Umgebung konnten die Änderungen vorgenommen werden, ohne dass die produktiven Daten negativ beeinflusst wurden. Der Störfaktor war jedoch immer der Modelhash in der Tabelle EdmMetadata. Entweder hat man diesen Eintrag mit aktualisiert oder aber die Konvention entfernt.

Seit ein paar Tagen ist nun die Beta 1 vom Entity Framework 4.3 draussen und ich wollte natürlich auch gleich die Anpassungen ausprobieren. Die Pakete gibt es über NuGet und die Unterstützung für die Installation von Vorabversionen benötigt im minium die Version 1.6. Ich musste zuvor auf diese Version aktualisieren. Die ältere Version Entity Framework.Migrations muss noch vom System entfernt werden (erfordert einen Neustart von Visual Studio).

Nach diesem Update besteht die Möglichkeit die Vorabversion mit dem Befehl:

Abbildung 1 Install-Package Entity Framework -IncludePreRelease
Abbildung 1

auf dem System zu installieren.

Also beginne ich mit einem kleinen Beispiel:


    public abstract class Product
    {
      public int Id { get; set; }
 
      [StringLength(50)]
      [Required]
      public string Name { get; set; }
 
      [StringLength(400)]
      public string Description { get; set; }
    }
 
    public class Book : Product
    {
      [StringLength(10)]
      [Required]
      public string ISBN10 { get; set; }
 
      [StringLength(13)]
      [Required]
      public string ISBN13 { get; set; }
 
      public int LanguageCD { get; set; }
 
      [Required]
      public int Pages { get; set; }
    }
 
    public class EBook : Book
    {
      [Required]
      public string Filename { get; set; }
    }
 
    public class Hardcover : Book
    {
      [StringLength(20)]
      [Required]
      public string Size { get; set; }
 
      [Required]
      public double Weight { get; set; }
    }
 
    public class BookInheritanceContext : DbContext
    {
 
      public BookInheritanceContext()
        : base("EfCodeFirstMigrations")
      { 
 
      }
 
      public IDbSet<Product> Products { get; set; }
 
      protected override void OnModelCreating(DbModelBuilder modelBuilder)
      {
        //  TPH ist Standardkonvention, da beste Performance.
        //  Für  Individual-Lösungen  macht das auch durchaus Sinn , wenn
        //  jedoch die Release-Tauglichkeit  gewährleistet werden muss
        //  ist TPT die bessere Wahl.
 
        //  Mapping  für benutzerdefinierten Diskriminator 
        // // const  string   discriminator  = "ProductTypeNbr ";
        // // modelBuilder . Entity <Product >( )
        ////  .Map<Book>(m => m.Requires(discriminator).HasValue(2))
        ////  .Map<EBook>(m => m.Requires(discriminator).HasValue(3))
        ////  .Map<Hardcover>(m => m.Requires(discriminator).HasValue(4))
        ////  .ToTable("Product");
 
        base.OnModelCreating(modelBuilder);
      }
    }

Nun beginnt die Phase der Erweiterung. In diesem Zusammenhang ist es wichtig zu akzeptieren, dass die Arbeit mit NuGet ein wenig intensiver wird. Im neuen Release sollen nun ein paar Erweiterungen vorgenommen werden, die Auswirkungen auf das Datenmodell haben werden.

Der erste Schritt ist nun das öffnen der NuGet-Konsole und die Eingabe von:

Abbildung 2 Enable-Migrations
Abbildung 2

auszuführen. Nach dieser Aktion befindet sich ein neuer Ordner Migrations im Projekt.

Abbildung 3 Neuer Ordner Migrations
Abbildung 3

Ich beginne nun mit meinen Anpassungen an der Klasse EBook. In der neuen Version soll die Möglichkeit bestehen, dass pro Buch mehrere Dateien für alternative Dateiformate (mobi, epub, pdf usw.) hinterlegt werden können.


public class EBook : Book
    {
      [Required]
      public string Filename { get; set; }
 
      [Required]
      public ICollection<BookFile> AlternativeFiles { get; internal set; }
    }
 
    public class BookFile
    {
      public int Id { get; set; }
      public int Type { get; set; }
      public int Filename { get; set; }
    }

Nach der Fertigstellung geht es nun darum das Update der Datenbank vorzunehmen. Auch hier ist die primäre Schaltzentrale die NuGet-Konsole. Zuerst muss jedoch eine kleine Anpassung in der Configuration-Klasse vorgenommen werden. Im Konstruktur muss der Wert von AutomaticMigrationsEnabled auf true gesetzt werden.

Kommen wir zurück auf die NuGet-Konsole, mit dem Befehl:

Abbildung 4 Update-Database -Script
Abbildung 4

wird ein SQL-Skript mit den notwendigen Änderungen auf der Datenbank erstellt.

Der Output für dieses Beispiel:


CREATE TABLE [BookFiles] (
    [Id] [int] NOT NULL IDENTITY,
    [Type] [int] NOT NULL,
    [Filename] [int] NOT NULL,
    [EBook_Id] [int],
    CONSTRAINT [PK_BookFiles] PRIMARY KEY ([Id])
)
CREATE INDEX [IX_EBook_Id] ON [BookFiles]([EBook_Id])
ALTER TABLE [BookFiles] ADD CONSTRAINT [FK_BookFiles_Products_EBook_Id] FOREIGN KEY ([EBook_Id]) REFERENCES [Products] ([Id])
CREATE TABLE [__MigrationHistory] (
    [MigrationId] [nvarchar](255) NOT NULL,
    [CreatedOn] [datetime] NOT NULL,
    [Model] [varbinary](max) NOT NULL,
    [ProductVersion] [nvarchar](32) NOT NULL,
    CONSTRAINT [PK___MigrationHistory] PRIMARY KEY ([MigrationId])
)
BEGIN TRY
    EXEC sp_MS_marksystemobject '__MigrationHistory'
END TRY
BEGIN CATCH
END CATCH
INSERT INTO [__MigrationHistory] ([MigrationId], [CreatedOn], [Model], [ProductVersion]) VALUES ('201201151955537_AutomaticMigration', '2012-01-15T19:55:54.189Z', 0x1F8B...EDMX-Modell...0, '4.3.0-beta1')

Neben den Tabellen wird neu auch für die Fremdschlüssel-Spalten ein Fremdschlüsselindex erstellt. Bisher war das ein negativer Unterschied zum Model First - Ansatz, der mit der Version EF 4.3 der Vergangenheit angehören wird.

Was auch auffällt ist die Systemtabelle __MigrationHistory, in der das aktuelle Abbild des Modells gespeichert wird. Die Tabelle EdmMetadata existiert nicht mehr. Mit EF 4.3 gehört diese ebenfalls der Vergangenheit an. Bei bestehenden Modellen wird diese jedoch erst entfernt, wenn die Datenbank neu erstellt wird. Ein Detail, welches sich daraus ergibt: Migrations funktionieren nur mit dem SQL-Server!!!!

Ohne den Swich "-Script" lassen sich die Änderungen direkt an die Datenbank übertragen. Bei dem automatischen Ansatz würde ich persönlich darauf verzichten, da es keine Möglichkeit der Versionierung gibt. Dieser Teil lässt sich mit dem Speichern der SQL-Skripts jedoch organisatorisch in den Griff bekommen. Beim codebasierten Ansatz ist es ein wenig eleganter gelöst, jedoch sind die Möglichkeiten auch hier begrenzt.

Bei dieser Beta soll es sich um die Letzte handeln, sodass im Laufe des ersten Quartals die finale Version Entity Framework 4.3 zur Verfügung stehen wird.

Ich finde diesen Ansatz sehr interessant, da nun auch beim codezentrierten Ansatz mit dem Entity Framework Schema-Migrations möglich werden. Als Entwickler muss man jedoch berücksichtigen, dass diese nur mit dem SQL-Server funktionieren. Zudem ist ein Round-Trip nicht, bzw. nur mit einem nicht im Verhältnis stehenden Aufwand möglich. (Stichwort: Meet in the Middle). Hier beginnt das Problem aber häufig mit der Planung.

Weitere nützliche Informationen befinden sich in den Blogs vom ADO.NET - Team unter:

Entity Framework 4.1 - Change Tracking

Zwei Monate lang keinen Blogpost mehr verfasst. Was Sharepoint alles anrichten kann, aber das ist eine andere Geschichte.

Im Bereich Change Tracking kann Code First schnell sehr langsam werden, ist in etwa mit Oracle und ANSI-SQL zu vergleichen. ;-)

In dieser Hinsicht sind die beiden Beiträge von Arthur Vickers sehr wertvoll zum Verständnis dieser Problematik. Der erste Beitrag geht auf die unterstützten Typen für Lazy Loading und Change Tracking ein, während der zweite Beitrag die Performance-Aspekte beleuchet. 

Sehr interessant...

Translate this page

Kategorien