Beziehungen, Navigationseigenschaften und Fremdschlüssel

In diesem Artikel finden Sie eine Übersicht darüber, wie Entity Framework Beziehungen zwischen Entitäten verwaltet. Außerdem enthält er einen Leitfaden zum Zuordnen und Bearbeiten von Beziehungen.

Beziehungen in EF

In relationalen Datenbanken werden Beziehungen (auch als Zuordnungen bezeichnet) zwischen Tabellen über Fremdschlüssel definiert. Ein Fremdschlüssel (FS) ist eine Spalte oder eine Kombination von Spalten, mit deren Hilfe ein Link zwischen den Daten in zwei Tabellen eingerichtet und erzwungen wird. Es gibt im Allgemeinen drei Typen von Beziehungen: 1:1, 1:n und m:n. In einer 1:n-Beziehung wird der Fremdschlüssel in der Tabelle definiert, die die n-Seite der Beziehung darstellt. Die m:n-Beziehung umfasst das Definieren einer dritten Tabelle (als Verknüpfungstabelle bezeichnet), deren Primärschlüssel aus den Fremdschlüsseln der beiden verknüpften Tabellen besteht. In einer 1:1-Beziehung fungiert der Primärschlüssel zusätzlich als Fremdschlüssel, sodass es in beiden Tabellen keine separate Fremdschlüsselspalte gibt.

Die folgende Abbildung zeigt zwei Tabellen in einer 1:n-Beziehung. Die Tabelle Course ist die abhängige Tabelle, da sie die Spalte DepartmentID enthält, die sie mit der Tabelle Department verknüpft.

Department and Course tables

In Entity Framework kann eine Entität über eine Zuordnung oder Beziehung mit anderen Entitäten verknüpft sein. Jede Beziehung enthält zwei Seiten, die den Entitätstyp und die Multiplizität des Typs (1; 0 oder 1; n) für die beiden Entitäten in dieser Beziehung beschreiben. Die Beziehung wird möglicherweise von einer referenziellen Einschränkung geregelt, die beschreibt, welche Seite der Beziehung die Prinzipalrolle und welche die abhängige Rolle ist.

Navigationseigenschaften bieten eine Möglichkeit, eine Zuordnung zwischen zwei Entitätstypen zu steuern. Jedes Objekt kann für jede Beziehung, an der es beteiligt ist, über eine Navigationseigenschaft verfügen. Mit Navigationseigenschaften können Sie Beziehungen in beiden Richtungen steuern und verwalten, wobei entweder ein Verweisobjekt zurückgegeben wird (wenn die Multiplizität 1 oder 0 ist) oder eine Auflistung von Objekten (wenn die Multiplizität n ist). Sie können auch eine unidirektionale Navigation verwenden. In diesem Fall definieren Sie die Navigationseigenschaft nur für einen der Typen der Beziehung, und nicht für beide.

Es wird empfohlen, Eigenschaften in das Modell einzuschließen, die Fremdschlüsseln in der Datenbank zugeordnet sind. Wenn Fremdschlüsseleigenschaften einbezogen wurden, können Sie durch das Ändern des Fremdschlüsselwerts für ein abhängiges Objekt eine Beziehung erstellen oder ändern. Diese Art von Zuordnung wird Fremdschlüsselzuordnung genannt. Die Verwendung von Fremdschlüsseln ist noch wichtiger, wenn Sie mit getrennten Entitäten arbeiten. Beachten Sie, dass bei der Arbeit mit 1:1- oder 1:0..1-Beziehungen, dass es keine separate Fremdschlüsselspalte gibt, sondern dass die Primärschlüsseleigenschaft als Fremdschlüssel fungiert und immer im Modell enthalten ist.

