Novità di EF Core 5.0

L'elenco seguente include le nuove funzionalità principali di EF Core 5.0. Per l'elenco completo dei problemi nella versione, vedere il tracker dei problemi.

Come versione principale, EF Core 5.0 contiene anche diverse modifiche di rilievo, che sono miglioramenti dell'API o modifiche comportamentali che potrebbero avere un impatto negativo sulle applicazioni esistenti.

Molti-a-molti

EF Core 5.0 supporta relazioni molti-a-molti senza eseguire il mapping esplicito della tabella di join.

Si considerino ad esempio questi tipi di entità:

public class Post
{
    public int Id { get; set; }
    public string Name { get; set; }
    public ICollection<Tag> Tags { get; set; }
}

public class Tag
{
    public int Id { get; set; }
    public string Text { get; set; }
    public ICollection<Post> Posts { get; set; }
}

EF Core 5.0 riconosce questa relazione come relazione molti-a-molti per convenzione e crea automaticamente una PostTag tabella join nel database. I dati possono essere sottoposti a query e aggiornati senza fare riferimento in modo esplicito alla tabella di join, semplificando notevolmente il codice. La tabella join può comunque essere personalizzata ed eseguita query in modo esplicito, se necessario.

Per altre informazioni, vedere la documentazione completa su molti-a-molti.

Suddividere le query

A partire da EF Core 3.0, EF Core genera sempre una singola query SQL per ogni query LINQ. In questo modo si garantisce la coerenza dei dati restituiti entro i vincoli della modalità di transazione in uso. Tuttavia, questo può diventare molto lento quando la query usa Include o una proiezione per riportare più raccolte correlate.

EF Core 5.0 consente ora una singola query LINQ, incluse le raccolte correlate, di essere suddivise in più query SQL. Ciò può migliorare significativamente le prestazioni, ma può comportare incoerenze nei risultati restituiti se i dati cambiano tra le due query. Le transazioni serializzabili o snapshot possono essere usate per attenuare questo problema e ottenere coerenza con le query suddivise, ma ciò può comportare altri costi di prestazioni e differenze comportamentali.

Si consideri, ad esempio, una query che esegue il pull in due livelli di raccolte correlate usando Include:

var artists = context.Artists
    .Include(e => e.Albums)
    .ToList();

Per impostazione predefinita, EF Core genererà il codice SQL seguente quando si usa il provider SQLite:

SELECT a."Id", a."Name", a0."Id", a0."ArtistId", a0."Title"
FROM "Artists" AS a
LEFT JOIN "Album" AS a0 ON a."Id" = a0."ArtistId"
ORDER BY a."Id", a0."Id"

Con le query suddivise, viene invece generato il codice SQL seguente:

SELECT a."Id", a."Name"
FROM "Artists" AS a
ORDER BY a."Id"

SELECT a0."Id", a0."ArtistId", a0."Title", a."Id"
FROM "Artists" AS a
INNER JOIN "Album" AS a0 ON a."Id" = a0."ArtistId"
ORDER BY a."Id"

Le query suddivise possono essere abilitate inserendo il nuovo AsSplitQuery operatore in qualsiasi punto della query LINQ o a livello globale nel modello.OnConfiguring Per altre informazioni, vedere la documentazione completa sulle query suddivise.

Registrazione semplice e diagnostica migliorata

EF Core 5.0 introduce un modo semplice per configurare la registrazione tramite il nuovo LogTo metodo. Di seguito verranno visualizzati i messaggi di registrazione nella console, inclusi tutti i messaggi SQL generati da EF Core:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    => optionsBuilder.LogTo(Console.WriteLine);

Inoltre, è ora possibile chiamare ToQueryString su qualsiasi query LINQ, recuperando il codice SQL eseguito dalla query:

Console.WriteLine(
    ctx.Artists
    .Where(a => a.Name == "Pink Floyd")
    .ToQueryString());

Infine, diversi tipi di EF Core sono stati dotati di una proprietà avanzata DebugView che fornisce una visualizzazione dettagliata degli elementi interni. Ad esempio, ChangeTracker.DebugView è possibile consultare per vedere esattamente quali entità vengono rilevate in un determinato momento.

