Řešení identit v EF Core

A DbContext může sledovat pouze jednu instanci entity s libovolnou danou hodnotou primárního klíče. To znamená, že více instancí entity se stejnou hodnotou klíče se musí přeložit na jednu instanci. Tomu se říká "řešení identity". Řešení identit zajišťuje, že Entity Framework Core (EF Core) sleduje konzistentní graf bez nejednoznačností vztahů nebo hodnot vlastností entit.

Tip

Tento dokument předpokládá, že stavy entit a základy sledování změn EF Core jsou srozumitelné. Další informace o těchto tématech najdete v tématu Sledování změn v EF Core .

Tip

Celý kód v tomto dokumentu můžete spustit a ladit tak, že si stáhnete ukázkový kód z GitHubu.

Úvod

Následující kód se dotazuje na entitu a pak se pokusí připojit jinou instanci se stejnou hodnotou primárního klíče:

using var context = new BlogsContext();

var blogA = context.Blogs.Single(e => e.Id == 1);
var blogB = new Blog { Id = 1, Name = ".NET Blog (All new!)" };

try
{
    context.Update(blogB); // This will throw
}
catch (Exception e)
{
    Console.WriteLine($"{e.GetType().FullName}: {e.Message}");
}

Spuštěním tohoto kódu vznikne následující výjimka:

System.InvalidOperationException: Instance typu entity Blog nelze sledovat, protože již probíhá sledování jiné instance s hodnotou klíče {ID: 1}. Při připojování existujících entit se ujistěte, že je připojena pouze jedna instance entity s danou hodnotou klíče.

EF Core vyžaduje jednu instanci, protože:

  • Hodnoty vlastností se můžou lišit mezi více instancemi. Při aktualizaci databáze potřebuje EF Core vědět, které hodnoty vlastností se mají použít.
  • Vztahy s jinými entitami se můžou lišit mezi několika instancemi. Například "blogA" může souviset s jinou kolekcí příspěvků než blogB.

Výše uvedená výjimka se běžně vyskytují v těchto situacích:

  • Při pokusu o aktualizaci entity
  • Při pokusu o sledování serializovaného grafu entit
  • Pokud selhává nastavení hodnoty klíče, která se negeneruje automaticky
  • Při opětovném použití instance DbContext pro více jednotek práce

Každá z těchto situací je popsána v následujících částech.

Aktualizace entity

Existuje několik různých přístupů k aktualizaci entity novými hodnotami, jak je popsáno v řešení Change Tracking v EF Core a explicitně sledování entit. Tyto přístupy jsou popsané níže v kontextu řešení identit. Důležitým bodem, který je třeba si všimnout, je, že každý z přístupů používá buď dotaz, nebo volání jednoho z Update nich, Attachale nikdy obojí.

Aktualizace hovoru

Entita, která se má aktualizovat, často nepochází z dotazu na DbContext, který použijeme pro SaveChanges. Například ve webové aplikaci může být instance entity vytvořena z informací v požadavku POST. Nejjednodušší způsob, jak to zvládnout, je použít DbContext.Update nebo DbSet<TEntity>.Update. Příklad:

public static void UpdateFromHttpPost1(Blog blog)
{
    using var context = new BlogsContext();

    context.Update(blog);

    context.SaveChanges();
}

V tomto případě:

  • Vytvoří se pouze jedna instance entity.
  • Instance entity není dotazována z databáze jako součást aktualizace.
  • Všechny hodnoty vlastností budou aktualizovány v databázi bez ohledu na to, jestli se skutečně změnily nebo ne.
  • Provede se jedna doba odezvy databáze.

Dotaz pak použije změny.

Obvykle není známo, které hodnoty vlastností byly ve skutečnosti změněny při vytvoření entity z informací v požadavku POST nebo podobné. Často je vhodné pouze aktualizovat všechny hodnoty v databázi, jak jsme to udělali v předchozím příkladu. Pokud ale aplikace zpracovává mnoho entit a pouze malý počet těchto skutečných změn, může být užitečné omezit odeslané aktualizace. Toho lze dosáhnout spuštěním dotazu, který bude sledovat entity, které aktuálně existují v databázi, a následným použitím změn u těchto sledovaných entit. Příklad:

public static void UpdateFromHttpPost2(Blog blog)
{
    using var context = new BlogsContext();

    var trackedBlog = context.Blogs.Find(blog.Id);

    trackedBlog.Name = blog.Name;
    trackedBlog.Summary = blog.Summary;

    context.SaveChanges();
}