Wenn im Modell keine Fremdschlüsselspalten enthalten sind, werden die Zuordnungsinformationen als unabhängiges Objekt verwaltet. Beziehungen werden über Objektverweise anstelle von Fremdschlüsseleigenschaften nachverfolgt. Dieser Zuordnungstyp wird als unabhängige Zuordnung bezeichnet. Die gängigste Methode, eine unabhängige Zuordnung zu ändern, besteht im Ändern der Navigationseigenschaften, die für jede an der Zuordnung beteiligte Entität generiert werden.

Sie können wahlweise einen oder beide Zuordnungstypen im Modell verwenden. Wenn Sie jedoch eine reine m:n-Beziehung haben, die mit einer Verknüpfungstabelle verbunden ist, die nur Fremdschlüssel enthält, verwendet EF eine unabhängige Zuordnung, um eine solche m:n-Beziehung zu verwalten.   

Die folgende Abbildung zeigt ein konzeptionelles Modell, das mit dem Entity Framework Designer erstellt wurde. Das Modell enthält zwei Entitäten in einer 1:n-Beziehung. Beide Entitäten weisen Navigationseigenschaften auf. Course ist die abhängige Entität, für die die DepartmentID-Eigenschaft als Fremdschlüssel definiert wurde.

Department and Course tables with navigation properties

Der folgende Codeschnipsel zeigt dasselbe Modell, das mit Code First erstellt wurde.

public class Course
{
  public int CourseID { get; set; }
  public string Title { get; set; }
  public int Credits { get; set; }
  public int DepartmentID { get; set; }
  public virtual Department Department { get; set; }
}

public class Department
{
   public Department()
   {
     this.Courses = new HashSet<Course>();
   }  
   public int DepartmentID { get; set; }
   public string Name { get; set; }
   public decimal Budget { get; set; }
   public DateTime StartDate { get; set; }
   public int? Administrator {get ; set; }
   public virtual ICollection<Course> Courses { get; set; }
}

Konfigurieren oder Zuordnen von Beziehungen

Im restlichen Artikel wird erläutert, wie Sie mithilfe von Beziehungen auf Daten zugreifen und diese bearbeiten. Informationen zum Einrichten von Beziehungen in Ihrem Modell finden Sie auf den folgenden Seiten.

Erstellen und Ändern von Beziehungen

Wenn Sie die Beziehung bei einer Fremdschlüsselzuordnung ändern, ändert sich der Zustand eines abhängigen Objekts mit vom Zustand EntityState.Unchanged in EntityState.Modified. In einer unabhängigen Beziehung wird der Zustand des abhängigen Objekts bei einer Änderung der Beziehung nicht geändert.

