Я все еще изучаю Entity Framework самостоятельно, но, возможно, я могу немного помочь. При работе с Entity Framework вам необходимо знать, как вы обрабатываете различные контексты. Похоже, вы пытаетесь максимально локализовать свой контекст, говоря:
public void Save(Category category)
{
using (var db = new NorthwindContext())
{
...
}
}
... в вашем методе доступа к данным. Вы делали то же самое в вашем GetById
методе? Если да, помните ли вы, чтобы отсоединить полученный объект, чтобы он мог быть присоединен позже в другом контексте?
public Category GetById(int categoryId)
{
using (var db = new NorthwindContext())
{
Category category = (from c in db.Category where Category.ID == categoryId select c).First();
db.Detach(category);
}
}
Таким образом, когда вы звоните Attach
, он не пытается наступить на уже подключенный контекст. Это помогает?
Как вы указали в своем комментарии, это создает проблему, когда вы пытаетесь изменить элемент, а затем сказать своему слою базы данных сохранить его, поскольку после отсоединения элемента от его контекста он больше не отслеживает изменения, которые были сделаны к нему. Есть несколько способов обойти эту проблему, но ни один из них не совершенен.
- Если ваша архитектура поддерживает это, вы можете расширить область своего контекста настолько, чтобы ваш метод Save мог использовать тот же контекст, что и ваш метод GetById. Это помогает полностью избежать проблемы присоединения / отсоединения, но может подтолкнуть уровень данных немного ближе к вашей бизнес-логике, чем вы хотели бы.
- Вы можете загрузить новый экземпляр элемента из нового контекста на основе его идентификатора, установить все его свойства на основе переданной категории и затем сохранить его. Это потребует двух обращений к базе данных для того, что действительно нужно только одному, и это не очень поддерживаемо.
- Вы можете копаться в самом контексте, чтобы пометить свойства категории как измененные.
Например:
public void Save(Category category)
{
using (var db = new NorthwindContext())
{
db.Attach(category);
var stateEntry = db.ObjectStateManager.GetObjectStateEntry(category);
foreach (var propertyName in stateEntry.CurrentValues.DataRecordInfo.FieldMetadata.Select(fm => fm.FieldType.Name)) {
stateEntry.SetModifiedProperty(propertyName);
}
db.SaveChanges();
}
}
Это выглядит немного уродливее, но в целом должно быть более производительным и обслуживаемым. Кроме того, если вы хотите, вы можете сделать его достаточно универсальным, чтобы добавить его в метод расширения, чтобы вам не приходилось видеть или повторять уродливый код, но вы по-прежнему извлекали из него функциональность.