Per altre informazioni, vedere la documentazione relativa alla registrazione e all'intercettazione.

Inclusione filtrata

Il Include metodo supporta ora il filtro delle entità incluse:

var blogs = context.Blogs
    .Include(e => e.Posts.Where(p => p.Title.Contains("Cheese")))
    .ToList();

Questa query restituirà blog insieme a ogni post associato, ma solo quando il titolo del post contiene "Cheese".

Per altre informazioni, vedere la documentazione completa sull'inclusione filtrata.

Mapping di tabella per tipo (TPT)

Per impostazione predefinita, EF Core esegue il mapping di una gerarchia di ereditarietà dei tipi .NET a una singola tabella di database. Questa operazione è nota come mapping TPH (Table-Per-Hierarchy). EF Core 5.0 consente anche di eseguire il mapping di ogni tipo .NET in una gerarchia di ereditarietà a una tabella di database diversa; noto come mapping di tabella per tipo (TPT).

Si consideri ad esempio questo modello con una gerarchia mappata:

public class Animal
{
    public int Id { get; set; }
    public string Name { get; set; }
}

public class Cat : Animal
{
    public string EducationLevel { get; set; }
}

public class Dog : Animal
{
    public string FavoriteToy { get; set; }
}

Con TPT viene creata una tabella di database per ogni tipo nella gerarchia:

CREATE TABLE [Animals] (
    [Id] int NOT NULL IDENTITY,
    [Name] nvarchar(max) NULL,
    CONSTRAINT [PK_Animals] PRIMARY KEY ([Id])
);

CREATE TABLE [Cats] (
    [Id] int NOT NULL,
    [EducationLevel] nvarchar(max) NULL,
    CONSTRAINT [PK_Cats] PRIMARY KEY ([Id]),
    CONSTRAINT [FK_Cats_Animals_Id] FOREIGN KEY ([Id]) REFERENCES [Animals] ([Id]) ON DELETE NO ACTION,
);

CREATE TABLE [Dogs] (
    [Id] int NOT NULL,
    [FavoriteToy] nvarchar(max) NULL,
    CONSTRAINT [PK_Dogs] PRIMARY KEY ([Id]),
    CONSTRAINT [FK_Dogs_Animals_Id] FOREIGN KEY ([Id]) REFERENCES [Animals] ([Id]) ON DELETE NO ACTION,
);

Per altre informazioni, vedere la documentazione completa su TPT.

Mapping di entità flessibili

I tipi di entità vengono comunemente mappati a tabelle o viste, in modo che EF Core estrae il contenuto della tabella o della vista durante l'esecuzione di query per tale tipo. EF Core 5.0 aggiunge opzioni di mapping aggiuntive, in cui è possibile eseguire il mapping di un'entità a una query SQL (denominata "query di definizione") o a una funzione con valori di tabella (TVF):

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Post>().ToSqlQuery(
        @"SELECT Id, Name, Category, BlogId FROM posts
          UNION ALL
          SELECT Id, Name, ""Legacy"", BlogId from legacy_posts");

    modelBuilder.Entity<Blog>().ToFunction("BlogsReturningFunction");
}

È anche possibile eseguire il mapping delle funzioni con valori di tabella a un metodo .NET anziché a un oggetto DbSet, consentendo il passaggio dei parametri; il mapping può essere configurato con HasDbFunction.

Infine, è ora possibile eseguire il mapping di un'entità a una vista durante l'esecuzione di query (o a una funzione o la definizione di una query), ma a una tabella durante l'aggiornamento:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder
        .Entity<Blog>()
        .ToTable("Blogs")
        .ToView("BlogsView");
}

Tipi di entità di tipo condiviso e contenitori di proprietà

EF Core 5.0 consente di eseguire il mapping dello stesso tipo CLR a più tipi di entità diversi; tali tipi sono noti come tipi di entità di tipo condiviso. Anche se qualsiasi tipo CLR può essere usato con questa funzionalità, .NET Dictionary offre un caso d'uso particolarmente interessante che chiamiamo "contenitori di proprietà":

