如何使用Entity Framework映射具有实体集合的派生类

时间:2017-02-03 16:18:15

标签: c# entity-framework ef-fluent-api

如何将DogsCats映射到Toys而不是Rats,因为他们不是宠物?

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; }
}

我尝试使用流畅的映射:

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

Owner必须是Cat类型,而不是Animal,但当然,Dog希望Owner属于Dog类型}},而不是AnimalCat。我可以在Dog上创建CatToy属性,但这似乎是一种黑客攻击,而不是解决方案。

1 个答案:

答案 0 :(得分:5)

实体框架的问题在于它希望实体之间存在非常紧密的关系,它确实存在。如果你说&{39; Toy:你被允许拥有任何Animal作为所有者,但只有一些Animal类型会让你作为一个孩子。然后,实体框架将其视为“我不允许Toy隐含地引用Animal,因为Animal并不知道任何事情关于 ToyToy实际上与CatDog相关。&#39;

解决此问题的最简单方法是使Pet成为Toys的班级,然后让CatDog继承Pet班级,其中{ {1}}仍然是Rat

Animal
然后,

EF会创建一个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; } } 表,其中Pets列的属性 我们的对象(DiscriminatorDog都不是数据库实体的时间越长):

Cat

当我们添加名为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); 的{​​{1}}时,我们会得到:

Cat

如果你不喜欢这个数据库结构,另一个选择是创建一个Frank类,其中每个Id Discriminator Name CollarColor 1 Cat Frank NULL 得到一个,然后PetAttributes是那个类,每个玩具都归Pet所有,然后生活继续。唯一的问题是导航变为Toys,但您可以构建一个PetAttribute - 仅限属性来绕过它。

要删除Cat.Attributes.Toys列,我们可以添加:

get

然后这构建了一个新的,但仍然不是最佳的DB结构:

Discriminator

所以,最后也是最后一个选项,使用modelBuilder.Ignore<Pet>(); 创建一个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); 类:

PetAttributes

我们覆盖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; }
}

我们得到一个新的表结构:

OnModelCreating

然后,我们可以将更多属性移动到protected override void OnModelCreating(DbModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); modelBuilder.Ignore<Pet>(); modelBuilder.Entity<Pet>().HasRequired(p => p.Attributes).WithRequiredDependent(a => a.Owner); } 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); ,并创建Pet - 如果它们位于PetAttributes中,则只为它们添加属性:

get
PetAttributes

正如我们所看到的,EF使很难但不是不可能将一个实体映射为多个其他实体关系的许多部分。这主要是由于类型限制:EF 使得很难在 type 错误的关系中出错。

相关问题