У меня есть приложение Silverlight RIA, в котором я делюсь моделями и доступом к данным между веб-приложением MVC и приложением Silverlight, используя директивы компилятора, а для сервера, чтобы увидеть, в каком контексте я работаю, я бы проверил, был ли объект ChangeSet ненулевой (это означает, что я работал под RIA, а не под MvC). Все работает нормально, но у меня были проблемы с кодом по умолчанию, сгенерированным методами службы домена.
Допустим, у меня была сущность Person, которая принадлежала к определенным группам (групповая сущность). Объект Person имеет набор групп, которые я добавляю или удаляю. После внесения изменений приложение SL вызовет сервер, чтобы сохранить изменения. Что я заметил, так это то, что записи групповых объектов будут вставлены первыми. Это нормально, так как я изменяю существующего человека. Однако, поскольку каждый объект группы также имеет ссылку на существующего человека, вызов AddObject пометит весь граф, включая человека, которого я пытаюсь изменить, как добавленный. Затем, когда вызывается оператор Update, сгенерированный по умолчанию код попытается присоединить человека, который теперь имеет состояние «Добавлено», к контексту с не очень веселыми результатами.
Когда я делаю первоначальный вызов сущности или набора сущностей в запросе, все EntityKeys для сущностей заполняются. На клиенте EntityKey заполняется для каждого объекта. Когда объект возвращается от клиента для обновления на сервере, EntityKey имеет значение null. Я создал новый проект службы RIA и убедился, что это так. Я использую RIA Services SP1 и не использую композицию. Я как бы понимаю проблему EntityKey - отслеживание изменений выполняется в двух разных контекстах. EF не знает об отслеживании изменений на стороне SL. Тем не менее, он возвращает граф объектов, включая связанные объекты, поэтому использование AddObject является проблемой, если я сначала не проверю базу данных на наличие объекта с тем же ключом.
У меня есть код, который работает. Я не знаю, насколько ХОРОШО это работает, но сегодня я провожу дополнительные испытания, чтобы посмотреть, что происходит. Вот:
/// <summary>
/// Updates an existing object.
/// </summary>
/// <typeparam name="TBusinessObject"></typeparam>
/// <param name="obj"></param>
protected void Update<TBusinessObject>(TBusinessObject obj) where TBusinessObject : EntityObject
{
if (this.ChangeSet != null)
{
ObjectStateManager objectStateManager = ObjectContext.ObjectStateManager;
ObjectSet<TBusinessObject> entitySet = GetEntitySet<TBusinessObject>();
string setName = entitySet.EntitySet.Name;
EntityKey key = ObjectContext.CreateEntityKey(setName, obj);
object dbEntity;
if (ObjectContext.TryGetObjectByKey(key, out dbEntity) && obj.EntityState == System.Data.EntityState.Detached)
{
// An object with the same key exists in the DB, and the entity passed
// is marked as detached.
// Solution: Mark the object as modified, and any child objects need to
// be marked as Unchanged as long as there is no Domainoperation.
ObjectContext.ApplyCurrentValues(setName, obj);
}
else if (dbEntity != null)
{
// In this case, tryGetObjectByKey said it failed, but the resulting object is
// filled in, leading me to believe that it did in fact work.
entitySet.Detach(obj); // Detach the entity
try
{
ObjectContext.ApplyCurrentValues(setName, obj); // Apply the changes to the entity in DB
}
catch (Exception)
{
entitySet.Attach(obj); // Re-attach the entity
ObjectContext.ApplyCurrentValues(setName, obj); // Apply the changes to the entity in DB'
}
}
else
{
// Add it..? Update must have been called mistakenly.
entitySet.AddObject(obj);
}
}
else
DirectInsertUpdate<TBusinessObject>(obj);
}
Краткое пошаговое руководство: если ChangeSet имеет значение null, я не нахожусь в контексте RIA и, следовательно, могу вызвать другой метод для обработки вставки/обновления и немедленного сохранения. Насколько я могу судить, это работает нормально. Для RIA я генерирую ключ и смотрю, существует ли он в базе данных. Если это так, и объект, с которым я работаю, отсоединен, я применяю эти значения; в противном случае я принудительно отсоединяю и применяю значения, которые работают с добавленным состоянием из любых предыдущих вызовов Insert.
Есть ли лучший способ сделать это? Мне кажется, что я делаю здесь слишком много работы.