Sledování vs. Dotazy bez sledování

Sledování chování řídí, pokud Entity Framework Core uchovává informace o instanci entity ve svém sledování změn. Pokud je entita sledována, všechny změny zjištěné v entitě se zachovají do databáze během SaveChanges. EF Core také opravuje navigační vlastnosti mezi entitami ve výsledku sledovacího dotazu a entitami, které jsou v sledování změn.

Poznámka

Typy entit bez klíčů se nikdy nesledují. Kdykoli tento článek uvádí typy entit, odkazuje na typy entit, které mají definovaný klíč.

Tip

Ukázku pro tento článek najdete na GitHubu.

Sledování dotazů

Ve výchozím nastavení se sledují dotazy, které vracejí typy entit. Sledovací dotaz znamená, že všechny změny instancí entit jsou trvalé SaveChanges. V následujícím příkladu je změna hodnocení blogů zjištěna a trvale uložena v databázi během SaveChanges:

var blog = context.Blogs.SingleOrDefault(b => b.BlogId == 1);
blog.Rating = 5;
context.SaveChanges();

Když se výsledky vrátí ve sledovacím dotazu, EF Core zkontroluje, jestli už je entita v kontextu. Pokud EF Core najde existující entitu, vrátí se stejná instance, která může potenciálně použít méně paměti a být rychlejší než dotaz bez sledování. EF Core nepřepíše aktuální a původní hodnoty vlastností entity v položce hodnotami databáze. Pokud se entita v kontextu nenajde, EF Core vytvoří novou instanci entity a připojí ji k kontextu. Výsledky dotazu neobsahují žádnou entitu, která je přidána do kontextu, ale ještě není uložená do databáze.

Dotazy bez sledování

Dotazy bez sledování jsou užitečné, když se výsledky používají ve scénáři jen pro čtení. Obvykle jsou rychlejší, protože není potřeba nastavovat informace o sledování změn. Pokud entity načtené z databáze nemusí být aktualizovány, měl by se použít dotaz bez sledování. Jednotlivé dotazy je možné nastavit tak, aby nebyly žádné sledování. Dotaz bez sledování také poskytuje výsledky na základě toho, co je v databázi, a ignoruje všechny místní změny nebo přidané entity.

var blogs = context.Blogs
    .AsNoTracking()
    .ToList();

Výchozí chování sledování lze změnit na úrovni kontextové instance:

context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;

var blogs = context.Blogs.ToList();

Následující část vysvětluje, kdy dotaz bez sledování může být méně efektivní než sledovací dotaz.

Rozlišení identity

Vzhledem k tomu, že sledovací dotaz používá sledování změn, EF Core v sledovacím dotazu řeší identity. Při materializaci entity vrátí EF Core stejnou instanci entity z sledování změn, pokud je již sledována. Pokud výsledek obsahuje stejnou entitu vícekrát, vrátí se stejná instance pro každý výskyt. Dotazy bez sledování:

  • Nepoužívejte sledování změn a nevyužívejte řešení identity.
  • Vrátí novou instanci entity i v případě, že stejná entita je ve výsledku vícekrát obsažena.

Sledování a sledování bez sledování je možné kombinovat ve stejném dotazu. To znamená, že můžete mít dotaz bez sledování, který ve výsledcích provádí překlad identity. Stejně jako AsNoTracking dotazovatelný operátor jsme přidali další operátor AsNoTrackingWithIdentityResolution<TEntity>(IQueryable<TEntity>). Do výčtu QueryTrackingBehavior se přidá také přidružená položka. Pokud je dotaz na použití překladu identity nakonfigurovaný bez sledování, při generování výsledků dotazu se na pozadí použije samostatný sledování změn, takže každá instance je materializována pouze jednou. Vzhledem k tomu, že tento sledování změn se liší od sledování změn v kontextu, výsledky nejsou sledovány kontextem. Jakmile se dotaz plně vyčíslí, sledování změn přestane být v rozsahu a uvolňování paměti podle potřeby.

