Fluent API – Kapcsolatok

Megjegyzés:

Ez a lap a fluent API használatával a Code First modellben lévő kapcsolatok beállításáról nyújt tájékoztatást. Az EF-kapcsolatokról és az adatok kapcsolatok használatával való eléréséről és módosításáról a Kapcsolatok > navigációs tulajdonságok című témakörben olvashat.

A Code First használatakor a modellt a tartományi CLR-osztályok definiálásával határozhatja meg. Az Entity Framework alapértelmezés szerint a Code First konvenciók használatával rendeli le az osztályokat az adatbázissémához. A Code First elnevezési konvenciók használata esetén a legtöbb esetben a Code First használatával állíthatja be a táblák közötti kapcsolatokat az osztályokban megadott idegen kulcsok és navigációs tulajdonságok alapján. Ha az osztályok meghatározásakor nem követi az egyezményeket, vagy ha módosítani szeretné a konvenciók működését, a fluent API-val vagy adatjegyzetekkel konfigurálhatja az osztályokat, hogy a Code First leképezhesse a táblák közötti kapcsolatokat.

Introduction

A fluent API-val való kapcsolat konfigurálásakor először az EntityTypeConfiguration példányt kell használnia, majd a HasRequired, a HasOptional vagy a HasMany metódus használatával megadhatja, hogy az entitás milyen típusú kapcsolatban vesz részt. A HasRequired és a HasOptional metódusok egy lambda kifejezést használnak, amely egy referencianavigációs tulajdonságot jelöl. A HasMany metódus egy lambda kifejezést használ, amely egy gyűjtemény navigációs tulajdonságát jelöli. Ezután konfigurálhat inverz navigációs tulajdonságot a WithRequired, a WithOptional és a WithMany metódusokkal. Ezek a metódusok túlterheléssel rendelkeznek, amelyek nem fogadnak el argumentumokat, és számosság megadására használhatók egyirányú navigációval.

Ezután a HasForeignKey metódussal konfigurálhatja az idegen kulcs tulajdonságait. Ez a metódus egy lambda kifejezést használ, amely az idegen kulcsként használni kívánt tulajdonságot jelöli.

Kötelező és nem kötelező kapcsolat konfigurálása (egy-a-nullához vagy egyhez)

Az alábbi példa egy-a-nullához vagy-egyhez kapcsolatot konfigurál. Az OfficeAssignment rendelkezik az InstructorID tulajdonságával, amely egy elsődleges kulcs és egy idegen kulcs, mivel a tulajdonság neve nem követi azt a konvenciót, amelyet a HasKey metódus használ az elsődleges kulcs konfigurálásához.

// Configure the primary key for the OfficeAssignment
modelBuilder.Entity<OfficeAssignment>()
    .HasKey(t => t.InstructorID);

// Map one-to-zero or one relationship
modelBuilder.Entity<OfficeAssignment>()
    .HasRequired(t => t.Instructor)
    .WithOptional(t => t.OfficeAssignment);

Olyan kapcsolat konfigurálása, amelyben mindkét végpontra szükség van (egy az egyhez)

A legtöbb esetben az Entity Framework képes kikövetkeztetni, hogy melyik típus a függő, és melyik a fő egy kapcsolatban. Ha azonban a kapcsolat mindkét vége kötelező, vagy mindkét fél opcionális, az Entity Framework nem tudja azonosítani a függőt és a tagot. Ha a kapcsolat mindkét vége kötelező, használja a WithRequiredPrincipal vagy a WithRequiredDependent parancsot a HasRequired metódus után. Ha a kapcsolat mindkét vége választható, használja a WithOptionalPrincipal vagy a WithOptionalDependent metódusokat a HasOptional után.

// Configure the primary key for the OfficeAssignment
modelBuilder.Entity<OfficeAssignment>()
    .HasKey(t => t.InstructorID);

modelBuilder.Entity<Instructor>()
    .HasRequired(t => t.OfficeAssignment)
    .WithRequiredPrincipal(t => t.Instructor);

Több-a-többhöz kapcsolat konfigurálása

Az alábbi kód egy több az többhöz kapcsolatot konfigurál a Tanfolyam és az Oktató típusok között. Az alábbi példában az alapértelmezett Code First konvenciók egy kapcsolótáblát hoznak létre. Ennek eredményeként a CourseInstructor tábla Course_CourseID és Instructor_InstructorID oszlopokkal jön létre.

modelBuilder.Entity<Course>()
    .HasMany(t => t.Instructors)
    .WithMany(t => t.Courses)

Ha meg szeretné adni az illesztőtábla nevét és a tábla oszlopainak nevét, a Map metódussal további konfigurációt kell végeznie. Az alábbi kód a CourseInstructor táblát hozza létre CourseID és InstructorID oszlopokkal.

modelBuilder.Entity<Course>()
    .HasMany(t => t.Instructors)
    .WithMany(t => t.Courses)
    .Map(m =>
    {
        m.ToTable("CourseInstructor");
        m.MapLeftKey("CourseID");
        m.MapRightKey("InstructorID");
    });

Kapcsolat konfigurálása egy navigációs tulajdonsággal

Az egyirányú (más néven egyirányú) kapcsolat az, amikor a navigációs tulajdonság csak az egyik kapcsolat végén van definiálva, és nem mindkettőn. A Code First szerint a konvenció alapján az egyirányú kapcsolatot mindig egy-a-többhöz értelemben veszik. Ha például egy-egy kapcsolatot kíván létrehozni az Oktató és az OfficeAssignment között, ahol navigációs tulajdonság csak az Oktató típusban van, a kapcsolat konfigurálásához a fluent API-t kell használnia.

