Entity Framework не обновляет значение, которое изменяется с помощью триггера
Моя таблица Sections
(SQL Server) имеет ID
в качестве первичного ключа (int, identity)
и SortIndex
column (int) для целей сортировки.
В базе данных есть триггер, который устанавливает SortIndex := ID
в каждом INSERT
. Очевидно, что я хочу изменить индекс сортировки позже, заменив значения для двух строк.
Я получаю доступ к данным с помощью Entity Framework, все с веб-приложением MVC3.
Проблема заключается в том, что Entity Framework не обновляет значение SortIndex
после того, как я вставляю новый объект в таблицу. Он также кэширует все данные, поэтому следующий вызов для получения всех объектов из этой таблицы также даст неправильное значение SortIndex
для этого объекта.
Я попытался изменить StoreGeneratedPattern
для этого столбца в EDMX
. Это кажется отличным и элегантным, но не решает проблему.
Если я установлен в Identity
, это заставляет EF правильно обновлять значение, но оно становится только для чтения (исключение возникает при попытке изменить). Установка его в Computed
аналогична, но вместо генерируемого исключения значения просто не записываются в БД.
Я могу воссоздать объект EF каждый раз, если мне нужно использовать его после вставки объекта, просто выполнив:
DatabaseEntities db = new DatabaseEntities()
Но это кажется уродливым обходным путем для меня.
Какое решение этой проблемы?
Очевидно, что что-то, что не требует от меня каких-либо действий после каждого INSERT
(и рисковать тем, что оно забыто и незаметно) является предпочтительным.
Ответы
Ответ 1
Короче StoreGeneratedPattern
означает: значение обрабатывается хранилищем, и ваше приложение никогда не будет изменять его. В таком случае вы получите автоматически генерируемое хранилище после вызова SaveChanges
.
Если вы не используете StoreGeneratedPattern
, вы не получите значение, и вам придется принудительно выполнить другое выполнение запроса для обновления вашего объекта. Например, вы можете:
objectContext.Refresh(RefreshMode.StoreWins, yourSection);
Обычно ситуации, когда вам нужно обновлять значения в обеих базе данных через триггеры и приложение, не очень хорошо играют с EF (и, возможно, также с другими инструментами ORM).
Ответ 2
Я нашел ответ от "Ladislav Mrnka" точным и отметил его как принятый. Вот и другие способы обхода, которые я нашел, пытаясь найти какое-то решение. Однако решение, которое я искал, вообще невозможно.
Одна из возможностей - установить StoreGeneratedPattern = Computed
, чтобы знать EF, это значение вычисляется. И затем сделайте хранимую процедуру, чтобы фактически изменить значение SortIndex
. Как правило, это изменение значений в две строки (своп их), чтобы изменить порядок сортировки. Эта процедура вместе с триггером при INSERT
дает гарантию, что данные остаются согласованными в БД. Невозможно создать новую строку без правильного значения, установленного в SortIndex
, невозможно, чтобы два объекта имели одно и то же значение (если хранимая процедура не имеет ошибки), и невозможно вручную каким-то образом сломать значение, поскольку это невозможно для редактирования через EF. Похоже на отличное решение.
Легко можно хранить хранимые процедуры, сопоставленные с функциями в EF.
Проблема заключается в том, что теперь можно ввести новую строку, а EF правильно обновляет данные в своем кеше, но кеш не обновляется после вызова хранимой процедуры. Тем не менее необходима ручная обновленная или обновленная функция. В противном случае следующий вызов для получения объектов, отсортированных по SortIndex
, даст неправильные результаты.
Кроме этого, можно установить MergeOption = MergeOption.OverwriteChanges
для нескольких объектов, что заставляет EF лучше обновлять данные из БД. После этого можно перечитать объект после его вставки или вызова хранимой процедуры, и он будет обновлен. Однако чтение коллекции объектов с помощью db.Section.OrderBy(o => o.SortIndex)
будет по-прежнему возвращать кешированные результаты с неправильным порядком сортировки.
Если кому-то интересно, возможно сделать MergeOption
по умолчанию чем-то другим, добавив частичный класс EF, а затем частичный метод OnContextCreated
, как здесь:
public partial class DatabaseEntities
{
partial void OnContextCreated()
{
Subsection.MergeOption = MergeOption.OverwriteChanges;
Section.MergeOption = MergeOption.OverwriteChanges;
Function.MergeOption = MergeOption.OverwriteChanges;
}
}
Ответ 3
Знаете ли вы, что вы снова будете работать с этим столбцом в том же запросе?
Я бы использовал сценарий контекста для каждого запроса, который обычно выдает из многих проблем, потому что с каждым запросом создается новый EF-контекст, поэтому у вас есть свежие данные один раз за запрос.
С долговечным контекстом, как вы описали, могут возникнуть несоответствия.
В любом случае StoreGeneratedPattern, установленный для вычисления, должен быть прав. Но он обновляется только тогда, когда вы храните фактический объект. Он не обновляется, вставляя или обновляя любой другой объект.
из http://msdn.microsoft.com/en-us/library/dd296755(v=vs.90).aspx
Если вы создаете новый объект или изменяете существующий объект, значения свойств с помощью StoreGeneratedPattern, установленного на Computed, извлекаются с сервера при вызове метода SaveChanges в вашем приложении.
Если вы присвоите значение для свойства с параметром StoreGeneratedPattern, вычисленным в приложении, значение будет перезаписано сгенерированным сервером значением, когда вы вызываете метод SaveChanges.
Мы используем параметр вычисляемого значения для SQL GUID с секвенцией, и он работает нормально.