我尝试设置实体框架来定义一个父子关系,并为该关系定义变量。一个项目由其他项目的比例组成。如何使用Entity Framework 6进行设计。
假设我的父项是磅蛋糕,而我的孩子是鸡蛋,面粉,糖和黄油。对于磅蛋糕,每个孩子的比例为1。让我们保持简单。
public class Item
{
public Guid Id { get; set; }
public string Name { get; set; }
}
public class Composition
{
public Guid Id { get; set; }
public Guid ParentId { get; set; } // Id of Parent Item
public Guid ChildId { get; set; } // Id of Child Item
public int Ratio { get; set; } // Number of Child that compose the parent.
}
我最初的DbContext是
public class TestContext : DbContext
{
public DbSet<Item> Items { get; set; }
}
关于使用Add-Migration Initial
创建迁移。系统生成此迁移代码:
public override void Up()
{
CreateTable(
"dbo.Compositions",
c => new
{
Id = c.Guid(nullable: false),
ParentId = c.Guid(nullable: false),
ChildId = c.Guid(nullable: false),
Ratio = c.Int(nullable: false),
})
.PrimaryKey(t => t.Id);
CreateTable(
"dbo.Items",
c => new
{
Id = c.Guid(nullable: false),
Name = c.String(),
})
.PrimaryKey(t => t.Id);
}
迁移创建了我的2个实体,但它们没有关系。因此,我无法轻松地从项目表导航到我的所有作品。我使用Linpad和LinqPad不创建导航属性。这是正常现象,我从未对我的POCO说过,我的Item表中必须存在ParentId和ChildId。
为了在数据库中创建约束并能够进行导航,我认为我必须添加导航属性并将其链接到外键。我将在此处尝试几种解决方案并发表评论。
解决方案1-仅添加导航属性
public class Composition
{
public Guid Id { get; set; }
public Guid ParentId { get; set; } // Id of Parent Item
[ForeignKey(nameof(ParentId))]
public Item Parent { get; set; }
public Guid ChildId { get; set; } // Id of Child Item
[ForeignKey(nameof(ChildId))]
public Item Child { get; set; }
public int Ratio { get; set; } // Number of Child that compose the parent.
}
关于使用Add-Migration Initial创建迁移。系统生成此迁移代码:
public override void Up()
{
CreateTable(
"dbo.Compositions",
c => new
{
Id = c.Guid(nullable: false),
ParentId = c.Guid(nullable: false),
ChildId = c.Guid(nullable: false),
Ratio = c.Int(nullable: false),
})
.PrimaryKey(t => t.Id)
.ForeignKey("dbo.Items", t => t.ChildId, cascadeDelete: true)
.ForeignKey("dbo.Items", t => t.ParentId, cascadeDelete: true)
.Index(t => t.ParentId)
.Index(t => t.ChildId);
CreateTable(
"dbo.Items",
c => new
{
Id = c.Guid(nullable: false),
Name = c.String(),
})
.PrimaryKey(t => t.Id);
}
但是为什么级联删除为true?我不想在每次删除项目时删除所有树。我知道我可以将Child或ChildId定义为可为空,将Parent或ParentId定义为可为空,但是我不能接受。在我的成分表中,父级和子级不能为空。这不是逻辑,我创建的链接的一侧是值,而另一侧是null。我可以删除组成链接,但是如果创建它,那么双方都必须存在。
而且,我不能Udpate-Database
此版本,因为
在表'Compositions'上引入FOREIGN KEY约束'FK_dbo.Compositions_dbo.Items_ParentId'可能会导致循环或多个级联路径。指定ON DELETE NO ACTION或ON UPDATE NO ACTION,或修改其他FOREIGN KEY约束。
解决方案2-使用Fluent API
我采用我的解决方案1,并尝试添加缺少的信息以帮助我的OnModelCreating()覆盖系统
public class TestContext : DbContext
{
public DbSet<Item> Items { get; set; }
public DbSet<Composition> Compositions { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Composition>()
.HasRequired(c => c.Parent)
// And I have no idea how to do here
}
}
解决方案3-物品上的成分收集
public class Item
{
public Guid Id { get; set; }
public string Name { get; set; }
public ICollection<Composition> Compositions { get; set; }
}
public class Composition
{
public Guid Id { get; set; }
public Guid ParentId { get; set; } // Id of Parent Item
//[ForeignKey(nameof(ParentId))]
//public Item Parent { get; set; }
public Guid ChildId { get; set; } // Id of Child Item
[ForeignKey(nameof(ChildId))]
public Item Child { get; set; }
public int Ratio { get; set; } // Number of Child that compose the parent.
}
关于使用Add-Migration Initial创建迁移。系统生成此迁移代码:
public override void Up()
{
CreateTable(
"dbo.Compositions",
c => new
{
Id = c.Guid(nullable: false),
ParentId = c.Guid(nullable: false),
ChildId = c.Guid(nullable: false),
Ratio = c.Int(nullable: false),
})
.PrimaryKey(t => t.Id)
.ForeignKey("dbo.Items", t => t.ChildId, cascadeDelete: true)
.Index(t => t.ChildId);
CreateTable(
"dbo.Items",
c => new
{
Id = c.Guid(nullable: false),
Name = c.String(),
})
.PrimaryKey(t => t.Id);
}
似乎更好,但我仍然可以级联删除为真。
解决方案4-将父子组合放在项目上
public class Item
{
public Guid Id { get; set; }
public string Name { get; set; }
public ICollection<Composition> ChildCompositions { get; set; }
public ICollection<Composition> ParentCompositions { get; set; }
}
public class Composition
{
public Guid Id { get; set; }
public Guid ParentId { get; set; } // Id of Parent Item
[ForeignKey(nameof(ParentId))]
public Item Parent { get; set; }
public Guid ChildId { get; set; } // Id of Child Item
[ForeignKey(nameof(ChildId))]
public Item Child { get; set; }
public int Ratio { get; set; } // Number of Child that compose the parent.
}
然后是迁移脚本
public override void Up()
{
CreateTable(
"dbo.Compositions",
c => new
{
Id = c.Guid(nullable: false),
ParentId = c.Guid(nullable: false),
ChildId = c.Guid(nullable: false),
Ratio = c.Int(nullable: false),
Item_Id = c.Guid(),
Item_Id1 = c.Guid(),
})
.PrimaryKey(t => t.Id)
.ForeignKey("dbo.Items", t => t.Item_Id)
.ForeignKey("dbo.Items", t => t.Item_Id1)
.ForeignKey("dbo.Items", t => t.ChildId, cascadeDelete: true)
.ForeignKey("dbo.Items", t => t.ParentId, cascadeDelete: true)
.Index(t => t.ParentId)
.Index(t => t.ChildId)
.Index(t => t.Item_Id)
.Index(t => t.Item_Id1);
CreateTable(
"dbo.Items",
c => new
{
Id = c.Guid(nullable: false),
Name = c.String(),
})
.PrimaryKey(t => t.Id);
}
逻辑。系统无法知道我命名的ParentComposition集合必须链接到ParentId外键,而ChildComposition集合必须链接到ChildId外键吗?因此,系统将创建新的外键。
在Update-Database
上我收到错误
在表'Compositions'上引入FOREIGN KEY约束'FK_dbo.Compositions_dbo.Items_ParentId'可能会导致循环或多个级联路径。指定ON DELETE NO ACTION或ON UPDATE NO ACTION,或修改其他FOREIGN KEY约束。
解决方案5-再次返回Fluent API
但是我现在可以浏览属性,所以也许我会找到想要的东西:
public class Item
{
public Guid Id { get; set; }
public string Name { get; set; }
public ICollection<Composition> ParentCompositions { get; set; }
public ICollection<Composition> ChildCompositions { get; set; }
}
public class Composition
{
public Guid Id { get; set; }
public Guid ParentId { get; set; } // Id of Parent Item
[ForeignKey(nameof(ParentId))]
public Item Parent { get; set; }
public Guid ChildId { get; set; } // Id of Child Item
[ForeignKey(nameof(ChildId))]
public Item Child { get; set; }
public int Ratio { get; set; } // Number of Child that compose the parent.
}
public class TestContext : DbContext
{
public DbSet<Item> Items { get; set; }
public DbSet<Composition> Compositions { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Item>()
.HasMany(c => c.ChildCompositions)
.WithRequired(c => c.Parent)
//.HasForeignKey(c => c.ParentId)
.WillCascadeOnDelete(false);
modelBuilder.Entity<Item>()
.HasMany(c => c.ParentCompositions)
.WithRequired(c => c.Child)
//.HasForeignKey(c => c.ChildId)
.WillCascadeOnDelete(false);
}
}
给我迁移脚本:
public override void Up()
{
CreateTable(
"dbo.Compositions",
c => new
{
Id = c.Guid(nullable: false),
ParentId = c.Guid(nullable: false),
ChildId = c.Guid(nullable: false),
Ratio = c.Int(nullable: false),
})
.PrimaryKey(t => t.Id)
.ForeignKey("dbo.Items", t => t.ParentId)
.ForeignKey("dbo.Items", t => t.ChildId)
.Index(t => t.ParentId)
.Index(t => t.ChildId);
CreateTable(
"dbo.Items",
c => new
{
Id = c.Guid(nullable: false),
Name = c.String(),
})
.PrimaryKey(t => t.Id);
}
而且我可以用这个数据库来更新我的数据库。但是,奇怪的是,当我测试他在LinqPad中得到的结果时,似乎在ChildCompositions列表和ParentCompositions列表之间存在倒置。
我尝试在Fluent API中进行更改,但未成功。
解决方案可能是删除ForeignKey
属性,而是通过模型构建器添加关系:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Composition>().HasOne<Item>(x => x.ChildItem).WithMany().OnDelete(DeleteBehavior.Restrict);
modelBuilder.Entity<Composition>().HasOne<Item>(x => x.ParentItem).WithMany().OnDelete(DeleteBehavior.Restrict);
}
因此,您可以显式设置删除级联行为。
我尝试通过添加Composition
实体和两个Item
,然后删除Composition
实体来进行尝试。这两个Item
保留在数据库中。我认为这就是预期的方式。