我如何映射派生的 class 具有 Entity Framework 的实体集合
How can I map a derived class having a collection of entities with Entity Framework
如何映射 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
。我可以在 Toy
上制作一个 Dog
和一个 Cat
属性,但这似乎是一个 hack,而不是一个解决方案。
Entity Framework 的问题在于它想要在实体之间建立 非常紧密的关系 ,它确实如此。如果你说'Toy
:你可以拥有任何 Animal
作为所有者,但只有一些 Animal
类型会让你成为 child' 然后 Entity Framework 读作“我不能允许 Toy
隐含地引用 Animal
,因为 Animal
不知道任何 关于 Toy
,而Toy
实际上与Cat
或Dog
有关。'
解决此问题的最简单方法是使 Pet
成为 class 和 Toys
,然后使 Cat
和 Dog
继承 Pet
class,其中 Rat
仍然是 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 然后将创建一个 Pets
table,其中包含一个 Discriminator
列和 both 我们的 objects 的属性( 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);
当我们添加一个名为 Frank
的 Cat
时,我们得到:
Id Discriminator Name CollarColor
1 Cat Frank NULL
如果你不喜欢这个数据库结构,另一种选择是创建一个PetAttributes
class,每个Pet
得到一个,然后Toys
是那个class的一部分,每个玩具都属于一个PetAttribute
,然后生活还要继续。唯一的问题是您的导航变为 Cat.Attributes.Toys
,但您可以构建一个仅 get
的 属性 来解决这个问题。
要删除 Discriminator
列,我们可以添加:
modelBuilder.Ignore<Pet>();
然后构建一个新的,但仍然是 non-optimal 数据库结构:
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);
所以,最后一个也是最后一个选项,用 Toys
:
创建一个 PetAttributes
class
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);
}
我们得到一个新的 table 结构:
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
-only 属性,如果它们在 PetAttributes
:
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);
正如我们所见,EF 使得将一个实体映射为 多个 其他实体关系的多个部分变得困难但并非不可能 .这主要是由于类型限制:EF also 使得在 type 错误的关系中很难出错。
如何映射 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
。我可以在 Toy
上制作一个 Dog
和一个 Cat
属性,但这似乎是一个 hack,而不是一个解决方案。
Entity Framework 的问题在于它想要在实体之间建立 非常紧密的关系 ,它确实如此。如果你说'Toy
:你可以拥有任何 Animal
作为所有者,但只有一些 Animal
类型会让你成为 child' 然后 Entity Framework 读作“我不能允许 Toy
隐含地引用 Animal
,因为 Animal
不知道任何 关于 Toy
,而Toy
实际上与Cat
或Dog
有关。'
解决此问题的最简单方法是使 Pet
成为 class 和 Toys
,然后使 Cat
和 Dog
继承 Pet
class,其中 Rat
仍然是 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 然后将创建一个 Pets
table,其中包含一个 Discriminator
列和 both 我们的 objects 的属性( 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);
当我们添加一个名为 Frank
的 Cat
时,我们得到:
Id Discriminator Name CollarColor
1 Cat Frank NULL
如果你不喜欢这个数据库结构,另一种选择是创建一个PetAttributes
class,每个Pet
得到一个,然后Toys
是那个class的一部分,每个玩具都属于一个PetAttribute
,然后生活还要继续。唯一的问题是您的导航变为 Cat.Attributes.Toys
,但您可以构建一个仅 get
的 属性 来解决这个问题。
要删除 Discriminator
列,我们可以添加:
modelBuilder.Ignore<Pet>();
然后构建一个新的,但仍然是 non-optimal 数据库结构:
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);
所以,最后一个也是最后一个选项,用 Toys
:
PetAttributes
class
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);
}
我们得到一个新的 table 结构:
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
-only 属性,如果它们在 PetAttributes
:
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);
正如我们所见,EF 使得将一个实体映射为 多个 其他实体关系的多个部分变得困难但并非不可能 .这主要是由于类型限制:EF also 使得在 type 错误的关系中很难出错。