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 either method. Both methods will return an entity object if the record is present in the database, otherwise they will return null .

What is the difference and when should you use which method?

Answer:

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

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

Consider each:

  • The Find() method takes the primary key of the record as a parameter.

    Its peculiarity is that, unlike other methods, it first accesses memory and looks for a record among the context objects tracked by the EntityFramework, and only then (if nothing is found) it performs a query to the database. If an object is found in the context that has not yet been saved 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 should be noted that the selection includes not one record, but two SELECT TOP (2) . This is used internally by EF to validate record uniqueness. If the result is more than one record with the same primary key, EF will throw an exception:

    System.InvalidOperationException: "The sequence contains more than one element"

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

    For example, let's find a record with Title == "test" :

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

    FirstOrDefault() , unlike Find() executes 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 searches for only one SELECT TOP (1) record. 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 no entry is found, an exception will be thrown:

    System.InvalidOperationException: "The 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: "The sequence contains more than one element"

  • The Single() method is similar to SingleOrDefault() and differs in that if no entry is found, an exception will be thrown:

    System.InvalidOperationException: "The sequence contains no elements"


Let's summarize:

  1. When to use Find() ?

    When you need to search for the primary key and select all the entity data. Find() does not execute the request 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 the loading of dependent data (for example, via Include() ) – Find() will not let you do this.

  2. When to use the rest of the methods?

    If you need to select specific 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 a record (without *OrDefault() exceptions), the *OrDefault() methods will *OrDefault() :

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

    If it is assumed that the context can contain local copies of the data, you can get them from there without having to query the database. To do this, you need to refer to 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