Хобрук: Ваш путь к мастерству в программировании

Приложение SL RIA — вставка и обновление с использованием стандартного сгенерированного кода не работает — есть ли лучший способ?

У меня есть приложение 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.

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


Ответы:


1

В таком случае, когда вы добавляете объекты группы в Person.Groups, я бы подумал о том, чтобы просто сохранить человека и ожидать, что RIA будет обрабатывать группы для меня.

Но давайте сделаем шаг назад, как вы пытаетесь сохранить свои изменения? Вы не должны сохранять/обновлять объекты по одному. Все, что вам нужно сделать, это вызвать DomainContext.SubmitChanges, и все ваши изменения должны быть сохранены.

Я работаю с довольно сложными проектами, и мне редко приходится прикасаться к добавлению/обновлению кода.

02.02.2012
  • Однако это не всегда так. Как я уже упоминал, если граф объектов, возвращаемый для добавления, содержит ссылку на существующий объект, и я добавляю этот объект, измененный объект будет помечен как добавленный. Я не могу создать композицию из этих элементов, потому что они используются в разных объектах, и, насколько мне известно, если вы пометите объект как композицию для другого объекта, вы не сможете пометить его как композицию для другого объекта... но если Я не прав в этом предположении, пожалуйста, дайте мне знать. 02.02.2012

  • 2

    На этот вопрос не было четкого ответа, поэтому я расскажу вам, что я сделал... что ничего. Вот как я справился с этим в службах RIA, используя приведенный выше код, поскольку я разделял модель клиента RIA и модель сервера.

    Поработав со службами RIA в течение полутора лет, я нахожусь в лагере, который считает, что службы RIA хороши для работы с небольшими и менее сложными приложениями. Если вы можете использовать [Composite] для своих сущностей, чего я не мог для многих своих сущностей, то все в порядке.

    Службы RIA могут сделать объединение небольших приложений, в которых вы хотите использовать объект из EF, очень быстрым, но если вы хотите использовать POCO или предвидите, что ваше приложение станет сложным в будущем, я бы придерживался построения POCO на стороне службы и передачи те, которые через обычный WCF, и используя общие поведения, создавая частичные классы POCO и делясь кодом поведения с клиентом. Когда вы пытаетесь создать модели, которые работают одинаково на клиенте и сервере, мне пришлось написать невероятное количество кода, чтобы заставить его работать.

    Это определенно возможно сделать, я сделал это; но есть много обручей, через которые вы должны пройти, чтобы все работало хорошо, и я никогда полностью не принимал во внимание такие вещи, как списки предварительной загрузки вашей общей модели для использования на клиенте, тогда как сервер не нуждался в этих предварительно загруженных каждый раз и фактически без необходимости замедлял загрузку веб-страницы и противостоял написанию хакерских вызовов методов, которые мне пришлось принять на клиенте. (Извините за наплыв.) Техника, которую я выбрал, определенно имела свои недостатки.

    25.05.2012
    Новые материалы

    Основы принципов S.O.L.I.D, Javascript, Git и NoSQL
    каковы принципы S.O.L.I.D? Принципы SOLID призваны помочь разработчикам создавать надежные, удобные в сопровождении приложения. мы видим пять ключевых принципов. Принципы SOLID были разработаны..

    Как настроить Selenium в проекте Angular
    Угловой | Селен Как настроить Selenium в проекте Angular Держите свое приложение Angular и тесты Selenium в одной рабочей области и запускайте их с помощью Mocha. В этой статье мы..

    Аргументы прогрессивного улучшения почти всегда упускают суть
    В наши дни в кругах веб-разработчиков много болтают о Progressive Enhancement — PE, но на самом деле почти все аргументы с обеих сторон упускают самую фундаментальную причину, по которой PE..

    Введение в Джанго Фреймворк
    Схема «работать умно, а не усердно» В этой и последующих статьях я познакомлю вас с тем, что такое фреймворк Django и как создать свое первое приложение с помощью простых и понятных шагов, а..

    Настольный ПК как «одно кольцо, чтобы править всеми» домашних компьютеров
    Вид после 9 месяцев использования С настольных компьютеров все началось, но в какой-то момент они стали «серверами», и мы все перешли на ноутбуки. В прошлом году я столкнулся с идеей настольных..

    Расширенные методы безопасности для VueJS: реализация аутентификации без пароля
    Руководство, которое поможет вам создавать безопасные приложения в долгосрочной перспективе Безопасность приложений часто упускается из виду в процессе разработки, потому что основная..

    стройный-i18следующий
    Представляем стройную оболочку для i18next. Эта библиотека, основанная на i18next, заключает экземпляр i18next в хранилище svelte и отслеживает события i18next, такие как languageChanged,..