Поделиться через


Критические изменения в EF Core 9 (EF9)

На этой странице описаны изменения API и поведения, которые могут нарушить обновление существующих приложений с EF Core 8 до EF Core 9. Проверьте предыдущие критические изменения при обновлении из более ранней версии EF Core:

Целевая платформа

EF Core 9 предназначен для .NET 8. Это означает, что существующие приложения, предназначенные для .NET 8, могут продолжать делать это. Приложения, предназначенные для более старых версий .NET, .NET Core и платформа .NET Framework, должны быть предназначены для .NET 8 или .NET 9 для использования EF Core 9.

Итоги

Примечание.

Если вы используете Azure Cosmos DB, ознакомьтесь с отдельным разделом ниже в Azure Cosmos DB с критическими изменениями.

Критические изменения Воздействие
EF.Functions.Unhex() Теперь возвращается byte[]? Низкая
Проверено arity аргументов null в SqlFunctionExpression Низкая
ToString() Метод теперь возвращает пустую строку для null экземпляров Низкая
Зависимости общей платформы были обновлены до версии 9.0.x Низкая

Изменения низкой степени влияния

EF.Functions.Unhex() Теперь возвращается byte[]?

Отслеживание проблемы #33864

Старое поведение

Функция EF.Functions.Unhex() была ранее аннотирована для возврата byte[].

Новое поведение

Начиная с EF Core 9.0, Unhex() теперь заметен для возврата byte[]?.

Почему

Unhex() преобразуется в функцию SQLite unhex , которая возвращает значение NULL для недопустимых входных данных. В результате Unhex() возвращено null для этих случаев в нарушение заметки.

Устранение проблем

Если вы уверены, что текстовое содержимое, переданное для Unhex() представления допустимой шестнадцатеричной строки, можно просто добавить оператор null-forgiving в качестве утверждения о том, что вызов никогда не вернет значение NULL:

var binaryData = await context.Blogs.Select(b => EF.Functions.Unhex(b.HexString)!).ToListAsync();

В противном случае добавьте проверку среды выполнения на значение NULL для возвращаемого значения Unhex().

Проверено arity аргументов null в SqlFunctionExpression

Отслеживание проблемы #33852

Старое поведение

Ранее можно было создать SqlFunctionExpression с другим числом аргументов и аргументами распространения значений NULL.

Новое поведение

Начиная с EF Core 9.0, EF теперь выдает, если число аргументов и аргументов распространения null не совпадает.

Почему

Не совпадающее число аргументов и аргументов распространения null может привести к непредвиденному поведению.

Устранение проблем

Убедитесь, что число argumentsPropagateNullability элементов совпадает с числом argumentsэлементов. Если сомнение используется false для аргумента NULL.

ToString() Метод теперь возвращает пустую строку для null экземпляров

Отслеживание проблемы #33941

Старое поведение

Ранее EF вернул несогласованные результаты для ToString() метода при значении nullаргумента. Например, ToString() для свойства с null возвращаемым nullзначением, но для выражений, не являющихся свойствамиbool?, значение null которого было возвращеноTruebool?. Поведение также было несогласовано для других типов данных, например ToString() для null перечисления значений, возвращаемых пустой строкой.

Новое поведение

Начиная с EF Core 9.0 ToString() метод теперь последовательно возвращает пустую строку во всех случаях, когда значение аргумента равно null.

Почему

Старое поведение было несогласованным в разных типах данных и ситуациях, а также не совпадало с поведением C#.

Устранение проблем

Чтобы вернуться к старому поведению, переопределите запрос соответствующим образом:

var newBehavior = context.Entity.Select(x => x.NullableBool.ToString());
var oldBehavior = context.Entity.Select(x => x.NullableBool == null ? null : x.NullableBool.ToString());

Зависимости общей платформы были обновлены до версии 9.0.x

Старое поведение

Приложения, использующие Microsoft.NET.Sdk.Web пакет SDK и targetting net8.0, разрешают такие пакеты, как System.Text.Json, Microsoft.Extensions.Logging Microsoft.Extensions.Caching.MemoryMicrosoft.Extensions.Configuration.Abstractionsи Microsoft.Extensions.DependencyModel из общей платформы, поэтому эти сборки обычно не будут развертываться с приложением.

