实体框架和添加POCO而不添加子对象

时间:2014-06-18 19:17:30

标签: c# entity-framework

所以也许我正在以错误的方式解决这个问题,但我想从StackOverflow上的好人那里得到关于如何更正确地做到这一点的意见。

我有一个程序必须从Entity Framework 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将被“添加”而其他的将被“修改”或“未更改”,但我会获得Entity Framework抛出的外键引用异常。

我的“修复”只是将其分解为许多不同的调用:

  1. 我创建了新的ProcessedProduct记录,但我将Product值指定为null。
  2. 我使用Id查询该ProcessedProduct记录,查询具有其ID的相应产品,并将该产品分配给新检索的ProcessedProduct记录。但是,我必须将Category属性清空,否则这将添加一个新的重复类别记录。我保存并修改了ProcessedProduct记录。
  3. 最后,我再次查询ProcessedProduct以及ProductCategory,然后将该ProductCategory分配给ProcessedProduct.BaseProduct的Category属性。我可以再次保存,现在我已经创建了我需要的所有记录,而没有制作任何副本。
  4. 然而,这种方法似乎很复杂,因为我最初想做的就是保存新的父记录,而不是创建重复的子记录。有没有更好的方法来做这件事,我错过了?谢谢!

    编辑:我想更大的问题是说我有一大堆这些子复杂对象的复杂对象。创建新父级的最简单方法是什么,而不必遍历子对象的整个图形,一次用一层更新父级?

1 个答案:

答案 0 :(得分:6)

我强烈建议不要设置产品&amp;编辑时作为导航属性的类别。正如您所看到的那样,当您将已处理产品的图表(附带产品和类别)添加到EF上下文时,它会将图表中的所有内容标记为已添加并插入所有内容。 我总是推荐的模式(尼古拉也在他的评论中提出,所以像我一样投票他的评论:))是在你的实体中包含FK ID并设置这些值,而不是导航。例如 newRecord.ProductId = theProductIdValue。

我有很多人在哭“但是外键?万能!它们会让我的课堂变得如此肮脏和不纯洁!”但是在他们看到在没有与这些场景中的导航混合的情况下对事物进行编码变得更容易之后,他们又回来说“好吧,这是值得的!”

顺便说一句,如果你在企业课程中谈论我的EF,我有一个关于处理这个问题的整个模块......它被称为在断开连接的场景中的图形。 :)