Creazione e configurazione di un modello

EF Core usa un modello di metadati per descrivere il mapping dei tipi di entità dell'applicazione al database sottostante. Questo modello viene creato usando un set di convenzioni , euristica che cercano modelli comuni. Il modello può quindi essere personalizzato usando attributi di mapping (noti anche come annotazioni di dati) e/o chiamate ai ModelBuilder metodi (noti anche come API Fluent) in OnModelCreating, entrambi i quali eseguiranno l'override della configurazione eseguita dalle convenzioni.

La maggior parte della configurazione può essere applicata a un modello destinato a qualsiasi archivio dati. I provider possono anche abilitare la configurazione specifica di un archivio dati specifico e possono anche ignorare la configurazione non supportata o non applicabile. Per la documentazione sulla configurazione specifica del provider, vedere la sezione Provider di database .

Suggerimento

È possibile visualizzare gli esempi di questo articolo in GitHub.

Usare l'API Fluent per configurare un modello

È possibile eseguire l'override del OnModelCreating metodo nel contesto derivato e usare l'API Fluent per configurare il modello. Questo è il metodo di configurazione più efficace e consente di specificare la configurazione senza modificare le classi di entità. La configurazione dell'API Fluent ha la precedenza più elevata e sostituisce le convenzioni e le annotazioni dei dati. La configurazione viene applicata nell'ordine in cui vengono chiamati i metodi e, in caso di conflitti, la chiamata più recente eseguirà l'override della configurazione specificata in precedenza.

using Microsoft.EntityFrameworkCore;

namespace EFModeling.EntityProperties.FluentAPI.Required;

internal class MyContext : DbContext
{
    public DbSet<Blog> Blogs { get; set; }

    #region Required
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Blog>()
            .Property(b => b.Url)
            .IsRequired();
    }
    #endregion
}

public class Blog
{
    public int BlogId { get; set; }
    public string Url { get; set; }
}

Suggerimento

Per applicare la stessa configurazione a più oggetti nel modello, vedere configurazione in blocco.

Raggruppamento delle configurazioni

Per ridurre le dimensioni del metodo OnModelCreating, è possibile estrarre tutte le configurazioni di un tipo di entità in una classe separata che implementa IEntityTypeConfiguration<TEntity>.

public class BlogEntityTypeConfiguration : IEntityTypeConfiguration<Blog>
{
    public void Configure(EntityTypeBuilder<Blog> builder)
    {
        builder
            .Property(b => b.Url)
            .IsRequired();
    }
}

Richiamare quindi il metodo Configure da OnModelCreating.

new BlogEntityTypeConfiguration().Configure(modelBuilder.Entity<Blog>());

Applicazione di tutte le configurazioni in un assembly

È possibile applicare tutte le configurazioni specificate nei tipi che implementano IEntityTypeConfiguration in un determinato assembly.

modelBuilder.ApplyConfigurationsFromAssembly(typeof(BlogEntityTypeConfiguration).Assembly);

Nota

L'ordine in cui verranno applicate le configurazioni non è definito, pertanto questo metodo deve essere usato solo quando l'ordine non è importante.

Uso dei EntityTypeConfigurationAttribute tipi di entità

Anziché chiamare Configurein modo esplicito , un EntityTypeConfigurationAttribute oggetto può invece essere inserito nel tipo di entità in modo che EF Core possa trovare e usare la configurazione appropriata. Ad esempio:

[EntityTypeConfiguration(typeof(BookConfiguration))]
public class Book
{
    public int Id { get; set; }
    public string Title { get; set; }
    public string Isbn { get; set; }
}

Questo attributo significa che EF Core userà l'implementazione specificata IEntityTypeConfiguration ogni volta che il Book tipo di entità viene incluso in un modello. Il tipo di entità è incluso in un modello usando uno dei meccanismi normali. Ad esempio, creando una DbSet<TEntity> proprietà per il tipo di entità:

public class BooksContext : DbContext
{
    public DbSet<Book> Books { get; set; }

    //...

In alternativa, registrandolo in OnModelCreating:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Book>();
}

Nota

EntityTypeConfigurationAttribute I tipi non verranno individuati automaticamente in un assembly. I tipi di entità devono essere aggiunti al modello prima che l'attributo venga individuato in tale tipo di entità.

Usare le annotazioni dei dati per configurare un modello

È anche possibile applicare determinati attributi (noti come Annotazioni dati) alle classi e alle proprietà. Le annotazioni dei dati sostituiranno le convenzioni, ma saranno a loro volta ignorate dalla configurazione dell'API Fluent.