V tomto případě:

  • Sleduje se pouze jedna instance entity; ten, který je vrácen z databáze dotazem Find .
  • Update, Attachatd. se nepoužívají .
  • V databázi se aktualizují pouze hodnoty vlastností, které se skutečně změnily.
  • Jsou provedeny dvě doby odezvy databáze.

EF Core má některé pomocné rutiny pro přenos hodnot vlastností, jako je tato. Například PropertyValues.SetValues zkopíruje všechny hodnoty z daného objektu a nastaví je na sledovaný objekt:

public static void UpdateFromHttpPost3(Blog blog)
{
    using var context = new BlogsContext();

    var trackedBlog = context.Blogs.Find(blog.Id);

    context.Entry(trackedBlog).CurrentValues.SetValues(blog);

    context.SaveChanges();
}

SetValues přijímá různé typy objektů, včetně objektů pro přenos dat (DTO) s názvy vlastností, které odpovídají vlastnostem typu entity. Příklad:

public static void UpdateFromHttpPost4(BlogDto dto)
{
    using var context = new BlogsContext();

    var trackedBlog = context.Blogs.Find(dto.Id);

    context.Entry(trackedBlog).CurrentValues.SetValues(dto);

    context.SaveChanges();
}

Nebo slovník s položkami názvu/hodnoty pro hodnoty vlastnosti:

public static void UpdateFromHttpPost5(Dictionary<string, object> propertyValues)
{
    using var context = new BlogsContext();

    var trackedBlog = context.Blogs.Find(propertyValues["Id"]);

    context.Entry(trackedBlog).CurrentValues.SetValues(propertyValues);

    context.SaveChanges();
}

Další informace o práci s hodnotami vlastností najdete v tématu Přístup ke sledovaným entitě .

Použít původní hodnoty

Zatím každý přístup buď provedl dotaz před provedením aktualizace, nebo aktualizoval všechny hodnoty vlastností bez ohledu na to, jestli se změnily nebo ne. Chcete-li aktualizovat pouze hodnoty, které se změnily bez dotazování v rámci aktualizace, vyžadují specifické informace o tom, které hodnoty vlastností se změnily. Běžným způsobem, jak tyto informace získat, je odeslat zpět aktuální i původní hodnoty v http Post nebo podobné. Příklad:

public static void UpdateFromHttpPost6(Blog blog, Dictionary<string, object> originalValues)
{
    using var context = new BlogsContext();

    context.Attach(blog);
    context.Entry(blog).OriginalValues.SetValues(originalValues);

    context.SaveChanges();
}

V tomto kódu je entita s upravenými hodnotami nejprve připojena. To způsobí, že EF Core bude sledovat entitu Unchanged ve stavu, tj. bez hodnot vlastností označených jako změněné. Slovník původních hodnot se pak použije u této sledované entity. Označí se jako změněné vlastnosti s různými aktuálními a původními hodnotami. Vlastnosti, které mají stejné aktuální a původní hodnoty, nebudou označeny jako změněné.

V tomto případě:

  • Pomocí funkce Attach se sleduje pouze jedna instance entity.
  • Instance entity není dotazována z databáze jako součást aktualizace.
  • Použití původních hodnot zajistí, že se v databázi aktualizují pouze hodnoty vlastností, které se skutečně změnily.
  • Provede se jedna doba odezvy databáze.

Stejně jako u příkladů v předchozí části nemusí být původní hodnoty předány jako slovník; Instance entity nebo DTO budou fungovat také.

Tip

I když tento přístup má atraktivní charakteristiky, vyžaduje odeslání původních hodnot entity do a z webového klienta. Pečlivě zvažte, zda tato dodatečná složitost stojí za výhody; pro mnoho aplikací je jedním z jednodušších přístupů více pragmatičtější.

Připojení serializovaného grafu

EF Core pracuje s grafy entit propojených pomocí cizích klíčů a vlastností navigace, jak je popsáno v tématu Změna cizích klíčů a navigace. Pokud se tyto grafy vytvářejí mimo EF Core, například ze souboru JSON, můžou mít více instancí stejné entity. Tyto duplicity je potřeba před sledováním grafu přeložit na jednotlivé instance.

Grafy bez duplicit

Než začnete pokračovat, je důležité si uvědomit, že:

  • Serializátory mají často možnosti zpracování smyček a duplicitních instancí v grafu.
  • Volba objektu použitého jako kořen grafu může často pomoct omezit nebo odebrat duplicity.

