Обновление Entity Framework со связанной сущностью
-
06-07-2019 - |
Вопрос
Я использую EF, чтобы попытаться обновить сущность с помощью ASP.NET. Я создаю сущность, устанавливаю ее свойства, затем передаю ее обратно в EF на отдельном слое с идентификатором, чтобы можно было применить изменение. Я делаю это потому, что храню идентификатор объекта только тогда, когда он связан с элементами управления пользовательского интерфейса.
Все работает для стандартных свойств, но я не могу обновить Category.ID продукта (связанный объект). Я пробовал EntityKey, EntityReference и некоторые другие, но идентификатор категории не сохраняется. Это то, что у меня есть:
Product product = new Product();
product.CategoryReference.EntityKey = new EntityKey("ShopEntities.Categories", "CategoryID", categoryId);
product.Name = txtName.Text.Trim();
... other properties
StockControlDAL.EditProduct(productId, product);
public static void EditProduct(int productId, Product product) {
using(var context = new ShopEntities()) {
var key = new EntityKey("ShopEntities.Products", "ProductID", productId);
context.Attach(new Product() { ProductID = productId, EntityKey = key });
context.AcceptAllChanges();
product.EntityKey = key;
product.ProductID = productId;
context.ApplyPropertyChanges("ShopEntities.Products", product);
context.SaveChanges();
}
}
Я действительно хочу использовать EF, но у меня, похоже, возникли некоторые проблемы с его использованием в ASP.NET.
Решение
Это общепринятый ответ на этот вопрос MVC с Entity Framework
context.AttachTo(product.GetType().Name, product);
ObjectStateManager stateMgr = context.ObjectStateManager;
ObjectStateEntry stateEntry = stateMgr.GetObjectStateEntry(model);
stateEntry.SetModified();
context.SaveChanges();
Вы пробовали это?
[Обновлено, код сверху не работает]
Это маленькое свойство расширения, которое я использовал, чтобы легче было понять следующий блок кода:
public partial class Product
{
public int? CategoryID
{
set
{
CategoryReference.EntityKey = new EntityKey("ShopEntities.Categories", "CategoryID", value);
}
get
{
if (CategoryReference.EntityKey == null)
return null;
if (CategoryReference.EntityKey.EntityKeyValues.Count() > 0)
return (int)CategoryReference.EntityKey.EntityKeyValues[0].Value;
else
return null;
}
}
}
и это сработало для меня (на этот раз точно):
System.Data.EntityKey key = new System.Data.EntityKey("ShopEntities.Products", "ProductID", productId);
object originalItem;
product.EntityKey = key;
if (context.TryGetObjectByKey(key, out originalItem))
{
if (originalItem is EntityObject &&
((EntityObject)originalItem).EntityState != System.Data.EntityState.Added)
{
Product origProduct = originalItem as Product;
origProduct.CategoryID == product.CategoryID;//set foreign key again to change the relationship status
context.ApplyPropertyChanges(
key.EntitySetName, product);
}
}context.SaveChanges();
Конечно, это выглядит неприлично. Я думаю, что причина в том, что отношения EF имеют статус сущностей (изменен, добавлен, удален), и на основе этого состояния EF изменяет значение внешних ключей или удаляет строку, если имеется отношение многие ко многим. По какой-то причине (не знаю почему) статус отношений не изменяется так же, как статус свойства. Вот почему я должен был установить CategoryReference.EntityKey на originalItem, чтобы изменить статус отношения.
Другие советы
Причина, по которой это не получается, в два раза.
<Ол>Поэтому я хотел бы сделать что-то вроде этого (обратите внимание, что этот код интенсивно использует трюк под названием объекты-заглушки , чтобы избежать мошенничества с EntityKeys)
Product product = new Product();
// Use a stub because it is much easier.
product.Category = new Category {CategoryID = selectedCategoryID};
product.Name = txtName.Text.Trim();
... other properties
StockControlDAL.EditProduct(productId, originalCategoryID);
public static void EditProduct(Product product, int originalCategoryID ) {
using(var context = new ShopEntities())
{
// Attach a stub entity (and stub related entity)
var databaseProduct = new Product {
ProductID = product.ProductID,
Category = new Category {CategoryID = originalCategoryID}
};
context.AttachTo("Products", databaseProduct);
// Okay everything is now in the original state
// NOTE: No need to call AcceptAllChanges() etc, because
// Attach puts things into ObjectContext in the unchanged state
// Copy the scalar properties across from updated product
// into databaseProduct in the ObjectContext
context.ApplyPropertyChanges("ShopEntities.Products", product);
// Need to attach the updated Category and modify the
// databaseProduct.Category but only if the Category has changed.
// Again using a stub.
if (databaseProduct.Category.CategoryID != product.Category.CategoryID)
{
var newlySelectedCategory =
new Category {
CategoryID = product.Category.CategoryID
};
context.AttachTo("Categories", newlySelectedCategory)
databaseProduct.Category = newlySelectedCategory;
}
context.SaveChanges();
}
}
Это сделает работу, при условии отсутствия опечаток и т. д.