实体框架 - 分配给列表行为

本文关键字:列表 分配 框架 实体 | 更新日期: 2023-09-27 18:37:19

最近我在EntityFramework 6中发现了奇怪的行为,给出了以下程序谁能向我解释为什么

parent.Children = new List<ChildObject> { new ChildObject("newChild", "newValue") };

实际上新子项添加到子项列表中,而不是删除旧子项并添加新子项。

使用 db.Parents.Include(x => x.Children).First() 加载父级可以解决问题,这使得这种行为更加奇怪......

这是有意识的设计选择还是实际上是一个错误?

完整的测试程序:

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace EF_PlayWithList
{
    public class ChildObject
    {
        public int Id { get; protected set; }
        public int ParentId { get; protected set; }
        public string Name { get; protected set; }
        public string Value { get; protected set; }
        protected ChildObject() { }
        public ChildObject(string name, string value)
        {
            this.Name = name ?? "";
            this.Value = value ?? "";
        }
        public override string ToString()
        {
            return string.Format("{0}: {1}", this.Name, this.Value);
        }
    }
    public class ParentObject
    {
        public int Id { get; protected set; }
        public string Name { get; protected set; }
        public virtual IList<ChildObject> Children { get; set; }
        protected ParentObject() { }
        public ParentObject(string name)
        {
            this.Name = name;
            this.Children = new List<ChildObject>();
        }
        public void AddChild(ChildObject child)
        {
            this.Children.Add(child);
        }
        public override string ToString()
        {
            return string.Format("Parent '{0}' with {1} childs.", this.Name, this.Children.Count);
        }
    }
    class TestDbContext : DbContext
    {
        public DbSet<ParentObject> Parents { get; set; }
        public TestDbContext()
            : base()
        {
        }
        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Entity<ParentObject>()
                .HasMany(x => x.Children)
                .WithRequired()
                .HasForeignKey(x => x.ParentId)
                .WillCascadeOnDelete();
            modelBuilder.Entity<ParentObject>()
                .HasKey(x => x.Id);
            modelBuilder.Entity<ParentObject>()
                .Property(x => x.Id)
                .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
            modelBuilder.Entity<ChildObject>()
                .HasKey(x => new { x.Id, x.ParentId });
            modelBuilder.Entity<ChildObject>()
                .Property(x => x.Id)
                .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
             Database.SetInitializer(new DropCreateDatabaseAlways<TestDbContext>());
                using (var db = new TestDbContext())
                {
                    var parent = new ParentObject("superFooParent");
                    parent.AddChild(new ChildObject("foo", "1"));
                    parent.AddChild(new ChildObject("bar", "2"));
                    db.Parents.Add(parent);
                    db.SaveChanges();
                }
                using (var db = new TestDbContext())
                {
                    var parent = db.Parents.First();
                    parent.Children = new List<ChildObject>
                    {
                        new ChildObject("newChild", "newValue")
                    };
                    db.SaveChanges();
                }
                using (var db = new TestDbContext())
                {
                    foreach (var parent in db.Parents.Include(x => x.Children))
                    {
                        Console.WriteLine(parent);
                        foreach (var child in parent.Children)
                        {
                            Console.WriteLine("'t{0}", child);
                        }
                    }
                }
            Console.ReadLine();
        }
    }
}

实体框架 - 分配给列表行为

当您在 DbContext 上调用 SaveChanges 时,框架会检查它知道的所有实体,以查看修改、添加、删除的内容等。 如果从未加载旧的子项列表,则 DbContext 不知道它们,并且不会删除它们。 这是预期的行为。 如果希望框架为您进行数据库更改,则必须允许 DbContext 加载受影响的实体;否则,您将需要使用纯 SQL。