История снимков с Entity Framework - PullRequest
15 голосов
/ 21 сентября 2009

Я смотрел некоторые аудиторские хуки с Entity Framework. Многие из них показывают старые / новые значения сравнения. Это отлично подходит для аудита, но я ищу снимки объектов.

Например ... Допустим, у меня есть приложение, которое управляет продуктами. Продукт имеет несколько атрибутов и связанных с ним других объектов. Допустим, я меняю объект 10 раз. Скажем также, что важно, чтобы я мог просматривать экраны этих изменений объекта (не контрольный журнал, а то, как на самом деле выглядел экран в формате только для чтения). Что меня интересует, так это возможность извлечь исходный объект продукта EF (со всеми связанными данными) для всех 10 из этих изменений (в зависимости от того, что я хочу видеть) и использовать его для привязки к моему экрану.

Если я использую SQL Server, какой тип я должен использовать для сериализованного объекта в настоящее время (XML, BLOB-объекты и т. Д.)? Есть ли смысл делать это?

Ответы [ 3 ]

11 голосов
/ 21 сентября 2009

Посмотрим. У вас есть требование взять граф объектов и сериализовать его в базу данных в формате, который позволит вам впоследствии его материализовать. Я думаю, что есть инструменты, которые делают именно это. Мне кажется, что одним из них является Entity Framework.

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

Я бы предложил вам разрешить версионность всех типов ваших сущностей. Когда вы редактируете тип сущности, вы редактируете ревизию подсказки и сохраняете обратную ревизию, содержащую предыдущие значения. (Причина, по которой вы редактируете ревизию подсказки вместо вставки новой подсказки, состоит в том, что другие объекты, которые в настоящее время не материализованы в ObjectContext, могут содержать ссылки на подсказку, которые вы хотели бы сохранить как ссылки на подсказку, а не ссылки на обратная ревизия.)

При необходимости вы можете разбить таблицы SQL Server так, чтобы предыдущие версии сохранялись в другой файловой группе. Это позволит вам создавать резервные копии ревизий и обратных ревизий отдельно.

4 голосов
/ 27 мая 2016

Сначала вам нужно добавить набор свойств в ваши таблицы:

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

Тогда у вас есть несколько вариантов хранения истории версий. Вы можете

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

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

Как вы заполняете свои таблицы истории? Через обновление и удаление триггеров.

  • В триггере обновления для вашей сущности - скопируйте все предыдущие значения в таблицу истории. Для каждого внешнего ключа - также скопируйте текущую версию ссылочной сущности.
  • В триггере удаления - в основном то же самое.

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

Как вы смотрите свою историю? Просто используйте таблицу истории, так как она имеет все те же свойства, что и основная таблица - не должно быть проблемой. Но - при расширении внешних ключей - убедитесь, что версия объекта, на которую указывает ссылка, такая же, как вы храните в своей таблице истории. Если это не так - вам нужно перейти в таблицу History этой сущности, на которую ссылаются, и получить значения там. Таким образом, у вас всегда будет снимок того, как выглядит объект в тот момент, включая все ссылки.

В дополнение ко всему вышесказанному - вы также можете восстановить состояние вашей сущности до любой предыдущей версии.

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

3 голосов
/ 27 мая 2016

В недавно построенном проекте мы использовали метод SaveChanges в классе DbContext. Это дало нам доступ к экземпляру класса ChangeTracker. Звонок ChangeTracker.Entries() дает вам доступ к списку DbEntityEntry. DbEntityEntry обладает следующими интересными свойствами и методами:

  • State - это вновь созданный, измененный или удаляемый объект
  • Entity - копия объекта в его нынешнем виде
  • CurrentValues - перечисление отредактированных значений
  • OriginalValues - перечисление исходных значений

Мы создали набор POCO для наборов изменений и изменений, к которым мы могли бы затем получить доступ через EF. Это позволило нашим пользователям просматривать изменения уровня поля, а также даты и ответственных пользователей.

...