如何将Dogs
和Cats
映射到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
类型}},而不是Animal
或Cat
。我可以在Dog
上创建Cat
和Toy
属性,但这似乎是一种黑客攻击,而不是解决方案。
答案 0 :(得分:5)
实体框架的问题在于它希望实体之间存在非常紧密的关系,它确实存在。如果你说&{39; Toy
:你被允许拥有任何Animal
作为所有者,但只有一些Animal
类型会让你作为一个孩子。然后,实体框架将其视为“我不允许Toy
隐含地引用Animal
,因为Animal
并不知道任何事情关于 Toy
,Toy
实际上与Cat
或Dog
相关。&#39;
解决此问题的最简单方法是使Pet
成为Toys
的班级,然后让Cat
和Dog
继承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
列的属性 我们的对象(Discriminator
和Dog
都不是数据库实体的时间越长):
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 错误的关系中出错。