public class ProductsContext : DbContext
{
    public DbSet<Dictionary<string, object>> Products => Set<Dictionary<string, object>>("Product");

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.SharedTypeEntity<Dictionary<string, object>>("Product", b =>
        {
            b.IndexerProperty<int>("Id");
            b.IndexerProperty<string>("Name").IsRequired();
            b.IndexerProperty<decimal>("Price");
        });
    }
}

Queste entità possono quindi essere sottoposte a query e aggiornate esattamente come i tipi di entità normali con il proprio tipo CLR dedicato. Altre informazioni sono disponibili nella documentazione relativa ai contenitori delle proprietà.

Dipendenti obbligatori 1:1

In EF Core 3.1 la fine dipendente di una relazione uno-a-uno è sempre stata considerata facoltativa. Ciò è stato più evidente quando si usano entità di proprietà, poiché tutte le colonne dell'entità di proprietà sono state create come nullable nel database, anche se sono state configurate come necessarie nel modello.

In EF Core 5.0, una navigazione a un'entità di proprietà può essere configurata come dipendente necessaria. Ad esempio:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Person>(b =>
    {
        b.OwnsOne(e => e.HomeAddress,
            b =>
            {
                b.Property(e => e.City).IsRequired();
                b.Property(e => e.Postcode).IsRequired();
            });
        b.Navigation(e => e.HomeAddress).IsRequired();
    });
}

DbContextFactory

EF Core 5.0 introduce AddDbContextFactory e AddPooledDbContextFactory registra una factory per la creazione di istanze DbContext nel contenitore di inserimento delle dipendenze (D.I.) dell'applicazione. Ciò può essere utile quando il codice dell'applicazione deve creare ed eliminare manualmente le istanze di contesto.

