Sdílet prostřednictvím


Hromadná konfigurace modelu

Pokud je potřeba nakonfigurovat aspekt stejným způsobem napříč více typy entit, následující techniky umožňují omezit duplikaci kódu a konsolidovat logiku.

Podívejte se na úplný ukázkový projekt obsahující fragmenty kódu uvedené níže.

Hromadná konfigurace v OnModelCreating

Každý objekt tvůrce vrácený zpřístupňuje ModelBuilderModel objekt nebo Metadata vlastnost, která poskytuje přístup na nízké úrovni k objektům, které tvoří model. Konkrétně existují metody, které umožňují iterovat konkrétní objekty v modelu a použít na ně společnou konfiguraci.

V následujícím příkladu model obsahuje vlastní typ Currencyhodnoty:

public readonly struct Currency
{
    public Currency(decimal amount)
        => Amount = amount;

    public decimal Amount { get; }

    public override string ToString()
        => $"${Amount}";
}

Vlastnosti tohoto typu se ve výchozím nastavení nezjišťují, protože aktuální zprostředkovatel EF neví, jak ho namapovat na typ databáze. Tento fragment kódu OnModelCreating přidá všechny vlastnosti typu Currency a nakonfiguruje převaděč hodnot do podporovaného typu : 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))
    {
    }
}

Nevýhody rozhraní API metadat

  • Na rozdíl od rozhraní Fluent API je potřeba provést všechny úpravy modelu explicitně. Pokud byly například některé vlastnosti Currency nakonfigurovány jako navigace podle konvence, musíte nejprve odebrat navigaci odkazující na vlastnost CLR před přidáním vlastnosti typu entity. #9117 to zlepší.
  • Konvence se spouštějí po každé změně. Pokud odeberete navigaci zjištěnou konvencí, pak se konvence znovu spustí a může ji přidat zpět. Chcete-li zabránit tomu, aby k tomu došlo, budete muset buď zpozdit konvence až po přidání vlastnosti voláním DelayConventions() a pozdějším vyřazením vráceného objektu, nebo označit vlastnost CLR jako ignorována pomocí AddIgnored.
  • Typy entit můžou být přidány po této iteraci a konfigurace se na ně nepoužije. To může být obvykle zabráněno umístěním tohoto kódu na konec OnModelCreating, ale pokud máte dvě vzájemně závislé sady konfigurací, nemusí existovat pořadí, které jim umožní konzistentně použít.

Konfigurace před konvencí

EF Core umožňuje zadat konfiguraci mapování jednou pro daný typ CLR; tato konfigurace se pak použije na všechny vlastnosti tohoto typu v modelu, jak jsou zjištěny. Říká se tomu "konfigurace modelu před konvencí", protože konfiguruje aspekty modelu před tím, než se konvence vytváření modelu povolí spustit. Tato konfigurace je použita přepsáním ConfigureConventions typu odvozeného z DbContext.

Tento příklad ukazuje, jak nakonfigurovat všechny vlastnosti typu Currency tak, aby měly převaděč hodnot:

protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)
{
    configurationBuilder
        .Properties<Currency>()
        .HaveConversion<CurrencyConverter>();
}

A tento příklad ukazuje, jak nakonfigurovat některé omezující vlastnosti pro všechny vlastnosti typu string:

configurationBuilder
    .Properties<string>()
    .AreUnicode(false)
    .HaveMaxLength(1024);

Poznámka

Typ zadaný ve volání z ConfigureConventions může být základní typ, rozhraní nebo definice obecného typu. Všechny odpovídající konfigurace se použijí v pořadí od nejmenšího konkrétního:

  1. Rozhraní
  2. Základní typ
  3. Definice obecného typu
  4. Typ hodnoty bez hodnoty null
  5. Přesný typ

Důležité

Konfigurace před konvencí je ekvivalentní explicitní konfiguraci, která se použije, jakmile se do modelu přidá odpovídající objekt. Přepíše všechny konvence a datové poznámky. Například s výše uvedenou konfigurací budou všechny vlastnosti cizího klíče řetězce vytvořeny jako ne unicode s MaxLength 1024, i když se neshoduje s hlavním klíčem.

