我认为下面示例中的模型有问题,但我不知道是什么问题。
在示例中,我有一个 Container 类,其中的 Items 包含子 Items。一旦我尝试创建一个包含多个项目级别的容器,它就会失去与容器的关系,因此由于外键约束而失败。
我得到的异常(exception)是:
Microsoft.EntityFrameworkCore.DbUpdateException : An error occurred while updating the entries. See the inner exception for details. ---- System.Data.SqlClient.SqlException : The INSERT statement conflicted with the FOREIGN KEY constraint "FK_Items_Containers_ContainerId". The conflict occurred in database "Test", table "dbo.Containers", column 'Id'. The statement has been terminated.
当我使用 SQL Profiler 查看 EF Core 生成的 SQL 时,它尝试插入 0 作为所有非直接子级容器的 id。
因此,创建容器不是问题,第一级子级也不是问题,但是一旦我添加第二级,它就会失去与容器的关系。
public class Test
{
public class Container
{
[Required]
public int Id { get; set; }
public IEnumerable<Item> Items { get; set; }
}
public class Item
{
[Required]
public int Id { get; set; }
[Required]
public int ContainerId { get; set; }
public virtual Container Container { get; set; }
public int? ParentItemId { get; set; }
public virtual Item ParentItem { get; set; }
public IEnumerable<Item> ChildItems { get; set; }
}
public class TestContext : DbContext
{
public virtual DbSet<Container> Containers { get; set; }
public virtual DbSet<Item> Items { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Container>()
.HasMany(c => c.Items)
.WithOne(c => c.Container)
.HasForeignKey(c => c.ContainerId);
modelBuilder.Entity<Item>()
.HasOne(x => x.Container)
.WithMany(x => x.Items)
.HasForeignKey(x => x.ContainerId);
modelBuilder.Entity<Item>()
.HasOne(x => x.ParentItem)
.WithMany(x => x.ChildItems)
.HasForeignKey(x => x.ParentItemId);
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder.UseSqlServer(
@"Server=(localdb)\mssqllocaldb;Database=Test;Trusted_Connection=True;ConnectRetryCount=0");
}
public void ContextTest()
{
using (var context = new TestContext())
{
context.Database.EnsureDeleted();
context.Database.EnsureCreated();
var container = new Container();
container.Items = new List<Item>
{
new Item
{
ChildItems = new List<Item>
{
new Item()
}
}
};
context.Containers.Add(container);
context.SaveChanges();
}
}
}
最佳答案
当您将 Container
实例添加到上下文时,EF Core 关系修复过程将检查 Items
集合并自动分配 Item.Container
属性(property)(和FK)。然而,较低级别的项目既不包含在 Items
集合中,也不分配 Container
属性,因此 EF 将尝试使用 FK 包含的任何值(因为它不可为空,因此它将使用0
- 请注意,0
是非生成 key 的有效值)。
如果您想知道为什么它不递归地分配顶部Container
,答案是 - 因为模型并不暗示这种行为。从关系的角度来看,ParentItem.Container 和 ChildItem.Container 之间没有关系 - 它们具有不同的值是非常有效的。如果目的是所有子项共享根项容器,则实体模型包含冗余 - Container
属性/FK 必须可为空,并且仅为根项分配(基本上与 互斥)父项
)。
如果您想保持原样,则无法向 EF Core(或一般关系数据库)表达您的意图。因此,您需要通过将较低级别的项目添加到容器 Items
集合中来手动强制执行该约束,或者更简单 - 将容器实例分配给其 Container
属性:
var container = new Container();
container.Items = new List<ContainerItem>
{
new ContainerItem
{
ChildItems = new List<ContainerItem>
{
new ContainerItem
{
Container = container // <-- do the same for all non direct items
}
}
}
};
关于c# - 多维关系失去了与包装器的关系,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52982150/