=

Hrozba jménem Client Evaluation v Entity Framework Core

Jedna ze zajímavých funkcí, které přináší EF Core je tzv. Client vs. Server evaluation. Krom toho, že funkce je velmi užitečná a určitě najde své uplatnění, její nepochopení a neuvážené použítí zcela určitě způsobí v mnoha aplikacích výkonnostní potíže. Pokud jim chcete předejít, přečtěte si tento článek, kde riziko objasním.

👨‍🎓 Nové školení EF Core pro rok 2020

Školení nejnovější verze Entity Framework Core přímo u Vás ve firmě. Naučíte se používat EF Core v celém životním cyklu aplikace od vytvoření modelu, změn v podobě migrací až po dotazování a profilování databázových dotazů.

Více o školení Entity Framework Core

Pracujete-li delší dobu s Entity Framework 6.x, zajisté jste se setkali s chybou ve smyslu:

LINQ to Entities does not recognize the method 'xxx' and this method cannot be translated into a store expression.

Chyba upozorňuje na snahu předat SQL Providerovi nesplnitelný úkol v podobě překladu LINQ dotazu na dotaz databázového úložiště. Většina vývojářů při prvním setkání s touto chybou dlouze bádá, ale časem pochopí příčinu a dokáže se této chybě vyhnout. Příkladem může být dotaz:

var products = db.Products
    .OrderBy(x => x.Price)
    .Select(x => new
    {
        x.Title,
        x.SeoLink,
        Price = UpdatePrice(x.Price)
    })
    .Take(2).ToList();

Nejenže s Entity Framework Core už nevyhučíte na výjimce, ale dokonce dostanete správné a očekávané výsledky.

Client vs. Server Evaluation

DB Provider nově dokáže vyhodnocovat část LINQ dotazu na straně databázového serveru a část na straně klienta. Příklad uvedený nahoře by byl přeložen do klasického SELECT dotazu (upraveno pro čitelnost):

SELECT TOP 2 [x].[Title], [x].[SeoLink], [x].[Price]
FROM [Products] AS [x] ORDER BY [x].[Price]

Z SQL dotazu je patrné, že SQL server vrátí všechna potřebná data na klienta a zbytek, tedy provedení metody UpdatePrice() už se děje na klientovi s využitím vlastnosti Price. Vše vypadá jednoduše, ale musíme mít napaměti, že SQL Server vyhodnotí a vrátí data tak, aby s nimi klientská strana mohla dále pracovat. A tady si lze snadno naletět.

V příkladu výše probíhá řazení dle "surové" ceny, což bez problémů dokáže SQL Server. Proto provede řazení, vrátí 2 záznamy a na klientovi se pouze vlastnost Price nastaví na základě výstupu metody UpdatePrice().

Stačí ale drobný přehmat a vše je jinak:

var products = db.Products
    .Select(x => new
    {
        x.Title,
        x.SeoLink,
        Price = UpdatePrice(x.Price)
    })
    .OrderBy(x => x.Price)
    .Take(2).ToList();

Drobná změna v podobě přesunu OrderBy() již generuje o poznání nebezpečnější dotaz, ve kterém už není omezení na první 2 záznamy:

SELECT [x].[Title], [x].[SeoLink], [x].[Price]
FROM [Products] AS [x]

V tomto případě totiž chceme provést řazení až na základě toho, co vzejde z metody UpdatePrice(). Databázový server logicky nemůže takové řazení provést a proto SQL Provider zažádá zcela o všechna data ze serveru, aby nad nimi mohla být provedena metoda UpdatePrice() a teprve následně Take().

Stejné chování by nastalo, pokud by vývojář například použil Where(), v jehož podmínce by bylo něco, co databázový server nedokáže vyhodnotit.

Na první pohled vypadá všechno logicky (a logické to je), nicméně když neznalý vývojář přijde k existujícímu kódu s úkolem nastavit řazení záznamů a v dobré víře vrazí OrderBy() na konec dotazu (což je u EF 6.x standardní praktika), vytvoří v případě tabulek s větším objemem dat výkonnostní problém.

Zakázání Client Evaluation

Máte-li z potenciální hrozby strach, můžete tuto feature zakázat a dotazy sestavovat starým způsobem. Opět bude na vás, abyste se rozhodli co dáte za úkol SQL Serveru a co si přepočítáte na klientské straně ve své aplikaci. Funkci je možné zakázat v DbContextu v metodě OnConfiguring():

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
    .......
    optionsBuilder.ConfigureWarnings(w => w.Throw(RelationalEventId.QueryClientEvaluationWarning));

    base.OnConfiguring(optionsBuilder);
}

Jakmile se pokusí kdokoliv o Client Evaluation, dojde k vyhození výjimky:

Vyhození výjimky po přenastavení warningů

Pokud samozřejmě pracujete v menším týmu vývojářů a máte toto chování neustále na mysli, pak nemá smysl funkci vypínat a můžete ji ke své radosti využívat. V případě aplikací, kde si nelze podobné chyby dovolit nebo ve větších týmech s juniornějšími členy bych doporučil jít raději tradiční cestou.

Aktualizace k verzi 3.x

Ve verzi EF Core 3.0 / 3.1 se funkcionalita Client Evaluation vypnula. Vyhodnocení na straně klienta probíhá obvykle pouze na úrovni poslední projekce. V ostatních případech EF Core automaticky vyhodí výjimku. Hrozba je tedy definitivně zažehnána.

Miroslav Holec

Miroslav Holec

2. 5. 2018
upraven 20. 6. 2020

Článek se vztahuje k verzi produktu EF Core 1.0.0
Tento článek je již velmi zastaralý. Článek nemusí popisovat aktuální stav technologie, ideální řešení a můj současný pohled na dané téma.

Videa

RestApi.CZ

Právě jsem spustil průvodce Designem REST API

📖 Přečíst restapi.cz

Veřejná školení Místo Datum Délka Cena bez DPH Poznámky
Novinky v .NET 6 a C# 10 online 13. 10. 2022 1 den 3 900 Kč poslední šance registrovat
Vývoj aplikací v ASP.NET Core Praha + online 11/2022 1 den 4 900 Kč bestseller registrovat
Blazor Server & WebAssembly Praha + online 11/2022 2 dny 10 900 Kč registrovat
ASP.NET Core gRPC Praha + online 11/2022 1 den 4 900 Kč registrovat
Design REST API a Design First Praha + online 11/2022 2 dny 9 900 Kč exkluzivní registrovat
Vývoj REST API s Minimal APIs Praha + online 11/2022 1 den 5 900 Kč exkluzivní registrovat
Vývoj REST API v .NET MVC Praha + online 11/2022 1 den 4 900 Kč bestseller registrovat
Entity Framework Core Praha + online 11/2022 2 dny 11 900 Kč registrovat
Vývoj .NET aplikací pro Azure Praha + online 11/2022 1 den 5 900 Kč nový registrovat

kontakt zde

Dotazy, poptávky a objednávky

mirek@miroslavholec.cz
Loading