Condividi tramite


Confronto tra client e valutazione server

Come regola generale, Entity Framework Core tenta di valutare una query nel server il più possibile. EF Core converte parti della query in parametri, che possono essere valutati sul lato client. Il resto della query (insieme ai parametri generati) viene assegnato al provider di database per determinare la query di database equivalente da valutare nel server. EF Core supporta la valutazione parziale del client nella proiezione di primo livello (essenzialmente, l'ultima chiamata a Select()). Se la proiezione di primo livello nella query non può essere convertita nel server, EF Core recupererà i dati necessari dal server e valuterà le parti rimanenti della query nel client. Se EF Core rileva un'espressione, in qualsiasi posizione diversa dalla proiezione di primo livello, che non può essere convertita nel server, genera un'eccezione di runtime. Vedere Funzionamento delle query per comprendere in che modo EF Core determina cosa non può essere convertito nel server.

Nota

Prima della versione 3.0, la valutazione client supportata da Entity Framework Core in qualsiasi punto della query. Per altre informazioni, vedere la sezione versioni precedenti.

Suggerimento

È possibile visualizzare l'esempio di questo articolo in GitHub.

Valutazione client nella proiezione di primo livello

Nell'esempio seguente viene usato un metodo helper per standardizzare gli URL per i blog, restituiti da un database di SQL Server. Poiché il provider SQL Server non ha informazioni dettagliate su come viene implementato questo metodo, non è possibile convertirlo in SQL. Tutti gli altri aspetti della query vengono valutati nel database, ma il passaggio dell'oggetto restituito URL tramite questo metodo viene eseguito sul client.

var blogs = context.Blogs
    .OrderByDescending(blog => blog.Rating)
    .Select(
        blog => new { Id = blog.BlogId, Url = StandardizeUrl(blog.Url) })
    .ToList();
public static string StandardizeUrl(string url)
{
    url = url.ToLower();

    if (!url.StartsWith("http://"))
    {
        url = string.Concat("http://", url);
    }

    return url;
}

Valutazione client non supportata

Anche se la valutazione client è utile, a volte può comportare prestazioni scarse. Si consideri la query seguente, in cui il metodo helper viene ora usato in un filtro where. Poiché il filtro non può essere applicato nel database, tutti i dati devono essere estratti in memoria per applicare il filtro sul client. In base al filtro e alla quantità di dati nel server, la valutazione client potrebbe comportare prestazioni scarse. Entity Framework Core blocca quindi tale valutazione client e genera un'eccezione di runtime.

var blogs = context.Blogs
    .Where(blog => StandardizeUrl(blog.Url).Contains("dotnet"))
    .ToList();

Valutazione esplicita del client

Potrebbe essere necessario forzare la valutazione client in modo esplicito in alcuni casi, ad esempio:

  • La quantità di dati è ridotta in modo che la valutazione sul client non comporta un'enorme riduzione delle prestazioni.
  • L'operatore LINQ in uso non ha alcuna conversione lato server.

In questi casi, è possibile acconsentire esplicitamente alla valutazione client chiamando metodi come AsEnumerable o ToList (AsAsyncEnumerable o ToListAsync per async). AsEnumerable Usando si esegue lo streaming dei risultati, ma l'uso ToList di causerebbe il buffering creando un elenco, che richiede anche memoria aggiuntiva. Anche se si enumera più volte, l'archiviazione dei risultati in un elenco risulta più utile perché è presente una sola query nel database. A seconda dell'utilizzo specifico, è consigliabile valutare quale metodo è più utile per il caso.

var blogs = context.Blogs
    .AsEnumerable()
    .Where(blog => StandardizeUrl(blog.Url).Contains("dotnet"))
    .ToList();

Suggerimento

Se si usa AsAsyncEnumerable e si vuole comporre ulteriormente la query sul lato client, è possibile usare la libreria System.Interactive.Async che definisce gli operatori per enumerabili asincroni. Per altre informazioni, vedere Operatori linq lato client.

Potenziale perdita di memoria nella valutazione client

Poiché la traduzione e la compilazione delle query sono costose, EF Core memorizza nella cache il piano di query compilato. Il delegato memorizzato nella cache può usare il codice client durante la valutazione client della proiezione di primo livello. EF Core genera parametri per le parti valutate dal client dell'albero e riutilizza il piano di query sostituendo i valori dei parametri. Tuttavia, alcune costanti nell'albero delle espressioni non possono essere convertite in parametri. Se il delegato memorizzato nella cache contiene costanti di questo tipo, questi oggetti non possono essere sottoposti a Garbage Collection perché sono ancora sottoposti a riferimento. Se tale oggetto contiene un oggetto DbContext o altri servizi in esso contenuti, potrebbe causare l'aumento dell'utilizzo della memoria dell'app nel tempo. Questo comportamento è in genere un segno di perdita di memoria. EF Core genera un'eccezione ogni volta che si verifica tra costanti di un tipo di cui non è possibile eseguire il mapping usando il provider di database corrente. Le cause comuni e le relative soluzioni sono le seguenti:

  • Uso di un metodo di istanza: quando si usano metodi di istanza in una proiezione client, l'albero delle espressioni contiene una costante dell'istanza. Se il metodo non usa dati dell'istanza, prendere in considerazione la possibilità di rendere statico il metodo. Se sono necessari dati di istanza nel corpo del metodo, passare i dati specifici come argomento al metodo .
  • Passaggio di argomenti costanti al metodo: questo caso si verifica in genere usando this in un argomento al metodo client. Prendere in considerazione la suddivisione dell'argomento in in più argomenti scalari, che possono essere mappati dal provider di database.
  • Altre costanti: se una costante viene incontrato in qualsiasi altro caso, è possibile valutare se la costante è necessaria per l'elaborazione. Se è necessario avere la costante o se non è possibile usare una soluzione nei casi precedenti, creare una variabile locale per archiviare il valore e usare la variabile locale nella query. EF Core convertirà la variabile locale in un parametro.

Versioni precedenti

La sezione seguente si applica alle versioni di EF Core precedenti alla 3.0.

Le versioni precedenti di EF Core supportano la valutazione client in qualsiasi parte della query, non solo la proiezione di primo livello. Ecco perché le query simili a una pubblicata nella sezione Valutazione client non supportata hanno funzionato correttamente. Poiché questo comportamento potrebbe causare problemi di prestazioni non rilevati, EF Core ha registrato un avviso di valutazione client. Per altre informazioni sulla visualizzazione dell'output di registrazione, vedere Registrazione.

Facoltativamente, EF Core ha consentito di modificare il comportamento predefinito in modo da generare un'eccezione o non eseguire alcuna operazione durante la valutazione client (ad eccezione della proiezione). Il comportamento di generazione dell'eccezione sarebbe simile al comportamento nella versione 3.0. Per modificare il comportamento, è necessario configurare gli avvisi durante la configurazione delle opzioni per il contesto, in genere in DbContext.OnConfiguringo Startup.cs in se si usa ASP.NET Core.

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
    optionsBuilder
        .UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=EFQuerying;Trusted_Connection=True;")
        .ConfigureWarnings(warnings => warnings.Throw(RelationalEventId.QueryClientEvaluationWarning));
}