Ignorování typů

Konfigurace před konvencí také umožňuje ignorovat typ a zabránit jeho zjištění konvencí jako typu entity nebo jako vlastnosti typu entity:

configurationBuilder
    .IgnoreAny(typeof(IList<>));

Výchozí mapování typů

Ef obecně dokáže překládat dotazy s konstantami typu, který poskytovatel nepodporuje, pokud jste pro vlastnost tohoto typu zadali převaděč hodnot. V dotazech, které nezahrnují žádné vlastnosti tohoto typu, však neexistuje způsob, jak EF najít správný převaděč hodnot. V tomto případě je možné volat DefaultTypeMapping přidání nebo přepsání mapování typu poskytovatele:

configurationBuilder
    .DefaultTypeMapping<Currency>()
    .HasConversion<CurrencyConverter>();

Omezení konfigurace před konvencí

  • Tento přístup nelze nakonfigurovat v mnoha aspektech. #6787 tuto možnost rozbalí na více typů.
  • V současné době je konfigurace určena pouze typem CLR. #20418 by umožňovalo vlastní predikáty.
  • Tato konfigurace se provádí před vytvořením modelu. Pokud dojde ke konfliktům, ke kterým dojde při jeho použití, trasování zásobníku výjimek nebude obsahovat metodu ConfigureConventions , takže může být obtížnější najít příčinu.

Konvence

Poznámka

Vlastní konvence vytváření modelů byly zavedeny v EF Core 7.0.

Konvence vytváření modelů EF Core jsou třídy, které obsahují logiku aktivovanou na základě změn provedených v modelu při vytváření. Díky tomu bude model aktuální, protože se vytvoří explicitní konfigurace, použijí se atributy mapování a spustí se další konvence. Aby se této zásady mohli zúčastnit, implementuje každá konvence jedno nebo více rozhraní, která určují, kdy se spustí odpovídající metoda. Například konvence, která implementuje IEntityTypeAddedConvention , se aktivuje při každém přidání nového typu entity do modelu. Podobně platí, že konvence, která implementuje obojí IForeignKeyAddedConvention a IKeyAddedConvention bude aktivována při každém přidání klíče nebo cizího klíče do modelu.

Konvence vytváření modelů představují účinný způsob, jak řídit konfiguraci modelu, ale může být složité a obtížné se dostat doprava. V mnoha případech je možné použít konfiguraci modelu před konvencí k snadnému určení společné konfigurace vlastností a typů.

Přidání nové konvence

Příklad: Omezení délky nediskriminačních vlastností

Strategie mapování dědičnosti tabulek na hierarchii vyžaduje diskriminující sloupec k určení typu, který je reprezentován v jakémkoli daném řádku. Ef ve výchozím nastavení používá pro diskriminátor nevázaný řetězec sloupec, který zajistí, že bude fungovat pro jakoukoliv nediskriminační délku. Omezení maximální délky nediskriminačních řetězců ale může usnadnit ukládání a dotazy. Pojďme vytvořit novou konvenci, která to provede.

Konvence vytváření modelů EF Core se aktivují na základě změn provedených v modelu při vytváření. Díky tomu bude model aktuální, protože se vytvoří explicitní konfigurace, použijí se atributy mapování a spustí se další konvence. Aby se této konvence účastnila, implementuje každá konvence jedno nebo více rozhraní, která určují, kdy se konvence aktivuje. Například konvence, která implementuje IEntityTypeAddedConvention , se aktivuje při každém přidání nového typu entity do modelu. Podobně platí, že konvence, která implementuje obojí IForeignKeyAddedConvention a IKeyAddedConvention bude aktivována při každém přidání klíče nebo cizího klíče do modelu.

Znalost rozhraní, která se mají implementovat, může být složité, protože konfigurace modelu v jednom okamžiku může být změněna nebo odebrána v pozdějším okamžiku. Například klíč může být vytvořen konvencí, ale později nahrazen, když je explicitně nakonfigurován jiný klíč.

Pojďme to trochu konkrétněji provést prvním pokusem o implementaci nediskriminační konvence délky:

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);
        }
    }
}