using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.EntityFrameworkCore;

namespace EFModeling.EntityProperties.DataAnnotations.Annotations;

internal class MyContext : DbContext
{
    public DbSet<Blog> Blogs { get; set; }
}

[Table("Blogs")]
public class Blog
{
    public int BlogId { get; set; }

    [Required]
    public string Url { get; set; }
}

Convenzioni predefinite

EF Core include molte convenzioni di compilazione di modelli abilitate per impostazione predefinita. È possibile trovarli tutti nell'elenco delle classi che implementano l'interfaccia IConvention . Tuttavia, tale elenco non include convenzioni introdotte da provider di database e plug-in di terze parti.

Le applicazioni possono rimuovere o sostituire una di queste convenzioni, nonché aggiungere nuove convenzioni personalizzate che applicano la configurazione per i modelli non riconosciuti da Entity Framework.

Suggerimento

Il codice illustrato di seguito deriva da ModelBuildingConventionsSample.cs.

Rimozione di una convenzione esistente

A volte una delle convenzioni predefinite potrebbe non essere appropriata per l'applicazione, nel qual caso può essere rimossa.

Suggerimento

Se il modello non usa attributi di mapping (ovvero annotazioni di dati) per la configurazione, tutte le convenzioni con il nome che terminano AttributeConvention possono essere rimosse in modo sicuro per velocizzare la compilazione del modello.

Esempio: Non creare indici per le colonne chiave esterna

In genere è opportuno creare indici per le colonne di chiave esterna (FK) e quindi esiste una convenzione predefinita per questo: ForeignKeyIndexConvention. Esaminando la vista di debug del modello per un Post tipo di entità con relazioni con Blog e Author, è possibile vedere che vengono creati due indici, uno per la BlogId chiave FK e l'altro per FKAuthorId.

  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
    Navigations:
      Author (Author) ToPrincipal Author Inverse: Posts
      Blog (Blog) ToPrincipal Blog Inverse: Posts
    Keys:
      Id PK
    Foreign keys:
      Post {'AuthorId'} -> Author {'Id'} ToDependent: Posts ToPrincipal: Author ClientSetNull
      Post {'BlogId'} -> Blog {'Id'} ToDependent: Posts ToPrincipal: Blog Cascade
    Indexes:
      AuthorId
      BlogId

Tuttavia, gli indici presentano un sovraccarico e potrebbe non essere sempre appropriato crearli per tutte le colonne FK. A tale scopo, è possibile rimuovere l'oggetto ForeignKeyIndexConvention durante la compilazione del modello:

protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)
{
    configurationBuilder.Conventions.Remove(typeof(ForeignKeyIndexConvention));
}

Esaminando la visualizzazione di debug del modello per Post il momento, si noterà che gli indici negli FK non sono stati creati:

  EntityType: Post
    Properties:
      Id (int) Required PK AfterSave:Throw ValueGenerated.OnAdd
      AuthorId (no field, int?) Shadow FK
      BlogId (no field, int) Shadow Required FK
    Navigations:
      Author (Author) ToPrincipal Author Inverse: Posts
      Blog (Blog) ToPrincipal Blog Inverse: Posts
    Keys:
      Id PK
    Foreign keys:
      Post {'AuthorId'} -> Author {'Id'} ToDependent: Posts ToPrincipal: Author ClientSetNull
      Post {'BlogId'} -> Blog {'Id'} ToDependent: Posts ToPrincipal: Blog Cascade

Quando si desidera, gli indici possono comunque essere creati in modo esplicito per le colonne di chiave esterna, usando o con la IndexAttribute configurazione in OnModelCreating.

Visualizzazione debug

È possibile accedere alla visualizzazione di debug del generatore di modelli nel debugger dell'IDE. Ad esempio, con Visual Studio:

Accessing the model builder debug view from the Visual Studio debugger

È anche possibile accedervi direttamente dal codice, ad esempio per inviare la visualizzazione di debug alla console:

Console.WriteLine(context.Model.ToDebugString());

La visualizzazione di debug ha una forma breve e una maschera lunga. Il modulo lungo include anche tutte le annotazioni, che possono essere utili se è necessario visualizzare metadati relazionali o specifici del provider. È possibile accedere alla visualizzazione lunga anche dal codice:

Console.WriteLine(context.Model.ToDebugString(MetadataDebugStringOptions.LongDefault));