// Configure the primary Key for the OfficeAssignment
modelBuilder.Entity<OfficeAssignment>()
    .HasKey(t => t.InstructorID);

modelBuilder.Entity<Instructor>()
    .HasRequired(t => t.OfficeAssignment)
    .WithRequiredPrincipal();

Kaszkád törlés engedélyezése

Egy kapcsolat kaszkádolt törlését a WillCascadeOnDelete metódussal konfigurálhatja. Ha a függő entitás idegen kulcsa nem null értékű, akkor a Code First a kaszkádolt törlést állítja be a kapcsolaton. Ha a függő entitás idegen kulcsa null értéket is felvehet, a Code First nem állítja be a lánctörlést a kapcsolatra, és amikor a fő entitás törlődik, az idegen kulcs null értékűre lesz állítva.

A kaszkádolt törlési konvenciók a következőkkel távolíthatók el:

modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>()
modelBuilder.Conventions.Remove<ManyToManyCascadeDeleteConvention>()

Az alábbi kód konfigurálja a szükséges kapcsolatot, majd letiltja a kaszkádolt törlést.

modelBuilder.Entity<Course>()
    .HasRequired(t => t.Department)
    .WithMany(t => t.Courses)
    .HasForeignKey(d => d.DepartmentID)
    .WillCascadeOnDelete(false);

Összetett idegen kulcs konfigurálása

Ha a Részleg típus elsődleges kulcsa a DepartmentID és a Name tulajdonságokból állt, akkor az alábbiak szerint konfigurálhatja a részleg elsődleges kulcsát és a kurzustípusok idegen kulcsát:

// Composite primary key
modelBuilder.Entity<Department>()
.HasKey(d => new { d.DepartmentID, d.Name });

// Composite foreign key
modelBuilder.Entity<Course>()  
    .HasRequired(c => c.Department)  
    .WithMany(d => d.Courses)
    .HasForeignKey(d => new { d.DepartmentID, d.DepartmentName });

A modellben nem definiált idegen kulcs átnevezése

Ha úgy dönt, hogy nem határoz meg idegen kulcsot a CLR-típuson, de meg szeretné adni, hogy milyen névvel kell rendelkeznie az adatbázisban, tegye a következőket:

modelBuilder.Entity<Course>()
    .HasRequired(c => c.Department)
    .WithMany(t => t.Courses)
    .Map(m => m.MapKey("ChangedDepartmentID"));

Külső kulcsnév konfigurálása, amely nem követi a kód első konvencióját

Ha a kurzusosztály idegenkulcs-tulajdonságát a DepartmentID helyett SomeDepartmentID-nek nevezik, a következőket kell tennie annak megadásához, hogy a SomeDepartmentID legyen az idegen kulcs:

modelBuilder.Entity<Course>()
         .HasRequired(c => c.Department)
         .WithMany(d => d.Courses)
         .HasForeignKey(c => c.SomeDepartmentID);

Mintákban használt modell

Az alábbi Code First modellt használja a rendszer az oldalon található mintákhoz.

using System.Data.Entity;
using System.Data.Entity.ModelConfiguration.Conventions;
// add a reference to System.ComponentModel.DataAnnotations DLL
using System.ComponentModel.DataAnnotations;
using System.Collections.Generic;
using System;

public class SchoolEntities : DbContext
{
    public DbSet<Course> Courses { get; set; }
    public DbSet<Department> Departments { get; set; }
    public DbSet<Instructor> Instructors { get; set; }
    public DbSet<OfficeAssignment> OfficeAssignments { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        // Configure Code First to ignore PluralizingTableName convention
        // If you keep this convention then the generated tables will have pluralized names.
        modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
    }
}

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

    // Navigation property
    public virtual ICollection<Course> Courses { get; private set; }
}

public class Course
{
    public Course()
    {
        this.Instructors = new HashSet<Instructor>();
    }
    // Primary key
    public int CourseID { get; set; }

    public string Title { get; set; }
    public int Credits { get; set; }

    // Foreign key
    public int DepartmentID { get; set; }

    // Navigation properties
    public virtual Department Department { get; set; }
    public virtual ICollection<Instructor> Instructors { get; private set; }
}

public partial class OnlineCourse : Course
{
    public string URL { get; set; }
}

public partial class OnsiteCourse : Course
{
    public OnsiteCourse()
    {
        Details = new Details();
    }

    public Details Details { get; set; }
}

public class Details
{
    public System.DateTime Time { get; set; }
    public string Location { get; set; }
    public string Days { get; set; }
}

public class Instructor
{
    public Instructor()
    {
        this.Courses = new List<Course>();
    }

    // Primary key
    public int InstructorID { get; set; }
    public string LastName { get; set; }
    public string FirstName { get; set; }
    public System.DateTime HireDate { get; set; }

    // Navigation properties
    public virtual ICollection<Course> Courses { get; private set; }
}

public class OfficeAssignment
{
    // Specifying InstructorID as a primary
    [Key()]
    public Int32 InstructorID { get; set; }

    public string Location { get; set; }

    // When Entity Framework sees Timestamp attribute
    // it configures ConcurrencyCheck and DatabaseGeneratedPattern=Computed.
    [Timestamp]
    public Byte[] Timestamp { get; set; }

    // Navigation property
    public virtual Instructor Instructor { get; set; }
}