Pokud je to možné, použijte možnosti serializace a zvolte kořeny, které nemají za následek duplicity. Například následující kód používá Json.NET k serializaci seznamu blogů s přidruženými příspěvky:

using var context = new BlogsContext();

var blogs = context.Blogs.Include(e => e.Posts).ToList();

var serialized = JsonConvert.SerializeObject(
    blogs,
    new JsonSerializerSettings { ReferenceLoopHandling = ReferenceLoopHandling.Ignore, Formatting = Formatting.Indented });

Console.WriteLine(serialized);

Kód JSON vygenerovaný z tohoto kódu:

[
  {
    "Id": 1,
    "Name": ".NET Blog",
    "Summary": "Posts about .NET",
    "Posts": [
      {
        "Id": 1,
        "Title": "Announcing the Release of EF Core 5.0",
        "Content": "Announcing the release of EF Core 5.0, a full featured cross-platform...",
        "BlogId": 1
      },
      {
        "Id": 2,
        "Title": "Announcing F# 5",
        "Content": "F# 5 is the latest version of F#, the functional programming language...",
        "BlogId": 1
      }
    ]
  },
  {
    "Id": 2,
    "Name": "Visual Studio Blog",
    "Summary": "Posts about Visual Studio",
    "Posts": [
      {
        "Id": 3,
        "Title": "Disassembly improvements for optimized managed debugging",
        "Content": "If you are focused on squeezing out the last bits of performance for your .NET service or...",
        "BlogId": 2
      },
      {
        "Id": 4,
        "Title": "Database Profiling with Visual Studio",
        "Content": "Examine when database queries were executed and measure how long the take using...",
        "BlogId": 2
      }
    ]
  }
]

Všimněte si, že ve formátu JSON nejsou žádné duplicitní blogy ani příspěvky. To znamená, že jednoduchá volání Update budou fungovat na aktualizaci těchto entit v databázi:

public static void UpdateBlogsFromJson(string json)
{
    using var context = new BlogsContext();

    var blogs = JsonConvert.DeserializeObject<List<Blog>>(json);

    foreach (var blog in blogs)
    {
        context.Update(blog);
    }

    context.SaveChanges();
}

Zpracování duplicit

Kód v předchozím příkladu serializoval každý blog s přidruženými příspěvky. Pokud se tento postup změní tak, aby serializoval každý příspěvek s přidruženým blogem, pak se duplicity zavádějí do serializovaného JSON. Příklad:

using var context = new BlogsContext();

var posts = context.Posts.Include(e => e.Blog).ToList();

var serialized = JsonConvert.SerializeObject(
    posts,
    new JsonSerializerSettings { ReferenceLoopHandling = ReferenceLoopHandling.Ignore, Formatting = Formatting.Indented });

Console.WriteLine(serialized);

Serializovaný JSON teď vypadá takto:

[
  {
    "Id": 1,
    "Title": "Announcing the Release of EF Core 5.0",
    "Content": "Announcing the release of EF Core 5.0, a full featured cross-platform...",
    "BlogId": 1,
    "Blog": {
      "Id": 1,
      "Name": ".NET Blog",
      "Summary": "Posts about .NET",
      "Posts": [
        {
          "Id": 2,
          "Title": "Announcing F# 5",
          "Content": "F# 5 is the latest version of F#, the functional programming language...",
          "BlogId": 1
        }
      ]
    }
  },
  {
    "Id": 2,
    "Title": "Announcing F# 5",
    "Content": "F# 5 is the latest version of F#, the functional programming language...",
    "BlogId": 1,
    "Blog": {
      "Id": 1,
      "Name": ".NET Blog",
      "Summary": "Posts about .NET",
      "Posts": [
        {
          "Id": 1,
          "Title": "Announcing the Release of EF Core 5.0",
          "Content": "Announcing the release of EF Core 5.0, a full featured cross-platform...",
          "BlogId": 1
        }
      ]
    }
  },
  {
    "Id": 3,
    "Title": "Disassembly improvements for optimized managed debugging",
    "Content": "If you are focused on squeezing out the last bits of performance for your .NET service or...",
    "BlogId": 2,
    "Blog": {
      "Id": 2,
      "Name": "Visual Studio Blog",
      "Summary": "Posts about Visual Studio",
      "Posts": [
        {
          "Id": 4,
          "Title": "Database Profiling with Visual Studio",
          "Content": "Examine when database queries were executed and measure how long the take using...",
          "BlogId": 2
        }
      ]
    }
  },
  {
    "Id": 4,
    "Title": "Database Profiling with Visual Studio",
    "Content": "Examine when database queries were executed and measure how long the take using...",
    "BlogId": 2,
    "Blog": {
      "Id": 2,
      "Name": "Visual Studio Blog",
      "Summary": "Posts about Visual Studio",
      "Posts": [
        {
          "Id": 3,
          "Title": "Disassembly improvements for optimized managed debugging",
          "Content": "If you are focused on squeezing out the last bits of performance for your .NET service or...",
          "BlogId": 2
        }
      ]
    }
  }
]