Новое поведение

Хотя EF Core 9.0 по-прежнему поддерживает net8.0, он теперь ссылается на версии System.Text.Json9.0.x , Microsoft.Extensions.Logging Microsoft.Extensions.Caching.MemoryMicrosoft.Extensions.Configuration.Abstractionsи .Microsoft.Extensions.DependencyModel Приложения, предназначенные для net8.0, не смогут использовать общую платформу, чтобы избежать развертывания этих сборок.

Почему

Соответствующие версии зависимостей содержат последние исправления безопасности и их использование упрощает модель обслуживания для EF Core.

Устранение проблем

Измените приложение на целевой net9.0, чтобы получить предыдущее поведение.

Критические изменения в Azure Cosmos DB

Обширная работа прошла в том, чтобы сделать поставщик Azure Cosmos DB лучше в 9.0. Изменения включают ряд критически важных изменений; Если вы обновляете существующее приложение, внимательно ознакомьтесь со следующими сведениями.

Критические изменения Воздействие
Дискриминатор теперь именуется $type вместо Discriminator Высокая
Свойство id больше не содержит дискриминационный по умолчанию Высокая
Синхронизация операций ввода-вывода с помощью поставщика Azure Cosmos DB больше не поддерживается Средняя
Теперь запросы SQL должны проектируемые значения JSON напрямую Средняя
Неопределенные результаты теперь автоматически фильтруются из результатов запроса Средняя
Неправильно переведенные запросы больше не переводятся Средняя
HasIndex теперь бросает вместо того, чтобы игнорировать Низкая
IncludeRootDiscriminatorInJsonId был HasRootDiscriminatorInJsonId переименован в после версии 9.0.0-rc.2 Низкая

Изменения высокой степени влияния

Дискриминатор теперь именуется $type вместо Discriminator

Отслеживание проблемы #34269

Старое поведение

EF автоматически добавляет дискриминационные свойства в документы JSON, чтобы определить тип сущности, который представляет документ. В предыдущих версиях EF это свойство JSON используется Discriminator по умолчанию.

Новое поведение

Начиная с EF Core 9.0, дискриминатор теперь называется $type по умолчанию. Если у вас есть документы в Azure Cosmos DB из предыдущих версий EF, они используют старое Discriminator именование, а после обновления до EF 9.0 запросы к этим документам завершаются ошибкой.

Почему

Новая практика JSON использует $type свойство в сценариях, в которых необходимо определить тип документа. Например. System.Text.Json в NET также поддерживает полиморфизм, используя $type в качестве имени свойства по умолчанию (docs). Чтобы выровнять остальную часть экосистемы и упростить взаимодействие с внешними инструментами, было изменено значение по умолчанию.

Устранение проблем

Самый простой способ устранения рисков заключается в том, чтобы просто настроить имя дискриминационных свойств Discriminatorтак же, как и раньше:

modelBuilder.Entity<Session>().HasDiscriminator<string>("Discriminator");

Это делается для всех типов сущностей верхнего уровня, что делает EF так же, как и раньше.

На этом этапе вы также можете обновить все документы, чтобы использовать новое $type именование.

Теперь id свойство содержит только свойство ключа EF по умолчанию

Отслеживание проблемы #34179

Старое поведение

Ранее EF вставляет дискриминационные значения типа сущности в id свойство документа. Например, если вы сохранили тип сущности со свойством Blog Id , содержащим 8, свойство JSON id будет содержать Blog|8.

Новое поведение

Начиная с EF Core 9.0 свойство JSON id больше не содержит дискриминационных значений и содержит только значение свойства ключа. В приведенном выше примере свойство JSON id будет просто 8. Если у вас есть документы в Azure Cosmos DB из предыдущих версий EF, они имеют дискриминационное значение в свойстве JSON id , а после обновления до EF 9.0 запросы к этим документам завершаются ошибкой.

Почему

Поскольку свойство JSON id должно быть уникальным, дискриминатор ранее был добавлен в него, чтобы разрешить различным сущностям с одинаковым значением ключа существовать. Например, это позволило иметь как a, Blog так и Post свойство со Id значением 8 в одном контейнере и секции. Это лучше соответствует шаблонам моделирования данных реляционной базы данных, где каждый тип сущности сопоставляется с собственной таблицей и поэтому имеет собственное пространство ключей.

