История снимков с Entity Framework
-
12-09-2019 - |
Вопрос
Я рассматривал некоторые способы аудита с помощью Entity Framework.Многие из них показывают сравнение старых и новых значений.Это отлично подходит для контрольного журнала, но я хочу делать снимки объектов.
Например...Допустим, у меня есть приложение, которое управляет продуктами.Продукт имеет несколько атрибутов и связанных с ним других объектов.Допустим, я меняю объект 10 раз.Допустим также, что важно, чтобы я мог просматривать экраны этих изменений объекта (не контрольный журнал, а то, как на самом деле выглядел экран в формате только для чтения).Что меня интересует, так это возможность получить исходный объект продукта EF (со всеми связанными данными) для всех 10 этих изменений (в зависимости от того, что я хочу увидеть) и использовать его для привязки к моему экрану.
Если я использую SQL Server, какой тип сериализованного объекта мне следует использовать в настоящее время (XML, blob и т. д.)?Есть ли смысл это делать?
Решение
Давайте посмотрим.Вам необходимо взять граф объекта и сериализовать его в базу данных в формате, который позволит вам материализовать его позже.Я думаю, что есть инструменты, которые делают именно это.Один из них, как мне кажется, — это Entity Framework.
То, что вы хотите сделать, — это очень распространенная вещь.Рассмотрим вики-движок.Вики должна иметь предварительную версию, которую все видят, а также предыдущие версии каждого документа.Вики также должна иметь возможность отображать предыдущую ревизию точно так же, как отображается редакция кончика.Поэтому для них обоих следует использовать один и тот же формат хранения.
Я бы предложил вам разрешить версионирование всех типов сущностей.При редактировании типа объекта вы редактируете начальную версию и сохраняете предыдущую версию, содержащую предыдущие значения.(Причина, по которой вы редактируете версию подсказки вместо вставки новой подсказки, заключается в том, что другие объекты, которые в настоящее время не материализованы в ObjectContext, могут содержать ссылки на подсказку, которые вы хотели бы сохранить как ссылки на подсказку, а не ссылки на задняя ревизия.)
При необходимости вы можете секционировать таблицы SQL Server, чтобы предыдущие версии хранились в другой группе файлов.Это позволит вам отдельно создавать резервные копии версий наконечника и предыдущих версий.
Другие советы
Сначала вам нужно добавить в ваши таблицы набор свойств:
- Версия - время последней модификации (вместо времени также может быть автоинкрементный счетчик).
- LastModifiedBy — ссылка на пользователя, внесшего последнее изменение (если вы его сохранили).
Тогда у вас есть несколько вариантов хранения истории версий.Ты можешь
Создайте новую таблицу для каждой из основных таблиц, историю которых вы хотите сохранить.Эти таблицы истории будут иметь все те же поля, что и основная таблица, но первичные и внешние ключи не будут применяться.Для каждого внешнего ключа также сохраните версию ссылочной записи на момент создания версии.
ИЛИ вы можете сериализовать все, что интересно о вашей сущности, и хранить все эти сериализованные BLOB-объекты для всех сущностей, которые вы хотите версионировать, в одной глобальной таблице истории (лично я предпочитаю первый подход).
Как вы заполняете свои таблицы истории?Через триггеры обновления и удаления.
- В триггере обновления для вашей сущности скопируйте все предыдущие значения в таблицу истории.Для каждого внешнего ключа также скопируйте текущую версию объекта, на который ссылаются.
- В триггере удаления - в основном делайте то же самое.
Обратите внимание, что все больше и больше современных систем на самом деле ничего не удаляют.Они просто отметка вещи как удаленные.Если вы хотите следовать этому шаблону (который имеет несколько преимуществ) — вместо удаления добавьте флаг IsDeleted к вашим объектам (конечно, вам придется везде фильтровать удаленные объекты).
Как вы смотрите на свою историю?Просто используйте таблицу истории, поскольку она имеет все те же свойства, что и основная таблица, и это не должно быть проблемой.Но при расширении внешних ключей убедитесь, что версия ссылочного объекта совпадает с той, которую вы храните в своей таблице истории.Если это не так, вам нужно перейти в таблицу истории этого объекта, на который ссылаются, и получить там значения.Таким образом, у вас всегда будет снимок того, как объект выглядел в ЭТОТ момент, включая все ссылки.
В дополнение ко всему вышесказанному — вы также можете восстановить состояние вашей сущности до любой предыдущей версии.
Обратите внимание, что эта реализация, хотя и проста, может занимать некоторое пространство, поскольку она хранит снимок, происходят не только изменения.Если вы хотите просто сохранить изменения - в триггере обновления вы можете определить, какие поля были изменены, сериализовать их и сохранить в таблице глобальной истории.Таким образом, вы сможете, по крайней мере, показать в пользовательском интерфейсе, что и кем было изменено (хотя у вас могут возникнуть проблемы с возвратом к какой-либо предыдущей версии).
В проекте, который я недавно создал, мы использовали подключение к SaveChanges
метод в DbContext
сорт.Это дало нам доступ к экземпляру ChangeTracker
сорт.Вызов ChangeTracker.Entries()
дает вам доступ к списку DbEntityEntry
. DbEntityEntry
имеет следующие интересные свойства и методы:
State
- является ли объект вновь созданным, измененным или удаляемымEntity
- копия объекта в его нынешнем видеCurrentValues
- перечисление отредактированных значенийOriginalValues
- перечисление исходных значений
Мы создали набор POCO для наборов изменений и изменений, к которым затем можно было получить доступ через EF.Это позволило нашим пользователям просматривать изменения на уровне полей, а также даты и ответственных пользователей.