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 theAdded
state). If the entry is not found either there or there, it will returnnull
.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()
, unlikeFind()
, 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 returnnull
, 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 toFirstOrDefault()
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 toFirstOrDefault()
and returns an entity object ornull
. However, the request generates something similar to theFind()
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 toSingleOrDefault()
except that if an entry is not found, an exception will be thrown:System.InvalidOperationException: "Sequence contains no elements"
Let's summarize:
-
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, onlyId
andTitle
), you will have to use the rest of the methods. The same applies to loading dependent data (for example, throughInclude()
) –Find()
will not do this. -
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 theDbSet
object:Var topic = context.Topics.Local.FirstOrDefault(topic => topic.Id == topicId) ?? context.Topics.FirstOrDefault(topic => topic.Id == topicId);
Используемые источники:
- MSDN: Entity Framework Querying and Finding Entities
- Primer on Selecting Data Using Entity Framework
- StackOverflow: Are Find and Where().FirstOrDefault() equivalent?
- StackOverflow: Using .Find() & .Include() on the same query