In den folgenden Beispielen wird gezeigt, wie die Fremdschlüsseleigenschaft und die Navigationseigenschaft verwendet werden, um die verknüpften Objekte zuzuordnen. Bei Fremdschlüsselzuordnungen können Sie beide Methoden verwenden, um Beziehungen zu erstellen oder zu ändern. Bei unabhängigen Zuordnungen können Sie die Fremdschlüsseleigenschaft nicht verwenden.

  • Durch Zuweisen eines neuen Werts zu einer Fremdschlüsseleigenschaft wie im folgenden Beispiel.

    course.DepartmentID = newCourse.DepartmentID;
    
  • Der folgende Code entfernt eine Beziehung, indem der Fremdschlüssel auf NULL festgelegt wird. Beachten Sie, dass die Fremdschlüsseleigenschaft Nullwerte zulassen muss.

    course.DepartmentID = null;
    

    Hinweis

    Wenn sich der Verweis im hinzugefügten Zustand befindet (in diesem Beispiel das course-Objekt), wird die Verweisnavigationseigenschaft erst mit den Schlüsselwerten eines neuen Objekts synchronisiert, wenn SaveChanges aufgerufen wird. Es findet keine Synchronisierung statt, da der Objektkontext erst dann permanente Schlüssel für hinzugefügte Objekte enthält, wenn diese gespeichert werden. Wenn neue Objekte vollständig synchronisiert werden müssen, nachdem die Beziehung festgelegt wurde, verwenden Sie eine der folgenden Methoden.*

  • Durch das Zuweisen einer Navigationseigenschaft zu einem neuen Objekt. Der folgende Code erstellt eine Beziehung zwischen einer course-Entität und einer department-Entität. Wenn die Objekte an den Kontext angefügt werden, wird der department.Courses-Auflistung das course-Objekt hinzugefügt, und die entsprechende Fremdschlüsseleigenschaft für das course-Objekt wird auf den Wert der Schlüsseleigenschaft des department-Objekts festgelegt.

    course.Department = department;
    
  • Um die Beziehung zu löschen, legen Sie die Navigationseigenschaft auf null fest. Wenn Sie eine Entity Framework-Version verwenden, die auf .NET 4.0 basiert, muss das zugehörige Ende geladen werden, bevor Sie es auf NULL festlegen. Beispiel:

    context.Entry(course).Reference(c => c.Department).Load();
    course.Department = null;
    

    Ab Entity Framework 5.0, das auf .NET 4.5 basiert, können Sie die Beziehung auf NULL festlegen, ohne das zugehörige Ende zu laden. Sie können den aktuellen Wert auch mit der folgenden Methode auf NULL festlegen.

    context.Entry(course).Reference(c => c.Department).CurrentValue = null;
    
  • Durch das Löschen eines Objekts aus einer Entitätsauflistung oder das Hinzufügen eines Objekts zu einer Entitätsauflistung. Beispielsweise können Sie der department.Courses-Auflistung ein Objekt vom Typ Course hinzufügen. Dieser Vorgang erstellt eine Beziehung zwischen einem bestimmten course-Objekt und einem bestimmten department-Objekt. Wenn die Objekte an den Kontext angefügt werden, werden der Abteilungsverweis und die Fremdschlüsseleigenschaft für das course-Objekt auf das entsprechende department-Objekt festgelegt.

    department.Courses.Add(newCourse);
    
  • Mithilfe der ChangeRelationshipState-Methode können Sie den Zustand der angegebenen Beziehung zwischen zwei Entitätsobjekten ändern. Diese Methode wird meist bei der Arbeit mit n-schichtigen Anwendungen und einer unabhängigen Zuordnung verwendet (sie kann nicht mit einer Fremdschlüsselzuordnung verwendet werden). Um diese Methode zu verwenden, müssen Sie auch auf ObjectContext zurückgreifen, wie im folgenden Beispiel gezeigt.
    Im folgenden Beispiel besteht eine m:n-Beziehung zwischen Kursleitern und Kursen. Durch Aufrufen der ChangeRelationshipState-Methode und Übergeben des EntityState.Added-Parameters wird dem SchoolContext mitgeteilt, dass zwischen den beiden Objekten eine Beziehung hinzugefügt wurde:

    
    ((IObjectContextAdapter)context).ObjectContext.
      ObjectStateManager.
      ChangeRelationshipState(course, instructor, c => c.Instructor, EntityState.Added);
    

    Wenn Sie eine Beziehung aktualisieren (nicht nur hinzufügen), müssen Sie die alte Beziehung löschen, nachdem Sie die neue hinzugefügt haben:

    ((IObjectContextAdapter)context).ObjectContext.
      ObjectStateManager.
      ChangeRelationshipState(course, oldInstructor, c => c.Instructor, EntityState.Deleted);
    

Synchronisieren der Änderungen zwischen den Fremdschlüsseln und Navigationseigenschaften

Wenn Sie die Beziehung der Objekte ändern, die mit einer der oben beschriebenen Methoden an den Kontext angefügt wurden, muss Entity Framework die Fremdschlüssel, Verweise und Auflistungen synchronisieren. Entity Framework verwaltet diese Synchronisierung (auch als Beziehungskorrektur bezeichnet) automatisch für die POCO-Entitäten mit Proxys. Weitere Informationen finden Sie unter Verwenden von Proxys.

