Если нужно получить запись из базы данных по ее первичному ключу, можно воспользоваться и тем и другим методом. Оба метода вернут объект сущности, если запись присутствует в базе, в противном случае вернут null
В чем их разница и когда какой метод использовать?
Ответ
Для поиска и получения объекта сущности можно воспользоваться методами:
Find(), First(), FirstOrDefault(), Single(), SingleOrDefault().
Рассмотрим каждый:
Метод Find() принимает в качестве параметра первичный ключ записи.
Его особенность состоит в том, что в отличие от остальных методов, он сначала обращается к памяти и ищет запись среди объектов контекста, отслеживаемых EntityFramework'ом, и только потом (если ничего не нашел) выполняет запрос к бд. Если в контексте найдется объект, который еще не сохранен в базе, метод Find() все равно вернет его (с состоянием Added). Если запись не будет найдена ни там ни там, вернет null
Генерируемый SQL запрос:
SELECT TOP (2)
[Extent1].[Id] AS [Id],
[Extent1].[Title] AS [Title],
FROM [dbo].[Topics] AS [Extent1]
WHERE [Extent1].[Id] = @p0
Стоит обратить внимание, что в выборку попадает не одна запись, а две SELECT TOP (2). Это используется внутренними механизмами EF для проверки уникальности записи. Если в результате окажется более одной записи с одним и тем же первичным ключем, EF сгенерирует исключение:
System.InvalidOperationException: "Последовательность содержит более одного элемента"
Метод FirstOrDefault() принимает в качестве параметра предикат, что дает возможность искать не только по первичному ключу, но и по любому составленному условию.
Например, найдем запись, у которой Title == "test"
var topic = context.Topics.FirstOrDefault(topic => topic.Title == "test");
FirstOrDefault() в отличии от Find() каждый раз выполняет запрос к базе, не зависимо от того есть ли данные в контексте. Если из базы придет запись, отличающаяся от той что лежит в контексте, EF вернет запись из контекста. Если записи не окажется в базе, вернет null, даже если она есть в контексте.
Генерируемый SQL запрос:
SELECT TOP (1)
[Extent1].[Id] AS [Id],
[Extent1].[Title] AS [Title],
FROM [dbo].[Topics] AS [Extent1]
WHERE [Extent1].[Id] = @p__linq__0
Тут ищет только одну запись SELECT TOP (1). Если окажется что записей несколько, вернет первую.
Метод First() аналогичен FirstOrDefault() с одним отличием - если запись не найдена, будет сгенерировано исключение:
System.InvalidOperationException: "Последовательность не содержит элементов"
Метод SingleOrDefault() аналогичен FirstOrDefault() и возвращает объект сущности, либо null. Однако запрос генерирует подобный методу Find()
SELECT TOP (2)
[Extent1].[Id] AS [Id],
[Extent1].[Title] AS [Title],
FROM [dbo].[Topics] AS [Extent1]
WHERE [Extent1].[Id] = @p__linq__0
И при обнаружении более одной записи выкидывает исключение:
System.InvalidOperationException: "Последовательность содержит более одного элемента"
Метод Single() аналогичен SingleOrDefault() и отличается тем, что если запись не найдена, будет сгенерировано исключение:
System.InvalidOperationException: "Последовательность не содержит элементов"
Подведем итоги:
Когда использовать Find()?
Когда искать нужно по первичному ключу и выбрать нужно все данные сущности. Find() не выполняет запрос, если запись уже загружена в контекст и, как следствие, будет превосходить в производительности все остальные методы. Однако если нужно выбрать какие-то определенные поля (например только Id и Title), придется воспользоваться остальными методами. То же касается и подгрузки зависимых данных (например через Include()) - Find() это сделать не даст.
Когда использовать остальные методы?
Если нужна выборка определенных полей сущности или нужно также загрузить зависимые данные. Какие именно методы использовать зависит от индивидуальных требований: если вам нужно проверить существование записи (без генерации исключений), подойдут методы *OrDefault()
var topic = context.Topics
.Select(topic => new { topic.Id, topic.Title })
.FirstOrDefault(topic => topic.Id == 44);
if (topic == null)
{
// ...
}
Если предполагается, что контекст может содержать локальные копии данных, можно получить их оттуда, не выполняя запроса к базе. Для этого нужно обратиться к свойству Local объекта DbSet
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
Комментариев нет:
Отправить комментарий