Tato konvence implementuje IEntityTypeBaseTypeChangedConvention, což znamená, že se aktivuje při každé změně mapované hierarchie dědičnosti pro typ entity. Konvence pak vyhledá a nakonfiguruje diskriminující vlastnost řetězce pro hierarchii.

Tato konvence se pak používá voláním AddConfigureConventions:

protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)
{
    configurationBuilder.Conventions.Add(_ =>  new DiscriminatorLengthConvention1());
}

Poznámka

Místo přímého Add přidání instance konvence metoda přijímá objekt pro vytváření instancí konvence. To umožňuje konvenci používat závislosti od interního poskytovatele služeb EF Core. Vzhledem k tomu, že tato konvence neobsahuje žádné závislosti, je parametr poskytovatele služeb pojmenován _, což znamená, že se nikdy nepoužívá.

Sestavení modelu a zobrazení Post typu entity ukazuje, že to fungovalo – diskriminující vlastnost je nyní nakonfigurována na maximální délku 24:

 Discriminator (no field, string) Shadow Required AfterSave:Throw MaxLength(24)

Co se ale stane, když teď explicitně nakonfigurujeme jinou nediskriminační vlastnost? Příklad:

modelBuilder.Entity<Post>()
    .HasDiscriminator<string>("PostTypeDiscriminator")
    .HasValue<Post>("Post")
    .HasValue<FeaturedPost>("Featured");

Když se podíváme na zobrazení ladění modelu, zjistíme, že diskriminující délka už není nakonfigurovaná.

 PostTypeDiscriminator (no field, string) Shadow Required AfterSave:Throw

Je to proto, že při přidání vlastního diskriminace byla později odstraněna diskriminující vlastnost, kterou jsme nakonfigurovali v naší úmluvě. Můžeme se pokusit tento problém vyřešit implementací jiného rozhraní na naší konvenci, abychom reagovali na nediskriminační změny, ale zjištění, které rozhraní k implementaci není snadné.

Naštěstí existuje jednodušší přístup. Hodně času nezáleží na tom, jak model vypadá při jeho sestavení, pokud je konečný model správný. Kromě toho konfigurace, kterou chceme použít, často nemusí aktivovat další konvence pro reakci. Proto naše konvence může implementovat IModelFinalizingConvention. Konvence finalizace modelů se spouští po dokončení všech ostatních sestavení modelu a mají tak přístup k téměř konečnému stavu modelu. Na rozdíl od interaktivních konvencí, které reagují na každou změnu modelu, a ujistěte se, že je model v libovolném okamžiku OnModelCreating provádění metody aktuální. Konvence finalizace modelu se obvykle iteruje nad celým modelem, který konfiguruje prvky modelu, jak pokračuje. V tomto případě tedy v modelu najdeme každou diskriminaci a nakonfigurujeme ji:

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);
            }
        }
    }
}

Po vytvoření modelu pomocí této nové konvence zjistíme, že diskriminující délka je nyní správně nakonfigurovaná, i když byla upravena:

PostTypeDiscriminator (no field, string) Shadow Required AfterSave:Throw MaxLength(24)

Můžeme jít o krok dál a nakonfigurovat maximální délku tak, aby byla délka nejdelší diskriminující hodnoty:

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);
            }
        }
    }
}

Nyní je diskriminující maximální délka sloupce 8, což je délka "Vybrané", nejdelší diskriminující hodnota, která se používá.

PostTypeDiscriminator (no field, string) Shadow Required AfterSave:Throw MaxLength(8)

Příklad: Výchozí délka všech vlastností řetězce

Pojďme se podívat na jiný příklad, kde lze použít konvenci finalizace – nastavení výchozí maximální délky pro libovolnou vlastnost řetězce. Konvence vypadá poměrně podobně jako v předchozím příkladu:

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);
        }
    }
}

Tato konvence je docela jednoduchá. Najde každou vlastnost řetězce v modelu a nastaví její maximální délku na 512. Při pohledu na vlastnosti Postladicí zobrazení vidíme, že všechny vlastnosti řetězce teď mají maximální délku 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)

Poznámka

