Zur Zeit wird gefiltert nach: ctp5
Filter zurücksetzen

Entity Framework 4.1 – Aus der CTP 5 wird EF 4.1

Die Code First-Variante verpasst dem Entity Framework eine neue Version und wird als eigenständiges Update kommen. Ganz bewusst wird hier auf die Kombination mit dem Service Pack 1 verzichtet. Somit wird Code First noch im ersten Quartal bereitgestellt werden.

In meinen Vortrag von der VSone habe ich bereits die Pluggable Conventions verwendet, als Möglichkeit den Konfigurationsaufwand für TPT zu verringern. Diese Pluggable Conventions schaffen es nicht ins EF 4.1. Die Beispiele werden dementsprechend nicht mehr funktionieren.

Was mit dem EF 4.1 auch in DB First und Model First Einzug halten wird, ist die Validierung mit den DataAnnotations. Das finde ich sehr hilfreich, da es mir einen roten Faden im Bezug auf die Validierungsstrategie aufzeigt und unnötige Datenbankanfragen minimieren kann.

Was mit EF 4.1 Code First noch nicht unterstützt wird, sind Stored Procedures. Hier muss man sich im Hinterkopf behalten, was ich in diesem Beitrag aufgezeigt habe. Am besten zeichnet man vor Projektbeginn ein Umweltdiagramm, um alle Stakeholder ermitteln zu können. Gehört zwar zur alten Schule und macht auch fast niemand, aber es hat sich in meinen Augen bewährt.

Weitere Details gibt es im ADO.NET team blog.

Zurück

Entity Framework 4 CTP5 - Edmx-Datei vom DbContext erstellen oder wenn ein Methodenwechsel notwendig wird

Der Code First-Ansatz hat seine Reize. Mit den verbesserten Funktionalitäten, trotz kleiner Bugs, geht es auch zügig von der Hand. Mit der CTP5 hat auch eine T4 Vorlage Einzug gehalten, dass die Möglichkeit bietet, mit dem EDMX-Designer den DbContext zu nutzen. Einfach ausgedrückt ist der DbContext ein Wrapper auf den ObjectContext, der das Mapping bzw. den Umgang mit dem EDMX-File vereinfacht.

Dabei muss auch erwähnt werden, das Paradigmen, die einfach und locker von der Hand gehen, gerne verwendet werden. Die Codezentrierte Anwendungsentwicklung, als Vertreter des Top down-Ansatzes, kann aber in Konflikt mit einzelnen Stakeholdern stehen. Problematisch wird es, wenn man mit dem Top down-Ansatz begonnen hat und in einer späteren Phase Anforderungen hinzukommen, die aktuell mit der CTP5 nicht realisierbar sind. Zudem kann die Gefahr bestehen, dass die Top down Vorgehensweise zu einem Meet in the middle-Ansatz mutiert.

Ein mögliches Szenario, bei dem die Mapping-Philosophie an die Grenzen stösst, ist die Verwendung von Prozeduren und Views für die CRUD-Funktionalitäten, was mit den ObjectContext problemlos realisierbar ist. Mit Workarounds können diese Prozeduren für das Speichern, Ändern und Löschen verwendet werden, aber den Code möchte ich nicht warten.

Wenn ein CSO, Beauftragter für Datensicherheit oder ein DBA diese Anforderung in einer späteren Phase verlangt, wie es in der Realität üblich ist, stösst man mit dem Code First-Ansatz in der CTP5 sehr schnell an die Grenzen des machbaren. Aktuell ist hier das Mapping von Prozeduren oder Sichten nicht möglich und eine Variante wäre der Wechsel zum meist "unbeliebten" Bottom up. Der Vorteil für den Entwickler, es können Aufgaben an den DBA delegiert werden und die Gefahr in den Meet in the middle-Ansatz zu fallen ist ebenfalls minimiert.

Machen wir also ein kleines Beispiel, das völlig sinnfrei ist. Zuerst erstellen wir die Klassen:

