Archiv

Posts Tagged ‘Code-first Database generation with EF’

Entity Framework 5.0 und 1 zu n Beziehungen

Normalerweise wird ja bei der Architektur darauf geachtet, dass man so generell wie möglich ist. Im konkreten Fall mit Listen, werden in sehr vielen Fällen IEnumerable als Typ genommen, damit man alle Klassen die diese Schnittstelle implementieren, auf diese Eigenschaft anwenden kann. Nun kann es aber sein, dass man in Zusammenhang mit Code-First das eine oder andere Phänomen beobachtet, nämlich die Schlüsselgenerierung in der Datenbank. Als Beispiel für diesen Blogpost soll eine kleine Adressverwaltung dienen bei denen eine Person mehrere Adressen haben kann. Einen interessanten Beitrag Finden Sie hier auf Stackoverflow.

Ausgangslage

Die Ausgangslage bildet ein relativ einfaches Modell, in welchem wir folgende Klassen haben:

  • Person
  • Address

Wobei gemäss Modell eine Person mehr als eine Adresse haben kann. Nun auf der Datenbank benötigt man hier auf der Seite der Adressen-Tabelle einen Schlüssel, der auf die Person zeigt, die mit dieser Adresse assoziiert ist.

AssociationAttribute

Wenn die Klasse Person wie folgt aussieht:

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Web;

namespace AddressManager.Models
{
    ///
    /// Model class for a person.
    ///
    public class Person : PersistanceBase
    {
        ///
        /// Get's or set's the value for Firstname.
        ///
        [Required]
        public string Firstname { get; set; }
        ///
        /// Get's or set's the value for Lastname.
        ///
        [Required]
        public string Lastname { get; set; }
        ///
        /// Get's or set's the value for DateOfBirth.
        ///
        [DataType("datetime2")]
        public DateTime DateOfBirth { get; set; }
        ///
        /// Get's or set's the value for the assigned
        /// Addresses.
        ///
        public virtual IEnumerable<Address>Addresses { get; set; }
       ///
       /// Initializes a new instance of Person.
       ///
       public Person()
       {

       }
   }
}

dann fällt auf das die Adressen durch eine IEnumerable in Beziehung gebracht werden. Will man nun auf der Adresse eine Beziehung zur Person aufbauen, so muss in der Klasse Adresse eine weitere Eigenschaft hinzugefügt werden. Die Klasse Adresse sieht dann so aus:

using System.ComponentModel.DataAnnotations;

namespace AddressManager.Models
{
    ///
    /// Model class for an address
    /// associated to a person.
    ///
    public class Address : PersistanceBase
    {
        [Association("Address_Habitant", "Id", "Id", IsForeignKey = true)]
        public Person Habitant { get; set; }
        ///
        /// Get's or set's the value for Streetname.
        ///
        [Required]
        public string Streetname { get; set; }
        ///
        /// Get's or set's the value for Streetnumber.
        ///
        [Required]
        public int Streetnumber { get; set; }
        ///
        /// Get's or set's the value for Streetextension.
        ///
        public string Streetextension { get; set; }
        ///
        /// Get's or set's the value for Postalcode.
        ///
        [Required]
        public int Postalcode { get; set; }
        ///
        /// Get's or set's the value for City.
        ///
        [Required]
        public string City { get; set; }
        ///
        /// Initializes a new instance of Address.
        ///
        public Address()
        {

        }
    }
}

Wir müssen in diesem Fall eine zusätzliche Eigenschaft erstellen, die uns in der Datenbank die Beziehung darstellen lässt. Werden dann die Tabellen durch den DbContext generiert, so sieht die erstellte Datenbank und deren Tabellen wie folgt aus:

2013-09-19_Entity_Framework_Blogpost_01

Soweit alles gut, wir hoben die Anforderung erfüllt, dass einem Benutzer mehrere Adressen hinzugefügt werden können.

Automatische Generierung

Manchmal ist es ja ganz nett, wenn alles vollautomatisch gehen würde. In diesem Fall reicht nur die Wahl des entsprechenden Auflistungs-Typen. Damit Entity Framework die Beziehungen automatisch generiert, muss die Klasse Person nur wie folgt angepasst werden:

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Web;

namespace AddressManager.Models
{
     ///
     /// Model class for a person.
     ///

     public class Person : PersistanceBase
     {
     ///
     /// Get's or set's the value for Firstname.
     ///
    [Required]
    public string Firstname { get; set; }
    ///
    /// Get's or set's the value for Lastname.
    ///
    [Required]
    public string Lastname { get; set; }
    ///
    /// Get's or set's the value for DateOfBirth.
    ///
    [DataType("datetime2")]
    public DateTime DateOfBirth { get; set; }
    ///
    /// Get's or set's the value for the assigned
    /// Addresses.
    ///
    public virtual ICollection<Address>Addresses { get; set; }
    ///
    /// Get's or set's the value for the assigned
    /// Phones.
    ///public virtual ICollection Phones { get; set; }
    ///
    /// Initializes a new instance of Person.
    ///
    public Person()
    {

    }
  }
}

Wie macht es eigentlich der Entity Framework Designer?

Genau gleich wie das oben beschrieben wurde. Es lohnt sich zuweilen bei den generierten Klassen von Microsoft nachzuschauen wie sie es machen. Natürlich ist nicht alles Gold was glänzt, aber man erspart sich viel Arbeit und Kopfzerbrechen.

Wir haben die IEnumerable Schnittstelle durch die Schnittstelle ICollection ersetzt. Lassen wir nun den DbContext unsere Datenbank generieren, so werden die Tabellen wie folgt erstellt:

2013-09-19_Entity_Framework_Blogpost_02

Der Klasse Adresse konnten wir die zusätzliche Eigenschaft, die wir notabene nur für die Beziehungsdarstellung zwischen Person und Adresse verwendet haben, entfernt werden.

Mein Fazit

Arbeite ich mit Entity Framework und Code-first, so werde ich in Zukunft immer anstelle der IEnumerable Schnittstelle die ICollection verwenden, da ich dann meinen Code, meiner Ansicht nach, nicht mit SQL spezifischen Attributen versehen muss, nur damit mir eine Beziehung erstellt wird.

Für Anregungen, Kritik aber auch Lob danke ich bereits im Voraus.
kick it on dotnet-kicks.de

Advertisements