Сущность, связанная с Entity Framework 6, где предложение datetime не переводится в SQL

Как мне добавить фильтр даты в сгенерированный SQL-запрос без доступа к контексту базы данных?

EF выполняет очень дорогие запросы. Я не передаю и не использую результаты как IEnumerable, поэтому я ожидаю, что EF сделает эффективный запрос. Я ожидал, что он будет использовать время начала и окончания для фильтрации результатов в SQL.

EF переводит следующее

context.Channels
  .First(ch => ch.Channel_ID == id)
  .ChannelValues
  .Where(cv => start < cv.ValueTime && cv.ValueTime <= stop);

в

SELECT TOP (1) 
    [Extent1].[Channel_ID] AS [Channel_ID], 
    [Extent1].[ChannelType_ID] AS [ChannelType_ID], 
    [Extent1].[Name] AS [Name], 
    FROM [dbo].[Channel] AS [Extent1]
    WHERE [Extent1].[Channel_ID] = @p__linq__0
p__linq__0: '1' (Type = Int32, IsNullable = false)

SELECT 
    [Extent1].[Channel_ID] AS [Channel_ID], 
    [Extent1].[ValueTime] AS [ValueTime], 
    [Extent1].[Value] AS [Value]
    FROM [dbo].[ChannelValue] AS [Extent1]
    WHERE [Extent1].[Channel_ID] = @EntityKeyValue1
EntityKeyValue1: '1' (Type = Int32, IsNullable = false)

Первый SQL-запрос меня устраивает, но второй должен создать запрос, аналогичный тому, что делает следующий

context.ChannelValues
.Where(cv => cv.Channel_ID == id && start < cv.ValueTime && cv.ValueTime <= stop);

это приводит к

SELECT 
    [Extent1].[Channel_ID] AS [Channel_ID], 
    [Extent1].[ValueTime] AS [ValueTime], 
    [Extent1].[Value] AS [Value]
    FROM [dbo].[ChannelValue] AS [Extent1]
    WHERE ([Extent1].[Channel_ID] = @p__linq__0) AND (@p__linq__1 < [Extent1].[ValueTime]) AND ([Extent1].[ValueTime] <= @p__linq__2)
