Как сохранить сгенерированное сервером сложное дерево объектов с сущностями, которых еще нет в базе данных?

В моем спа-центре мне нужно отправить сложное дерево объектов клиенту и позволить пользователю заполнить свои данные, прежде чем объекты можно будет сохранить в базе данных. Таким образом, объекты не существуют в базе данных в этот момент. Дерево объектов строится в памяти на сервере и просто извлекается вызовом breeze.EntityQuery.from("ComplexeObjectTree"). Сущности привязываются через нокаут к веб-форме. Затем пользователь сначала вводит необходимые данные, прежде чем он сможет нажать кнопку сохранения. Некоторые свойства сущностей в форме являются необязательными и имеют значения по умолчанию. Если пользователь не изменит эти свойства, beeze не обнаружит никаких изменений и не поместит принадлежащие ему сущности в набор изменений. Они помечены как неизмененные, но их значения id по-прежнему -1 (еще не сохранены). Но сохранение базы данных может быть успешно выполнено только тогда, когда все объекты в дереве объектов, включая их отношения, были отправлены на сервер (для соблюдения ограничений базы данных). Операция сохранения должна привести к одному или нескольким операторам вставки, а также для тех сущностей, которые не изменились на клиенте, но являются новыми для базы данных (id==-1). Как я могу справиться с этим вариантом использования? Разве все объекты с первичным ключом id==-1 не должны быть помечены как добавленные и быть частью набора сохраненных изменений по умолчанию?

Любая помощь очень ценится,

Андреас


person AndyK    schedule 25.01.2013    source источник


Ответы (3)


Давайте поступим по-вашему и примем ваше утверждение, что граф объектов должен строиться на сервере. Если я правильно понимаю, вы предлагаете пользователю возможность настроить некоторые значения, которые вы генерируете на сервере, прежде чем зафиксировать их в постоянном хранилище. Звучит как хороший план.

"ComplexObjectTree" НЕ обязательно должен быть графом сущностей. Это может быть произвольный объект данных. Это все еще может быть тип CLR, но он не будет (или не будет содержать) объекты, описанные в метаданных, и они не будут известны EF или Db. Этот граф объектов существует просто как информация, которая управляет построением реального графа сущностей на клиенте. В нем есть все трудно поддающиеся расчету значения, которые вы не хотите рассчитывать на клиенте ... например, ответ на вопрос о смысле жизни и о том, кто выиграет Суперкубок в этом году.

Ваш запрос возвращает этот объект с данными, но без сущностей. Обратный вызов querySuccess, CreateComplexTree(data), создает граф объекта на клиенте из этих данных.

Граф сущностей будет состоять из сущностей, понятных клиенту Breeze благодаря метаданным. Если у вас нет возражений, эти сущности также распознаются на сервере и готовы к сохранению и запросу, как и любые другие сущности (вы можете обойти это предположение, если оно неверно... но давайте работать над одной проблемой за раз).

Итак, все готово. Вы ходите по графу данных, создавая объекты по ходу дела. Когда вы видите (-1) в данных, вы знаете, что нужно создать новый экземпляр типа. Скопируйте данные в объект инициализации объекта, который является вторым аргументом в manager.createEntity('Foo', {...}). Здесь нет бизнес-логики — нет расчетов — только мартышка копирования.

Я не могу сказать, должен ли быть вставлен КАЖДЫЙ объект в графе или только некоторые из них. Возможно, некоторые из них являются уже существующими сущностями, которые не изменяются, если пользователь их тоже не изменит. Ничего страшного. Вы можете использовать тот же подход для создания «неизменных» объектов на клиенте с помощью строки типа manager.createEntity('Bar', {...}, breeze.EntityState.Unchanged).

Теперь у вас есть правильный граф объектов Breeze, готовый к привязке, навигации и сохранению. Если пользователю нравится именно такой, каким его создал сервер, он нажимает кнопку [Сохранить], Breeze находит все изменения в графе сущностей, и они отправляются на сервер.

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

Надеюсь это поможет.