Všimněte si, že graf teď obsahuje více instancí blogu se stejnou hodnotou klíče a několik instancí Post se stejnou hodnotou klíče. Pokus o sledování tohoto grafu, jako jsme to udělali v předchozím příkladu, vyvolá následující:

System.InvalidOperationException: Instance typu entity Post nelze sledovat, protože již probíhá sledování jiné instance s hodnotou klíče {ID: 2}. Při připojování existujících entit se ujistěte, že je připojena pouze jedna instance entity s danou hodnotou klíče.

Tento problém můžeme vyřešit dvěma způsoby:

  • Použití možností serializace JSON, které zachovávají odkazy
  • Řešení identit při sledování grafu

Zachování odkazů

Json.NET tuto PreserveReferencesHandling možnost můžete zpracovat. Příklad:

var serialized = JsonConvert.SerializeObject(
    posts,
    new JsonSerializerSettings
    {
        PreserveReferencesHandling = PreserveReferencesHandling.All, Formatting = Formatting.Indented
    });

Výsledný json teď vypadá takto:

{
  "$id": "1",
  "$values": [
    {
      "$id": "2",
      "Id": 1,
      "Title": "Announcing the Release of EF Core 5.0",
      "Content": "Announcing the release of EF Core 5.0, a full featured cross-platform...",
      "BlogId": 1,
      "Blog": {
        "$id": "3",
        "Id": 1,
        "Name": ".NET Blog",
        "Summary": "Posts about .NET",
        "Posts": [
          {
            "$ref": "2"
          },
          {
            "$id": "4",
            "Id": 2,
            "Title": "Announcing F# 5",
            "Content": "F# 5 is the latest version of F#, the functional programming language...",
            "BlogId": 1,
            "Blog": {
              "$ref": "3"
            }
          }
        ]
      }
    },
    {
      "$ref": "4"
    },
    {
      "$id": "5",
      "Id": 3,
      "Title": "Disassembly improvements for optimized managed debugging",
      "Content": "If you are focused on squeezing out the last bits of performance for your .NET service or...",
      "BlogId": 2,
      "Blog": {
        "$id": "6",
        "Id": 2,
        "Name": "Visual Studio Blog",
        "Summary": "Posts about Visual Studio",
        "Posts": [
          {
            "$ref": "5"
          },
          {
            "$id": "7",
            "Id": 4,
            "Title": "Database Profiling with Visual Studio",
            "Content": "Examine when database queries were executed and measure how long the take using...",
            "BlogId": 2,
            "Blog": {
              "$ref": "6"
            }
          }
        ]
      }
    },
    {
      "$ref": "7"
    }
  ]
}

Všimněte si, že tento kód JSON nahradil duplicity odkazy, jako "$ref": "5" jsou odkazy na již existující instanci v grafu. Tento graf lze znovu sledovat pomocí jednoduchých volání Update, jak je znázorněno výše.

Podpora System.Text.Json v knihovnách základních tříd .NET (BCL) má podobnou možnost, která vytvoří stejný výsledek. Příklad:

var serialized = JsonSerializer.Serialize(
    posts, new JsonSerializerOptions { ReferenceHandler = ReferenceHandler.Preserve, WriteIndented = true });

Řešení duplicit

Pokud není možné odstranit duplicity v procesu serializace, pak ChangeTracker.TrackGraph poskytuje způsob, jak to zpracovat. TrackGraph funguje podobně jako AddAttach a Update s tím rozdílem, že před sledováním vygeneruje zpětné volání pro každou instanci entity. Toto zpětné volání lze použít ke sledování entity nebo k jejímu ignorování. Příklad:

public static void UpdatePostsFromJsonWithIdentityResolution(string json)
{
    using var context = new BlogsContext();

    var posts = JsonConvert.DeserializeObject<List<Post>>(json);

    foreach (var post in posts)
    {
        context.ChangeTracker.TrackGraph(
            post, node =>
            {
                var keyValue = node.Entry.Property("Id").CurrentValue;
                var entityType = node.Entry.Metadata;

                var existingEntity = node.Entry.Context.ChangeTracker.Entries()
                    .FirstOrDefault(
                        e => Equals(e.Metadata, entityType)
                             && Equals(e.Property("Id").CurrentValue, keyValue));

                if (existingEntity == null)
                {
                    Console.WriteLine($"Tracking {entityType.DisplayName()} entity with key value {keyValue}");

                    node.Entry.State = EntityState.Modified;
                }
                else
                {
                    Console.WriteLine($"Discarding duplicate {entityType.DisplayName()} entity with key value {keyValue}");
                }
            });
    }

    context.SaveChanges();
}

Pro každou entitu v grafu bude tento kód:

  • Vyhledání typu entity a hodnoty klíče entity
  • Vyhledání entity pomocí tohoto klíče v sledování změn
    • Pokud se entita najde, nebude provedena žádná další akce, protože entita je duplicitní.
    • Pokud se entita nenajde, sleduje se nastavením stavu na Modified

Výstupem spuštění tohoto kódu je:

Tracking EntityType: Post entity with key value 1
Tracking EntityType: Blog entity with key value 1
Tracking EntityType: Post entity with key value 2
Discarding duplicate EntityType: Post entity with key value 2
Tracking EntityType: Post entity with key value 3
Tracking EntityType: Blog entity with key value 2
Tracking EntityType: Post entity with key value 4
Discarding duplicate EntityType: Post entity with key value 4

Důležité

Tento kód předpokládá, že všechny duplicity jsou identické. Díky tomu můžete libovolně zvolit jeden z duplicit, který chcete sledovat, zatímco ostatní zahodíte. Pokud se duplicity mohou lišit, pak se kód bude muset rozhodnout, jak určit, který z nich se má použít, a jak kombinovat vlastnosti a navigační hodnoty dohromady.

Poznámka

Pro zjednodušení tento kód předpokládá, že každá entita má vlastnost primárního klíče s názvem Id. To může být kodifikováno do abstraktní základní třídy nebo rozhraní. Případně lze z metadat získat IEntityType vlastnost nebo vlastnosti primárního klíče, aby tento kód fungoval s libovolným typem entity.

Nepodařilo se nastavit hodnoty klíčů

Typy entit se často konfigurují tak, aby používaly automaticky generované hodnoty klíče. Toto je výchozí hodnota pro celočíselné a GUID vlastnosti nesložených klíčů. Pokud však typ entity není nakonfigurovaný tak, aby používal automaticky generované hodnoty klíče, musí být před sledováním entity nastavena explicitní hodnota klíče. Například pomocí následujícího typu entity:

public class Pet
{
    [DatabaseGenerated(DatabaseGeneratedOption.None)]
    public int Id { get; set; }

    public string Name { get; set; }
}

Zvažte kód, který se pokusí sledovat dvě nové instance entity bez nastavení hodnot klíče:

using var context = new BlogsContext();

context.Add(new Pet { Name = "Smokey" });

try
{
    context.Add(new Pet { Name = "Clippy" }); // This will throw
}
catch (Exception e)
{
    Console.WriteLine($"{e.GetType().FullName}: {e.Message}");
}

Tento kód vyvolá:

System.InvalidOperationException: Instance typu Pet nemůže být sledována, protože již probíhá sledování jiné instance s hodnotou klíče {ID: 0}. Při připojování existujících entit se ujistěte, že je připojena pouze jedna instance entity s danou hodnotou klíče.

Opravou je explicitní nastavení hodnot klíče nebo konfigurace vlastnosti klíče tak, aby používala vygenerované hodnoty klíče. Další informace najdete v tématu Vygenerované hodnoty .

Nadměrné využití jedné instance DbContext

DbContext je navržený tak, aby představoval krátkodobou jednotku práce, jak je popsáno v tématu Inicializace a konfigurace DbContext, a propracovaný ve funkci Change Tracking v EF Core. Neposledování těchto pokynů usnadňuje situaci, kdy se pokusíte sledovat více instancí stejné entity. Obvyklými příklady jsou:

  • Pomocí stejné instance DbContext nastavte stav testu a pak test spusťte. To často vede k tomu, že DbContext stále sleduje jednu instanci entity z testovacího nastavení a pak se pokouší připojit novou instanci ve správném testu. Místo toho pro nastavení stavu testu a správného testovacího kódu použijte jinou instanci DbContext.
  • Použití sdílené instance DbContext v úložišti nebo podobném kódu. Místo toho se ujistěte, že vaše úložiště používá pro každou jednotku práce jednu instanci DbContext.

