Я бы не назвал этот общий метод Update
в целом "деструктивным", но я согласен с тем, что он имеет ограниченные варианты использования, которые редко обсуждаются в этих реализациях репозитория. Полезен ли метод или нет, зависит от сценария, в котором вы хотите его применить.
В «прикрепленном сценарии» (например, приложение Windows Forms), когда вы загружаете объекты из базы данных, изменяете некоторые свойства, пока они все еще подключены к контексту EF, а затем сохраняете изменения, этот метод бесполезен, потому что контекст все равно будет отслеживать все изменения и знать в конце, какие столбцы должны быть обновлены или нет. В этом сценарии вам вообще не нужен метод Update (подсказка: DbSet<T>
(который является универсальным репозиторием) по этой причине не имеет метода Update
). А в ситуации параллелизма это деструктивно, да.
Однако неясно, иногда ли «обновление с отслеживанием изменений» не является разрушительным. Если два пользователя изменяют одно и то же свойство на разные значения, обновление с отслеживанием изменений для обоих пользователей сохранит новое значение столбца, и последний из них выиграет. Если это нормально или нет, зависит от приложения и от того, насколько безопасно оно хочет, чтобы изменения были сделаны. Если приложение запрещает когда-либо редактировать объект, который не является последней версией в базе данных до того, как изменение будет сохранено, оно не может допустить, чтобы последнее сохранение имело преимущество. Он должен был бы остановиться, заставить пользователя перезагрузить последнюю версию и просмотреть последние значения, прежде чем он введет свои изменения. Чтобы справиться с этой ситуацией, необходимы маркеры параллелизма, которые бы обнаруживали, что кто-то еще тем временем изменил запись. Но эти проверки параллелизма работают одинаково с обновлениями с отслеживанием изменений или при установке состояния сущности на Modified
. Разрушительный потенциал обоих методов останавливается исключениями параллелизма. Однако установка состояния на Modified
по-прежнему создает ненужные накладные расходы, поскольку в базу данных записываются неизмененные значения столбцов.
В «отключенном сценарии» (например, веб-приложение) обновление с отслеживанием изменений недоступно. Если вы не хотите устанавливать для всего объекта значение Modified
, вам необходимо загрузить последнюю версию из базы данных (в новом контексте), скопировать свойства, полученные из пользовательского интерфейса, и снова сохранить изменения. Однако это не предотвращает перезапись изменений, внесенных другим пользователем за это время, даже если они являются изменениями в других свойствах. Представьте, что два пользователя одновременно загружают одну и ту же сущность клиента в веб-форму. Пользователь 1 редактирует имя клиента и сохраняет. Пользователь 2 редактирует номер банковского счета клиента и сохраняет его через несколько секунд. Если объект загружается в новый контекст для выполнения обновления для пользователя 2, EF просто увидит, что имя клиента в базе данных (которое уже включает изменение пользователя 1) отличается от имени клиента, отправленного обратно пользователем 2 (которое по-прежнему старое имя клиента). Если вы скопируете значение имени клиента, свойство будет помечено как измененное, а старое имя будет записано в базу данных и перезапишет изменение пользователя 1. Это обновление будет столь же разрушительным, как и изменение состояния всего объекта на измененное. Чтобы избежать этой проблемы, вам нужно будет либо реализовать некоторое пользовательское отслеживание изменений на стороне клиента, которое распознает, изменил ли пользователь 2 имя клиента, а если нет, то просто не копирует значение в загруженный объект. Или вам придется снова работать с токенами параллелизма.
Вы не упомянули самое большое ограничение этого метода Update
в своем вопросе, а именно то, что он не обновляет никакие связанные объекты. Например, если у вашего объекта Device
есть связанная коллекция Parts
, и вы будете редактировать эту коллекцию в отдельном пользовательском интерфейсе (добавлять/удалять/изменять элементы), устанавливая состояние родительского объекта Device
на Modified
, эти изменения не сохранятся в базе данных. . Это повлияет только на скалярные (и сложные) свойства самого родителя Device
. В то время, когда я использовал репозитории такого типа, я назвал метод обновления FlatUpdate
, чтобы лучше указать это ограничение в имени метода. Я никогда не видел общего "DeepUpdate
". Работа со сложными графами объектов — это всегда не универсальная вещь, которая должна быть написана индивидуально для каждого типа объекта и в зависимости от ситуации. (К счастью, такая библиотека, как entity/" rel="nofollow">GraphDiff может ограничить объем кода, который необходимо написать для таких обновлений графа.)
Короче говоря:
- Для подключенных сценариев метод
Update
является избыточным, поскольку автоматическое отслеживание изменений EF выполняет всю необходимую работу для записи правильных инструкций UPDATE в базу данных, включая изменения в графах связанных объектов.
- Для отдельных сценариев это удобный способ выполнения обновлений простых сущностей без связей.
- Обновление графов объектов с родительскими и дочерними объектами в отдельном сценарии невозможно выполнить с помощью такого упрощенного метода
Update
и требует значительно большей (неуниверсальной) работы.
- Для безопасного управления параллелизмом требуются более сложные инструменты, такие как включение оптимистичных проверок параллелизма, предоставляемых EF, и обработка возникающих исключений параллелизма удобным для пользователя способом.
person
Slauma
schedule
13.02.2014