Zoeken…


Invoering

In het onderwerp wordt besproken hoe u met Entity Framework Code First één-op-veel en veel-op-veel relaties kunt toewijzen.

Eén-op-veel in kaart brengen

Laten we zeggen dat je twee verschillende entiteiten hebt, zoiets als dit:

public class Person
{
   public int PersonId { get; set; }
   public string Name { get; set; }
}

public class Car
{
   public int CarId { get; set; }
   public string LicensePlate { get; set; }
}

public class MyDemoContext : DbContext
{
   public DbSet<Person> People { get; set; }
   public DbSet<Car> Cars { get; set; }
}

En u wilt een één-op-veel-relatie tussen hen opzetten, dat wil zeggen dat één persoon nul, één of meer auto's kan hebben en één auto precies bij één persoon hoort. Elke relatie is bidirectioneel, dus als een persoon een auto heeft, is de auto van die persoon.

Wijzig hiervoor gewoon uw modelklassen:

public class Person
{
   public int PersonId { get; set; }
   public string Name { get; set; }
   public virtual ICollection<Car> Cars { get; set; } // don't forget to initialize (use HashSet)
}

public class Car
{
   public int CarId { get; set; }
   public string LicensePlate { get; set; }
   public int PersonId { get; set; }
   public virtual Person Person { get; set; }
}

En dat is alles :) Je hebt je relatie al opgezet. In de database wordt dit uiteraard weergegeven met buitenlandse sleutels.

Eén-op-veel in kaart brengen: tegen de conventie

In het laatste voorbeeld kunt u zien dat EF uitzoekt welke kolom de externe sleutel is en waar deze naar moet verwijzen. Hoe? Door conventies te gebruiken. Met een eigenschap van het type Person naam Person met een eigenschap PersonId concludeert EF dat PersonId een externe sleutel is en PersonId naar de primaire sleutel van de tabel die wordt voorgesteld door het type Person .

Maar wat als je zou PersonId veranderen OwnerID en persoon aan de eigenaar in het type auto?

public class Car
{
   public int CarId { get; set; }
   public string LicensePlate { get; set; }
   public int OwnerId { get; set; }
   public virtual Person Owner { get; set; }
}

Nou, helaas zijn in dit geval de conventies niet voldoende om het juiste DB-schema te produceren:

Geen zorgen; u kunt EF helpen met enkele hints over uw relaties en sleutels in het model. Gewoon configureer uw Car type om het te gebruiken OwnerId eigendom als de FK. Maak een entiteitstype-configuratie en pas deze toe in uw OnModelCreating() :

public class CarEntityTypeConfiguration : EntityTypeConfiguration<Car>
{
  public CarEntityTypeConfiguration()
  {
     this.HasRequired(c => c.Owner).WithMany(p => p.Cars).HasForeignKey(c => c.OwnerId);
  }
}

Dit betekent in feite dat Car een vereiste eigenschap heeft, Owner ( HasRequired () ) en in het type Owner wordt de eigenschap Cars gebruikt om terug te verwijzen naar de auto-entiteiten ( WithMany () ). En ten slotte wordt de eigenschap die de externe sleutel vertegenwoordigt, opgegeven ( HasForeignKey () ). Dit geeft ons het schema dat we willen:

U kunt de relatie ook configureren vanaf de kant Person :

public class PersonEntityTypeConfiguration : EntityTypeConfiguration<Person>
{
  public PersonEntityTypeConfiguration()
  {
    this.HasMany(p => p.Cars).WithRequired(c => c.Owner).HasForeignKey(c => c.OwnerId);
  }
}