Wenn Sie POCO-Entitäten ohne Proxys verwenden, müssen Sie sicherstellen, dass die DetectChanges-Methode aufgerufen wird, um die zugehörigen Objekte im Kontext zu synchronisieren. Beachten Sie, dass die folgenden APIs automatisch einen Aufruf von DetectChanges auslösen.

  • DbSet.Add
  • DbSet.AddRange
  • DbSet.Remove
  • DbSet.RemoveRange
  • DbSet.Find
  • DbSet.Local
  • DbContext.SaveChanges
  • DbSet.Attach
  • DbContext.GetValidationErrors
  • DbContext.Entry
  • DbChangeTracker.Entries
  • Ausführen einer LINQ-Abfrage für ein DbSet

In Entity Framework verwenden Sie häufig Navigationseigenschaften, um Entitäten zu laden, die mit der zurückgegebenen Entität über die definierte Zuordnung verknüpft sind. Weitere Informationen finden Sie unter Laden von verknüpften Objekten.

Hinweis

Wenn Sie in einer Fremdschlüsselzuordnung ein verknüpftes Ende eines abhängigen Objekts laden, wird das verknüpfte Objekt auf der Grundlage des Fremdschlüsselwerts des abhängigen Elements geladen, der sich gerade im Arbeitsspeicher befindet:

    // Get the course where currently DepartmentID = 2.
    Course course = context.Courses.First(c => c.DepartmentID == 2);

    // Use DepartmentID foreign key property
    // to change the association.
    course.DepartmentID = 3;

    // Load the related Department where DepartmentID = 3
    context.Entry(course).Reference(c => c.Department).Load();

In einer unabhängigen Zuordnung wird das verknüpfte Ende eines abhängigen Objekts auf der Grundlage des Fremdschlüsselwerts abgefragt, der sich gerade in der Datenbank befindet. Wenn die Beziehung geändert wurde und die Verweiseigenschaft für das abhängige Objekt auf ein anderes Prinzipalobjekt zeigt, das in den Objektkontext geladen wurde, versucht Entity Framework, eine Beziehung zu erstellen, die der auf dem Client definierten Beziehung entspricht.

Verwalten von Nebenläufigkeit

Sowohl bei Fremdschlüsselzuordnungen als auch bei unabhängigen Zuordnungen basieren Parallelitätsüberprüfungen auf den Entitätsschlüsseln und anderen Entitätseigenschaften, die im Modell definiert sind. Wenn Sie EF Designer zum Erstellen eines Modells verwenden, legen Sie das ConcurrencyMode-Attribut auf fixed fest, um anzugeben, dass die Eigenschaft auf Parallelität überprüft werden soll. Wenn Sie Code First zum Definieren eines Modells verwenden, verwenden Sie die ConcurrencyCheck-Anmerkung für Eigenschaften, die auf Parallelität überprüft werden sollen. Beim Arbeiten mit Code First können Sie auch die TimeStamp-Anmerkung verwenden, um anzugeben, dass eine Eigenschaft auf Parallelität überprüft werden soll. Sie können in einer Klasse jeweils nur eine Zeitstempeleigenschaft haben. Code First ordnet diese Eigenschaft einem Non-Nullable-Feld in der Datenbank zu.

Bei der Arbeit mit Entitäten, die in die Parallelitätsprüfung und -auflösung eingeschlossen werden, wird empfohlen, immer die Fremdschlüsselzuordnung zu verwenden.

Weitere Informationen finden Sie unter Behandeln von Nebenläufigkeitskonflikten.

Arbeiten mit sich überlappenden Schlüsseln

Überlappende Schlüssel sind zusammengesetzte Schlüssel, wobei einige Eigenschaften eines Schlüssels auch Teil eines anderen Schlüssels der Entität sind. Unabhängige Zuordnungen können keine überlappenden Schlüssel enthalten. Um eine Fremdschlüsselzuordnung zu ändern, die überlappende Schlüssel enthält, empfiehlt es sich, die Fremdschlüsselwerte zu ändern, statt die Objektverweise zu verwenden.