Totéž lze provést pomocí konfigurace před konvencí, ale použití konvence umožňuje další filtrování použitelných vlastností a pro datové poznámky přepsat konfiguraci.

A nakonec, než tento příklad opustíme, co se stane, když použijeme obojí MaxStringLengthConvention i DiscriminatorLengthConvention3 ve stejnou dobu? Odpověď spočívá v tom, jaké pořadí se přidají, protože konvence finalizace modelu se spouštějí v pořadí, v jakém se přidají. Pokud MaxStringLengthConvention tedy bude přidána jako poslední, spustí se naposledy a nastaví maximální délku nediskriminační vlastnosti na 512. Proto je v tomto případě lepší přidat DiscriminatorLengthConvention3 poslední, aby mohl přepsat výchozí maximální délku pouze diskriminačních vlastností a ponechat všechny ostatní vlastnosti řetězce jako 512.

Nahrazení existující konvence

Někdy místo toho, abychom úplně odstranili existující konvenci, chceme ji nahradit konvencí, která v podstatě dělá totéž, ale se změněným chováním. To je užitečné, protože stávající konvence už implementuje rozhraní, která je potřeba správně aktivovat.

Příklad: Mapování vlastností opt-in

EF Core mapuje všechny veřejné vlastnosti pro čtení i zápis podle konvence. To nemusí být vhodné pro způsob, jakým jsou definovány typy entit. Abychom to změnili, můžeme nahradit PropertyDiscoveryConvention vlastní implementací, která nemapuje žádnou vlastnost, pokud není explicitně namapována OnModelCreating nebo označena novým atributem s názvem Persist:

[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
public sealed class PersistAttribute : Attribute
{
}

Tady je nová konvence:

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;
            }
        }
    }
}

Tip

Při nahrazení předdefinované konvence by nová implementace konvence měla dědit z existující třídy konvence. Všimněte si, že některé konvence mají relační implementace nebo implementace specifické pro zprostředkovatele, v takovém případě by nová implementace konvence měla dědit z nejvýraznější existující třídy konvence pro poskytovatele databáze, který se používá.

Konvence se pak zaregistruje pomocí Replace metody v ConfigureConventions:

protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)
{
    configurationBuilder.Conventions.Replace<PropertyDiscoveryConvention>(
        serviceProvider => new AttributeBasedPropertyDiscoveryConvention(
            serviceProvider.GetRequiredService<ProviderConventionSetBuilderDependencies>()));
}

Tip

Jedná se o případ, kdy existující konvence obsahuje závislosti reprezentované objektem ProviderConventionSetBuilderDependencies závislosti. Ty jsou získány od interního poskytovatele služeb pomocí GetRequiredService a předány konstruktoru konvence.

Všimněte si, že tato konvence umožňuje mapování polí (kromě vlastností), pokud jsou označena značkou [Persist]. To znamená, že privátní pole můžeme použít jako skryté klíče v modelu.

Představte si například následující typy entit:

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; }
}

Model vytvořený z těchto typů entit je:

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

IsClean Za normálních okolností by se namapoval, ale protože není označený [Persist], je nyní považován za nemapovanou vlastnost.

Tip

Tuto konvenci nelze implementovat jako konvenci finalizace modelu, protože existují konvence finalizace modelu, které je potřeba spustit po namapování vlastnosti na další konfiguraci.

Aspekty implementace konvencí

EF Core sleduje, jak byla provedena každá část konfigurace. Toto je reprezentováno výčtem ConfigurationSource . Mezi různé druhy konfigurace patří:

  • Explicit: Element modelu byl explicitně nakonfigurován v OnModelCreating
  • DataAnnotation: Element modelu byl nakonfigurován pomocí atributu mapování (neboli datové poznámky) typu CLR.
  • Convention: Prvek modelu byl nakonfigurován konvencí vytváření modelu.

Konvence by nikdy neměly přepisovat konfiguraci označenou jako DataAnnotation nebo Explicit. Toho lze dosáhnout pomocí tvůrce konvence, IConventionPropertyBuildernapříklad , který je získán z Builder vlastnosti. Příklad:

property.Builder.HasMaxLength(512);

