Changements cassants dans EF Core 6.0
Les modifications de comportement et d’API suivantes peuvent interrompre la mise à jour des applications existantes vers EF Core 6.0.
Framework cible
EF Core 6.0 cible .NET 6. Les applications ciblant les versions antérieures de .NET, .NET Core et .NET Framework devront cibler .NET 6 pour utiliser EF Core 6.0.
Résumé
* Ces modifications sont particulièrement intéressantes pour les auteurs de fournisseurs et d’extensions de base de données.
Modifications à fort impact
Les dépendances facultatives imbriquées partageant une table et sans propriétés requises ne sont pas autorisées
Ancien comportement
Les modèles avec des dépendants facultatifs imbriqués partageant une table et sans propriétés requises sont autorisés, mais peuvent entraîner une perte de données lors de l’interrogation des données, puis l’enregistrement à nouveau. Par exemple, considérez le modèle suivant :
public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
public ContactInfo ContactInfo { get; set; }
}
[Owned]
public class ContactInfo
{
public string Phone { get; set; }
public Address Address { get; set; }
}
[Owned]
public class Address
{
public string House { get; set; }
public string Street { get; set; }
public string City { get; set; }
public string Postcode { get; set; }
}
Aucune des propriétés dans ContactInfo
ou Address
n’est requise, et tous ces types d’entités sont mappés à la même table. Les règles de dépendances facultatives (par opposition aux dépendances requises) indiquent que si toutes les colonnes de ContactInfo
sont null, aucune instance de ContactInfo
ne sera créée lors de l’interrogation du propriétaire Customer
. Toutefois, cela signifie également qu’aucune instance de Address
ne sera créée, même si les colonnes Address
ne sont pas null.
Nouveau comportement
Si vous tentez d’utiliser ce modèle, l’exception suivante est levée :
System.InvalidOperationException : le type d’entité « ContactInfo » est un dépendant facultatif à l’aide du partage de tables et contenant d’autres dépendants sans propriété non partagée requise pour identifier si l’entité existe. If all nullable properties contain a null value in database then an object instance won't be created in the query causing nested dependent's values to be lost. Ajoutez une propriété requise pour créer des instances avec des valeurs Null pour d’autres propriétés ou marquer la navigation entrante comme nécessaire pour toujours créer une instance.
Cela empêche la perte de données lors de l’interrogation et de l’enregistrement des données.
Pourquoi
L’utilisation de modèles avec des dépendants facultatifs imbriqués partageant une table et sans propriétés requises entraîne souvent une perte de données silencieuse.
Corrections
Évitez d’utiliser des dépendances facultatives partageant une table et sans propriétés requises. Il existe trois façons simples de procéder :
Rendez les dépendants nécessaires. Cela signifie que l’entité dépendante aura toujours une valeur après son interrogation, même si toutes ses propriétés sont null. Par exemple :
public class Customer { public int Id { get; set; } public string Name { get; set; } [Required] public Address Address { get; set; } }
Ou :
modelBuilder.Entity<Customer>( b => { b.OwnsOne(e => e.Address); b.Navigation(e => e.Address).IsRequired(); });
Assurez-vous que la dépendance contient au moins une propriété requise.
Mappez les dépendances facultatives à leur propre table, au lieu de partager une table avec le principal. Par exemple :
modelBuilder.Entity<Customer>( b => { b.ToTable("Customers"); b.OwnsOne(e => e.Address, b => b.ToTable("CustomerAddresses")); });
Les problèmes liés aux dépendances facultatives et exemples de ces atténuations sont inclus dans la documentation relative à nouveautés d’EF Core 6.0.
Changements à impact moyen
La modification du propriétaire d’une entité détenue lève désormais une exception
Ancien comportement
Il était possible de réassigner une entité appartenant à une autre entité propriétaire.
Nouveau comportement
Cette action lèvera à présent une exception :
Propriété « {entityType} ». {property}' fait partie d’une clé et ne peut donc pas être modifié ou marqué comme modifié. Pour modifier le principal d’une entité existante avec une clé étrangère identifiante, supprimez d’abord le dépendant et appelez « SaveChanges », puis associez le dépendant au nouveau principal.
Pourquoi
Même s’il n’est pas nécessaire que les propriétés de clé existent sur un type détenu, EF crée toujours des propriétés Shadow à utiliser comme clé primaire et la clé étrangère qui pointe vers le propriétaire. Lorsque l’entité propriétaire est modifiée, elle entraîne la modification des valeurs de la clé étrangère sur l’entité détenue et, étant donné qu’elle est également utilisée comme clé primaire, l’identité de l’entité change. Cela n’est pas encore entièrement pris en charge dans EF Core et n’a été autorisé de manière conditionnelle que pour les entités détenues, ce qui entraîne parfois l’incohérence de l’état interne.
Corrections
Au lieu d’affecter la même instance détenue à un nouveau propriétaire, vous pouvez attribuer une copie et supprimer l’ancienne.
Azure Cosmos DB : Les types d’entités associés sont découverts comme appartenant à
Problème de suivi #24803Nouveautés : valeur par défaut de propriété implicite
Ancien comportement
Comme dans d’autres fournisseurs, les types d’entités associés ont été découverts en tant que types normaux (non détenus).
Nouveau comportement
Les types d’entités associés seront désormais détenus par le type d’entité sur lequel ils ont été découverts. Seuls les types d’entités qui correspondent à une propriété DbSet<TEntity> sont découverts comme non détenus.
Pourquoi
Ce comportement suit le modèle commun de données de modélisation dans Azure Cosmos DB de l’incorporation de données associées dans un document unique. Azure Cosmos DB ne prend pas en charge en mode natif la jonction de différents documents. Par conséquent, la modélisation des entités associées comme non détenues a une utilité limitée.
Corrections
Pour configurer un type d’entité pour qu’il soit modelBuilder.Entity<MyEntity>();
d’appel non propriétaire
SQLite : Les connexions sont mises en pool
Problème de suivi #13837Nouveautés : valeur par défaut : propriété implicite
Ancien comportement
Auparavant, les connexions dans Microsoft.Data.Sqlite n’étaient pas mises en pool.
Nouveau comportement
À partir de 6,0, les connexions sont désormais regroupées par défaut. Cela entraîne l’ouverture des fichiers de base de données par le processus, même après la fermeture de l’objet de connexion ADO.NET.
Pourquoi
Le regroupement des connexions sous-jacentes améliore considérablement les performances de l’ouverture et de la fermeture d’objets de connexion ADO.NET. Cela est particulièrement visible pour les scénarios où l’ouverture de la connexion sous-jacente est coûteuse comme dans le cas du chiffrement, ou dans les scénarios où il existe beaucoup de connexion à la base de données.
Corrections
Le regroupement de connexions peut être désactivé en ajoutant Pooling=False
à une chaîne de connexion.
Certains scénarios (comme la suppression du fichier de base de données) peuvent maintenant rencontrer des erreurs indiquant que le fichier est toujours en cours d’utilisation. Vous pouvez effacer manuellement le pool de connexions avant d’effectuer des opérations du fichier à l’aide de SqliteConnection.ClearPool()
.
SqliteConnection.ClearPool(connection);
File.Delete(databaseFile);
Les relations plusieurs-à-plusieurs sans entités de jointure mappées sont désormais générées automatiquement
Ancien comportement
Génération automatique (ingénierie inverse) d’un DbContext
et des types d’entités d’une base de données existante mappées explicitement des tables de jointure pour joindre des types d’entités pour des relations plusieurs-à-plusieurs.
Nouveau comportement
Les tables de jointure simples contenant seulement deux propriétés de clé étrangère à d’autres tables ne sont plus mappées aux types d’entités explicites, mais sont plutôt mappées en tant que relation plusieurs-à-plusieurs entre les deux tables jointes.
Pourquoi
Les relations plusieurs-à-plusieurs sans types de jointure explicites ont été introduites dans EF Core 5.0 et sont un moyen plus propre et plus naturel de représenter des tables de jointure simples.
Corrections
Il existe deux atténuations. L’approche recommandée consiste à mettre à jour le code pour utiliser directement les relations plusieurs-à-plusieurs. Il est très rare que le type d’entité de jointure doit être utilisé directement lorsqu’il contient seulement deux clés étrangères pour les relations plusieurs-à-plusieurs.
Vous pouvez également rajouter l’entité de jointure explicite au modèle EF. Par exemple, en supposant une relation plusieurs-à-plusieurs entre Post
et Tag
, ajoutez le type de jointure et les navigations à l’aide de classes partielles :
public partial class PostTag
{
public int PostsId { get; set; }
public int TagsId { get; set; }
public virtual Post Posts { get; set; }
public virtual Tag Tags { get; set; }
}
public partial class Post
{
public virtual ICollection<PostTag> PostTags { get; set; }
}
public partial class Tag
{
public virtual ICollection<PostTag> PostTags { get; set; }
}
Ajoutez ensuite la configuration pour le type de jointure et les navigations à une classe partielle pour DbContext :
public partial class DailyContext
{
partial void OnModelCreatingPartial(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Post>(entity =>
{
entity.HasMany(d => d.Tags)
.WithMany(p => p.Posts)
.UsingEntity<PostTag>(
l => l.HasOne<Tag>(e => e.Tags).WithMany(e => e.PostTags).HasForeignKey(e => e.TagsId),
r => r.HasOne<Post>(e => e.Posts).WithMany(e => e.PostTags).HasForeignKey(e => e.PostsId),
j =>
{
j.HasKey("PostsId", "TagsId");
j.ToTable("PostTag");
});
});
}
}
Enfin, supprimez la configuration générée pour la relation plusieurs-à-plusieurs du contexte généré. Cela est nécessaire, car le type d’entité de jointure généré automatiquement doit être supprimé du modèle avant que le type explicite puisse être utilisé. Ce code doit être supprimé chaque fois que le contexte est généré automatiquement, mais parce que le code ci-dessus se trouve dans des classes partielles, il est conservé.
Notez qu’avec cette configuration, l’entité de jointure peut être utilisée explicitement, comme dans les versions précédentes d’EF Core. Toutefois, la relation peut également être utilisée comme relation plusieurs-à-plusieurs. Cela signifie que la mise à jour du code comme celle-ci peut être une solution temporaire tandis que le reste du code est mis à jour pour utiliser la relation comme un plusieurs-à-plusieurs de la manière naturelle.
Modifications à faible impact
Nettoyage du mappage entre les valeurs DeleteBehavior et ON DELETE
Ancien comportement
Certains mappages entre le comportement OnDelete()
d’une relation et le comportement ON DELETE
des clés étrangères dans la base de données étaient incohérents dans les migrations et la génération de modèles automatique.
Nouveau comportement
Le tableau suivant illustre les modifications apportées aux Migrations.
OnDelete() | EN CAS DE SUPPRESSION |
---|---|
NoAction | NO ACTION |
ClientNoAction | NO ACTION |
Restreindre | RESTRICT |
Cascade | CASCADE |
ClientCascade | |
SetNull | SET NULL |
ClientSetNull |
Les modifications apportées à génération de modèles automatique sont les suivantes.
EN CAS DE SUPPRESSION | OnDelete() |
---|---|
NO ACTION | ClientSetNull |
RESTRICT | |
CASCADE | En cascade |
SET NULL | SetNull |
Pourquoi
Les nouveaux mappages sont plus cohérents. Le comportement de base de données par défaut d’aucune ACTION est maintenant préféré par rapport au comportement de restriction plus restrictif et moins performant.
Corrections
Le comportement de OnDelete () par défaut des relations facultatives est ClientSetNull. Son mappage est passé de restriction à aucune ACTION. Cela peut entraîner la génération d’un grand nombre d’opérations dans la première migration ajoutée après la mise à niveau vers EF Core 6,0.
Vous pouvez choisir d’appliquer ces opérations ou de les supprimer manuellement de la migration, car elles n’ont aucun impact fonctionnel sur EF Core.
SQL Server ne prend pas en charge la restriction, donc ces clés étrangères ont déjà été créées en utilisant aucune ACTION. Les opérations de migration n’ont aucune incidence sur les SQL Server et peuvent être supprimées en toute sécurité.
Base de données en mémoire valide les propriétés requises ne contiennent pas de valeurs Null
Ancien comportement
La base de données en mémoire a autorisé l’enregistrement des valeurs Null même lorsque la propriété a été configurée comme nécessaire.
Nouveau comportement
La base de données en mémoire lève une Microsoft.EntityFrameworkCore.DbUpdateException
lorsque SaveChanges
ou SaveChangesAsync
est appelée et qu’une propriété requise est définie sur Null.
Pourquoi
Le comportement de la base de données en mémoire correspond désormais au comportement des autres bases de données.
Corrections
Le comportement précédent (c’est-à-dire ne pas vérifier les valeurs null) peut être restauré lors de la configuration du fournisseur en mémoire. Par exemple :
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder
.UseInMemoryDatabase("MyDatabase", b => b.EnableNullChecks(false));
}
Suppression de la dernière commande ORDER BY lors de la jointure pour les collections
Ancien comportement
Lors de l’exécution de SQL jointures sur des regroupements (relations un-à-plusieurs), EF Core utilisé pour ajouter une clause ORDER BY pour chaque colonne clé de la table jointe. Par exemple, le chargement de tous les blogs avec les publications associées a été effectué via les SQL suivantes :
SELECT [b].[BlogId], [b].[Name], [p].[PostId], [p].[BlogId], [p].[Title]
FROM [Blogs] AS [b]
LEFT JOIN [Post] AS [p] ON [b].[BlogId] = [p].[BlogId]
ORDER BY [b].[BlogId], [p].[PostId]
Ces ordonnancements sont nécessaires pour la matérialisation correcte des entités.
Nouveau comportement
La dernière commande pour une jointure de collection est désormais omise :
SELECT [b].[BlogId], [b].[Name], [p].[PostId], [p].[BlogId], [p].[Title]
FROM [Blogs] AS [b]
LEFT JOIN [Post] AS [p] ON [b].[BlogId] = [p].[BlogId]
ORDER BY [b].[BlogId]
Une clause ORDER BY pour la colonne ID de la publication n’est plus générée.
Pourquoi
Chaque commande en impose des tâches supplémentaires côté base de données, et le dernier classement n’est pas nécessaire pour les besoins de matérialisation de EF Core. Les données montrent que la suppression de ce dernier classement peut entraîner une amélioration significative des performances dans certains scénarios.
Corrections
Si votre application s’attend à ce que les entités jointes soient retournées dans un ordre particulier, faites cela explicitement en ajoutant un opérateur LINQ OrderBy
à votre requête.
DbSet n’implémente plus IAsyncEnumerable
Ancien comportement
DbSet<TEntity>, qui est utilisé pour exécuter des requêtes sur DbContext, utilisées pour implémenter IAsyncEnumerable<T>.
Nouveau comportement
DbSet<TEntity> n’implémente plus directement IAsyncEnumerable<T>.
Pourquoi
DbSet<TEntity> a été initialement faite pour implémenter des IAsyncEnumerable<T> principalement afin d’autoriser l’énumération directe sur celle-ci via la construction foreach
. Malheureusement, lorsqu’un projet fait également référence à System.Linq.Async afin de composer des opérateurs LINQ asynchrones côté client, cela a entraîné une erreur d’appel ambiguë entre les opérateurs définis sur IQueryable<T>
et ceux définis sur IAsyncEnumerable<T>
. C# 9 a ajouté extension GetEnumerator
prise en charge des foreach
boucles , en supprimant la raison principale d’origine de référencer IAsyncEnumerable
.
La grande majorité des utilisations de DbSet
continueront de fonctionner comme c’est le cas, puisqu’elles composent des opérateurs LINQ sur DbSet
, l’énumèrent, etc. Les seules utilisations rompues sont celles qui tentent de caster DbSet
directement vers IAsyncEnumerable
.
Corrections
Si vous devez faire référence à un DbSet<TEntity> en tant que IAsyncEnumerable<T>, appelez DbSet<TEntity>.AsAsyncEnumerable pour le caster explicitement.
Le type d’entité de retour de TVF est également mappé à une table par défaut
Ancien comportement
Un type d’entité n’a pas été mappé à une table par défaut lorsqu’il est utilisé comme type de retour d’une valeur TVF configurée avec HasDbFunction.
Nouveau comportement
Un type d’entité utilisé comme type de retour d’une fonction TVF conserve le mappage de table par défaut.
Pourquoi
Il n’est pas intuitif que la configuration d’une fonction TVF supprime le mappage de table par défaut pour le type d’entité de retour.
Corrections
Pour supprimer le mappage de table par défaut, appelez ToTable(EntityTypeBuilder, String):
modelBuilder.Entity<MyEntity>().ToTable((string?)null));
L’unicité du nom de la contrainte de validation est maintenant validée
Ancien comportement
Les contraintes de vérification portant le même nom ont été autorisées à être déclarées et utilisées sur la même table.
Nouveau comportement
La configuration explicite de deux contraintes de vérification portant le même nom sur la même table entraîne désormais une exception. Les contraintes de vérification créées par une convention reçoivent un nom unique.
Pourquoi
La plupart des bases de données n’autorisent pas la création de deux contraintes de vérification portant le même nom sur la même table, et certaines nécessitent qu’elles soient uniques même entre les tables. Cela entraînerait la levée d’une exception lors de l’application d’une migration.
Corrections
Dans certains cas, les noms de contrainte de validation valides peuvent être différents en raison de cette modification. Pour spécifier explicitement le nom souhaité, appelez HasName:
modelBuilder.Entity<MyEntity>().HasCheckConstraint("CK_Id", "Id > 0", c => c.HasName("CK_MyEntity_Id"));
Ajout des interfaces de métadonnées IReadOnly et suppression des méthodes d’extension
Ancien comportement
Il y avait trois ensembles d’interfaces de métadonnées : IModel, IMutableModel et IConventionModel ainsi que des méthodes d’extension.
Nouveau comportement
Un nouvel ensemble d’interfaces IReadOnly
a été ajouté, par exemple IReadOnlyModel. Les méthodes d’extension précédemment définies pour les interfaces de métadonnées ont été converties en méthodes d’interface par défaut.
Pourquoi
Les méthodes d’interface par défaut autorisent la substitution de l’implémentation, ce qui est exploité par la nouvelle implémentation du modèle d’exécution pour offrir de meilleures performances.
Corrections
Ces modifications ne doivent pas affecter la plupart du code. Toutefois, si vous utilisiez les méthodes d’extension via la syntaxe d’appel statique, elle doit être convertie en syntaxe d’appel d’instance.
IExecutionStrategy est désormais un service singleton
Nouveau comportement
IExecutionStrategy est maintenant un service singleton. Cela signifie que tout état ajouté dans les implémentations personnalisées reste entre les exécutions et que le délégué passé à ExecutionStrategy ne sera exécuté qu’une seule fois.
Pourquoi
Cette réduction des allocations sur deux chemins chauds dans EF.
Corrections
Les implémentations dérivant de ExecutionStrategy doivent effacer n’importe quel état dans OnFirstExecution().
La logique conditionnelle dans le délégué passé à ExecutionStrategy doit être déplacée vers une implémentation personnalisée de IExecutionStrategy.
SQL Server : plus d’erreurs sont considérées comme temporaires
Nouveau comportement
Les erreurs répertoriées dans le problème ci-dessus sont désormais considérées comme temporaires. Lors de l’utilisation de la stratégie d’exécution par défaut (sans nouvelle tentative), ces erreurs sont à présent encapsulées dans une instance d’exception d’addition.
Pourquoi
Nous continuons à recueillir des commentaires des utilisateurs et de l’équipe SQL Server sur les erreurs qui doivent être considérées comme temporaires.
Corrections
Pour modifier l’ensemble d’erreurs considérées comme temporaires, utilisez une stratégie d’exécution personnalisée qui pourrait être dérivée de SqlServerRetryingExecutionStrategy - résilience de connexion - EF Core.
Azure Cosmos DB : d’autres caractères sont échappés dans les valeurs « id »
Ancien comportement
Dans EF Core 5, seule '|'
a été échappée dans id
valeurs.
Nouveau comportement
Dans EF Core 6, '/'
, '\'
, '?'
et '#'
sont également placés dans des valeurs id
.
Pourquoi
Ces caractères ne sont pas valides, comme indiqué dans Resource.Id. L’utilisation de ces derniers dans id
entraîne l’échec des requêtes.
Corrections
Vous pouvez remplacer la valeur générée en la définissant avant que l’entité soit marquée comme Added
:
var entry = context.Attach(entity);
entry.Property("__id").CurrentValue = "MyEntity|/\\?#";
entry.State = EntityState.Added;
Certains services Singleton sont désormais étendus
Nouveau comportement
De nombreux services de requête et certains services au moment de la conception qui ont été enregistrés comme Singleton
sont désormais inscrits en tant que Scoped
.
Pourquoi
La durée de vie devait être modifiée pour permettre à une nouvelle fonctionnalité - DefaultTypeMapping - d’affecter les requêtes.
Les durées de vie des services au moment du design ont été ajustées pour correspondre aux durées de vie des services d’exécution afin d’éviter des erreurs lors de l’utilisation des deux.
Corrections
Utilisez TryAdd pour inscrire des services EF Core à l’aide de la durée de vie par défaut. Utilisez uniquement TryAddProviderSpecificServices pour les services qui ne sont pas ajoutés par EF.
Nouvelle API de mise en cache pour les extensions qui ajoutent ou remplacent des services
Ancien comportement
Dans EF Core 5, GetServiceProviderHashCode retourné long
et a été utilisé directement dans le cadre de la clé de cache pour le fournisseur de services.
Nouveau comportement
GetServiceProviderHashCode retourne maintenant int
et est utilisé uniquement pour calculer le code de hachage de la clé de cache pour le fournisseur de services.
En outre, ShouldUseSameServiceProvider doit être implémenté pour indiquer si l’objet actuel représente la même configuration de service et peut donc utiliser le même fournisseur de services.
Pourquoi
L’utilisation d’un code de hachage dans le cadre de la clé de cache a entraîné des collisions occasionnelles qui étaient difficiles à diagnostiquer et à résoudre. La méthode supplémentaire garantit que le même fournisseur de services est utilisé uniquement lorsque cela est approprié.
Corrections
De nombreuses extensions n’exposent aucune option qui affecte les services inscrits et peuvent utiliser l’implémentation suivante de ShouldUseSameServiceProvider:
private sealed class ExtensionInfo : DbContextOptionsExtensionInfo
{
public ExtensionInfo(IDbContextOptionsExtension extension)
: base(extension)
{
}
...
public override bool ShouldUseSameServiceProvider(DbContextOptionsExtensionInfo other)
=> other is ExtensionInfo;
}
Dans le cas contraire, des prédicats supplémentaires doivent être ajoutés pour comparer toutes les options pertinentes.
Nouvelle procédure d’initialisation de modèle de capture instantanée et au moment du design
Ancien comportement
Dans EF Core 5, les conventions spécifiques devaient être appelées avant que le modèle d’instantané ne soit prêt à être utilisé.
Nouveau comportement
IModelRuntimeInitializer a été introduit pour masquer certaines des étapes requises, et un modèle d’exécution a été introduit qui n’a pas toutes les métadonnées de migration, de sorte que le modèle au moment du design doit être utilisé pour la différence de modèle.
Pourquoi
IModelRuntimeInitializer supprime les étapes de finalisation du modèle, de sorte qu’elles peuvent maintenant être modifiées sans apporter d’autres changements cassants aux utilisateurs.
Le modèle d’exécution optimisé a été introduit pour améliorer les performances d’exécution. Il a plusieurs optimisations, dont l’une supprime les métadonnées qui ne sont pas utilisées au moment de l’exécution.
Corrections
L’extrait de code suivant montre comment vérifier si le modèle actuel est différent du modèle d’instantané :
var snapshotModel = migrationsAssembly.ModelSnapshot?.Model;
if (snapshotModel is IMutableModel mutableModel)
{
snapshotModel = mutableModel.FinalizeModel();
}
if (snapshotModel != null)
{
snapshotModel = context.GetService<IModelRuntimeInitializer>().Initialize(snapshotModel);
}
var hasDifferences = context.GetService<IMigrationsModelDiffer>().HasDifferences(
snapshotModel?.GetRelationalModel(),
context.GetService<IDesignTimeModel>().Model.GetRelationalModel());
Cet extrait de code montre comment implémenter IDesignTimeDbContextFactory<TContext> en créant un modèle en externe et en appelant UseModel:
internal class MyDesignContext : IDesignTimeDbContextFactory<MyContext>
{
public TestContext CreateDbContext(string[] args)
{
var optionsBuilder = new DbContextOptionsBuilder();
optionsBuilder.UseSqlServer(Configuration.GetConnectionString("DB"));
var modelBuilder = SqlServerConventionSetBuilder.CreateModelBuilder();
CustomizeModel(modelBuilder);
var model = modelBuilder.Model.FinalizeModel();
var serviceContext = new MyContext(optionsBuilder.Options);
model = serviceContext.GetService<IModelRuntimeInitializer>().Initialize(model);
return new MyContext(optionsBuilder.Options);
}
}
OwnedNavigationBuilder.HasIndex
retourne un type différent maintenant
Ancien comportement
Dans EF Core 5, HasIndex retourné IndexBuilder<TEntity>
où TEntity
est le type de propriétaire.
Nouveau comportement
HasIndex retourne maintenant IndexBuilder<TDependentEntity>
, où TDependentEntity
est le type appartenant.
Pourquoi
L’objet générateur retourné n’a pas été correctement typé.
Corrections
La recompilation de votre assembly par rapport à la dernière version de EF Core suffit pour résoudre les problèmes provoqués par cette modification.
DbFunctionBuilder.HasSchema(null)
remplace [DbFunction(Schema = "schema")]
Ancien comportement
Dans EF Core 5, l’appel de HasSchema avec null
valeur ne stockait pas la source de configuration, ce qui DbFunctionAttribute était en mesure de le remplacer.
Nouveau comportement
L’appel de HasSchema avec null
valeur stocke désormais la source de configuration et empêche l’attribut de le remplacer.
Pourquoi
La configuration spécifiée avec l’API ModelBuilder ne doit pas être substituable par les annotations de données.
Corrections
Supprimez l’appel HasSchema
pour permettre à l’attribut de configurer le schéma.
Les navigations pré-initialisées sont remplacées par les valeurs des requêtes de base de données
Ancien comportement
Les propriétés de navigation définies sur un objet vide n’ont pas été modifiées pour les requêtes de suivi, mais remplacées pour les requêtes de non-suivi. Par exemple, tenez compte des types d’entités suivants :
public class Foo
{
public int Id { get; set; }
public Bar Bar { get; set; } = new(); // Don't do this.
}
public class Bar
{
public int Id { get; set; }
}
Requête sans suivi pour Foo
y compris Bar
définir Foo.Bar
sur l’entité interrogée à partir de la base de données. Par exemple, ce code :
var foo = context.Foos.AsNoTracking().Include(e => e.Bar).Single();
Console.WriteLine($"Foo.Bar.Id = {foo.Bar.Id}");
Imprimé Foo.Bar.Id = 1
.
Toutefois, la même exécution de requête pour le suivi n’a pas remplacés Foo.Bar
avec l’entité interrogée à partir de la base de données. Par exemple, ce code :
var foo = context.Foos.Include(e => e.Bar).Single();
Console.WriteLine($"Foo.Bar.Id = {foo.Bar.Id}");
Imprimé Foo.Bar.Id = 0
.
Nouveau comportement
Dans EF Core 6.0, le comportement des requêtes de suivi correspond désormais à celui des requêtes sans suivi. Cela signifie que ce code :
var foo = context.Foos.AsNoTracking().Include(e => e.Bar).Single();
Console.WriteLine($"Foo.Bar.Id = {foo.Bar.Id}");
Et ce code :
var foo = context.Foos.Include(e => e.Bar).Single();
Console.WriteLine($"Foo.Bar.Id = {foo.Bar.Id}");
Imprimer Foo.Bar.Id = 1
.
Pourquoi
Il existe deux raisons pour apporter cette modification :
- Pour vous assurer que les requêtes de suivi et de non-suivi ont un comportement cohérent.
- Lorsqu’une base de données est interrogée, il est raisonnable de supposer que le code de l’application souhaite obtenir les valeurs stockées dans la base de données.
Corrections
Il existe deux atténuations :
- N’interrogez pas les objets de la base de données qui ne doivent pas être inclus dans les résultats. Par exemple, dans les extraits de code ci-dessus, ne
Include
Foo.Bar
pas si l’instanceBar
ne doit pas être retournée à partir de la base de données et incluse dans les résultats. - Définissez la valeur de la navigation après l’interrogation de la base de données. Par exemple, dans les extraits de code ci-dessus, appelez
foo.Bar = new()
après avoir exécuté la requête.
Pensez également à ne pas initialiser les instances d’entité associées aux objets par défaut. Cela implique que l’instance associée est une nouvelle entité, non enregistrée dans la base de données, sans aucune valeur de clé définie. Si, à la place, l’entité associée existe dans la base de données, les données du code sont fondamentalement à l’esprit des données stockées dans la base de données.
Les valeurs de chaîne enum inconnues dans la base de données ne sont pas converties en valeurs par défaut d’énumération lors de la requête
Ancien comportement
Les propriétés d’énumération peuvent être mappées à des colonnes de chaîne dans la base de données à l’aide de HasConversion<string>()
ou de EnumToStringConverter
. Cela entraîne la conversion de valeurs de chaîne EF Core dans la colonne en membres correspondants du type d’énumération .NET. Toutefois, si la valeur de chaîne ne correspondait pas au membre enum, la propriété a été définie sur la valeur par défaut de l’énumération.
Nouveau comportement
EF Core 6.0 lève maintenant une InvalidOperationException
avec le message « Impossible de convertir la valeur de chaîne '{value}
' de la base de données en n’importe quelle valeur dans l’énumération{enumType}
' mappée »
Pourquoi
La conversion de la valeur par défaut peut entraîner l’altération de la base de données si celle-ci est de nouveau enregistrée dans la base de données.
Corrections
Dans l’idéal, assurez-vous que la colonne de base de données contient uniquement des valeurs valides. Vous pouvez également implémenter une ValueConverter
avec l’ancien comportement.
DbFunctionBuilder. HasTranslation fournit désormais les arguments de fonction en tant que IReadOnlyList au lieu de IReadOnlyCollection
Ancien comportement
Lors de la configuration de la traduction pour une fonction définie par l’utilisateur à l’aide de HasTranslation
méthode, les arguments de la fonction ont été fournis en tant que IReadOnlyCollection<SqlExpression>
.
Nouveau comportement
Dans EF Core 6.0, les arguments sont désormais fournis en tant que IReadOnlyList<SqlExpression>
.
Pourquoi
IReadOnlyList
permet d’utiliser des indexeurs, de sorte que les arguments sont désormais plus faciles à accéder.
Corrections
Aucun. IReadOnlyList
implémente IReadOnlyCollection
interface. La transition doit donc être simple.
Le mappage de table par défaut n’est pas supprimé lorsque l’entité est mappée à une fonction table
Ancien comportement
Lorsqu’une entité est mappée à une fonction table, son mappage par défaut à une table a été supprimé.
Nouveau comportement
Dans EF Core 6,0, l’entité est toujours mappée à une table à l’aide du mappage par défaut, même si elle est également mappée à une fonction table.
Pourquoi
Les fonctions table qui retournent des entités sont souvent utilisées comme application auxiliaire ou pour encapsuler une opération qui retourne une collection d’entités, plutôt que comme un remplacement strict de la table entière. Cette modification vise à être plus conforme à l’intention de l’utilisateur.
Corrections
Le mappage à une table peut être explicitement désactivé dans la configuration du modèle :
modelBuilder.Entity<MyEntity>().ToTable((string)null);
dotnet-ef cible .NET 6
Ancien comportement
La commande dotnet-ef a ciblé .NET Core 3.1 depuis un certain temps. Cela vous a permis d’utiliser une version plus récente de l’outil sans installer de versions plus récentes du runtime .NET.
Nouveau comportement
Dans EF Core 6.0.6, l’outil dotnet-ef cible désormais .NET 6. Vous pouvez toujours utiliser l’outil sur les projets ciblant des versions antérieures de .NET et .NET Core, mais vous devez installer le runtime .NET 6 pour exécuter l’outil.
Pourquoi
Le SDK .NET 6.0.200 a mis à jour le comportement de dotnet tool install
sur osx-arm64 pour créer un shim osx-x64 pour les outils ciblant .NET Core 3.1. Pour maintenir une expérience par défaut de travail pour dotnet-ef, nous devions le mettre à jour pour cibler .NET 6.
Corrections
Pour exécuter dotnet-ef sans installer le runtime .NET 6, vous pouvez installer une version antérieure de l’outil :
dotnet tool install dotnet-ef --version 3.1.*
IModelCacheKeyFactory
implémentations doivent peut-être être mises à jour pour gérer la mise en cache au moment du design
Ancien comportement
IModelCacheKeyFactory
n’avez pas la possibilité de mettre en cache le modèle au moment du design séparément du modèle d’exécution.
Nouveau comportement
IModelCacheKeyFactory
a une nouvelle surcharge qui permet au modèle au moment du design d’être mis en cache séparément du modèle d’exécution. L’implémentation de cette méthode peut entraîner une exception similaire à :
System.InvalidOperationException : « La configuration demandée n’est pas stockée dans le modèle optimisé en lecture, utilisez 'DbContext.GetService<IDesignTimeModel>().Model'.'
Pourquoi
L’implémentation de modèles compilés nécessite la séparation des modèles au moment de la conception (utilisé lors de la génération du modèle) et du runtime (utilisé lors de l’exécution de requêtes, etc.). Si le code d’exécution a besoin d’accéder aux informations au moment du design, le modèle au moment du design doit être mis en cache.
Corrections
Implémentez la nouvelle surcharge. Par exemple :
public object Create(DbContext context, bool designTime)
=> context is DynamicContext dynamicContext
? (context.GetType(), dynamicContext.UseIntProperty, designTime)
: (object)context.GetType();
La navigation "{navigation}" est ignorée de "include" dans la requête, car elle est remplie automatiquement par le correctif. Si d’autres navigations sont spécifiées dans "include" par la suite, elles seront ignorées. Le retour dans l’arborescence include n’est pas autorisé.
NavigationBaseIncludeIgnored
est maintenant considéré comme une erreur par défaut
Ancien comportement
L’événement CoreEventId.NavigationBaseIncludeIgnored
est enregistré en tant qu’avertissement par défaut.
Nouveau comportement
L’événement CoreEventId.NavigationBaseIncludeIgnored
est enregistré en tant qu’erreur par défaut et entraîne l’envoi d’une exception.
Pourquoi
Étant donné que ces modèles de requête ne sont pas autorisés, EF Core envoie une erreur indiquant que les requêtes doivent être mises à jour.
Corrections
L’ancien comportement peut être restauré en configurant l’événement en tant qu’avertissement. Par exemple :
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder.ConfigureWarnings(b => b.Warn(CoreEventId.NavigationBaseIncludeIgnored));