Entity Framework не обновляет значение, которое изменяется триггером - PullRequest
11 голосов
/ 08 марта 2012

Моя таблица Sections (SQL Server) имеет ID в качестве первичного ключа (int, identity) и SortIndex столбец (int) для целей сортировки.

База данных имеет триггер, который устанавливает SortIndex := ID на каждом INSERT.Очевидно, что позже я хочу изменить индекс сортировки, меняя значения для двух строк.

Я обращаюсь к данным с помощью Entity Framework, все с помощью веб-приложения MVC3.

Проблема в Entity Frameworkне обновляет значение SortIndex после добавления нового объекта в таблицу.Он также кэширует все данные, поэтому следующий вызов для получения всех объектов из этой таблицы также даст неправильное значение SortIndex для этого объекта.

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

Если я установлю на Identity, это заставит EF корректно обновить значение, но оно станет доступным только для чтения (исключение выдается при попытке изменить).Установка его на Computed аналогична, но вместо генерируемого исключения значения просто не записываются в БД.

Я могу воссоздавать объект EF каждый раз, когда мне нужно использовать его после вставки объекта,просто сделав:

DatabaseEntities db = new DatabaseEntities()

Но мне кажется, что это уродливый обходной путь.

Какое решение этой проблемы?

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

Ответы [ 4 ]

15 голосов
/ 08 марта 2012

Короче говоря StoreGeneratedPattern означает: значение обрабатывается магазином, и ваше приложение никогда не изменит его. В таком случае вы получите автоматически сгенерированное значение после вызова SaveChanges.

Если вы не используете StoreGeneratedPattern, вы не получите значение, и вам придется принудительно выполнить другой запрос, чтобы обновить вашу сущность. Вы можете, например, сделать:

objectContext.Refresh(RefreshMode.StoreWins, yourSection);

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

3 голосов
/ 09 марта 2012

Я нашел ответ от «Ладислав Мрнка» точным и отметил его как принятый.Вот другие обходные пути, которые я нашел, пытаясь найти какое-то решение.Однако решение, которое я искал, в общем случае невозможно.

Одна из возможностей - установить 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;
    }
}
0 голосов
/ 22 ноября 2018

У меня была похожая ситуация с таблицей Sql Server Quat со столбцом varchar QuoteNumber, который является непервичным уникальным ключом, значение которого генерируется триггером после вставки. Триггер используется, потому что сгенерированное значение получается путем извлечения данных из таблицы внешнего ключа. Объявления идентификаторов схемы сервера SQL не позволяют извлекать информацию из других таблиц.

Я бы хотел, чтобы EF рассматривал этот столбец varchar как личность и ничего не делал с ним при обновлении и перечитывал его после вставки. EF сделает это, если есть свойство .HasDatabaseGeneratedOption (System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.Identity) для столбца без идентификатора в коде, который он генерирует для настройки сущности следующим образом (прокрутка вправо):

public QuoteConfiguration(string schema)
{
    ToTable("Quote", schema);
    HasKey(x => x.ID);

    Property(x => x.ID).HasColumnName(@"ID").HasColumnType("int").IsRequired().HasDatabaseGeneratedOption(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.Identity);
    Property(x => x.QuoteNumber).HasColumnName(@"Quote_Number").HasColumnType("varchar").IsOptional().IsUnicode(false).HasMaxLength(64).HasDatabaseGeneratedOption(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.Identity);
}

Моя модель EF является первым кодом и сгенерирована генератором обратного POCO Саймона Хьюза EntityFramework. Сначала я не мог понять, как заставить генератор добавить это свойство в столбец, который не объявлен как идентификатор в Sql Server.

Перечитывание всей сущности Quote после вставки не привело к получению автоматически сгенерированного QuoteNumber. Затем я обнаружил, что повторное чтение только столбца QuoteNumber после вставки побеждает кэш сущностей. Но я чувствовал себя грязно, делая это.

Наконец, я работал с Саймоном Хьюзом, чтобы узнать, как заставить его EF Reverse POCO сделать это для меня. Вы просто расширяете функцию UpdateColumn в своем файле * .tt следующим образом:

Settings.UpdateColumn = (Column column, Table table) =>
{
        if (table.Name.Equals("Quote", StringComparison.InvariantCultureIgnoreCase)
            && column.Name.Equals("Quote_Number", StringComparison.InvariantCultureIgnoreCase))
        {
            column.IsStoreGenerated = true;
        }
}
0 голосов
/ 08 марта 2012

Знаете ли вы, если вы будете работать с этим столбцом снова в том же запросе?

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

В случае долгоживущего контекста могут расти несоответствия, как вы описали.

В любом случае StoreGeneratedPattern, установленный на computed, должен быть верным. Но он обновляется только тогда, когда вы сохраняете фактическую сущность. Он не обновляется, вставляя или обновляя любую другую сущность.

от http://msdn.microsoft.com/en-us/library/dd296755(v=vs.90).aspx

Если вы создаете новую сущность или изменяете существующую сущность, значения свойств с StoreGeneratedPattern, для которого установлено значение Computed, извлекаются с сервера при вызове метода SaveChanges в приложении. Если вы присвоите значение свойству с StoreGeneratedPattern, для которого в приложении установлено значение Computed, это значение будет перезаписано сгенерированным сервером значением при вызове метода SaveChanges.

Мы используем опцию вычисляемого значения для SQL-идентификатора GUID, и он работает нормально.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...