services.AddDbContextFactory<SomeDbContext>(b =>
    b.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=Test"));

A questo punto, i servizi dell'applicazione, ad esempio ASP.NET Controller Core, possono quindi essere inseriti con IDbContextFactory<TContext>e usarli per creare istanze di contesto:

public class MyController : Controller
{
    private readonly IDbContextFactory<SomeDbContext> _contextFactory;

    public MyController(IDbContextFactory<SomeDbContext> contextFactory)
        => _contextFactory = contextFactory;

    public void DoSomeThing()
    {
        using (var context = _contextFactory.CreateDbContext())
        {
            // ...
        }
    }
}

Per altre informazioni, vedere la documentazione completa su DbContextFactory.

Ricompila la tabella SQLite

Rispetto ad altri database, SQLite è relativamente limitato nelle sue funzionalità di manipolazione dello schema; Ad esempio, l'eliminazione di una colonna da una tabella esistente non è supportata. EF Core 5.0 è compatibile con queste limitazioni creando automaticamente una nuova tabella, copiando i dati dalla tabella precedente, eliminando la tabella precedente e rinominando quello nuovo. Questa "ricompila" la tabella e consente l'applicazione sicura delle operazioni di migrazione non supportate in precedenza.

Per informazioni dettagliate sulle operazioni di migrazione ora supportate tramite ricompilazione delle tabelle, vedere questa pagina della documentazione.

Regole di confronto di database

EF Core 5.0 introduce il supporto per specificare regole di confronto del testo a livello di database, colonna o query. In questo modo è possibile configurare la distinzione tra maiuscole e minuscole e altri aspetti testuali in modo flessibile e non compromettere le prestazioni delle query.

Ad esempio, il codice seguente configurerà la Name colonna con distinzione tra maiuscole e minuscole in SQL Server e tutti gli indici creati nella colonna funzioneranno di conseguenza:

modelBuilder
    .Entity<User>()
    .Property(e => e.Name)
    .UseCollation("SQL_Latin1_General_CP1_CS_AS");

Per altre informazioni, vedere la documentazione completa sulle regole di confronto e sulla distinzione tra maiuscole e minuscole.

Contatori di eventi

EF Core 5.0 espone i contatori eventi che possono essere usati per tenere traccia delle prestazioni dell'applicazione e individuare varie anomalie. È sufficiente connettersi a un processo che esegue Entity Framework con lo strumento dotnet-counters :

> dotnet counters monitor Microsoft.EntityFrameworkCore -p 49496

[Microsoft.EntityFrameworkCore]
    Active DbContexts                                               1
    Execution Strategy Operation Failures (Count / 1 sec)           0
    Execution Strategy Operation Failures (Total)                   0
    Optimistic Concurrency Failures (Count / 1 sec)                 0
    Optimistic Concurrency Failures (Total)                         0
    Queries (Count / 1 sec)                                     1,755
    Queries (Total)                                            98,402
    Query Cache Hit Rate (%)                                      100
    SaveChanges (Count / 1 sec)                                     0
    SaveChanges (Total)                                             1

Per altre informazioni, vedere la documentazione completa sui contatori degli eventi.

Altre funzionalità

Compilazione di modelli

  • Le API di compilazione dei modelli sono state introdotte per semplificare la configurazione degli strumenti di confronto dei valori.
  • Le colonne calcolate possono ora essere configurate come archiviate o virtuali.
  • La precisione e la scalabilità possono ora essere configurate tramite l'API Fluent.
  • Sono state introdotte nuove API per la creazione di modelli per le proprietà di navigazione.
  • Sono state introdotte nuove API per la creazione di modelli per i campi, in modo analogo alle proprietà.
  • I tipi .NET PhysicalAddress e IPAddress possono ora essere mappati alle colonne della stringa di database.
  • È ora possibile configurare un campo sottostante tramite il nuovo [BackingField] attributo.
  • I campi sottostanti nullable sono ora consentiti, offrendo un supporto migliore per le impostazioni predefinite generate dall'archivio in cui l'impostazione predefinita CLR non è un valore sentinel valido (notevole bool).
  • È possibile usare un nuovo [Index] attributo in un tipo di entità per specificare un indice anziché usare l'API Fluent.
  • È possibile usare un nuovo [Keyless] attributo per configurare un tipo di entità senza chiave.
  • Per impostazione predefinita, EF Core ora considera i discriminatori completi, ovvero prevede di non visualizzare mai valori discriminatori non configurati dall'applicazione nel modello. Ciò consente alcuni miglioramenti delle prestazioni e può essere disabilitato se la colonna discriminatoria potrebbe contenere valori sconosciuti.

Query

  • Le eccezioni di errore di traduzione delle query contengono ora motivi più espliciti sui motivi dell'errore, per individuare il problema.
  • Le query senza rilevamento possono ora eseguire la risoluzione delle identità, evitando la restituzione di più istanze di entità per lo stesso oggetto di database.
  • Aggiunta del supporto per GroupBy con aggregazioni condizionali ,ad esempio GroupBy(o => o.OrderDate).Select(g => g.Count(i => i.OrderDate != null)).
  • Aggiunta del supporto per la conversione dell'operatore Distinct sugli elementi del gruppo prima dell'aggregazione.
  • Traduzione di Reverse.
  • DateTime Traduzione migliorata per SQL Server (ad esempio DateDiffWeek, DateFromParts).
  • Conversione di nuovi metodi in matrici di byte (ad esempio Contains, Length, SequenceEqual).
  • Conversione di alcuni operatori bit per bit aggiuntivi, ad esempio il complemento di due.
  • Traslazione di FirstOrDefault su stringhe.
  • Miglioramento della traduzione di query intorno alla semantica Null, con conseguente maggiore efficienza delle query.
  • Le funzioni mappate dall'utente possono ora essere annotate per controllare la propagazione dei valori Null, ottenendo di nuovo query più strette ed efficienti.
  • SQL che contiene blocchi CA edizione Standard è ora notevolmente più conciso.
  • La funzione SQL Server DATALENGTH può ora essere chiamata nelle query tramite il nuovo EF.Functions.DataLength metodo.
  • EnableDetailedErrors aggiunge dettagli aggiuntivi alle eccezioni.

Salvataggio

  • SaveChanges intercetta e eventi.
  • Sono state introdotte API per il controllo dei punti di salvataggio delle transazioni. Ef Core creerà automaticamente un punto di salvataggio quando SaveChanges viene chiamato e una transazione è già in corso ed eseguirne il rollback in caso di errore.
  • Un ID transazione può essere impostato in modo esplicito dall'applicazione, consentendo una più semplice correlazione degli eventi delle transazioni nella registrazione e altrove.
  • Le dimensioni massime predefinite del batch per SQL Server sono state modificate in 42 in base a un'analisi delle prestazioni di invio in batch.

Migrazioni e scaffolding

  • Le tabelle possono ora essere escluse dalle migrazioni.
  • Un nuovo dotnet ef migrations list comando mostra ora quali migrazioni non sono ancora state applicate al database (Get-Migration esegue la stessa operazione nella Console di gestione pacchetti).
  • Gli script delle migrazioni contengono ora istruzioni di transazione, se appropriato per migliorare i casi di gestione in cui l'applicazione di migrazione ha esito negativo.
  • Le colonne per le classi base non mappate vengono ora ordinate dopo altre colonne per i tipi di entità mappati. Si noti che influisce solo sulle tabelle appena create; l'ordine di colonna per le tabelle esistenti rimane invariato.
  • La generazione della migrazione può ora essere consapevole se la migrazione generata è idempotente e se l'output verrà eseguito immediatamente o generato come script.
  • Sono stati aggiunti nuovi parametri della riga di comando per specificare gli spazi dei nomi in Migrazioni e scaffolding.
  • Il comando dotnet ef database update accetta ora un nuovo --connection parametro per specificare il stringa di connessione.
  • Lo scaffolding dei database esistenti ora singolarizza i nomi delle tabelle, quindi le tabelle denominate People e Addresses verranno scaffolding nei tipi di entità denominati Person e Address. I nomi di database originali possono comunque essere mantenuti.
  • La nuova --no-onconfiguring opzione può indicare a EF Core di escludere OnConfiguring quando si esegue lo scaffolding di un modello.

Azure Cosmos DB

Sqlite

  • Le colonne calcolate sono ora supportate.
  • Il recupero di dati binari e stringa con GetBytes, GetChars e GetTextReader è ora più efficiente grazie all'uso di SqliteBlob e flussi.
  • L'inizializzazione di Sqlite Connessione ion è ora differita.

Altro

  • I proxy di rilevamento delle modifiche possono essere generati che implementano automaticamente INotifyPropertyChanging e INotifyPropertyChanged. Questo approccio offre un approccio alternativo al rilevamento delle modifiche che non analizza le modifiche quando SaveChanges viene chiamato.
  • È ora possibile modificare un DbConnection oggetto o stringa di connessione in un oggetto DbContext già inizializzato.
  • Il nuovo ChangeTracker.Clear metodo cancella dbContext di tutte le entità rilevate. In genere non è necessario quando si usa la procedura consigliata per creare una nuova istanza di contesto di breve durata per ogni unità di lavoro. Tuttavia, se è necessario reimpostare lo stato di un'istanza DbContext, l'uso del nuovo Clear() metodo è più efficiente e affidabile rispetto alla rimozione di massa di tutte le entità.
  • Gli strumenti da riga di comando di EF Core ora configurano automaticamente le ASPNETCORE_ENVIRONMENTvariabili di ambiente eDOTNET_ENVIRONMENT in "Sviluppo". Ciò offre l'esperienza quando si usa l'host generico in linea con l'esperienza per ASP.NET Core durante lo sviluppo.
  • Gli argomenti della riga di comando personalizzati possono essere trasmessi in IDesignTimeDbContextFactory<TContext>, consentendo alle applicazioni di controllare come viene creato e inizializzato il contesto.
  • Il fattore di riempimento dell'indice può ora essere configurato in SQL Server.
  • La nuova IsRelational proprietà può essere usata per distinguere quando si usa un provider relazionale e un provider non di relazione, ad esempio in memoria.