Het idee is hetzelfde, alleen de zijkanten zijn anders (let op hoe je het hele ding kunt lezen: 'deze persoon heeft veel auto's, elke auto met een vereiste eigenaar'). Het maakt niet uit of u de relatie configureert vanaf de kant Person of de kant Car . Je kunt zelfs beide opnemen, maar wees in dit geval voorzichtig om dezelfde relatie aan beide kanten op te geven!

In kaart brengen van nul of één-op-veel

In de vorige voorbeelden kan een auto niet bestaan zonder een persoon. Wat als je wilde dat de persoon optioneel was aan de autozijde? Nou, het is een beetje eenvoudig, weten hoe je één-op-veel moet doen. Wijzig gewoon de PersonId in Car om nul te worden:

public class Car
{
    public int CarId { get; set; }
    public string LicensePlate { get; set; }
    public int? PersonId { get; set; }
    public virtual Person Person { get; set; }
}

En gebruik vervolgens HasOptional () (of WithOptional () , afhankelijk van welke kant u de configuratie uitvoert ):

public class CarEntityTypeConfiguration : EntityTypeConfiguration<Car>
{
  public CarEntityTypeConfiguration()
  {
     this.HasOptional(c => c.Owner).WithMany(p => p.Cars).HasForeignKey(c => c.OwnerId);
  }
}

Veel te veel

Laten we doorgaan naar het andere scenario, waar elke persoon meerdere auto's kan hebben en elke auto meerdere eigenaren kan hebben (maar nogmaals, de relatie is bidirectioneel). Dit is een veel-op-veel-relatie. De eenvoudigste manier is om EF zijn magie te laten doen met behulp van conventies.

Wijzig het model als volgt:

 public class Person
{
   public int PersonId { get; set; }
   public string Name { get; set; }
   public virtual ICollection<Car> Cars { get; set; }
}

public class Car
{
   public int CarId { get; set; }
   public string LicensePlate { get; set; }        
   public virtual ICollection<Person> Owners { get; set; }
}

En het schema:

Bijna perfect. Zoals u ziet, heeft EF de noodzaak van een join-tafel erkend, waar u personen-auto-paren kunt bijhouden.

Veel-op-veel: de join-tabel aanpassen

Misschien wilt u de velden in de join-tabel een beetje vriendelijker maken. U kunt dit doen met behulp van de gebruikelijke configuratiemethoden (nogmaals, het maakt niet uit vanaf welke kant u de configuratie uitvoert):

public class CarEntityTypeConfiguration : EntityTypeConfiguration<Car>
{
   public CarEntityTypeConfiguration()
   {
      this.HasMany(c => c.Owners).WithMany(p => p.Cars)
          .Map(m =>
              {
                 m.MapLeftKey("OwnerId");
                 m.MapRightKey("CarId");
                 m.ToTable("PersonCars");
              }
        );
  }
}

Zelfs heel gemakkelijk te lezen: deze auto heeft veel eigenaren ( HasMany () ), waarbij elke eigenaar veel auto's heeft ( WithMany () ). Wijs dit toe zodat u de linkersleutel toewijst aan OwnerId ( MapLeftKey () ), de rechtersleutel aan CarId ( MapRightKey () ) en het hele ding aan de tabel PersonCars ( ToTable () ). En dit geeft u precies dat schema:

Veel-op-veel: aangepaste join-entiteit

Ik moet toegeven dat ik niet echt een fan ben van het laten afleiden van EF zonder een join-entiteit. U kunt geen extra informatie traceren naar een personen-auto-vereniging (laten we zeggen de datum vanaf wanneer deze geldig is), omdat u de tabel niet kunt wijzigen.

Het CarId in de join-tabel maakt ook deel uit van de primaire sleutel, dus als de familie een nieuwe auto koopt, moet u eerst de oude associaties verwijderen en nieuwe toevoegen. EF verbergt dit voor je, maar dit betekent dat je deze twee bewerkingen moet doen in plaats van een eenvoudige update (om nog maar te zwijgen van het feit dat frequente invoegingen / verwijderingen kunnen leiden tot indexfragmentatie - goed dat daar een gemakkelijke oplossing voor is).

In dit geval kunt u een join-entiteit maken met een verwijzing naar zowel een specifieke auto als een specifieke persoon. Kortom, je ziet je veel-op-veel-associatie als een combinatie van twee één-op-veel-associaties:

public class PersonToCar
{
   public int PersonToCarId { get; set; }
   public int CarId { get; set; }
   public virtual Car Car { get; set; }
   public int PersonId { get; set; }
   public virtual Person Person { get; set; }
   public DateTime ValidFrom { get; set; }
}

public class Person
{
  public int PersonId { get; set; }
  public string Name { get; set; }
  public virtual ICollection<PersonToCar> CarOwnerShips { get; set; }
}

public class Car
{
  public int CarId { get; set; }
  public string LicensePlate { get; set; }        
  public virtual ICollection<PersonToCar> Ownerships { get; set; }
}

public class MyDemoContext : DbContext
{
  public DbSet<Person> People { get; set; }
  public DbSet<Car> Cars { get; set; }
  public DbSet<PersonToCar> PersonToCars { get; set; }
}

Dit geeft me veel meer controle en het is een stuk flexibeler. Ik kan nu aangepaste gegevens toevoegen aan de associatie en elke associatie heeft zijn eigen primaire sleutel, zodat ik de auto of de eigenaarreferentie erin kan bijwerken.

Merk op dat dit echt slechts een combinatie is van twee één-op-veel relaties, dus u kunt alle configuratie-opties gebruiken die in de vorige voorbeelden zijn besproken.



Modified text is an extract of the original Stack Overflow Documentation
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow