2017-02-03 2 views
2

Wie kann ich und Cats zuordnen, um eine Sammlung von Toys zu haben, aber nicht Rats, weil sie keine Haustiere sind?Wie kann ich eine abgeleitete Klasse mit einer Sammlung von Entitäten mit Entity Framework zuordnen

public abstract class Animal { 
    public int Id { get; set; } 
} 

public class Cat : Animal { 
    public virtual ICollection<Toy> Toys { get; set; } 
} 

public class Dog : Animal { 
    public virtual ICollection<Toy> Toys { get; set; } 
} 

public class Rat : Animal { 

} 

public class Toy { 
    public int Id { get; set; } 

    public string Name { get; set; } 

    public Animal Owner { get; set; } 

    public int OwnerId { get; set; } 
} 

Ich habe versucht, mit fließend Mapping, tun:

public class CatEntityConfiguration : EntityConfiguration<Cat> { 
    public CatEntityConfiguration() { 
     HasMany(c => c.Toys).WithRequired(t => t.Owner); 
    } 
} 

Aber Owner hat der Typ sein Cat, nicht Animal, aber dann will natürlich DogOwner von Dog Typ sein, nicht Animal oder Cat. Ich könnte eine Dog und eine Cat Eigenschaft auf Toy machen, aber das scheint wie ein Hack, keine Lösung.

+0

Die Antwort hängt davon ab, wie Sie die Vererbung abbilden möchten. Möchten Sie auch die Basisklasse 'Animal' abbilden, oder möchten Sie EF davon abhalten, so dass jeder Subtyp seine eigene unabhängige Zuordnung hat? –

+0

@GertArnold Die Basisklasse und die abgeleiteten Klassen sind bereits zugeordnet. Ich nehme an, ich hätte Animal als abstrakt markieren sollen. Aber ja, Animal ist eine Entität, die EF kennt. –

Antwort

5

Das Problem mit Entity Framework ist, dass es eine sehr enge Beziehung zwischen Entitäten will, tut es wirklich. Wenn Sie sagen 'Toy: Sie dürfen Animal als Besitzer haben, aber nur einige Animal Arten haben Sie als Kind' dann liest Entity Framework das als 'Ich kann Toy nicht erlauben, eine Referenz auf Animal implizit zu haben , weil Animal nichts weiß überToy und Toy ist eigentlich mit einem Cat oder einem Dog verbunden.

Der einfachste Weg, dies zu beheben, ist Pet eine Klasse mit Toys zu machen, dann Cat machen und Dog erben die Pet Klasse, wo Rat noch ein Animal:

public abstract class Animal 
{ 
    public int Id { get; set; } 
} 

public abstract class Pet : Animal 
{ 
    public virtual ICollection<Toy> Toys { get; set; } 
} 

public class Cat : Pet 
{ 
    public string Name { get; set; } 
} 

public class Dog : Pet 
{ 
    public int CollarColor { get; set; } 
} 

public class Rat : Animal 
{ 

} 

public class Toy 
{ 
    public int Id { get; set; } 
    public Pet Owner { get; set; } 
} 

EF wird dann erstellen Pets Tisch mit einer Discriminator Spalte und den Eigenschaften sowohl unsere Objekte (Dog und Cat sind nicht mehr die Datenbank-Entities):

CREATE TABLE [dbo].[Pets] (
    [Id]   INT   IDENTITY (1, 1) NOT NULL, 
    [Discriminator] NVARCHAR (128) NOT NULL, 
    [Name]   NVARCHAR (MAX) NULL, 
    [CollarColor] INT   NULL, 
    CONSTRAINT [PK_dbo.Pets] PRIMARY KEY CLUSTERED ([Id] ASC) 
); 

CreateTable(
    "dbo.Pets", 
    c => new 
     { 
      Id = c.Int(nullable: false, identity: true), 
      Discriminator = c.String(nullable: false, maxLength: 128), 
      Name = c.String(), 
      CollarColor = c.Int(), 
     }) 
    .PrimaryKey(t => t.Id); 

Und wenn wir einen Cat namens Frank hinzufügen, erhalten wir:

Id Discriminator Name CollarColor 
1 Cat    Frank NULL 

Wenn Sie diese DB-Struktur nicht mögen, die andere Option ist eine PetAttributes Klasse zu erstellen, von was jedes Pet bekommt eins, und dann ist Toys ein Teil dieser Klasse, jedes Spielzeug ist im Besitz eines PetAttribute, und dann geht das Leben weiter. Das einzige Problem ist Ihre Navigation wird Cat.Attributes.Toys, aber Sie könnten eine get -only-Eigenschaft erstellen, um das zu umgehen.