EF 9.0 обычно изменил сопоставление, чтобы быть более согласованным с общими методиками и ожиданиями NoSQL Azure Cosmos DB, а не соответствовать ожиданиям пользователей, поступающих от реляционных баз данных. Кроме того, наличие дискриминационных значений в id свойстве затрудняет взаимодействие внешних средств и систем с документами JSON, созданными EF; такие внешние системы обычно не знают о дискриминационных значениях EF, которые по умолчанию являются производными от типов .NET.

Устранение проблем

Проще всего настроить EF для включения дискриминационных в свойство JSON id , как и раньше. Для этой цели был введен новый параметр конфигурации:

modelBuilder.Entity<Session>().HasDiscriminatorInJsonId();

Это делается для всех типов сущностей верхнего уровня, что делает EF так же, как и раньше.

На этом этапе вы также можете обновить все документы, чтобы переписать их свойство JSON id . Обратите внимание, что это возможно только в том случае, если сущности разных типов не используют одно и то же значение идентификатора в одном контейнере.

Изменения средней степени влияния

Синхронизация операций ввода-вывода с помощью поставщика Azure Cosmos DB больше не поддерживается

Отслеживание проблемы #32563

Старое поведение

Ранее вызов синхронных методов, таких ToList как или SaveChanges вызов EF Core, блокируют синхронно использование .GetAwaiter().GetResult() при выполнении асинхронных вызовов в пакете SDK Azure Cosmos DB. Это может привести к взаимоблокировке.

Новое поведение

Начиная с EF Core 9.0 EF теперь вызывается по умолчанию при попытке использовать синхронный ввод-вывод. Сообщение об исключении — "Azure Cosmos DB не поддерживает синхронный ввод-вывод. Не забудьте использовать и правильно ожидать только асинхронные методы при использовании Entity Framework Core для доступа к Azure Cosmos DB. Дополнительные сведения см. в статье https://aka.ms/ef-cosmos-nosync ".

Почему

Синхронная блокировка асинхронных методов может привести к взаимоблокировке, а пакет SDK Azure Cosmos DB поддерживает только асинхронные методы.

Устранение проблем

В EF Core 9.0 ошибка может быть отключена с помощью следующих способов:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
    optionsBuilder.ConfigureWarnings(w => w.Ignore(CosmosEventId.SyncNotSupported));
}

Это говорится, что приложения должны перестать использовать API синхронизации с Azure Cosmos DB, так как это не поддерживается пакетом SDK Azure Cosmos DB. Возможность подавления исключения будет удалена в будущем выпуске EF Core, после чего единственным вариантом будет использование асинхронных API.

Теперь запросы SQL должны проектируемые значения JSON напрямую

Отслеживание проблемы 25527

Старое поведение

Ранее EF создал запросы, такие как следующие:

SELECT c["City"] FROM root c

Такие запросы приводят к тому, что Azure Cosmos DB упаковывает каждый результат в объект JSON следующим образом:

[
    {
        "City": "Berlin"
    },
    {
        "City": "México D.F."
    }
]
Новое поведение

Начиная с EF Core 9.0, EF теперь добавляет VALUE модификатор в запросы следующим образом:

SELECT VALUE c["City"] FROM root c

Такие запросы приводят к тому, что Azure Cosmos DB возвращает значения напрямую без упаковки:

[
    "Berlin",
    "México D.F."
]

Если приложение использует запросы SQL, такие запросы, скорее всего, будут нарушены после обновления до EF 9.0, так как они не включают VALUE модификатор.

Почему

Упаковка каждого результата в дополнительный объект JSON может привести к снижению производительности в некоторых сценариях, раздувает полезные данные результата JSON и не является естественным способом работы с Azure Cosmos DB.

Устранение проблем

Чтобы устранить эту проблему, просто добавьте VALUE модификатор в проекции запросов SQL, как показано выше.

Неопределенные результаты теперь автоматически фильтруются из результатов запроса

Отслеживание проблемы 25527

Старое поведение

