【发布时间】:2014-07-20 13:47:50
【问题描述】:
我是使用 Entity Framework 6 的新手,但在尝试更新实体时遇到了困难。我正在使用 Code First 方法,我的模型简化为有问题的类,如下所示:
public class Document
{
public long Key { get; set; }
public string Name { get; set; }
}
public class Batch
{
public long Key { get; set; }
public virtual List<Document> Documents { get; private set; }
public void Add(Document document)
{
Documents.Add(document);
}
public void Remove(Document document)
{
Documents.RemoveAt(Documents.FindIndex(d => d.Key == document.Key));
}
}
批处理和文档之间存在一对多关系,通过独立关联建模,因此没有明确的 FK。 一个重要的特殊性是 Document 类不知道 Batch。 这与 Stackoverflow 上的其他类似问题不同,例如 this one。实体框架在 Document 表中生成一个可以为空的列 Batch_Key。
这是上下文类:
public class MyDbContext : DbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Document>().HasKey(d => d.Key);
modelBuilder.Entity<Batch>().HasKey(b => b.Key);
modelBuilder.Entity<Batch>().HasMany(b => b.Documents).WithOptional().WillCascadeOnDelete(false);
base.OnModelCreating(modelBuilder);
}
}
我有一个带有 Update() 方法的 BatchRepository 类,该方法接收 Batch 参数并更新数据库中的批次:
public class BatchRepository
{
public void Update(Batch item)
{
using (var dbCtx = new MyDbContext(DBContextName))
{
batchesContext.Batches.Attach(item);
batchesContext.Entry(item).State = EntityState.Modified;
batchesContext.SaveChanges();
}
}
}
这是一个用于从批处理中删除文档的单元测试,它使用 Update() 方法:
[TestMethod]
public void CheckRemoveDocument()
{
var batches = BatchRepository.FindAll().ToList();
var batch = batches[0];
var batchKey = batches[0].Key;
var doc = batch.Documents[0];
int batchNumberOfDocuments = batch.Documents.Count;
batch.Remove(doc);
BatchRepository.Update(batch);
batch = BatchRepository.FindBy(batchKey);
Assert.AreEqual(batchNumberOfDocuments - 1, batch.Documents.Count);
}
测试失败,batchNumberOfDocuments 和之前一样。如果我实现一个方法来从批次中删除所有文档,如下所示:
public class BatchRepository
{
public void RemoveDocuments(Batch item)
{
using (var dbCtx = new MyDbContext(DBContextName))
{
var existingBatch = batchesContext.Batches.Find(item.Key);
batchesContext.Entry(existingBatch).Collection(b => b.Documents).Load();
existingBatch.Documents.RemoveAll(d => true);
batchesContext.SaveChanges();
}
}
}
以下测试成功:
[TestMethod]
public void CheckRemoveAllDocuments()
{
var batches = BatchRepository.FindAll().ToList();
var batch = batches[0];
var batchKey = batches[0].Key;
BatchRepository.RemoveDocuments(batch);
batch = BatchRepository.FindBy(batchKey);
Assert.AreEqual(0, batch.Documents.Count);
}
所以 EF 正确地跟踪批次和文档之间的关系,将 Batch_Key 列设置为 NULL 以删除文档。为什么这在这种情况下有效,但在我要更新的情况下无效?我认为这是因为链接到批次的文档实体未附加到上下文。问题是我不知道来自 Update() 方法参数的批处理是否有更多文档、更少文档或完全不同的文档列表(另外,可能是文档列表以外的其他内容更改)。
这样的实现:
public class BatchRepository
{
public void Update(Batch item)
{
using (var dbCtx = new MyDbContext(DBContextName))
{
var existingBatch = batchesContext.Batches.Find(item.Key);
batchesContext.Entry(existingBatch).Collection(b => b.Documents).Load();
foreach (var doc in item.Documents)
{
batchesContext.Documents.Attach(doc);
batchesContext.Entry(doc).State = EntityState.Modified;
}
batchesContext.SaveChanges();
}
}
}
抛出异常:
System.InvalidOperationException:附加类型的实体 “文档”失败,因为另一个相同类型的实体已经拥有 相同的主键值。使用“附加”时可能会发生这种情况 方法或将实体的状态设置为“未更改”或“已修改” 如果图中的任何实体具有冲突的键值。这可能是 因为有些实体是新的,还没有收到 数据库生成的键值。在这种情况下,使用“添加”方法或 'Added' 实体状态来跟踪图形,然后设置状态 将非新实体酌情改为“未更改”或“已修改”。
应该如何实现 Update() 方法才能正确执行更新?
谢谢你,如果这个问题听起来令人困惑,我很抱歉,这完全是由于我对 EF 的困惑。
【问题讨论】:
标签: c# entity-framework entity-framework-6