Um diese Discriminator Spalte loszuwerden, könnten wir hinzufügen:

modelBuilder.Ignore<Pet>(); 

diese dann eine neue, bauen aber immer noch nicht optimal DB-Struktur:

CreateTable(
    "dbo.Toys", 
    c => new 
     { 
      Id = c.Int(nullable: false, identity: true), 
      Cat_Id = c.Int(), 
      Dog_Id = c.Int(), 
     }) 
    .PrimaryKey(t => t.Id) 
    .ForeignKey("dbo.Cats", t => t.Cat_Id) 
    .ForeignKey("dbo.Dogs", t => t.Dog_Id) 
    .Index(t => t.Cat_Id) 
    .Index(t => t.Dog_Id); 

So, die letzte und letzte Option, erstellen Sie eine PetAttributes Klasse mit der Toys:

public abstract class Pet : Animal 
{ 
    public PetAttributes Attributes { get; set; } 
} 

public class PetAttributes 
{ 
    [Key] 
    public int OwnerId { get; set; } 

    [ForeignKey(nameof(OwnerId))] 
    public Pet Owner { get; set; } 

    public virtual ICollection<Toy> Toys { get; set; } 
} 

public class Toy 
{ 
    public int Id { get; set; } 
    public PetAttributes Owner { get; set; } 
} 

Wir außer Kraft setzen OnModelCreating:

protected override void OnModelCreating(DbModelBuilder modelBuilder) 
{ 
    base.OnModelCreating(modelBuilder); 

    modelBuilder.Ignore<Pet>(); 
    modelBuilder.Entity<Pet>().HasRequired(p => p.Attributes).WithRequiredDependent(a => a.Owner); 
} 

Und wir bekommen eine neue Tabellenstruktur:

CreateTable(
    "dbo.Cats", 
    c => new 
     { 
      Id = c.Int(nullable: false, identity: true), 
      Name = c.String(), 
      Attributes_OwnerId = c.Int(), 
     }) 
    .PrimaryKey(t => t.Id) 
    .ForeignKey("dbo.PetAttributes", t => t.Attributes_OwnerId) 
    .Index(t => t.Attributes_OwnerId); 

CreateTable(
    "dbo.PetAttributes", 
    c => new 
     { 
      OwnerId = c.Int(nullable: false, identity: true), 
     }) 
    .PrimaryKey(t => t.OwnerId); 

CreateTable(
    "dbo.Toys", 
    c => new 
     { 
      Id = c.Int(nullable: false, identity: true), 
      Owner_OwnerId = c.Int(), 
     }) 
    .PrimaryKey(t => t.Id) 
    .ForeignKey("dbo.PetAttributes", t => t.Owner_OwnerId) 
    .Index(t => t.Owner_OwnerId); 

Dann können wir mehr Attribute in Pet oder PetAttributes, bewegen und schaffen get -nur Eigenschaften für sie, wenn sie in PetAttributes sind:

public abstract class Pet : Animal 
{ 
    public string Name { get; set; } 

    public PetAttributes Attributes { get; set; } 

    public ICollection<Toy> Toys => Attributes.Toys; 
} 
CreateTable(
    "dbo.Cats", 
    c => new 
     { 
      Id = c.Int(nullable: false, identity: true), 
      Name = c.String(), 
      Attributes_OwnerId = c.Int(), 
     }) 
    .PrimaryKey(t => t.Id) 
    .ForeignKey("dbo.PetAttributes", t => t.Attributes_OwnerId) 
    .Index(t => t.Attributes_OwnerId); 

CreateTable(
    "dbo.Dogs", 
    c => new 
     { 
      Id = c.Int(nullable: false, identity: true), 
      CollarColor = c.Int(nullable: false), 
      Name = c.String(), 
      Attributes_OwnerId = c.Int(), 
     }) 
    .PrimaryKey(t => t.Id) 
    .ForeignKey("dbo.PetAttributes", t => t.Attributes_OwnerId) 
    .Index(t => t.Attributes_OwnerId); 

Wie wir sehen können, EF macht es schwierig, aber nicht unmöglich eine Einheit als viele Teil mehrere andere Entitätsbeziehungen abzubilden. Dies ist weitgehend auf die Typabhängigkeiten zurückzuführen: EF auch macht es schwierig, einen Fehler in Typ Fehler zu machen.

Verwandte Themen