Překlad identit a dotazy

K překladu identity dochází automaticky, když se entity sledují z dotazu. To znamená, že pokud je již sledována instance entity s danou hodnotou klíče, použije se tato existující sledované instance místo vytvoření nové instance. To má důležitý výsledek: pokud se data v databázi změnila, neprojeví se to ve výsledcích dotazu. To je dobrý důvod, proč pro každou jednotku práce použít novou instanci DbContext, jak je popsáno v tématu Inicializace a Konfigurace DbContext, a propracovaný ve funkci Change Tracking v EF Core.

Důležité

Je důležité vědět, že EF Core vždy provádí dotaz LINQ na dbSet s databází a vrací pouze výsledky na základě toho, co je v databázi. Pokud jsou však vrácené entity již sledovány, použijí se sledované instance místo vytváření instancí z dat v databázi.

Reload() nebo GetDatabaseValues() se dají použít, když se sledované entity musí aktualizovat nejnovějšími daty z databáze. Další informace najdete v tématu Přístup ke sledovaným entitě .

Na rozdíl od sledování dotazů neprovádí žádné dotazy pro sledování řešení identit. To znamená, že dotazy bez sledování můžou vracet duplicity stejně jako v případě serializace JSON popsané výše. Obvykle se nejedná o problém, pokud se výsledky dotazu serializují a odesílají klientovi.

Tip

Neprovádějte rutinně dotaz bez sledování a pak připojte vrácené entity ke stejnému kontextu. Bude to pomalejší i obtížnější než použití sledovacího dotazu.

Dotazy bez sledování neprovádějí překlad identit, protože to má vliv na výkon streamování velkého počtu entit z dotazu. Důvodem je to, že řešení identit vyžaduje sledování jednotlivých vrácených instancí, aby se místo pozdějšího vytvoření duplikátu dala použít.

Dotazy bez sledování je možné vynutit k provedení řešení identity pomocí AsNoTrackingWithIdentityResolution<TEntity>(IQueryable<TEntity>). Dotaz pak bude sledovat vrácené instance (bez jejich sledování normálním způsobem) a zajistit, aby se ve výsledcích dotazu nevytvořily žádné duplicity.

Přepsání rovnosti objektů

EF Core používá při porovnávání instancí entit rovnost odkazů. To platí i v případě, že typy entit přepíší Object.Equals(Object) nebo jinak změní rovnost objektů. Existuje však jedno místo, kde přepsání rovnosti může mít vliv na chování EF Core: když navigace v kolekci používají přepsánou rovnost místo rovnosti odkazu, a proto hlásí více instancí jako stejné.

Z tohoto důvodu se doporučuje zabránit přepsání rovnosti entit. Pokud se používá, nezapomeňte vytvořit navigace kolekce, které vynucují rovnost odkazů. Můžete například vytvořit porovnávač rovnosti, který používá rovnost odkazů:

public sealed class ReferenceEqualityComparer : IEqualityComparer<object>
{
    private ReferenceEqualityComparer()
    {
    }

    public static ReferenceEqualityComparer Instance { get; } = new ReferenceEqualityComparer();

    bool IEqualityComparer<object>.Equals(object x, object y) => x == y;

    int IEqualityComparer<object>.GetHashCode(object obj) => RuntimeHelpers.GetHashCode(obj);
}

(Počínaje rozhraním .NET 5 je součástí seznamu BCL jako ReferenceEqualityComparer.)

Tento porovnávač se pak dá použít při vytváření navigace kolekce. Příklad:

public ICollection<Order> Orders { get; set; }
    = new HashSet<Order>(ReferenceEqualityComparer.Instance);

Porovnání klíčových vlastností

Kromě porovnání rovnosti je potřeba hodnoty klíčů také uspořádat. To je důležité pro zabránění zablokování při aktualizaci více entit v jednom volání SaveChanges. Všechny typy používané pro vlastnosti primárního, alternativního nebo cizího klíče, stejně jako typy používané pro jedinečné indexy, musí implementovat IComparable<T> a IEquatable<T>. Typy, které se běžně používají jako klíče (int, Guid, řetězec atd.), už podporují tato rozhraní. Vlastní typy klíčů mohou tato rozhraní přidat.