var blogs = context.Blogs
    .AsNoTrackingWithIdentityResolution()
    .ToList();

Konfigurace výchozího chování sledování

Pokud zjistíte, že měníte chování sledování u mnoha dotazů, můžete místo toho změnit výchozí nastavení:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
    optionsBuilder
        .UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=EFQuerying.Tracking;Trusted_Connection=True")
        .UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking);
}

Tím se ve výchozím nastavení neshledává žádné dotazy. Stále můžete přidat AsTracking , abyste mohli provádět sledování konkrétních dotazů.

Sledování a vlastní projekce

I když typ výsledku dotazu není typem entity, EF Core bude ve výchozím nastavení sledovat typy entit obsažených ve výsledku. V následujícím dotazu, který vrátí anonymní typ, budou sledovány Blog instance v sadě výsledků.

var blog = context.Blogs
    .Select(
        b =>
            new { Blog = b, PostCount = b.Posts.Count() });

Pokud sada výsledků obsahuje typy entit pocházející ze složení LINQ, EF Core je bude sledovat.

var blog = context.Blogs
    .Select(
        b =>
            new { Blog = b, Post = b.Posts.OrderBy(p => p.Rating).LastOrDefault() });

Pokud sada výsledků neobsahuje žádné typy entit, neprovádí se žádné sledování. V následujícím dotazu vrátíme anonymní typ s některými hodnotami z entity (ale žádné instance skutečného typu entity). Neexistují žádné sledované entity přicházející z dotazu.

var blog = context.Blogs
    .Select(
        b =>
            new { Id = b.BlogId, b.Url });

EF Core podporuje vyhodnocování klientů v projekci nejvyšší úrovně. Pokud EF Core materializuje instanci entity pro vyhodnocení klienta, bude sledována. Vzhledem k tomu, že předáváme blog entity do metody StandardizeURLklienta, EF Core také bude sledovat instance blogu.

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

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

    return url;
}

EF Core nesleduje instance entit bez klíčů obsažené ve výsledku. EF Core ale sleduje všechny ostatní instance typů entit s klíčem podle výše uvedených pravidel.

Předchozí verze

Před verzí 3.0 měla EF Core určité rozdíly v tom, jak bylo sledování provedeno. Mezi velmi vhodné rozdíly patří:

  • Jak je vysvětleno na stránce Hodnocení klienta a serveru, podporuje ef Core vyhodnocení klienta v jakékoli části dotazu před verzí 3.0. Vyhodnocení klienta způsobilo materializaci entit, které nebyly součástí výsledku. Ef Core proto analyzoval výsledek, aby zjistil, co se má sledovat. Tento návrh měl určité rozdíly:

    • Vyhodnocení klienta v projekci, které způsobilo materializaci, ale nevrátilo materializovanou instanci entity, nebylo sledováno. Následující příklad nesledoval blog entity.

      var blogs = context.Blogs
          .OrderByDescending(blog => blog.Rating)
          .Select(
              blog => new { Id = blog.BlogId, Url = StandardizeUrl(blog) })
          .ToList();
      
    • EF Core v určitých případech nesledoval objekty přicházející z složení LINQ. Následující příklad nesledoval Post.

      var blog = context.Blogs
          .Select(
              b =>
                  new { Blog = b, Post = b.Posts.OrderBy(p => p.Rating).LastOrDefault() });
      
  • Pokaždé, když výsledky dotazu obsahovaly typy entit bez klíčů, celý dotaz se neprosledoval. To znamená, že typy entit s klíči, které jsou ve výsledku, nebyly sledovány ani.

  • EF Core slouží k řešení identit v dotazech bez sledování. Pomocí slabých odkazů bylo možné sledovat entity, které už byly vráceny. Pokud tedy sada výsledků obsahovala stejnou entitu vícekrát, získáte stejnou instanci pro každý výskyt. I když předchozí výsledek se stejnou identitou vypršel rozsah a získal uvolňování paměti, EF Core vrátil novou instanci.