Ранее EF создал запросы, такие как следующие:

SELECT c["City"] FROM root c

Такие запросы приводят к тому, что Azure Cosmos DB упаковывает каждый результат в объект JSON следующим образом:

[
    {
        "City": "Berlin"
    },
    {
        "City": "México D.F."
    }
]

Если какой-либо из результатов не определен (например City , свойство отсутствует в документе), возвращается пустой документ, и EF возвращается null для этого результата.

Новое поведение

Начиная с EF Core 9.0, EF теперь добавляет VALUE модификатор в запросы следующим образом:

SELECT VALUE c["City"] FROM root c

Такие запросы приводят к тому, что Azure Cosmos DB возвращает значения напрямую без упаковки:

[
    "Berlin",
    "México D.F."
]

Поведение Azure Cosmos DB заключается в автоматическом фильтрации undefined значений из результатов. Это означает, что если один из City свойств отсутствует в документе, запрос вернет только один результат, а не два результата, с одним из nullних.

Почему

Упаковка каждого результата в дополнительный объект JSON может привести к снижению производительности в некоторых сценариях, раздувает полезные данные результата JSON и не является естественным способом работы с Azure Cosmos DB.

Устранение проблем

Если получение null значений для неопределенных результатов важно для приложения, выполните undefined объединение значений с null использованием нового EF.Functions.Coalesce оператора:

var users = await context.Customer
    .Select(c => EF.Functions.CoalesceUndefined(c.City, null))
    .ToListAsync();

Неправильно переведенные запросы больше не переводятся

Отслеживание проблемы #34123

Старое поведение

Ранее ef переведенные запросы, такие как следующие:

var sessions = await context.Sessions
    .Take(5)
    .Where(s => s.Name.StartsWith("f"))
    .ToListAsync();

Однако преобразование SQL для этого запроса было неверным:

SELECT c
FROM root c
WHERE ((c["Discriminator"] = "Session") AND STARTSWITH(c["Name"], "f"))
OFFSET 0 LIMIT @__p_0

В SQL WHERE предложение вычисляется перед OFFSET предложением и LIMIT предложениями, Take но в приведенном выше запросе LINQ оператор отображается перед операторомWhere. В результате такие запросы могут возвращать неверные результаты.

Новое поведение

Начиная с EF Core 9.0 такие запросы больше не переводятся, и создается исключение.

Почему

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

Устранение проблем

Если вы были довольны предыдущим поведением и хотели бы выполнить тот же SQL, просто переключите порядок операторов LINQ:

var sessions = await context.Sessions
    .Where(s => s.Name.StartsWith("f"))
    .Take(5)
    .ToListAsync();

К сожалению, Azure Cosmos DB в настоящее время не поддерживает OFFSET предложения в LIMIT вложенных запросах SQL, что является правильным переводом исходного запроса LINQ.

Изменения низкой степени влияния

HasIndex теперь бросает вместо того, чтобы игнорировать

Отслеживание проблемы #34023

Старое поведение

Ранее вызовы HasIndex были проигнорированы поставщиком EF Cosmos DB.

Новое поведение

Теперь поставщик создает исключение, если HasIndex задано.

Почему

В Azure Cosmos DB все свойства индексируются по умолчанию, и индексирование не требуется указывать. Хотя можно определить настраиваемую политику индексирования, она в настоящее время не поддерживается EF и может выполняться через портал Azure без поддержки EF. Так как HasIndex звонки ничего не делают, они больше не допускаются.

Устранение проблем

Удалите все вызовы HasIndex.

IncludeRootDiscriminatorInJsonId был HasRootDiscriminatorInJsonId переименован в после версии 9.0.0-rc.2

Отслеживание проблемы #34717

Старое поведение

API IncludeRootDiscriminatorInJsonId появился в версии 9.0.0 rc.1.

Новое поведение

Для окончательного выпуска EF Core 9.0 API был переименован в HasRootDiscriminatorInJsonId

Почему

Другой связанный API был переименован, чтобы начать Has с, а не Include, поэтому он был переименован для согласованности.

Устранение проблем

Если код использует IncludeRootDiscriminatorInJsonId API, просто измените его на ссылку HasRootDiscriminatorInJsonId .