Обучение
Схема обучения
Use advance techniques in canvas apps to perform custom updates and optimization - Training
Use advance techniques in canvas apps to perform custom updates and optimization
Этот браузер больше не поддерживается.
Выполните обновление до Microsoft Edge, чтобы воспользоваться новейшими функциями, обновлениями для системы безопасности и технической поддержкой.
Указанные ниже изменения в API и поведении могут нарушить работу существующих приложений при их обновлении до EF Core 5.0.0.
EF Core 3.1 предназначается для платформы .NET Standard 2.0, которая поддерживается платформой .NET Framework.
EF Core 5.0 предназначается для платформы .NET Standard 2.1, которая не поддерживается платформой .NET Framework. Это означает, что EF Core 5.0 нельзя использовать с приложениями .NET Framework.
Это часть масштабного процесса между группами .NET, направленного на унификацию на базе единой целевой платформы .NET. Дополнительные сведения см. в разделе Перспективы .NET Standard.
В приложениях .NET Framework по-прежнему возможно использование выпуска EF Core 3.1, который рассчитан на долгосрочную поддержку (LTS). Кроме того, можно обновить приложения для работы с .NET Core 3.1 и (или) .NET Core 5, которые поддерживают .NET Standard 2.1.
GetColumnName()
возвращал имя столбца, с которым сопоставлено свойство.
GetColumnName()
по-прежнему возвращает имя столбца, с которым сопоставлено свойство, но это поведение стало неоднозначным, так как EF Core 5 поддерживает TPT и одновременное сопоставление с представлением или функцией, где эти сопоставления могут использовать разные имена столбцов для одного и того же свойства.
Мы пометили этот метод как устаревший, чтобы помочь пользователям более точно перегружать GetColumnName(IProperty, StoreObjectIdentifier).
Если тип сущности сопоставляется только с одной таблицей и никогда не сопоставляется с представлениями, функциями или несколькими таблицами, GetColumnBaseName(IReadOnlyProperty) можно использовать в EF Core 5.0 и 6.0 для получения имени таблицы. Например:
var columnName = property.GetColumnBaseName();
В EF Core 7.0 это снова можно заменить новым GetColumnName
, который ведет себя так же, как исходный вариант для простых, только сопоставлений отдельных таблиц.
Если тип сущности может быть сопоставлен с представлениями, функциями или несколькими таблицами, StoreObjectIdentifier необходимо получить для идентификации таблицы, представления или функции. Затем его можно использовать для получения имени столбца для этого объекта хранилища. Например:
var columnName = property.GetColumnName(StoreObjectIdentifier.Table("Users", null)));
В EF Core, как правило, не устанавливались значения точности и масштаба для объектов SqlParameter. Это означает, что полные значения точности и масштаба отправлялись в SQL Server, где на этом этапе выполнялось округление в зависимости от значений точности и масштаба, заданных для столбца базы данных.
Теперь EF Core устанавливает значения точности и масштаба для параметров, используя значения, которые заданы в свойствах модели EF Core. Таким образом, округление выполняется в SqlClient. Следовательно, если заданные значения точности и масштаба не совпадают с аналогичными в базе данных, округление может выполняться иначе.
Для новых возможностей SQL Server, включая функцию Always Encrypted, требуется полностью указывать аспекты параметров. Кроме того, в результате внесенных в SqlClient изменений вместо усечения десятичных значений выполняется округление, что согласуется с поведением SQL Server. Благодаря этому EF Core может задавать эти аспекты для корректной настройки десятичных значений без изменения поведения.
Сопоставьте свойства десятичного значения с использованием имени типа, которое включает в себя значения точности и масштаба. Например:
public class Blog
{
public int Id { get; set; }
[Column(TypeName = "decimal(16, 5)")]
public decimal Score { get; set; }
}
Также можно использовать HasPrecision
в API построения модели. Например:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>().Property(e => e.Score).HasPrecision(16, 5);
}
При необходимости можно настроить только навигацию к основной сущности. Поэтому при применении RequiredAttribute
к переходу к зависимой сущности (сущности, содержащей внешний ключ) или ее маркировки в качестве не допускающей значения NULL вместо этого будет создан внешний ключ для определяющего типа сущности.
Благодаря добавленной поддержке обязательных зависимых объектов теперь можно пометить любую навигацию по ссылке как обязательную, а это означает, что в случае, показанном выше, внешний ключ будет определен на другой стороне отношения и свойства не будут помечены как обязательные.
Вызов IsRequired
перед указанием зависимого элемента теперь неоднозначен.
modelBuilder.Entity<Blog>()
.HasOne(b => b.BlogImage)
.WithOne(i => i.Blog)
.IsRequired()
.HasForeignKey<BlogImage>(b => b.BlogForeignKey);
Необходимо новое поведение для обеспечения поддержки требуемых зависимых объектов (см. #12100).
Удалите атрибут RequiredAttribute
из навигации в зависимый объект и поместите его вместо этого в навигацию к основному объекту или настройте связь в OnModelCreating
.
modelBuilder.Entity<Blog>()
.HasOne(b => b.BlogImage)
.WithOne(i => i.Blog)
.HasForeignKey<BlogImage>(b => b.BlogForeignKey)
.IsRequired();
Типы сущностей сопоставлялись с определяющими запросами на уровне ядра. Каждый раз, когда тип сущности использовался в запросе, корень типа сущности заменялся определяющим запросом для любого поставщика.
Интерфейсы API для определяющего запроса стали нерекомендуемыми. Появились новые интерфейсы API для конкретных поставщиков.
Хотя определяющий запрос был реализован как заменяющий при использовании корня запроса в запросе, существовал ряд проблем.
new { ... }
в методе Select
, определение его как сущности требовало дополнительной работы и не согласовывалось с тем, как номинальные типы в запросе обрабатываются в EF Core.FromSql
по-прежнему требуется для передачи строки SQL в виде выражения LINQ.Изначально определяющие запросы появились как представления на стороне клиента, предназначенные для использования с поставщиком в памяти для сущностей без ключей (аналогично представлениям базы данных в реляционных базах данных). Такое определение упрощает тестирование приложения с базой данных в памяти. Впоследствии они стали применяться более широко. Это было полезной возможностью, но вносило несогласованность и затрудняло понимание. Поэтому мы решили упростить принцип их работы. Теперь определяющий запрос на основе LINQ предназначен исключительно для поставщика в памяти и обрабатывается по-особому. Дополнительные сведения см. в этой проблеме.
Для реляционных поставщиков используйте метод ToSqlQuery
в OnModelCreating
и передайте строку SQL, которая должна использоваться для типа сущности.
Для поставщика в памяти используйте метод ToInMemoryQuery
в OnModelCreating
и передайте запрос SQL, который должен использоваться для типа сущности.
В EF Core 3.1 навигации по ссылкам, которые изначально инициализированы значениями, отличными от NULL, иногда перезаписывались экземплярами сущностей из базы данных, независимо от того, совпадали ли значения ключей. Однако в других случаях в EF Core 3.1 выполнялось обратное действие и оставлялось существующее значение, отличное от NULL.
Начиная с версии EF Core 5.0, навигации по пустой ссылке, которая не является пустой, никогда не перезаписываются экземплярами, возвращаемыми запросом.
Обратите внимание, что безотложная инициализация навигации по коллекции для пустой коллекции по-прежнему поддерживается.
Инициализация свойства навигации по ссылкам для "пустого" экземпляра сущности приводит к неоднозначному состоянию. Например:
public class Blog
{
public int Id { get; set; }
public Author Author { get; set; ) = new Author();
}
Обычно запрос блогов и авторов сначала создает экземпляры Blog
, а затем задает соответствующие экземпляры Author
на основе данных, возвращаемых из базы данных. Однако в этом случае каждое свойство Blog.Author
будет уже инициализировано для пустого свойства Author
. Кроме EF Core, не существует способа выяснить, является ли этот экземпляр "пустым". Таким образом, перезапись этого экземпляра может без уведомления привести к выводу допустимого Author
. Таким образом, EF Core 5.0 теперь не перезаписывает уже инициализированную навигацию.
Это новое поведение также соответствует обычному поведению EF6, хотя при исследовании мы также обнаружили некоторые случаи несогласованности в EF6.
В случае обнаружения такого прерывания исправление будет способствовать остановке активной инициализации свойств навигации по ссылкам.
Вызов ToView(string)
заставляет миграции игнорировать тип сущности в дополнение к сопоставлению его с представлением.
Теперь ToView(string)
помечает тип сущности как не сопоставленный с таблицей в дополнение к сопоставлению его с представлением. Это приводит к тому, что при первой миграции после обновления до EF Core 5 предпринимается попытка удаления таблицы по умолчанию для этого типа сущности, так как он больше не игнорируется.
EF Core теперь позволяет сопоставлять тип сущности и с таблицей, и с представлением одновременно, поэтому ToView
больше нельзя использовать для указания, что тип сущности следует игнорировать при миграции.
Используйте следующий код, чтобы пометить сопоставленную таблицу как исключенную из миграции:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<User>().ToTable("UserView", t => t.ExcludeFromMigrations());
}
ToTable(null)
сбрасывает имя таблицы в значение по умолчанию.
ToTable(null)
теперь помечает тип сущности как не сопоставленный с какой-либо таблицей.
EF Core теперь позволяет сопоставлять тип сущности и с таблицей, и с представлением одновременно, поэтому ToTable(null)
используется для указания, что тип сущности не сопоставлен ни с какой таблицей.
Используйте следующий код, чтобы сбрасывать имя таблицы в значение по умолчанию, если отсутствует сопоставление с представлением или DbFunction:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<User>().Metadata.RemoveAnnotation(RelationalAnnotationNames.TableName);
}
Метод HasGeometricDimension использовался для включения дополнительных измерений (Z и M) для столбцов геометрии. Однако он влиял только на создание базы данных. Его не требовалось указывать для запроса значений с дополнительными измерениями. Он также не работал корректно при вставке или обновлении значений с дополнительными измерениями (см. проблему #14257).
Чтобы включить вставку и обновление геометрических значений с дополнительными измерениями (Z и M), необходимо указать измерение в составе имени типа столбца. Этот интерфейс API более точно соответствует базовой работе функции AddGeometryColumn расширения SpatiaLite.
Использование метода HasGeometricDimension после указания измерения в типе столбца является ненужным и избыточным, поэтому этот метод полностью удален.
Чтобы указать измерение, используйте HasColumnType
:
modelBuilder.Entity<GeoEntity>(
x =>
{
// Allow any GEOMETRY value with optional Z and M values
x.Property(e => e.Geometry).HasColumnType("GEOMETRYZM");
// Allow only POINT values with an optional Z value
x.Property(e => e.Point).HasColumnType("POINTZ");
});
Свойство ключа секции добавлялось только в альтернативный ключ, включающий id
.
В соответствии с соглашением свойство ключа секции теперь также добавляется в первичный ключ.
Это изменение делает модель более согласованной с семантикой Azure Cosmos DB и повышает производительность Find
и некоторых запросов.
Чтобы предотвратить добавление свойства ключа секции в первичный ключ, настройте его в OnModelCreating
.
modelBuilder.Entity<Blog>()
.HasKey(b => b.Id);
Теневое свойство, сопоставленное со свойством JSON id
, также называлось id
.
Теневое свойство, создаваемое в соответствии с соглашением, теперь называется __id
.
Это изменение снижает вероятность того, что свойство id
будет конфликтовать с существующим свойством типа сущности.
Чтобы вернуться к поведению, принятому в версии 3.x, настройте свойство id
в OnModelCreating
.
modelBuilder.Entity<Blog>()
.Property<string>("id")
.ToJsonProperty("id");
Свойства типа byte[] хранились в виде числового массива.
Свойства типа byte[] теперь хранятся в виде строки base64.
Такое представление byte[] ближе к ожидаемому и используется по умолчанию в основных библиотеках сериализации JSON.
Существующие данные, хранящиеся в виде числовых массивов, будут по-прежнему запрашиваться правильно, но в настоящее время не существует способа восстановить прежнее поведение вставки. Если это ограничение не позволяет реализовать ваш сценарий, оставьте комментарий к этой проблеме
Ранее методы расширения назывались GetPropertyName
и SetPropertyName
.
Старый интерфейс API был удален, и были добавлены новые методы: GetJsonPropertyName
и SetJsonPropertyName
.
Данное изменение устраняет неоднозначность в отношении того, что настраивают эти методы.
Используйте новый API.
Генераторы значений вызывались только при изменении состояния сущности на «добавлено».
Генераторы значений теперь вызываются, когда состояние сущности меняется с "отсоединено" на "не изменено", "обновлено" или "удалено", а свойство содержит значения по умолчанию.
Это изменение было необходимо для улучшения работы со свойствами, которые не сохраняются в хранилище данных, а их значения всегда создаются на клиенте.
Чтобы предотвратить вызов генератора значений, присвойте свойству значение, отличное от значения по умолчанию, до изменения состояния.
API IMigrationsModelDiffer
определялся с помощью IModel
.
Теперь API IMigrationsModelDiffer
использует IRelationalModel
. Однако моментальный снимок модели по-прежнему содержит только IModel
, так как этот код является частью приложения, и Entity Framework не может изменить его без внесения значительных изменений.
IRelationalModel
— это вновь добавленное представление схемы базы данных. Его использование для поиска различий выполняется быстрее и точнее.
Используйте следующий код для сравнения модели из snapshot
с моделью из context
.
var dependencies = context.GetService<ProviderConventionSetBuilderDependencies>();
var relationalDependencies = context.GetService<RelationalConventionSetBuilderDependencies>();
var typeMappingConvention = new TypeMappingConvention(dependencies);
typeMappingConvention.ProcessModelFinalizing(((IConventionModel)modelSnapshot.Model).Builder, null);
var relationalModelConvention = new RelationalModelConvention(dependencies, relationalDependencies);
var sourceModel = relationalModelConvention.ProcessModelFinalized(snapshot.Model);
var modelDiffer = context.GetService<IMigrationsModelDiffer>();
var hasDifferences = modelDiffer.HasDifferences(
((IMutableModel)sourceModel).FinalizeModel().GetRelationalModel(),
context.Model.GetRelationalModel());
Мы планируем улучшить этот процесс в версии 6.0 (см. #22031).
Можно было изменить значение дискриминатора перед вызовом метода SaveChanges
В приведенном выше случае будет вызвано исключение.
EF не ожидает изменения типа сущности, пока он еще отслеживается, поэтому изменение значения дискриминатора оставляет контекст в несогласованном состоянии, что может привести к непредвиденному поведению.
Если необходимо изменить значение дискриминатора и контекст будет удален сразу после вызова SaveChanges
, дискриминатор можно сделать изменяемым.
modelBuilder.Entity<BaseEntity>()
.Property<string>("Discriminator")
.Metadata.SetAfterSaveBehavior(PropertySaveBehavior.Save);
Относящиеся к конкретному поставщику методы EF.Functions содержали реализацию для выполнения на стороне клиента, что позволяло им выполняться в поставщике InMemory. Например, EF.Functions.DateDiffDay
— это относящийся к SQL Server метод, который работал в поставщике InMemory.
Методы, относящиеся к конкретному поставщику, были изменены так, чтобы в теле метода создавалось исключение, блокирующее вычисление метода на стороне клиента.
Методы, относящиеся к конкретному поставщику, сопоставляются с функцией базы данных. Вычисление, выполняемое сопоставленной функцией базы данных, не всегда может реплицироваться на стороне клиента в LINQ. Поэтому результаты, полученные при выполнении одного и того же метода на сервере и в клиенте, могут различаться. Так как эти методы используются в LINQ для преобразования в определенные функции базы данных, их не нужно вычислять на стороне клиента. Так как поставщик InMemory — это другая база данных, эти методы недоступны для него. При попытке выполнить их для поставщика InMemory или любого другого поставщика, который не преобразует эти методы, создается исключение.
Так как точно воссоздать поведение функций базы данных невозможно, содержащие их запросы следует тестировать на основе базы данных того же типа, что и рабочая база данных.
Ранее допускалось определение только одного индекса для конкретного набора свойств. Имя для базы данных индекса настраивалось с использованием IndexBuilder.HasName.
Теперь допускается несколько индексов для одного набора свойств. Эти индексы отличаются именами в пределах модели. По соглашению имя модели используется в качестве имени базы данных, но его также можно указать независимо с использованием HasDatabaseName.
В будущем мы хотим включить для набора свойств параллельные индексы с сортировкой по возрастанию и убыванию, а также индексы с разными параметрами сортировки. Это изменение продвигает нас в этом направлении.
Любой код, который ранее вызывал IndexBuilder.HasName, необходимо изменить для вызова HasDatabaseName.
Если в проекте есть миграции, созданные до версии EF Core 2.0.0, вы можете игнорировать предупреждения для этих файлов, подавив их с помощью #pragma warning disable 612, 618
.
Ранее нужно было устанавливать отдельный пакет, чтобы преобразовывать имена DbSet и коллекций во множественное число и имена таблиц в единственное число при создании шаблонов DbContext и типов сущностей путем реконструирования из схемы базы данных.
Теперь EF Core содержит собственное средство для преобразования во множественное число на основе библиотеки Humanizer. Именно эту библиотеку использует Visual Studio, рекомендуя имена переменных.
Использование форм множественного числа для свойств коллекций и единственного числа для типов и ссылочных свойств считается идиоматичным в .NET.
Чтобы отключить средство для преобразования во множественное число, укажите параметр --no-pluralize
для dotnet ef dbcontext scaffold
или -NoPluralize
для Scaffold-DbContext
.
В версиях EF Core, предшествующих версии 5.0, поддерживалась только одна форма свойства навигации, представленная интерфейсом INavigation
.
EF Core 5.0 представляет связи "многие ко многим", которые используют "пропуск навигации". Они представлены интерфейсом ISkipNavigation
, а большинство функциональных возможностей INavigation
переданы в общий базовый интерфейс: INavigationBase
.
Большая часть функций между обычной навигацией и пропуском навигации одинаковы. Тем не менее пропуск навигации имеет другую связь с внешними ключами по сравнению с обычной навигацией, поскольку вовлеченные внешние ключи находятся не на обоих концах связи, а скорее в объекте соединения.
Во многих случаях приложения могут переключиться на использование нового базового интерфейса, не нуждаясь в дополнительных изменениях. Однако если навигация используется для доступа к свойствам внешнего ключа, код приложения нужно либо ограничить только обычными переходами, либо обновить, чтобы он мог выполнять соответствующие действия как для обычной, так и для пропускной навигации.
Distinct
или GroupBy
, больше не поддерживаются.Старое поведение
Ранее мы позволяли выполнять запросы, включающие коррелированные коллекции с последующим свойством GroupBy
и некоторые запросы с использованием Distinct
.
Пример GroupBy:
context.Parents
.Select(p => p.Children
.GroupBy(c => c.School)
.Select(g => g.Key))
Пример Distinct
, в частности запросы Distinct
, в которых внутренняя проекция коллекции не содержит первичный ключ:
context.Parents
.Select(p => p.Children
.Select(c => c.School)
.Distinct())
Эти запросы могли возвращать неверные результаты, если внутренняя коллекция содержала дубликаты, однако работала должным образом, если все элементы во внутренней коллекции являлись уникальными.
Новое поведение
Эти запросы больше не поддерживаются. Создается исключение, указывающее, что у нас недостаточно сведений для правильного создания результатов.
Причина
В сценариях с коррелированными коллекциями, чтобы назначить сущность коллекции правильному родительскому объекту, нужно знать первичный ключ сущности. Если внутренняя коллекция не использует GroupBy
или Distinct
, отсутствующий первичный ключ можно просто добавить в проекцию. Однако при использовании GroupBy
и Distinct
это не удастся сделать, так как такое действие приведет к изменению результата операции GroupBy
или Distinct
.
Устранение рисков
Перепишите запрос, чтобы не использовать операции GroupBy
или Distinct
во внутренней коллекции, и выполните эти операции на клиенте.
context.Parents
.Select(p => p.Children.Select(c => c.School))
.ToList()
.Select(x => x.GroupBy(c => c).Select(g => g.Key))
context.Parents
.Select(p => p.Children.Select(c => c.School))
.ToList()
.Select(x => x.Distinct())
Старое поведение
Ранее в отдельных случаях коллекцию запрашиваемых типов можно было использовать в проекции, например как аргумент для конструктора List<T>
:
context.Blogs
.Select(b => new List<Post>(context.Posts.Where(p => p.BlogId == b.Id)))
Новое поведение
Эти запросы больше не поддерживаются. Создается исключение с указанием на невозможность создания объекта запрашиваемого типа и предложением того, как это можно исправить.
Причина
Мы не можем реализовать объект запрашиваемого типа, поэтому такие объекты автоматически создавались с помощью типа List<T>
. Это часто приводило к выводу исключения из-за несоответствия типов, которое было непонятным и могло быть неожиданным для некоторых пользователей. Мы решили распознать шаблон и выдать более осмысленное исключение.
Устранение рисков
Добавьте вызов ToList()
после запрашиваемого объекта в проекции:
context.Blogs.Select(b => context.Posts.Where(p => p.BlogId == b.Id).ToList())
Обучение
Схема обучения
Use advance techniques in canvas apps to perform custom updates and optimization - Training
Use advance techniques in canvas apps to perform custom updates and optimization
Документация
Критические изменения в EF Core 6.0 — EF Core
Полный список критических изменений, появившихся в Entity Framework Core 6.0
Критические изменения в EF Core 3.x — EF Core
Полный список критических изменений, появившихся в Entity Framework Core 3.x
Критические изменения в EF Core 8.0 (EF8) — EF Core
Полный список критических изменений, представленных в Entity Framework Core 8.0 (EF8)