c# – Differences between Find(), FirstOrDefault() methods when used with Entity Framework

Question:

If you need to get a record from a database by its primary key, you can use both methods. Both methods will return an entity object if the entry is present in the database, otherwise they will return null .

What is their difference and when to use which method?

Answer:

To search and get an entity object, you can use the following methods:

Find() , First() , FirstOrDefault() , Single() , SingleOrDefault() .

Let's consider each:

  • The Find() method takes the entry's primary key as a parameter.

    Its peculiarity is that, unlike other methods, it first accesses the memory and searches for a record among the context objects tracked by the EntityFramework, and only then (if it does not find anything) executes a query to the database. If there is an object in the context that is not yet stored in the database, the Find() method will still return it (with the Added state). If the entry is not found either there or there, it will return null .

    Generated SQL query:

     SELECT TOP (2) [Extent1].[Id] AS [Id], [Extent1].[Title] AS [Title], FROM [dbo].[Topics] AS [Extent1] WHERE [Extent1].[Id] = @p0

    It is worth noting that not one record falls into the selection, but two SELECT TOP (2) . This is used internally by EF to check if an entry is unique. If the result is more than one record with the same primary key, EF will throw an exception:

    System.InvalidOperationException: "Sequence contains more than one element"

  • The FirstOrDefault() method takes a predicate as a parameter, which makes it possible to search not only by the primary key, but also by any condition.

    For example, let's find a post that has Title == "test" :

     var topic = context.Topics.FirstOrDefault(topic => topic.Title == "test");

    FirstOrDefault() , unlike Find() , performs a query to the database every time, regardless of whether there is data in the context. If a record comes from the database that differs from the one in the context, EF will return the record from the context. If the record is not in the database, it will return null , even if it is in the context.

    Generated SQL query:

     SELECT TOP (1) [Extent1].[Id] AS [Id], [Extent1].[Title] AS [Title], FROM [dbo].[Topics] AS [Extent1] WHERE [Extent1].[Id] = @p__linq__0

    It looks for only one entry SELECT TOP (1) . If it turns out that there are several records, it will return the first one.

  • The First() method is similar to FirstOrDefault() with one difference – if the entry is not found, an exception will be thrown:

    System.InvalidOperationException: "Sequence contains no elements"

  • The SingleOrDefault() method is similar to FirstOrDefault() and returns an entity object or null . However, the request generates something similar to the Find() method:

     SELECT TOP (2) [Extent1].[Id] AS [Id], [Extent1].[Title] AS [Title], FROM [dbo].[Topics] AS [Extent1] WHERE [Extent1].[Id] = @p__linq__0

    And if more than one entry is found, it throws an exception:

    System.InvalidOperationException: "Sequence contains more than one element"

  • The Single() method is similar to SingleOrDefault() except that if an entry is not found, an exception will be thrown:

    System.InvalidOperationException: "Sequence contains no elements"


Let's summarize:

  1. When to use Find() ?

    When you need to search by primary key and select all entity data. Find() does not execute the query if the record is already loaded into the context and, as a result, will outperform all other methods in performance. However, if you need to select some specific fields (for example, only Id and Title ), you will have to use the rest of the methods. The same applies to loading dependent data (for example, through Include() ) – Find() will not do this.

  2. When to use other methods?

    If you need to select certain fields of an entity or you also need to load dependent data. Which methods to use depends on individual requirements: if you need to check for the existence of an entry (without throwing exceptions), the *OrDefault() methods will do:

     var topic = context.Topics .Select(topic => new { topic.Id, topic.Title }) .FirstOrDefault(topic => topic.Id == 44); if (topic == null) { // ... }

    If you expect the context to contain local copies of the data, you can get it from there without querying the database. To do this, you need to access the Local property of the DbSet object:

     Var topic = context.Topics.Local.FirstOrDefault(topic => topic.Id == topicId) ?? context.Topics.FirstOrDefault(topic => topic.Id == topicId);

Используемые источники:

Scroll to Top