Zur Zeit wird gefiltert nach: ef4
Filter zurücksetzen

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:

Zurück

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

Zurück

Entity Framework 4 - Der Designer stottert bei zusammengesetzten Schlüsseln (CompositeKey)

Heute wurde ich mit einem interessanten Problem im Entity Designer bei zusammengesetzten Schlüsseln konfrontiert. Das Modell wurde mit dem Assistenten angelegt. Dabei war die Einstellungen "Fremdschlüsselspalten in das Modell einbeziehen" aktiviert und es wurde die Standard-Code Generierungsvorlage verwendet.

Das Modell hatte in etwa folgenden Aufbau:

Abbildung 1 Teilmodell im Entity Framework mit Composite Key
Abbildung 1

Der erste Test, der sich mit dem Einfügen von Daten in die Datenbank befasste, scheiterte und lieferte folgendes Resultat:

Abbildung 2 Insert-Test schlägt fehl, bei Entitäten mit Composite Key
Abbildung 2

Die passende Fehlermeldung dazu lautet:

Die Personen.Test.CommentTest.Comment_Insert_Test-Testmethode hat eine Ausnahme ausgelöst: System.Data.UpdateException: EntitySet 'Comment' kann nicht aktualisiert werden, denn es hat eine DefiningQuery, und im <ModificationFunctionMapping>-Element ist kein <InsertFunction>-Element zur Unterstützung des aktuellen Vorgangs vorhanden.

Im ersten Moment beginnt das überlegen, DefiningQuery? Warum legt der Designer ein DefiningQuery für diese Tabelle an? In diesem Fall kann der Insert-Mechanismus gar nicht funktionieren, weil keine Prozeduren vorhanden sind. Ein Blick in das XML (Öffnen mit XML Text Editor) bestätigt auch die Fehlermeldung:


    <edmx:StorageModels>
      <Schema Namespace="Model.Store" Alias="Self" Provider="System.Data.SqlClient" ProviderManifestToken="2008" xmlns:store="http://schemas.microsoft.com/ado/2007/12/edm/EntityStoreSchemaGenerator" xmlns="http://schemas.microsoft.com/ado/2009/02/edm/ssdl">
        <EntityContainer Name="ModelStoreContainer">
          <EntitySet Name="Comment" EntityType="Model.Store.Comment" store:Type="Tables" store:Schema="dbo" store:Name="Comment">
            <DefiningQuery>SELECT 
      [Comment].[PersonID] AS [PersonID], 
      [Comment].[CommentTypeID] AS [CommentTypeID], 
      [Comment].[Note] AS [CO_Note]
      FROM [dbo].[Comment] AS [Comment]</DefiningQuery>
          </EntitySet>

Prozeduren will ich für dieses Szenario auf keinen Fall generieren, also bleibt mir nichts anderes übrig, als das XML anzupassen. Dazu gehe ich folgendermassen vor:

  1. DefinigQuery-Tag mit Inhalt entfernen
  2. store:Name=“Comment“ entfernen
  3. store:Schema=“dbo“ ändern in Schema=“dbo“
  4. XML-Datei speichern

Ist das Ganze angepasst, bleibt folgendes XML übrig:

    
              <EntitySet Name="Comment" EntityType="Model.Store.Comment" store:Type="Tables" Schema="dbo" >
              </EntitySet>
    
    

    Zur Sicherheit wird das File im Designer geöffnet und validiert. Nach dieser Anpassung wird der Test erneut ausgeführt und der Einfüge-Test läuft nun fehlerfrei.

    Abbildung 3 Test nach Anpassung der XML-Datei
    Abbildung 3

    Daraus lässt sich schliessen, dass auch im Entity Framework 4 ein wenig Basiswissen zum Aufbau der XML-Datei nicht schaden kann. Den Nachteil dieser Variante will ich auch nicht vorenthalten. Mit jeder Modell-Anpassung müssen diese Schritte wiederholt werden.

    Dieses Problem kann bei der codezentrierten Anwendungsentwicklung mit dem DbContext sicherlich auch entstehen, da der DbContext das XML zur Laufzeit erstellt. Dann besteht nicht die einfache Möglichkeit, dass XML anzupassen. Wer mit anderen Datenbanken wie zum Beispiel MySQL im codezentrierten Ansatz gearbeitet hat, kennt sicherlich die Probleme jenseits des dbo-Schemas. ;-)

    Zurück

    Entity Framework Präsentationen

    Die TechDays 2011 in Basel liegen schon einige Zeit zurück und wir haben es als .NET User Group Bern nun endlich geschafft, einen kleinen Rückblick online zu stellen.

    Johnny hat schon vor längerer Zeit seine Eindrücke in einem Beitrag festgehalten.

    Eigentlich hatte ich vor die Tipps und Tricks zum Entity Framework stückchenweise als Beiträge zu veröffentlichen, aber auch für mich gilt das Gesetz: Der Tag hat 24 Stunden. ;-) In dieser Hinsicht nutze ich den Beitrag, um die Techdays-Präsentationen zu verlinken.

    Für mich waren die TechDays auch eine lehrreiche Erfahrung, denn im Vergleich zu unseren User Group-Events stand ich einerseits vor viel mehr Personen mit unterschiedlichen Kenntnisstand, andererseits wollte ich auch viel Wissen in die 60 min. packen. Das hatte natürlich Auswirkungen auf die Demo’s, es waren nicht so viele. *fg*, also lag ich im Level 200. 

    Level 200, da hatten einige ihre Mühe mit der Erwartungskonformität. Während man von Webcasts her die Skala 100, 200, 300 als Beginner, Fortgeschritten und Profi gewohnt ist, war die Bedeutung bei den TechDays: Keine Demos (100), Wenige Demos (200) und Live-Demo (300).

    In Entwickler verunsichern, da ist Microsoft zurzeit auf Platz 1. Da ist diese Skala noch das kleinere Übel. ;-)

      Zurück

      Entity Framework Versionierung

      Versionierung ist in manchen Situationen nicht einfach. Ein Beispiel wie das Ganze recht komplex und mühsam für einen Anwender oder Developer werden kann, ist das Entity Framework.

      Um ein wenig Licht ins Dunkel zu bekommen, hat das ADO.NET - Team einen Blogpost verfasst und um Feedback gebeten. Weitere Informationen gibt es hier.

      Zurück

      Translate this page

      Kategorien