Klassen (C#)
  public abstract class AbstractEntity01
  {
    public int Id { get; set; }

    [StringLength(100), Required]
    public string AbstractEntity01Field1 { get; set; }

    [StringLength(100), Required]
    public string AbstractEntity01Field2 { get; set; }
  }

  public abstract class AbstractEntity02 : AbstractEntity01
  {
    public int Id { get; set; }

    [StringLength(100), Required]
    public string AbstractEntity02Field1 { get; set; }

    [StringLength(100), Required]
    public string AbstractEntity02Field2 { get; set; }
  }

  public abstract class AbstractEntity03 : AbstractEntity02
  {
    public int Id { get; set; }

    [StringLength(100), Required]
    public string AbstractEntity03Field1 { get; set; }

    [StringLength(100), Required]
    public string AbstractEntity03Field2 { get; set; }
  }

  public abstract class AbstractEntity04 : AbstractEntity03
  {
    public int Id { get; set; }

    [StringLength(100), Required]
    public string AbstractEntity04Field1 { get; set; }

    [StringLength(100), Required]
    public string AbstractEntity04Field2 { get; set; }
  }

  public class Sample : AbstractEntity04
  {
    public int Id { get; set; }

    [StringLength(100), Required]
    public string TestField3 { get; set; }

    [StringLength(100), Required]
    public string TestField4 { get; set; }
  }

  public class Navigation01
  {
    public int Id { get; set; }
    [StringLength(100)]
    public string TestField3 { get; set; }
    [StringLength(100)]
    public string TestField4 { get; set; }
    public int SampleId { get; set; }
    public Sample Sample { get; set; }
  }

  public class Navigation02
  {
    public int Id { get; set; }
    [StringLength(100)]
    public string TestField3 { get; set; }
    [StringLength(100)]
    public string TestField4 { get; set; }
    public int SampleId { get; set; }
    public Sample Sample { get; set; }
  }

Anschliessend den DbContext:

DbContext (C#)
  public class VoelligSinnfreiContext : DbContext
  {
    public IDbSet<AbstractEntity01> AbstractEntities01 { get; set; }
    public IDbSet<Navigation01> Navigations01 { get; set; }
    public IDbSet<Navigation02> Navigations02 { get; set; }
  }

Der Test der CRUD-Funktionalität ist auch erfolgreich, wie nachfolgende Abbildung zeigt:

Abbildung 1
Abbildung 1 Erfolgreicher Test des DbContext

Jetzt besteht das Problem, dass der DBA mit Unterstützung des CSO und des Beauftragten für Datensicherheit, in einer späteren Phase gespeicherte Prozeduren und Sichten fordert. Natürlich ist das Berechtigungskonzept angepasst und der Test der CRUD-Funktionalität funktioniert bedingt durch die geänderten Anforderungen nicht mehr:

Abbildung 2
Abbildung 2 Test bei Änderung der Anforderung auf View/Prozedur-Konzept

Mit dem Meet in the Middle-Ansatz würde man jetzt den Versuch starten, mithilfe von DbContext..SqlQuery und SqlCommand und manuellen Mapping die Funktionalität wieder zum laufen zu bekommen.

Beim Mapping auf die View würde es wie folgt aussehen:

Abbildung 3
Abbildung 3 Manuelles Mapping auf die Sicht vwSample

Wird der Test erneut durchgeführt, so funktioniert eine Methode wieder.

Abbildung 4
Abbildung 4 Erster erfolgreicher Test nach Anpassung des Mappings

Wenn der Entwickler nun wieder die Berechtigung erhält, die Datenbank zu erstellen kommt es schon zum ersten Problem: Während der Erstellung wird dann aus der Sicht eine Tabelle. Bei dieser Vorgehensweise rutscht man also vom ursprünglich geplanten Top down-Ansatz in den Meet in the middle-Ansatz. Das Mapping der Prozeduren wird dann richtig spannend, da sich der Entwickler neben der Handarbeit des Mappings mithilfe von SqlCommand auch um die State-Aktualisierung bei Create/Update/Delete kümmern muss.

Eine andere Variante wäre jetzt der Wechsel zum Bottom up-Ansatz und die Verwendung des EDM-Designers. Dazu benötigen wir natürlich eine EDMX-Datei. Aus dem DbContext heraus kann diese wie folgt erstellt werden:

DbContext (C#)
  
  public class VoelligSinnfreiContext : DbContext
  {
    public IDbSet<AbstractEntity01> AbstractEntities01 { get; set; }
    public IDbSet<Navigation01> Navigations01 { get; set; }
    public IDbSet<Navigation02> Navigations02 { get; set; }

    protected override void OnModelCreating(System.Data.Entity.ModelConfiguration.ModelBuilder modelBuilder)
    {
      this.CreateEdmFile(modelBuilder);
      base.OnModelCreating(modelBuilder);
    }

    protected void CreateEdmFile(System.Data.Entity.ModelConfiguration.ModelBuilder model)
    {
      string path = @"Pfad";
      var edm = model.Build(new DbProviderInfo("System.Data.SqlClient", "2008"));
      using (var writer = new XmlTextWriter(Path.Combine(path, "VoelligSinnfreiContext.edmx"),
        Encoding.UTF8))
      {
        edm.WriteEdmx(new DbProviderInfo("System.Data.SqlClient", "2008"), writer);
      }
    }
  }

Dieser Ansatz ist jedoch nicht besonders intuitiv und es wird evtl. noch Anpassungen mit der RTM geben.

Die erstellte EDMX-Datei bildet das Modell wie folgt ab:

Abbildung 5
Abbildung 5 Abgeleitetes Modell vom DbContext basierend auf den Konventionen

Mit dieser Variante kann aktuell das Mapping von Sichten und Prozeduren einfacher vorgenommen werden und dieser Kompromiss kann einen ebenfalls viel "Gefrickel" und Ärger ersparen.

Das Mapping der Prozeduren erfolgt nun auf Ebene der Sample-Entity, zuvor müssen diese jedoch dem Modell hinzugefügt werden.

Abbildung 6
Abbildung 6 Mapping der Prozeduren nach der Aktualisierung von der Datenbank

Jetzt muss nur noch die Codegenerierungsvorlage angepasst werden, damit die bestehende Arbeit nicht vom Prozess der Codegenerierung vernichtet wird. Die T4-Vorlage für den DbContext heisst ADO.NET DbContext Generator.

Um die geleistete Arbeit nicht zu vernichten, wird der EntityFrameworkFileTemplateManager durch den FileTemplateManager ersetzt und definiert, dass bestehende Dateien nicht überschrieben werden sollen.

Abbildung 7
Abbildung 7 Anpassung der Code-Generierungsvorlage, damit die bestehenden Klassen nicht vernichtet werden

Nur noch die typische Anpassung des Connectionstrings mit den Angaben zu den Metadaten und mal schauen, was der Test nach den Änderungen sagt:

Abbildung 8
Abbildung 8 Test nach dem Wechsel auf EDMX-Designer und das Mapping der View und der Prozeduren

Die Funktionalität ist also wieder gewährleistet.

Im Bezug auf die Prozeduren, hier sind für die Zukunft bereits Anpassungen geplant. Mit dem DbContext soll es eines Tages möglich sein, diese gleich mit zu erstellen. Wann es so weit sein wird, weiss ich aber auch nicht. Wenn dieser Punkt realisiert ist, wäre der Wechsel zurück ohne Probleme möglich.

Aber auch der Ansatz mit der Erstellung des EDMX-Files hat so seine Ecken und Kanten. Dieser funktioniert nur einwandfrei, wenn man völlig auf die Konventionen setzt. Aktuell kommt es zu Ausnahmen, wenn die manuellen Mappings berücksichtigt werden sollen.

Eine andere Alternative wäre natürlich auch der Wechsel auf einen anderen O/R Mapper, wenn die nötige Zeit dafür besteht. Aber auch hier gilt: Vorsicht vor Meet in the middle!

Zurück

18.02.2011
22:44

Folien und Beispiele der VSone Session Inheritance Strategien mit dem Entity Framework

Mit diesem Beitrag werden die Beispiele nachgeliefert. Im Zip-File befinden sich auch die Datenbankskripts, die bedingt durch die enthaltenen Testdaten etwas gross sind.

Bevor die Skripts ausgeführt werden, müssen die Datenbanken manuell angelegt werden.

Eine Warnung zum Extrem-Beispiel mit dem Span-Query, es ist definitiv nicht zur Nachahmung für produktive Projekte empfohlen, es soll nur die möglichen Workarounds mit dem Kontext-Cache aufzeigen, um das Ganze optimieren zu können. Für die Optimierung in Verbindung mit Paging war die Zeit leider zu knapp.

Materialen

Blogbeiträge passend zum Vortrag

Zurück

Entity Framework 4 CTP5 – ComplexType by Convention

In einem Beispiel zu T4 habe ich mit einem einfachen Klassendiagramm und T4 den Code erstellen lassen. Für komplexe Typen habe ich dabei die Klasse ComplexTypeConfiguration<T> verwendet.

Abbildung 1
Abbildung 1 Klassenmodell als Grundlage für die Erstellung der Klassen mit T4

Bei der Klasse Address handelt es sich dabei um einen komplexen Typ. Dieser soll innerhalb der Tabelle der Klasse Customer persistiert werden.

Mit folgendem Mapping lässt sich dies erreichen:

Mapping ComplexType (C#)

internal class AddressConfiguration : ComplexTypeConfiguration<Address> 
{ 
	public AddressConfiguration()
	{
		this.Property(x => x.Street).IsRequired().IsVariableLength().HasMaxLength(80).IsUnicode();
		this.Property(x => x.City).IsRequired().IsVariableLength().HasMaxLength(100).IsUnicode();
		this.Property(x => x.PostalCode).IsRequired().IsVariableLength().HasMaxLength(10).IsUnicode();
	}
}

Wie diese Konfiguration dem DbContext hinzugefügt wird, ist im Beitrag DB First mit dem DbContext beschrieben.

Eine weitere interessante Möglichkeit ist das Mapping bei Konventionen. Wenn die Address-Klasse keine Id-Eigenschaft besitzt und in einer anderen Klasse wie zum Beispiel Customer verwendet wird, greift neu die Konvention und die Eigenschaften werden innerhalb der Customer-Tabelle angelegt.

Ein kleines Beispiel:

Klasse Customer (C#)

[MetadataType(typeof(CustomerMetadata))]
public class Customer
{
  public int Id { get; internal set; }
  public string Name { get; set; }
  public Address OfficeAddress { get; internal set; }
}


Klasse Address (C#)

///[MetadataType(typeof(AddressMetadata))]
public class Address
{
  public string Street { get; set; }
  public string Postalcode { get; set; }
  public string City { get; set; }
}

Die Kunden-Klasse enthält nun die Adresse als eigenständigen Typ. Der DbContext hat dabei folgenden Aufbau:

Notwendiger DbContext für Szenario (C#)

public class TestContext : DbContext
{
  public DbSet Customers { get; set; }
}

Nach der Erstellung der Datenbank enthält die Tabelle Customers auch die Eigenschaften der Klasse Address.

Abbildung 2
Abbildung 2 Mapping Mehere Klassen auf eine Tabelle

Bei genauer Betrachtung der Abbildung 2 fällt aber auf, dass die Datentypen wieder eine Länge von 4000 Zeichen haben. Im Code wird zudem ersichtlich, dass das MetadataAttribute für die Klasse Address auskommentiert ist.

Das liegt in einem Bug begründet. Werden die DataAnnonations auf Metadaten-Ebene verwendet, so begrüsst einen folgende Fehlermeldung:

System.Data.Entity.ModelConfiguration.ModelValidationException: One or more validation errors were detected during model generation:

  • System.Data.Edm.EdmEntityType: : EntityType 'Address' has no key defined. Define the key for this EntityType.
  • System.Data.Edm.EdmEntitySet: EntityType: The EntitySet Addresses is based on type Address that has no keys defined.

Die Fehlermeldung sagt mir also, dass ein Schlüssel erwartet wird. Einen Workaround dafür gibt es auch, einfach verwenden wie in der CTP 4 bzw. der Klasse Address das ComplexType-Attribut verpassen . ;-)

Zurück

Entity Framework 4 CTP 5 – Braucht es das NotMapped-Attribut überhaupt?

Beim CodeFirst-Ansatz hat das NotMapped-Attribut die Aufgabe Eigenschaften für das O/R-Mapping zu ignorieren. Notwendig wird dieses, wenn Auto-implemented properties oder Eigenschaften verwendet werden, die den Getter und Setter implementieren.

Interessant wird es, wenn Eigenschaften nur den Getter implementieren (Read only properties). In diesem Szenario ist die Kennzeichnung mit NotMapped nicht notwendig.

Dazu ein kleines Beispiel mit der Customer-Klasse.

Customer-Klasse mit Read-only Eigenschaft (C#)

[MetadataType(typeof(CustomerMetadata))]
public class Customer
{
  public int Id { get; internal set; }
  public string Name { get; set; }
  public string Street { get; set; }
  public string Postalcode { get; set; }
  public string City { get; set; }

  public string LetterAddress
  {
    get
    {
      return String.Format(
        "{0}\r\n{1}\r\n{2} {3}", 
        this.Name, 
        this.Street, 
        this.Postalcode, 
        this.City);
    }
  }
}

Neu besitzt die Klasse eine Eigenschaft mit den Namen LetterAddress, die nur einen Getter implementiert. Nach der Neuerstellung der Datenbank hat die Tabelle auf der Datenbank den Aufbau wie in Abbildung 1.

Abbildung 1
Abbildung 1 Aufbau der Tabelle auf Datenbankebene ohne Spalte LetterAddress

Dabei wird ersichtlich, dass Eigenschaften die nur einen Getter implementieren während der Erstellung ignoriert werden. Sobald der Setter ins Spiel kommt, ist das Attribut NotMapped weiterhin notwendig.

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