Entity Framework и добавление POCO без добавления дочерних объектов

#c# #entity-framework

#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 со следующим определением и передаю ранее полученный продукт в качестве базового продукта:

 public class ProcessedProduct
{
  public int Id { get;set; }
  public virtual Product BaseProduct { get;set; }
}
  

Я использую слой репозитория, который я видел на уроке EF по Pluralsight и который я намеревался использовать здесь. Я добавил все соответствующие фрагменты ниже:

 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, но также создает новый продукт и новую строку категории. Я попытался вручную изменить отслеживание изменений для каждого объекта, чтобы ProcessedProduct был «добавлен», а остальные были бы либо «изменены», либо «без изменений», но я бы получил исключения ссылки на внешний ключ, создаваемые Entity Framework.

Мое «исправление» состояло в том, чтобы просто разбить это на несколько разных вызовов:

  1. Я создаю новую запись ProcessedProduct, но присваиваю значению продукта значение null.
  2. Я запрашиваю эту запись ProcessedProduct с идентификатором, запрашиваю соответствующий продукт с его идентификатором и присваиваю этот продукт вновь полученной записи ProcessedProduct. Однако я должен обнулить свойство Category, иначе это добавит новую повторяющуюся запись категории. Я сохраняю, и запись ProcessedProduct изменяется.
  3. Наконец, я запрашиваю ProcessedProduct еще раз, а также ProductCategory, а затем присваиваю эту ProductCategory свойству категории ProcessedProduct .Базовый продукт. Я могу сохранить еще раз, и теперь я создал все нужные мне записи, не создавая никаких дубликатов.

Однако этот подход кажется довольно запутанным, поскольку все, что я изначально хотел сделать, это сохранить новую родительскую запись и просто не создавать повторяющиеся дочерние записи. Есть ли лучший способ сделать это, которого мне не хватает? Спасибо!

Редактировать: И я предполагаю, что более важный вопрос заключается в том, что у меня есть сложный объект с целой кучей этих дочерних сложных объектов. Какой самый простой способ создать нового родительского объекта без необходимости проходить через весь граф дочерних объектов, чтобы обновлять родительский объект с ними по одному слою за раз?

Комментарии:

1. вы пропустили BaseProductId (или что-то еще) в вашем ProcessedProduct? или он у вас есть, но вы его не показывали?

2. Нет, если я посмотрю на отладку, все идентификаторы будут там, как и ожидалось (именно там я фактически получаю ProductID для будущих поисков). Если я посмотрю на первоначально созданный ProcessedProduct, я могу получить идентификатор для ProcessedProduct. Продукт. Идентификатор, а также ProcessedProduct . Продукт. Category.Id .

3. Просто обычно я создаю явно add id зависимых объектов. В этом случае вы можете попробовать просто создать новый ProcessedProduct, установив этот идентификатор.

4. Можете ли вы показать нам код, как вы вставляете ProcessedProduct?

5. @J.W. Код добавлен выше. Дайте мне знать, если вам нужно что-то еще, хотя я думаю, что это примерно покрывает это.

Ответ №1:

Я настоятельно рекомендую не устанавливать Product amp; Category в качестве свойств навигации при редактировании. Как вы видели, когда вы добавляете график обработанного продукта (с привязкой продукта и категории) к контексту EF, он помечает все на графике как добавленное и выполняет вставки для всего. Шаблон, который я всегда рекомендую (и Николай также предложил в своем комментарии, так что проголосуйте за его комментарий, как и я :)), заключается в том, чтобы включить идентификаторы FK в вашу сущность и установить эти значения, а не навигации. например, newRecord .ProductID=значение ProductID.

Многие люди плакали: «но внешние ключи? ewwww! Они сделают мои классы такими грязными и нечистыми! » но после того, как они увидели, насколько проще кодировать вещи, не запутываясь в навигации в этих сценариях, они вернулись, чтобы сказать: «Хорошо, это того стоило!»

Кстати, если вы говорите о моем курсе EF в Enterprise, у меня есть целый модуль, посвященный решению этой проблемы problem…it это называется чем-то вроде графиков в несвязанных сценариях. 🙂