p__linq__0: '1' (Type = Int32, IsNullable = false)
p__linq__1: '7/1/2018 12:00:00 AM' (Type = DateTime2, IsNullable = false)
p__linq__2: '7/23/2018 11:45:00 AM' (Type = DateTime2, IsNullable = false

Там, где мне это действительно нужно, у меня нет доступа к DatabaseContext.

context.Channels.Include(ch => ch.ChannelValues).Where(ch => ch.Channel_Id = id && ch => start < ch.ChannelValue.ValueTime && ch.ChannelValue.ValueTime <= stop) меняет его в лучшую сторону? Это не идеальный вопрос для случая. Я просто хотел намекнуть вам использовать include.

Alexander Schmidt 23.08.2018 15:20

@ sprinter252 Спасибо, но у меня нет доступа к контексту базы данных. У меня есть только сущность канала. Я пробовал включить, это делает запрос более сложным, но не добавляет предложения datetime where

Badger 23.08.2018 15:31

Итак .. Какой у вас вопрос?

MetalJacketNL 23.08.2018 15:53

@MennoB Как мне добавить фильтр даты в запрос SQL без доступа к контексту базы данных? Отредактирую свой вопрос.

Badger 23.08.2018 16:01

Что вы имеете в виду, когда говорите «У меня нет доступа к DatabaseContext».? Я не понимаю, почему вы просто не используете второй подход для начала. Что дает вам первый подход, чего нет во втором? Вы включили ленивую загрузку?

Flater 23.08.2018 16:13

@Flater Я полагаю, я мог бы повторно фактор, чтобы передать контекст базы данных во все мои функции ... Но это кажется ненужным, когда у EF есть вся информация для начала. Насколько я понимаю, по умолчанию используется ленивая загрузка.

Badger 23.08.2018 16:32

@Badger: Я не понимаю, почему у вас нет доступа к контексту базы данных, но каким-то образом все еще есть доступ к самому EF. Я думаю, что это скорее причина проблемы, не могли бы вы подробнее рассказать об этой части кода?

Flater 23.08.2018 16:34

Если вы хотите, чтобы ваш запрос выполнял некоторые действия с вашей базой данных, EF нужен ваш DatabaseContext. Вы можете использовать те же лямбды в Коллекциях для управления данными, но в конце концов всегда будет что-то вроде DatabaseContext.SomeMethod () для управления данными в базе данных.

MetalJacketNL 23.08.2018 16:35

@Flater Итак, я передаю канал context.Channels.First(ch => ch.Channel_ID == id) другой функции, затем в этой функции я хочу получить доступ к значениям, связанным с этим каналом channel.ChannelValues .Where(cv => start < cv.ValueTime && cv.ValueTime <= stop);.

Badger 23.08.2018 16:59

@MennoB Я знаю, что переменная канала, которую я использую, находится за прокси-объектом, который имеет доступ к ObjectContext (не может найти контекст базы данных) и использует его для запроса БД, но я не могу получить доступ к этому ObjectContext без большого размышления .

Badger 23.08.2018 17:02

@Badger: Тебе действительно не следует этого делать. Фактически вы все еще передаете свой контекст, поскольку он является частью «ленивой загрузки» в сущности. Однако вы больше не можете напрямую получить доступ к своему контексту. Здесь есть много подводных камней, например если ваш первый метод использовал using() для вашего контекста, возврат из метода удаляет контекст, даже если вы все еще не загрузили свои данные лениво. Вы должны вернуть все данные, которые вам нужны. перед вы закрываете свой контекст, а передача лениво загруженного объекта от метода к методу значительно усложнит вашу жизнь как разработчика.

Flater 23.08.2018 17:03

@Badger: Я до сих пор не понимаю, почему нельзя просто использовать второй вариант.

Flater 23.08.2018 17:04

@Flater using(var context = new DatabaseContext) { var channel = context.Channels.First(ch => ch.Channel_ID == id); var total = Totalize(channel); return total; }

Badger 23.08.2018 17:09

@Flater Предыдущий комментарий - это просто пример, поэтому я не сохраняю объект после того, как контекст удален. Второй вариант не используется, потому что он включает в себя несколько функций, я мог бы провести рефакторинг, но это похоже на то, что EF должен делать, иначе свойства навигации действительно полезны только для отношений от 1: 1 до 1: 100

Badger 23.08.2018 17:16
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
14
119
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

«EF делает очень дорогие запросы» ммм, у вас есть контроль над тем, как вы хотели бы использовать фреймворк с его ограничением *, если вы знаете, что что-то плохо, но все же делаете это так, это не ошибка EF.

Вы могли бы сделать это именно так, как вы.

т.е. если бы вы сделали это с необработанным sql, как бы вы это сделали.

Затем просто сделайте то же самое в EF.

var channel = context.Channels.First(x=> x.Channel_ID == id)
channel.ChannelValues = context.ChannelValues.Where(x => x.Channel_ID == id 
                                             && start < x.ValueTime 
                                             && x.ValueTime <= stop
                              ).ToList();

Фактический ответ, который вы даете, не отличается от второго предложения OP (объединенный Where без первого). Другая часть (сохранение подмножества как части channel.ChannelValues) на самом деле не является частью вопроса.

Flater 23.08.2018 16:35

Приведенный пример кода является моим вторым примером, однако у меня нет доступа к контексту базы данных в функции, которая требует значений. SQL в моем вопросе генерируется EF.

Badger 23.08.2018 16:35

@Badger извините, но я действительно не понимаю, о чем вы спрашиваете. «Как мне добавить фильтр даты в сгенерированный запрос SQL без доступа к контексту базы данных?» Пожалуйста, просто укажите sql, который вы хотите сгенерировать .... тогда мы можем преобразовать его в linq

Seabizkit 24.08.2018 09:52

@Flater, извините, я просто пытаюсь понять, о чем спрашивают. см. ответ на OP. выше.

Seabizkit 24.08.2018 09:53
Ответ принят как подходящий

Я не думаю, что вторая часть запроса вообще передается в SQL.

Это говорит SQL вернуть все ChannelValues ​​с Channel_ID, равным id.

context.Channels
  .First(ch => ch.Channel_ID == id)
  .ChannelValues

Затем в памяти выполняется второе предложение where.

.Where(cv => start < cv.ValueTime && cv.ValueTime <= stop);

Чтобы выполнить все это в базе данных, вам нужно будет сделать что-то вроде этого:

context.Channels
  .Where(ch => ch.Channel_ID == id)
  .SelectMany(x => x.ChannelValues)
  .Where(cv => start < cv.ValueTime && cv.ValueTime <= stop)

Хотя я не знаю, отвечает ли это на ваш вопрос, как это сделать без доступа к контексту базы данных.

Если ленивая загрузка включена, .ChannelValues еще не будет создавать экземпляры данных (поскольку они еще не были перечислены). Последующий .Where() не будет операцией в памяти. Если ленивая загрузка отключена, то в итоге будет получен NullReferenceException, поскольку ChannelValues имеет значение null, потому что не было явного Include.

Flater 23.08.2018 16:38
SelectMany() здесь практически ничего не решает. First() по своей сути создает экземпляр объекта канала, независимо от того, используете ли вы SelectMany() после этого (или нет).
Flater 23.08.2018 16:40

На тесте был загружен эквивалент .ChannelValues, а не ноль. Заменили First() на Where()

Scrobi 23.08.2018 16:52

Но почему это может быть лучше, чем второе предложение OP? Конечный результат такой же.

Flater 23.08.2018 16:53
Не думаю ... Это можно было бы сказать с большей уверенностью :) Здесь происходит то, что context.Channels.First() извлекает Channel из базы данных, затем его ChannelValues загружаются ленивой загрузкой (все они!), И, наконец, эти значения фильтруются в памяти. Действительно, возможное решение - использовать SelectMany.
Gert Arnold 23.08.2018 16:54

Я принимаю это, потому что это самый близкий ответ. Оказывается, ленивая загрузка EntityFramework не такая «ленивая», как хотелось бы. Я мог бы написать свой собственный метод расширения для First, который возвращает мой собственный прокси-объект, чтобы делать то, что я хочу, чтобы EF делал. Спасибо всем.

Badger 27.08.2018 08:25

Другие вопросы по теме