Volání HasMaxLength tvůrce konvencí nastaví maximální délku pouze v případě, že ještě nebyl nakonfigurován atributem mapování nebo v OnModelCreating.

Metody tvůrce, jako je tento, mají také druhý parametr: fromDataAnnotation. Tuto možnost nastavte, true pokud konvence provádí konfiguraci jménem atributu mapování. Příklad:

property.Builder.HasMaxLength(512, fromDataAnnotation: true);

Tím se nastaví ConfigurationSource hodnota DataAnnotation, což znamená, že hodnotu lze nyní přepsat explicitním mapováním , OnModelCreatingale ne konvencí atributů bez mapování.

Pokud aktuální konfiguraci nejde přepsat, metoda se vrátí null, je potřeba ji zohlednit, pokud potřebujete provést další konfiguraci:

property.Builder.HasMaxLength(512)?.IsUnicode(false);

Všimněte si, že pokud konfiguraci unicode nejde přepsat, bude i nadále nastavena maximální délka. V případě, že potřebujete nakonfigurovat omezující vlastnosti pouze v případě, že jsou obě volání úspěšná, můžete to předem zkontrolovat voláním CanSetMaxLength a 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);
            }
        }
    }
}

Zde můžeme mít jistotu, že volání HasMaxLength se nevrátí null. Stále se doporučuje použít instanci tvůrce vrácenou z, protože HasMaxLength se může lišit od propertyBuilder.

Poznámka

Jiné konvence se neaktivují okamžitě po provedení změny, jsou zpožděné, dokud všechny konvence nedokončí zpracování aktuální změny.

IConventionContext

Všechny metody konvence mají IConventionContext<TMetadata> také parametr. Poskytuje metody, které by mohly být užitečné v některých konkrétních případech.

Příklad: NotMappedAttribute – konvence

Tato konvence hledá NotMappedAttribute typ, který je přidán do modelu, a pokusí se odebrat tento typ entity z modelu. Pokud je ale typ entity z modelu odebrán, nemusí se spouštět žádné jiné konvence, které implementují ProcessEntityTypeAdded . Toho lze dosáhnout voláním 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

Každý objekt tvůrce předaný konvenci zveřejňuje Metadata vlastnost, která poskytuje nízkoúrovňový přístup k objektům, které tvoří model. Konkrétně existují metody, které umožňují iterovat konkrétní objekty v modelu a použít na ně společnou konfiguraci, jak je vidět v příkladu: Výchozí délka pro všechny vlastnosti řetězce. Toto rozhraní API je podobné jako IMutableModel v hromadné konfiguraci.

Upozornění

Doporučuje se vždy provádět konfiguraci voláním metod v tvůrci vystavených Builder jako vlastnost, protože tvůrci kontrolují, zda daná konfigurace přepíše něco, co bylo již zadáno pomocí rozhraní Fluent API nebo datových poznámek.

Kdy použít každý přístup pro hromadnou konfiguraci

Rozhraní API metadat použijte v případech:

  • Konfiguraci je potřeba použít v určitém okamžiku a nereagovat na pozdější změny v modelu.
  • Rychlost sestavování modelu je velmi důležitá. Rozhraní API metadat má méně bezpečnostních kontrol, takže může být o něco rychlejší než jiné přístupy, ale použití kompilovaného modelu by přineslo ještě lepší časy spuštění.

Konfigurace modelu před konvencí použijte v následujících případech:

  • Podmínka použitelnosti je jednoduchá, protože závisí pouze na typu.
  • Konfigurace se musí použít v jakémkoli okamžiku, kdy je v modelu přidána vlastnost daného typu a přepisuje datové poznámky a konvence.

Konvence finalizace použijte v následujících případech:

  • Podmínka použitelnosti je složitá.
  • Konfigurace by neměla přepsat to, co jsou určené datovými poznámkami.

Interaktivní konvence používejte v následujících případech:

  • Na sobě závisí více konvencí. Konvence dokončení se spouštějí v pořadí, v jakém byly přidány, a proto nemůžou reagovat na změny provedené pozdějšími konvencemi.
  • Logika se sdílí mezi několika kontexty. Interaktivní konvence jsou bezpečnější než jiné přístupy.