Способы хранения объекта в нескольких постбэках - PullRequest
5 голосов
/ 08 января 2010

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

  • Изменить детали доставки / оплаты (все простые тексты / выпадающие списки)
  • Добавить / удалить / редактировать товары в заказе - это делается с помощью сетки
  • Добавить / удалить вложения

Продукты и вложения хранятся в отдельных таблицах БД с внешним ключом для заказа.

Entity Framework (4.0) используется в качестве ORM.

Я хочу разрешить пользователям вносить любые изменения в порядок, которые они хотят, и только когда они нажимают «Сохранить», я хочу зафиксировать изменения в базе данных.Это не проблема с текстовыми полями / флажками и т. Д., Так как я могу просто положиться на ViewState, чтобы получить необходимую информацию.Однако сетка представляет для меня гораздо большую проблему, так как я не могу найти хороший и простой способ сохранить изменения, сделанные пользователем, без фиксации изменений в базе данных.Сохранение дерева объектов Order в Session / ViewState - это не совсем та опция, с которой я бы хотел пойти, поскольку объекты могут стать очень большими.

Так что вопрос в том, как мне сохранить изменения, внесенные пользователем.сделано до готовности к «Сохранить».

Быстрое примечание - я искал SO, чтобы попытаться найти решение, однако все, что я нашел, было предложениями использовать Session и / или ViewState - оба из которых я хотел быскорее не использовать из-за потенциального размера деревьев моих объектов

Ответы [ 13 ]

4 голосов
/ 10 января 2010

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

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

2 голосов
/ 08 января 2010

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

Во-вторых, вы можете рассмотреть гибридный подход - использовать состояние сеанса (или кэш) в течение короткого времени, чтобы избежать ненужных поездок в БД или другое внешнее хранилище. После некоторого простоя запишите кэшированное состояние на диск или в БД. Самый простой способ сделать это - сериализовать ваши объекты в текст (используя сериализацию или библиотеку типа proto-buffers ). Это позволяет избежать создания избыточной или дублирующей структуры данных для реляционного сбора текущих данных. Если вам не нужно запрашивать содержимое этих данных - это разумный подход.

Кроме того, в мире баз данных описываемая вами проблема называется длительная транзакция . По сути, вы хотите избежать внесения изменений в данные, пока не достигнете определенной пользователем точки фиксации. Существуют методы, которые вы можете использовать на уровне базы данных, такие как гипотетические представления и вместо триггеров , чтобы инкапсулировать поведение, которое вы на самом деле не фиксируете изменения , Данные находятся в БД (в реальных таблицах), но видны только пользователю, работающему с ними. Это, вероятно, более сложная реализация, чем вы, возможно, захотите предпринять, и она требует навязчивых изменений в уровне персистентности и модели данных, но позволяет приложению не знать о проблеме.

2 голосов
/ 08 января 2010

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

1 голос
/ 16 января 2010

Рассматривали ли вы использование профиля пользователя? .Net поставляется с SqlProfileProvider прямо из коробки. Это позволит вам для каждого пользователя получить его профиль и сохранить временные данные как переменную в профиле. К сожалению, я думаю, что для этого требуется, чтобы ваш «Порядок» был сериализуемым, но я полагаю, что все параметры, кроме Session, до сих пор требовали бы того же.

Преимущество этого в том, что оно будет сохраняться в случае сбоев, сессий, простоя сервера и т. Д., И его довольно легко настроить. Вот пример сайта. Как только вы его настроите, вы также можете найти его полезным для хранения другой пользовательской информации, такой как настройки, избранное, просмотренные элементы и т. Д.

1 голос
/ 12 января 2010

как насчет сериализации вашего объекта Domain (содержимого вашей таблицы или корзины) в JSON и сохранения его в скрытой переменной? У Скотту есть хорошая статья о том, как сериализовать объекты в JSON. Масштабируемый по всей ферме серверов и предполагающий, что он не увеличит полезную нагрузку на вашу страницу. Может быть, вы можете написать свой собственный сериализатор JSON для выполнения «компактной сериализации» (вам не понадобится название продукта, идентификатор продукта, идентификатор SKU и т. Д., Может быть, вы просто можете «сериализовать» productID и количество)

1 голос
/ 11 января 2010

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

