Страницы

Поиск по вопросам

четверг, 28 ноября 2019 г.

Различия методов Find(), FirstOrDefault() при использовании с Entity Framework

#c_sharp #.net #entity_framework #linq #c_sharp_faq


Если нужно получить запись из базы данных по ее первичному ключу, можно воспользоваться
и тем и другим методом. Оба метода вернут объект сущности, если запись присутствует
в базе, в противном случае вернут null.

В чем их разница и когда какой метод использовать?
    


Ответы

Ответ 1



Для поиска и получения объекта сущности можно воспользоваться методами: 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

Комментариев нет:

Отправить комментарий