#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.
Мое «исправление» состояло в том, чтобы просто разбить это на несколько разных вызовов:
- Я создаю новую запись ProcessedProduct, но присваиваю значению продукта значение null.
- Я запрашиваю эту запись ProcessedProduct с идентификатором, запрашиваю соответствующий продукт с его идентификатором и присваиваю этот продукт вновь полученной записи ProcessedProduct. Однако я должен обнулить свойство Category, иначе это добавит новую повторяющуюся запись категории. Я сохраняю, и запись ProcessedProduct изменяется.
- Наконец, я запрашиваю 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 это называется чем-то вроде графиков в несвязанных сценариях. 🙂