Сохранять каждое изменение как серию событий, например, UserChangedShippingAddress, UserAlteredLineItem, UserDeletedLineItem, UserAddedLineItem.

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

Когда пользователь нажимает кнопку Сохранить, вы можете воспроизвести события и сохранить обновленную модель заказа в базе данных.

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

Обслуживание невероятно простое, поскольку благодаря легкости, с которой вы можете реализовать объект домена, автоматизированное тестирование легко используется, чтобы убедиться, что система ведет себя так, как вы ожидаете (при этом также документируя свои намерения для других разработчиков).

Поскольку вы используете базу данных, решение хорошо масштабируется на нескольких веб-серверах.

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

По сути, это тот же подход, который описан Фредди Риосом, с небольшим количеством подробностей о том, как и какое хорошее ключевое слово для вас искать с помощью =)

http://jasondentler.com/blog/2009/11/simple-domain-events/ и http://www.udidahan.com/2009/06/14/domain-events-salvation/ являются хорошим фоновым чтением о событиях в домене. Вы также можете захотеть ознакомиться с источником событий, поскольку это, по сути, то, что вы будете делать (объект моментального снимка, события записи, события воспроизведения, объект моментального снимка снова).

1 голос
/ 08 января 2010

Рассматривали ли вы сохранение информации в объекте JavaScript, а затем отправку этой информации на ваш сервер, как только пользователь нажмет save?

0 голосов
/ 16 января 2010

Насколько большим вы считаете большой? Если вы говорите сессионное состояние (чтобы оно не возвращалось назад / вперед к реальному пользователю, например, к состоянию просмотра), тогда состояние часто является довольно хорошим вариантом. Все, кроме провайдера состояния в процессе, использует сериализацию, но вы можете повлиять на , как сериализуется. Например, я бы хотел создать локальную модель, которая представляет просто состояние, которое мне небезразлично (плюс любая информация об id / rowversion) для этой операции (а не полные доменные сущности, которые могут иметь дополнительные издержки) .

Чтобы еще больше снизить издержки сериализации, я бы подумал об использовании чего-то вроде protobuf-net; это может использоваться как реализация для ISerializable, позволяя очень легковесным сериализованным объектам (как правило, намного меньше, чем BinaryFormatter, XmlSerializer и т. д.), которые дешевы для восстановления при запросах страниц.

Когда страница будет наконец сохранена, я обновлю свои доменные сущности из локальной модели и отправлю изменения.

Для информации, чтобы использовать атрибутный объект protobuf-net с сериализаторами состояния (обычно BinaryFormatter), вы можете использовать:

// a simple, sessions-state friendly light-weight UI model object
[ProtoContract]
public class MyType {
    [ProtoMember(1)]
    public int Id {get;set;}

    [ProtoMember(2)]
    public string Name {get;set;}

    [ProtoMember(3)]
    public double Value {get;set;}
    // etc

    void ISerializable.GetObjectData(
        SerializationInfo info,StreamingContext context)
    {
        Serializer.Serialize(info, this);
    }

    public MyType() {} // default constructor

    protected MyType(SerializationInfo info, StreamingContext context)
    {
        Serializer.Merge(info, this);
    }
}
0 голосов
/ 13 января 2010

Являются ли конечные пользователи внутренними или внешними клиентами? Если ваши клиенты являются внутренними пользователями, возможно, стоит взглянуть на альтернативный набор технологий. Вместо веб-форм рассмотрите возможность использования платформы, подобной Silverlight, и реализации там богатого графического интерфейса.

После этого вы можете хранить сложные бизнес-объекты в апплете, обеспечивать постоянное отслеживание процесса редактирования в течение нескольких сеансов через автономное хранилище и легко интегрировать с внутренними службами, которые обеспечивают сохранение / обработку окончательного заказа. Все с сохранением доступа через Интернет (хотя большинство клиентов * nix закрываются).

Альтернативные варианты включают Adobe Flex или AJAX, в зависимости от ресурсов и потребностей.

0 голосов
/ 13 января 2010

Я бы пошел на viewstate, независимо от того, что вы сказали раньше. Если вы храните только то, что вам нужно, например, { id: XX, numberOfProducts: 3 }, и отбрасываете каждый элемент, который не выбран пользователем на данный момент; размер viewstate вряд ли будет проблемой, если вы не храните все дерево объектов.

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

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

...