实体框架和添加poco而不添加子对象
本文关键字:添加 对象 poco 框架 实体 | 更新日期: 2023-09-27 18:09:27
所以也许我解决这个问题的方式是错误的,但我想从你的意见从你优秀的人在StackOverflow关于如何更正确地做到这一点。
我有一个程序,它必须从实体框架6.0代码优先上下文的存储库中检索信息,对所包含的信息做一些工作,然后向数据库添加一个新记录。
无论如何,下面是我通过存储库从EF检索的类的简化外观:
public class Product
{
public int Id { get;set; }
public virtual ProductCategory Category { get;set; }
public string Name { get;set; }
}
然后,我用以下定义构建一个ProcessedProduct,并将之前检索到的Product作为BaseProduct传入:
public class ProcessedProduct
{
public int Id { get;set; }
public virtual Product BaseProduct { get;set; }
}
我使用了我在Pluralsight的EF课程中看到的存储库层,并在这里使用。我在下面添加了所有相关的部分:
public class MyContext : BaseContext<MyContext>, IMyContext
{
//Lots of IDbSets for each context
public void SetModified(object entity)
{
Entry(entity).State = EntityState.Modified;
}
public void SetAdd(object entity)
{
Entry(entity).State = EntityState.Added;
}
}
public class MyRepository : IMyRepository
{
private readonly IMyContext _context;
public MyRepository(IUnitOfWork uow)
{
_context = uow.Context as IMyContext;
}
public ProcessedProduct FindProcessedProduct(int id)
{
return _context.ProcessedProducts.Find(id);
}
public ProductCategory FindCategory(int id)
{
return _context.Categories.Find(id);
}
public int AddProcessedProductWithoutProduct(ProcessedProduct newRecord)
{
newRecord.Product = null;
Save();
return newRecord.Id;
}
public int UpdateProcessedProductWithProductButWithoutChildProperties(int processedProductId, int productId)
{
var processedProduct = FindProcessedProduct(processedProductId);
processedProduct.BaseProduct = FindProduct(productId);
processedProduct.BaseProduct.Category = null;
_context.SetModified(product);
Save();
return processedProduct.Id;
}
public int UpdateProductChildren(int processedProductId, int categoryId)
{
var processedProduct = FindProcessedProduct(processedProductId);
var category = FindCategory(categoryId);
processedProduct.BaseProduct.Category = category;
_context.SetModified(product);
Save();
return processedProduct.Id;
}
}
最后,这是把它们连在一起的部分:
try
{
//Create the processed product without the product instance
var processedProductId = repo.AddProcessedProductWithoutProduct(finishedProduct);
//Now, update this processed product record with the product. This way, we don't create a
//duplicate product.
processedProductId = repo.UpdateProcessedProductWithProductButWithoutChildProperties(processedProductId, product.Id);
//Finally, update the category
processedProductId = repo.UpdateProductChildren(processedProductId, product.Category.Id);
//Done!
}
当我尝试将这个ProcessedProduct插入到EF中时,它正确地创建了ProcessedProduct记录,但是它也创建了一个新的Product和新的Category行。我曾尝试手动更改每个对象的更改跟踪,因此ProcessedProduct将被"添加",而其他对象将被"修改"或"不变",但我会得到实体框架抛出的外键引用异常。
我的"解决方案"是简单地将其分解为许多不同的调用:
- 我创建了新的ProcessedProduct记录,但是我将Product值赋值为null。
- 我用Id查询该ProcessedProduct记录,用其Id查询相应的Product,并将该Product分配给新检索的ProcessedProduct记录。但是,我必须取消类别属性,否则这将添加一个新的重复类别记录。我保存并修改了ProcessedProduct记录。
- 最后,我再次查询ProcessedProduct以及ProductCategory,然后将ProductCategory分配给ProcessedProduct. baseproduct的Category属性。我可以再保存一次,现在我已经创建了所有我需要的记录,没有任何重复。
然而,这种方法似乎相当复杂,因为我最初想做的只是保存新的父记录,而不是简单地创建重复的子记录。有没有更好的方法来做这件事,我错过了?谢谢!
编辑:我想更大的问题是,假设我有一个复杂的对象,有一大堆这些子复杂对象。创建一个新的父对象,而不需要遍历整个子对象图来一次一层地更新父对象,最简单的方法是什么?
我强烈建议不要设置Product &编辑时将其分类为导航属性。正如你所看到的,当你添加加工产品的图表(与产品&类别附加)到EF上下文,它将图中的所有内容标记为添加并在所有内容上插入。我一直推荐的模式(Nikolai也在他的评论中建议,所以像我一样给他的评论投票:))是在您的实体中包含FK id并设置这些值,而不是导航。如。newRecord.ProductId = theProductIdValue。
我有很多人哭过"但是外键?ewwww !它们会让我的类变得如此肮脏和不纯!"但是当他们看到在这些场景中不与导航纠缠的情况下编写代码是多么容易之后,他们会回来说:"好吧,这是值得的!"
顺便说一句,如果你说的是我在企业课程中的EF,我有一个完整的模块来处理这个问题…这是关于非连通情况下的图。:)