person Ward    schedule 25.01.2013
comment
Я полностью понимаю ваш подход. Но я думаю, что может быть более простой способ: предположим, что все объекты на клиенте сопоставляются с объектами EF на сервере. И предположим далее, что все эти сущности уже существуют в базе данных. В этом случае я могу безопасно создать свой граф объектов на сервере из этих сущностей EF и получить его непосредственно клиенту в виде сущностей бриза и привязать его непосредственно к форме без какой-либо воссоздающей логики, как вы предложили. Любые изменения будут обработаны ветерком! Что-то я не понимаю, почему это невозможно с вновь созданными объектами на сервере?... - person AndyK; 26.01.2013
comment
... Я думаю, что проблема здесь в том, что ветер предполагает, что все сущности, поступающие с сервера, существуют и не изменяются. Я думаю, что здесь отсутствует понятие «новый» или «добавленный» при выборке сущностей с сервера. - person AndyK; 26.01.2013
comment
Да, Breeze предполагает, что все объекты, поступающие с сервера, существуют и не изменяются. Это основная семантика того, как работает Breeze, OData, Ember.Data, RIA Services... и любая другая система, ориентированная на сущности, известная мне. Мы вряд ли изменим эту семантику без супер веских причин для этого... особенно когда существует вполне разумный обходной путь, как я показал. Вот еще один подход: дать новым объектам уникальные идентификаторы и придумать какой-нибудь другой способ определить, что они новые. Затем извлеките их из кеша и измените их EntityState на Added. - person Ward; 26.01.2013
comment
То, что не разрешено... никогда не будет разрешено... это два объекта в одном и том же кеше с одним и тем же ключом. Мы также не будем предполагать, что ID=-1 что-то значит; мы не знаем вашу схему нумерации ID. - person Ward; 26.01.2013
comment
Спасибо Ward за вашу обширную помощь, и я могу жить с предложенными обходными путями (!). Но я все еще думаю, что эта функция будет иметь большое значение для Breeze. Просто утверждение, что эта функция отсутствует и в других библиотеках, меня не убеждает. И я не вижу никаких технических проблем с его реализацией. Ваш второй подход, установка для EntitySate значения Added для некоторого уникального идентификатора, очень хорош. Но когда я пытался это реализовать, я не смог найти entityAspect.setAdded(). Посмотрев в исходном коде, я нашел реализацию. Но это было закомментировано в строке entityAspect.js 550…? - person AndyK; 26.01.2013
comment
Святой дым! Вы правы, что setAdded был удален (или никогда не был там)! Я узнаю, почему и планируем ли мы его восстанавливать. Без него логика немного сложнее. По сути, вы должны (а) создать новую сущность, (б) скопировать значения свойств простых данных из входящей «фальшивой» сущности, (в) удалить «фальшивую» сущность. Или можно было схитрить (это JavaScript)... но я не хочу говорить как. Мы свяжемся с вами. - person Ward; 26.01.2013
comment
Имеет ли смысл размещать запрошенные объекты, поступающие с сервера в состоянии, отличном от официального, неизмененного? Можете ли вы получить несколько голосов за это? За десять лет меня ни разу не попросили об этой функции. Все ожидают, что они останутся неизменными; должны ли они теперь проверить? Дополнительная сложность для поддержки этого ... как в Breeze, так и, что более важно, для неосторожного разработчика приложений ... должна быть оправдана спросом. Мы выслушаем, если вы сможете заручиться этим требованием. На данный момент стоимость этой функции явно перевешивает потенциальную выгоду. Рад оказаться неправым. Спасибо за идею. - person Ward; 26.01.2013
comment
Чтобы быть абсолютно ясным, мы понимаем и ценим вашу мотивацию и просьбу. Такая обратная связь жизненно важна для нас и для будущего Breeze. Хотя мы вряд ли будем следовать вашему предложению, если оно не получит широкой поддержки, мы благодарны вам за то, что вы сделали это предложение и проявили такой большой интерес. Мы надеемся, что альтернативный обходной путь сослужит вам хорошую службу. - person Ward; 27.01.2013

Обычно вы создаете объекты на клиенте, а не на сервере. Таким образом, сущности являются новыми и будут сохранены в методе сохранения.

Поскольку объекты поступают с сервера и не изменяются на клиенте, у сервера нет причин что-либо делать с ними, когда они возвращаются, поскольку они находятся в том же состоянии, что и при выходе.

Поэтому создайте новые объекты на клиенте, а затем вызовите сохранение изменений.

person Bryant    schedule 25.01.2013
comment
Хорошо, это допустимый вариант, и мы думали об этом раньше. Но мы отказались от этого, потому что нам нужно начать реализовывать основные части бизнес-логики на уровне пользовательского интерфейса. И я думаю, что это не очень хорошая идея из соображений обслуживания и безопасности (просто подумайте об изменении модели базы данных). Другая причина заключается в том, что в нашей бизнес-логике происходит вычисление количества партий. Наш опыт до сих пор показывает, что массовые вычисления в java-скрипте и в C# всегда дают разные результаты. C# — это единственное место, где мы хотим выполнять вычисления — следовательно, бизнес-логика - person AndyK; 25.01.2013
comment
Я думаю, что мог бы справиться с вариантом использования, если бриз предлагает общий способ получить все объекты в кеше клиента перед сохранением, ключ которого равен -1. Затем я могу просто установить его состояние на «Добавлено», и оно станет частью набора изменений. - person AndyK; 25.01.2013
comment
Несколько моментов. (1) Ваша модель все еще находится на вашем бизнес-уровне на сервере и передается через метаданные, поэтому вы не перемещаете бизнес-логику. (2) Для безопасности это не имеет значения. Любой злонамеренный клиент может отредактировать/добавить то, что он хочет в javascript, вы должны проверить в любом случае на сервере. (3) Вы по-прежнему можете выполнять расчеты на сервере (через веб-сервис или при сохранении). - person Bryant; 25.01.2013

Брайант совершенно прав, таков его ответ. Этот ответ просто для получения разъяснений.

Не совсем уверен, что понимаю вопрос, но просто для ясности: EntityManager не поддерживает несколько сущностей одного типа с одним и тем же «ключом» (т.е. -1 в вашем случае). Он рассматривает все объекты одного типа с одним и тем же ключом как «один и тот же» объект.

Похоже, что вы хотите получить кучу «фальшивых» объектов в entityManager, а затем условно преобразовать несколько из них в «настоящие» новые объекты» и сохранить их, чтобы эти записи были добавлен в базу данных. Это то, чего вы пытаетесь достичь?

person Jay Traband    schedule 25.01.2013
comment
да. Если вы создаете объект на клиенте, Breeze присваивает -1 параметру pk. Если вы создаете две сущности на клиенте, у вас есть две сущности с pk == -1 и состоянием Added. - person AndyK; 25.01.2013
comment
На самом деле, если вы создадите два объекта с автоматически сгенерированным pk, первый должен иметь pk, равный -1, а второй будет иметь идентификатор -2. EntityManager не позволит добавить или прикрепить к нему две сущности с одним и тем же ключом. - person Jay Traband; 30.01.2013