多维关系失去了与包装器的关系 [英] Multi dimensional relationship looses the relation with wrapper

查看:75
本文介绍了多维关系失去了与包装器的关系的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我认为下面的示例中的模型有问题,但是我不知道是什么。

I think it's something wrong with the model in the example below, but I can't figure out what.

在示例中,我有一个包含Items的Container类包含子项。一旦我尝试创建一个包含一层以上Item的容器,它就会失去与容器的关系,因此由于外键约束而失败。

In the example I have a Container class with Items containing sub Items. As soon as I try to create a container with more than one level of Items it looses the relationship with the container and therefore fails due to foreign key constraints.

get is:


Microsoft.EntityFrameworkCore.DbUpdateException:更新条目时发生错误。有关
的详细信息,请参见内部异常。
---- System.Data.SqlClient.SqlException:INSERT语句与FOREIGN KEY约束
FK_Items_Containers_ContainerId相冲突。该冲突发生在数据库
测试,表 dbo.Containers的 Id列中。
语句已终止。

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.

当我查看EF Core使用SQL Profiler生成的SQL时,它将尝试为所有非直属子代插入0作为容器的ID。

When I look at the SQL that EF Core generates, using SQL Profiler, it tries to insert 0 as id for the Container for all non direct children.

因此,创建容器不是问题,第一级子代也不是问题,但尽快我添加了第二个级别,它松开了与容器的关系。

So, creating the Container is not a problem, nor is the first level children, but as soon as I add a second level it looses the relationship with the Container.

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 属性(和FK)。但是,较低级别的项目既不包含在 Items 集合中,也没有分配 Container 属性,因此EF将尝试使用任何内容FK包含的值(由于它不可为空,因此将使用 0 -请注意, 0 是未生成的有效值

When you add Container instance to the context, the EF Core relationship fixup process will examine the Items collection and will automatically assign Item.Container property (and the FK). However the lower level items and neither contained in Items collection nor have Container property assigned, so EF will try to use whatever value FK contains (since it's non nullable, it will use the 0 - note that 0 is valid value for non generated key).

如果您想知道为什么它没有递归分配顶部 Container ,答案是-因为模型并不暗示这种行为。从关系的角度来看, ParentItem.Container ChildItem.Container 之间没有关系-它们具有不同的值是很有效的。如果意图是所有子项共享根项容器,则实体模型包含冗余-容器属性/ FK必须为空,并且仅分配给根项(基本上与 ParentItem 互斥。

If you are wondering why it isn't assigning the top Container recursively, the answer is - because the model does not imply such behavior. From relational standpoint there is no relation between ParentItem.Container and ChildItem.Container - it's pretty valid they to have different values. If the intention is that all child items share the root item container, then the entity model contains redundancy - Container property/FK must be nullable and assigned only for the root items (basically mutually exclusive with ParentItem).

如果要保持现状,就没有办法向EF Core(或一般的关系数据库)表达您的意图。因此,您需要手动执行该约束,方法是将较低级别的项目添加到容器 Items 集合,或者将容器实例分配到其 Container 属性:

If you want to keep it the way it is, there is no way to express your intent to EF Core (or relational database in general). So you need to enforce that constraint manually by either adding lower level items to the container Items collection, or the easier - assign container instance to their Container property:

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

这篇关于多维关系失去了与包装器的关系的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