Remarque
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
Lorsqu’un aspect doit être configuré de la même façon sur plusieurs types d’entités, les techniques suivantes permettent de réduire la duplication du code et de consolider la logique.
Consultez l’exemple de projet complet contenant les extraits de code présentés ci-dessous.
Configuration en bloc dans OnModelCreating
Chaque objet générateur retourné par ModelBuilder expose une propriété Model ou Metadata qui fournit un accès de bas niveau aux objets qui composent le modèle. En particulier, il existe des méthodes qui vous permettent d’itérer sur des objets spécifiques dans le modèle et d’appliquer une configuration commune à celles-ci.
Dans l’exemple suivant, le modèle contient un type Currencyde valeur personnalisé :
public readonly struct Currency
{
public Currency(decimal amount)
=> Amount = amount;
public decimal Amount { get; }
public override string ToString()
=> $"${Amount}";
}
Les propriétés de ce type ne sont pas découvertes par défaut, car le fournisseur EF actuel ne sait pas comment le mapper à un type de base de données. Cet extrait de code ajoute OnModelCreating toutes les propriétés du type Currency et configure un convertisseur de valeur vers un type pris en charge : decimal.
foreach (var entityType in modelBuilder.Model.GetEntityTypes())
{
foreach (var propertyInfo in entityType.ClrType.GetProperties())
{
if (propertyInfo.PropertyType == typeof(Currency))
{
entityType.AddProperty(propertyInfo)
.SetValueConverter(typeof(CurrencyConverter));
}
}
}
public class CurrencyConverter : ValueConverter<Currency, decimal>
{
public CurrencyConverter()
: base(
v => v.Amount,
v => new Currency(v))
{
}
}
Inconvénients de l’API de métadonnées
- Contrairement à l’API Fluent, chaque modification du modèle doit être effectuée explicitement. Par exemple, si certaines des
Currencypropriétés ont été configurées comme navigations par une convention, vous devez d’abord supprimer la navigation référençant la propriété CLR avant d’ajouter une propriété de type d’entité pour celle-ci. #9117 améliore cela. - Les conventions s’exécutent après chaque modification. Si vous supprimez une navigation découverte par une convention, la convention s’exécute à nouveau et peut l’ajouter. Pour éviter ce problème, vous devrez retarder les conventions jusqu’à ce que la propriété soit ajoutée en appelant DelayConventions() et en supprimant ultérieurement l’objet retourné ou pour marquer la propriété CLR comme ignorée à l’aide AddIgnored.
- Les types d’entités peuvent être ajoutés une fois cette itération effectuée et la configuration ne sera pas appliquée. Cela peut généralement être évité en plaçant ce code à la fin,
OnModelCreatingmais si vous avez deux ensembles de configurations interdépendants, il se peut qu’il n’y ait pas d’ordre qui leur permettra d’être appliqués de manière cohérente.
Configuration de pré-convention
EF Core permet à la configuration de mappage d’être spécifiée une fois pour un type CLR donné ; cette configuration est ensuite appliquée à toutes les propriétés de ce type dans le modèle à mesure qu’elles sont découvertes. Il s’agit de la « configuration du modèle de pré-convention », car elle configure les aspects du modèle avant que les conventions de génération de modèle soient autorisées à s’exécuter. Cette configuration est appliquée en redéfinissant ConfigureConventions sur le type dérivé de DbContext.
Cet exemple montre comment configurer toutes les propriétés de type Currency pour avoir un convertisseur de valeur :
protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)
{
configurationBuilder
.Properties<Currency>()
.HaveConversion<CurrencyConverter>();
}
Et cet exemple montre comment configurer certaines facettes sur toutes les propriétés de type string:
configurationBuilder
.Properties<string>()
.AreUnicode(false)
.HaveMaxLength(1024);
Remarque
Le type spécifié dans un appel ConfigureConventions peut être un type de base, une interface ou une définition de type générique. Toutes les configurations correspondantes sont appliquées dans l’ordre à partir du moins spécifique :
- Interface
- Type de base
- Définition de type générique
- Type de valeur non nullable
- Type exact
Importante
La configuration de pré-convention équivaut à une configuration explicite appliquée dès qu’un objet correspondant est ajouté au modèle. Elle remplace toutes les conventions et annotations de données. Par exemple, avec la configuration ci-dessus, toutes les propriétés de clé étrangère de type chaîne seront créées comme non-unicode avec MaxLength de 1024, même si cela ne correspond pas à la clé principale.
Ignorer les types
La configuration de pré-convention permet également d’ignorer un type et d’empêcher sa découverte par des conventions en tant que type d’entité ou en tant que propriété sur un type d’entité :
configurationBuilder
.IgnoreAny(typeof(IList<>));
Mappage de type par défaut
En règle générale, EF est en mesure de traduire des requêtes avec des constantes d’un type qui n’est pas pris en charge par le fournisseur, tant que vous avez spécifié un convertisseur de valeur pour une propriété de ce type. Toutefois, dans les requêtes qui n’impliquent aucune propriété de ce type, il n’existe aucun moyen pour EF de trouver le convertisseur de valeur correct. Dans ce cas, il est possible d’appeler DefaultTypeMapping pour ajouter ou remplacer un mappage de type de fournisseur :
configurationBuilder
.DefaultTypeMapping<Currency>()
.HasConversion<CurrencyConverter>();
Limitations de la configuration pré-conventionnelle
- De nombreux aspects ne peuvent pas être configurés avec cette approche. #6787 étend cette valeur à d’autres types.
- Actuellement, la configuration est déterminée uniquement par le type CLR. #20418 autorise les prédicats personnalisés.
- Cette configuration est effectuée avant la création d’un modèle. S’il existe des conflits qui se produisent lors de son application, la trace de la pile d’exceptions ne contient pas la
ConfigureConventionsméthode. Il peut donc être plus difficile de trouver la cause.
Conventions
Les conventions de génération de modèles EF Core sont des classes qui contiennent une logique déclenchée en fonction des modifications apportées au modèle au fur et à mesure qu’il est généré. Cela conserve le modèle up-to-date à mesure que la configuration explicite est effectuée, les attributs de mappage sont appliqués et d’autres conventions s’exécutent. Pour y participer, chaque convention implémente une ou plusieurs interfaces qui déterminent quand la méthode correspondante sera déclenchée. Par exemple, une convention qui implémente IEntityTypeAddedConvention sera déclenchée chaque fois qu’un nouveau type d’entité est ajouté au modèle. De même, une convention qui implémente les deux IForeignKeyAddedConvention et IKeyAddedConvention sera déclenchée chaque fois qu’une clé ou une clé étrangère est ajoutée au modèle.
Les conventions de création de modèles constituent un moyen puissant de contrôler la configuration du modèle, mais peuvent être complexes et difficiles à obtenir correctement. Dans de nombreux cas, la configuration du modèle de pré-convention peut être utilisée à la place pour spécifier facilement la configuration commune pour les propriétés et les types.
Ajout d’une nouvelle convention
Exemple : Limiter la longueur des propriétés de discriminateur
La stratégie de mappage d'héritage table par hiérarchie nécessite une colonne discriminante pour indiquer le type représenté dans chaque ligne donnée. Par défaut, EF utilise une colonne de chaîne non délimitée pour le discriminateur, ce qui garantit qu’elle fonctionnera pour toute longueur de discriminateur. Toutefois, la limitation de la longueur maximale des chaînes de discrimination peut rendre plus efficace le stockage et les requêtes. Créons une convention qui le fera.
Les conventions de génération de modèles EF Core sont déclenchées en fonction des modifications apportées au modèle au fur et à mesure qu’il est généré. Cela conserve le modèle up-to-date à mesure que la configuration explicite est effectuée, les attributs de mappage sont appliqués et d’autres conventions s’exécutent. Pour y participer, chaque convention implémente une ou plusieurs interfaces qui déterminent quand la convention sera déclenchée. Par exemple, une convention qui implémente IEntityTypeAddedConvention sera déclenchée chaque fois qu’un nouveau type d’entité est ajouté au modèle. De même, une convention qui implémente les deux IForeignKeyAddedConvention et IKeyAddedConvention sera déclenchée chaque fois qu’une clé ou une clé étrangère est ajoutée au modèle.
Connaître les interfaces à implémenter peut être difficile, car la configuration apportée au modèle à un moment donné peut être modifiée ou supprimée ultérieurement. Par exemple, une clé peut être créée par convention, mais ensuite remplacée ultérieurement lorsqu’une autre clé est configurée explicitement.
Rendons cela plus concret en tentant pour la première fois d’implémenter la convention de la longueur de discrimination :
public class DiscriminatorLengthConvention1 : IEntityTypeBaseTypeChangedConvention
{
public void ProcessEntityTypeBaseTypeChanged(
IConventionEntityTypeBuilder entityTypeBuilder,
IConventionEntityType? newBaseType,
IConventionEntityType? oldBaseType,
IConventionContext<IConventionEntityType> context)
{
var discriminatorProperty = entityTypeBuilder.Metadata.FindDiscriminatorProperty();
if (discriminatorProperty != null
&& discriminatorProperty.ClrType == typeof(string))
{
discriminatorProperty.Builder.HasMaxLength(24);
}
}
}
Cette convention implémente IEntityTypeBaseTypeChangedConvention, ce qui signifie qu’elle sera déclenchée chaque fois que la hiérarchie d’héritage mappée pour un type d’entité est modifiée. La convention recherche et configure ensuite la propriété de discrimination de chaîne pour la hiérarchie.
Cette convention est ensuite utilisée en appelant Add :ConfigureConventions
protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)
{
configurationBuilder.Conventions.Add(_ => new DiscriminatorLengthConvention1());
}
Remarque
Au lieu d’ajouter directement une instance de la convention, la Add méthode accepte une fabrique pour créer des instances de la convention. Cela permet à la convention d’utiliser des dépendances du fournisseur de services interne EF Core. Étant donné que cette convention n’a pas de dépendances, le paramètre du fournisseur de services est nommé _, ce qui indique qu’il n’est jamais utilisé.
La création du modèle et l’analyse du Post type d’entité indique que cela a fonctionné : la propriété de discrimination est désormais configurée avec une longueur maximale de 24 :
Discriminator (no field, string) Shadow Required AfterSave:Throw MaxLength(24)
Mais que se passe-t-il si nous configurons maintenant explicitement une autre propriété de discriminateur ? Par exemple:
modelBuilder.Entity<Post>()
.HasDiscriminator<string>("PostTypeDiscriminator")
.HasValue<Post>("Post")
.HasValue<FeaturedPost>("Featured");
En examinant la vue de débogage du modèle, nous constatons que la longueur du discriminateur n’est plus configurée.
PostTypeDiscriminator (no field, string) Shadow Required AfterSave:Throw
Cela est dû au fait que la propriété de discrimination que nous avons configurée dans notre convention a été supprimée ultérieurement lorsque le discriminateur personnalisé a été ajouté. Nous pourrions tenter de résoudre ce problème en implémentant une autre interface sur notre convention pour réagir aux modifications du discriminateur, mais déterminer l’interface à implémenter n’est pas facile.
Heureusement, il y a une approche plus facile. La plupart du temps, peu importe à quoi ressemble le modèle pendant sa construction, tant que le modèle final est correct. En outre, la configuration que nous voulons appliquer n’a souvent pas besoin de déclencher d’autres conventions pour réagir. Par conséquent, notre convention peut implémenter IModelFinalizingConvention.
Les conventions de finalisation de modèle s’exécutent une fois que toute autre génération de modèle est terminée, et ont donc accès à l’état quasi final du modèle. Cela est opposé aux conventions interactives qui réagissent à chaque modification de modèle et s’assurer que le modèle est up-to-date à n’importe quel point de l’exécution de la OnModelCreating méthode. Une convention de finalisation de modèle effectue généralement une itération sur l’ensemble du modèle configurant des éléments de modèle au fur et à mesure. Ainsi, dans ce cas, nous allons trouver chaque discriminateur dans le modèle et le configurer :
public class DiscriminatorLengthConvention2 : IModelFinalizingConvention
{
public void ProcessModelFinalizing(IConventionModelBuilder modelBuilder, IConventionContext<IConventionModelBuilder> context)
{
foreach (var entityType in modelBuilder.Metadata.GetEntityTypes()
.Where(entityType => entityType.BaseType == null))
{
var discriminatorProperty = entityType.FindDiscriminatorProperty();
if (discriminatorProperty != null
&& discriminatorProperty.ClrType == typeof(string))
{
discriminatorProperty.Builder.HasMaxLength(24);
}
}
}
}
Après avoir généré le modèle avec cette nouvelle convention, nous constatons que la longueur du discriminateur est maintenant configurée correctement même si elle a été personnalisée :
PostTypeDiscriminator (no field, string) Shadow Required AfterSave:Throw MaxLength(24)
Nous pouvons aller plus loin et configurer la longueur maximale pour être la longueur de la valeur de discrimination la plus longue :
public class DiscriminatorLengthConvention3 : IModelFinalizingConvention
{
public void ProcessModelFinalizing(IConventionModelBuilder modelBuilder, IConventionContext<IConventionModelBuilder> context)
{
foreach (var entityType in modelBuilder.Metadata.GetEntityTypes()
.Where(entityType => entityType.BaseType == null))
{
var discriminatorProperty = entityType.FindDiscriminatorProperty();
if (discriminatorProperty != null
&& discriminatorProperty.ClrType == typeof(string))
{
var maxDiscriminatorValueLength =
entityType.GetDerivedTypesInclusive().Select(e => ((string)e.GetDiscriminatorValue()!).Length).Max();
discriminatorProperty.Builder.HasMaxLength(maxDiscriminatorValueLength);
}
}
}
}
À présent, la longueur maximale de la colonne de discrimination est 8, qui est la longueur de « Featured », la valeur de discriminateur la plus longue utilisée.
PostTypeDiscriminator (no field, string) Shadow Required AfterSave:Throw MaxLength(8)
Exemple : Longueur par défaut de toutes les propriétés de chaîne
Examinons un autre exemple dans lequel une convention de finalisation peut être utilisée : définition d’une longueur maximale par défaut pour n’importe quelle propriété de chaîne. La convention ressemble assez à l’exemple précédent :
public class MaxStringLengthConvention : IModelFinalizingConvention
{
public void ProcessModelFinalizing(IConventionModelBuilder modelBuilder, IConventionContext<IConventionModelBuilder> context)
{
foreach (var property in modelBuilder.Metadata.GetEntityTypes()
.SelectMany(
entityType => entityType.GetDeclaredProperties()
.Where(
property => property.ClrType == typeof(string))))
{
property.Builder.HasMaxLength(512);
}
}
}
Cette convention est assez simple. Elle recherche chaque propriété de chaîne dans le modèle et définit sa longueur maximale sur 512. En examinant dans la vue de débogage les propriétés de Post, nous voyons que toutes les propriétés de chaîne ont maintenant une longueur maximale de 512.
EntityType: Post
Properties:
Id (int) Required PK AfterSave:Throw ValueGenerated.OnAdd
AuthorId (no field, int?) Shadow FK Index
BlogId (no field, int) Shadow Required FK Index
Content (string) Required MaxLength(512)
Discriminator (no field, string) Shadow Required AfterSave:Throw MaxLength(512)
PublishedOn (DateTime) Required
Title (string) Required MaxLength(512)
Remarque
La même chose peut être effectuée par la configuration de pré-convention, mais l’utilisation d’une convention permet de filtrer davantage les propriétés applicables et pour que les annotations de données remplacent la configuration.
Enfin, avant de quitter cet exemple, que se passe-t-il si nous utilisons à la fois MaxStringLengthConvention et DiscriminatorLengthConvention3 en même temps ? La réponse est qu’elle dépend de l’ordre dans lequel elles sont ajoutées, car les conventions de finalisation du modèle s’exécutent dans l’ordre dans lequel elles sont ajoutées. Par conséquent, si MaxStringLengthConvention est ajouté en dernier, il s’exécutera en dernier et fixera la longueur maximale de la propriété "discriminant" à 512. Par conséquent, dans ce cas, il est préférable d’ajouter DiscriminatorLengthConvention3 en dernier afin qu’elle puisse remplacer la longueur maximale par défaut pour les propriétés de juste discrimination, tout en laissant toutes les autres propriétés de chaîne comme 512.
Remplacement d’une convention existante
Parfois, plutôt que de supprimer complètement une convention existante, nous voulons le remplacer par une convention qui fait essentiellement la même chose, mais avec un comportement modifié. Cela est utile, car la convention existante implémentera déjà les interfaces qu’il doit déclencher de manière appropriée.
Exemple : Mappage de propriétés Opt-in
EF Core mappe toutes les propriétés en lecture-écriture publique par convention. Cela peut ne pas convenir à la façon dont vos types d’entités sont définis. Pour changer cela, nous pouvons remplacer le PropertyDiscoveryConvention par notre propre implémentation qui ne mappe de propriété que si elle est explicitement mappée dans OnModelCreating ou marquée avec un nouvel attribut appelé Persist.
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
public sealed class PersistAttribute : Attribute
{
}
Voici la nouvelle convention :
public class AttributeBasedPropertyDiscoveryConvention : PropertyDiscoveryConvention
{
public AttributeBasedPropertyDiscoveryConvention(ProviderConventionSetBuilderDependencies dependencies)
: base(dependencies)
{
}
public override void ProcessEntityTypeAdded(
IConventionEntityTypeBuilder entityTypeBuilder,
IConventionContext<IConventionEntityTypeBuilder> context)
=> Process(entityTypeBuilder);
public override void ProcessEntityTypeBaseTypeChanged(
IConventionEntityTypeBuilder entityTypeBuilder,
IConventionEntityType? newBaseType,
IConventionEntityType? oldBaseType,
IConventionContext<IConventionEntityType> context)
{
if ((newBaseType == null
|| oldBaseType != null)
&& entityTypeBuilder.Metadata.BaseType == newBaseType)
{
Process(entityTypeBuilder);
}
}
private void Process(IConventionEntityTypeBuilder entityTypeBuilder)
{
foreach (var memberInfo in GetRuntimeMembers())
{
if (Attribute.IsDefined(memberInfo, typeof(PersistAttribute), inherit: true))
{
entityTypeBuilder.Property(memberInfo);
}
else if (memberInfo is PropertyInfo propertyInfo
&& Dependencies.TypeMappingSource.FindMapping(propertyInfo) != null)
{
entityTypeBuilder.Ignore(propertyInfo.Name);
}
}
IEnumerable<MemberInfo> GetRuntimeMembers()
{
var clrType = entityTypeBuilder.Metadata.ClrType;
foreach (var property in clrType.GetRuntimeProperties()
.Where(p => p.GetMethod != null && !p.GetMethod.IsStatic))
{
yield return property;
}
foreach (var property in clrType.GetRuntimeFields())
{
yield return property;
}
}
}
}
Conseil / Astuce
Lors du remplacement d’une convention intégrée, la nouvelle implémentation de convention doit hériter de la classe de convention existante. Notez que certaines conventions ont des implémentations relationnelles ou spécifiques au fournisseur, auquel cas la nouvelle implémentation de convention doit hériter de la classe de convention existante la plus spécifique pour le fournisseur de base de données en cours d’utilisation.
La convention est ensuite inscrite à l’aide de la Replace méthode dans ConfigureConventions:
protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)
{
configurationBuilder.Conventions.Replace<PropertyDiscoveryConvention>(
serviceProvider => new AttributeBasedPropertyDiscoveryConvention(
serviceProvider.GetRequiredService<ProviderConventionSetBuilderDependencies>()));
}
Conseil / Astuce
Il s’agit d’un cas où la convention existante a des dépendances, représentées par l’objet ProviderConventionSetBuilderDependencies de dépendance. Celles-ci sont obtenues à partir du fournisseur de services interne à l'aide de GetRequiredService et transmises au constructeur de convention.
Notez que cette convention permet aux champs d’être mappés (en plus des propriétés) tant qu’ils sont marqués avec [Persist]. Cela signifie que nous pouvons utiliser des champs privés comme clés masquées dans le modèle.
Par exemple, tenez compte des types d’entités suivants :
public class LaundryBasket
{
[Persist]
[Key]
private readonly int _id;
[Persist]
public int TenantId { get; init; }
public bool IsClean { get; set; }
public List<Garment> Garments { get; } = new();
}
public class Garment
{
public Garment(string name, string color)
{
Name = name;
Color = color;
}
[Persist]
[Key]
private readonly int _id;
[Persist]
public int TenantId { get; init; }
[Persist]
public string Name { get; }
[Persist]
public string Color { get; }
public bool IsClean { get; set; }
public LaundryBasket? Basket { get; set; }
}
Le modèle créé à partir de ces types d’entités est :
Model:
EntityType: Garment
Properties:
_id (_id, int) Required PK AfterSave:Throw ValueGenerated.OnAdd
Basket_id (no field, int?) Shadow FK Index
Color (string) Required
Name (string) Required
TenantId (int) Required
Navigations:
Basket (LaundryBasket) ToPrincipal LaundryBasket Inverse: Garments
Keys:
_id PK
Foreign keys:
Garment {'Basket_id'} -> LaundryBasket {'_id'} ToDependent: Garments ToPrincipal: Basket ClientSetNull
Indexes:
Basket_id
EntityType: LaundryBasket
Properties:
_id (_id, int) Required PK AfterSave:Throw ValueGenerated.OnAdd
TenantId (int) Required
Navigations:
Garments (List<Garment>) Collection ToDependent Garment Inverse: Basket
Keys:
_id PK
Normalement, IsClean aurait été mappé, mais comme il n’est pas marqué avec [Persist], il est maintenant traité comme une propriété non mappée.
Conseil / Astuce
Cette convention n’a pas pu être implémentée en tant que convention de finalisation de modèle, car il existe des conventions de finalisation de modèle existantes qui doivent s’exécuter une fois que la propriété est mappée pour la configurer davantage.
Considérations relatives à l’implémentation des conventions
EF Core effectue le suivi de la façon dont chaque partie de la configuration a été effectuée. Ceci est représenté par l’énumération ConfigurationSource . Les différents types de configuration sont les suivants :
-
Explicit: l’élément de modèle a été configuré explicitement dansOnModelCreating -
DataAnnotation: L’élément de modèle a été configuré à l’aide d’un attribut de mappage (alias annotation de données) sur le type CLR -
Convention: L’élément de modèle a été configuré par une convention de construction de modèle
Les conventions ne doivent jamais remplacer la configuration marquée comme DataAnnotation ou Explicit. Cela est obtenu à l’aide d’un générateur de conventions, par exemple, le IConventionPropertyBuilder, qui est obtenu à partir de la Builder propriété. Par exemple:
property.Builder.HasMaxLength(512);
L’appel HasMaxLength au générateur de conventions définit uniquement la longueur maximale si elle n’a pas déjà été configurée par un attribut de mappage ou dans OnModelCreating.
Les méthodes de générateur telles que celles-ci ont également un deuxième paramètre : fromDataAnnotation. Définissez cette valeur true si la convention effectue la configuration pour le compte d’un attribut de mappage. Par exemple:
property.Builder.HasMaxLength(512, fromDataAnnotation: true);
Cela définit la valeur ConfigurationSource sur DataAnnotation, ce qui signifie que la valeur peut désormais être remplacée par un mappage explicite sur OnModelCreating, mais pas par des conventions d'attribut sans mappage.
Si la configuration actuelle ne peut pas être remplacée, la méthode retourne null, cela doit être pris en compte si vous devez effectuer une configuration supplémentaire :
property.Builder.HasMaxLength(512)?.IsUnicode(false);
Notez que si la configuration Unicode ne peut pas être remplacée, la longueur maximale est toujours définie. Dans le cas où vous devez configurer les facettes uniquement lorsque les deux appels réussissent, vous pouvez vérifier cela de manière préemptive en appelant CanSetMaxLength et CanSetIsUnicode:
public class MaxStringLengthNonUnicodeConvention : IModelFinalizingConvention
{
public void ProcessModelFinalizing(IConventionModelBuilder modelBuilder, IConventionContext<IConventionModelBuilder> context)
{
foreach (var property in modelBuilder.Metadata.GetEntityTypes()
.SelectMany(
entityType => entityType.GetDeclaredProperties()
.Where(
property => property.ClrType == typeof(string))))
{
var propertyBuilder = property.Builder;
if (propertyBuilder.CanSetMaxLength(512)
&& propertyBuilder.CanSetIsUnicode(false))
{
propertyBuilder.HasMaxLength(512)!.IsUnicode(false);
}
}
}
}
Ici, nous pouvons être sûrs que l’appel à HasMaxLength ne retournera pas null. Il est encore recommandé d’utiliser l’instance du générateur retournée à partir de HasMaxLength, car elle pourrait être différente de celle de propertyBuilder.
Remarque
Les autres conventions ne sont pas déclenchées immédiatement après une modification d’une convention, elles sont retardées jusqu’à ce que toutes les conventions aient terminé le traitement de la modification actuelle.
IConventionContext
Toutes les méthodes de convention ont également un IConventionContext<TMetadata> paramètre. Il fournit des méthodes qui pourraient être utiles dans certains cas spécifiques.
Exemple : Convention NotMappedAttribute
Cette convention recherche NotMappedAttribute sur un type qui est ajouté au modèle et essaie de supprimer ce type d'entité du modèle. Toutefois, si le type d’entité est supprimé du modèle, toutes les autres conventions qui implémentent ProcessEntityTypeAdded n’ont plus besoin d’être exécutées. Pour ce faire, appelez StopProcessing():
public virtual void ProcessEntityTypeAdded(
IConventionEntityTypeBuilder entityTypeBuilder,
IConventionContext<IConventionEntityTypeBuilder> context)
{
var type = entityTypeBuilder.Metadata.ClrType;
if (!Attribute.IsDefined(type, typeof(NotMappedAttribute), inherit: true))
{
return;
}
if (entityTypeBuilder.ModelBuilder.Ignore(entityTypeBuilder.Metadata.Name, fromDataAnnotation: true) != null)
{
context.StopProcessing();
}
}
IConventionModel
Chaque objet générateur transmis à la convention expose une Metadata propriété qui fournit un accès de bas niveau aux objets qui composent le modèle. En particulier, il existe des méthodes qui vous permettent d’effectuer une itération sur des objets spécifiques dans le modèle et d’appliquer une configuration commune comme indiqué dans l’exemple : Longueur par défaut pour toutes les propriétés de chaîne. Cette API est similaire à IMutableModel celle illustrée dans la configuration en bloc.
Avertissement
Il est recommandé d’effectuer toujours une configuration en appelant des méthodes sur le générateur exposé en tant que Builder propriété, car les générateurs vérifient si la configuration donnée remplacerait un élément déjà spécifié à l’aide de l’API Fluent ou des annotations de données.
Quand utiliser chaque approche pour la configuration en bloc
Utilisez l’API de métadonnées quand :
- La configuration doit être appliquée à un certain moment et ne pas réagir aux modifications ultérieures dans le modèle.
- La vitesse de construction du modèle est très importante. L’API de métadonnées a moins de vérifications de sécurité et peut donc être légèrement plus rapide que d’autres approches, mais l’utilisation d’un modèle compilé génère même de meilleurs temps de démarrage.
Utilisez la configuration du modèle de pré-convention lorsque :
- La condition d’applicabilité est simple, car elle dépend uniquement du type.
- La configuration doit être appliquée à tout moment, une propriété du type donné est ajoutée dans le modèle et remplace les annotations et conventions de données
Utilisez les conventions de finalisation lorsque :
- La condition d’applicabilité est complexe.
- La configuration ne doit pas remplacer ce qui est spécifié par les annotations de données.
Utilisez des conventions interactives quand :
- Plusieurs conventions dépendent les unes des autres. La finalisation des conventions s’exécute dans l’ordre dans lequel elles ont été ajoutées et ne peut donc pas réagir aux modifications apportées par la finalisation ultérieure des conventions.
- La logique est partagée entre plusieurs contextes. Les conventions interactives sont plus sûres que d’autres approches.