Entity framework - 分配给列表行为
Entity framework - assignment to list behaviour
最近我在 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.
最近我在 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.