Linq для EntityFramework DateTime

108

В своем приложении я использую Entity Framework.

Мой стол

-Article
-period
-startDate

Мне нужны совпадающие записи => DateTime.Now > startDate and (startDate + period) > DateTime.Now

Я пробовал этот код, но теперь он работает

Context.Article
    .Where(p => p.StartDate < DateTime.Now)
    .Where(p => p.StartDate.AddDays(p.Period) > DateTime.Now)

Когда я запускаю свой код, возникает следующее исключение

LINQ to Entities не распознает метод System.DateTime AddDays (Double), и этот метод нельзя преобразовать в выражение хранилища.

Yucel
источник
Какой тип period? AddDaysнеправильная функция, если это double.
Craig Stuntz

Ответы:

201

При использовании LINQ to Entity Framework ваши предикаты внутри предложения Where переводятся в SQL. Вы получаете эту ошибку, потому что нет перевода на SQL, DateTime.Add()который имеет смысл.

Быстрый обходной путь - прочитать результаты первого оператора Where в память, а затем использовать LINQ to Objects для завершения фильтрации:

Context.Article.Where(p => p.StartDate < DateTime.Now)
               .ToList()
               .Where(p => p.StartDate.AddDays(p.Period) > DateTime.Now);

Вы также можете попробовать метод EntityFunctions.AddDays, если используете .NET 4.0:

Context.Article.Where(p => p.StartDate < DateTime.Now)
               .Where(p => EntityFunctions.AddDays(p.StartDate, p.Period)
                   > DateTime.Now);

Примечание: EF 6сейчас System.Data.Entity.DbFunctions.AddDays.

Джастин Нисснер
источник
42
Это опасный обходной путь. Что произойдет, если ToList () вернет огромное количество данных?
Стефан П.
7
Спасибо за подсказку EntityFunctions.AddDays. Я использую EF .net 4.0, но я не знал о EntityFunctions, изучу его.
Стефан П.
2
@SaeedAlg - В первом примере необходимо прочитать элементы в память. Во втором примере я сохранил два предложения Where, чтобы соответствовать исходному формату. Даже если вы сожмете их в одно предложение Where, Entity Framework сгенерирует идентификационный SQL, так что это действительно вопрос удобочитаемости.
Джастин Нисснер
2
@Justin Niessner, большое спасибо, я пытаюсь делать это в течение четырех часов, вы спасаете мне жизнь за секунды, еще раз спасибо
Yucel
2
Предоставляя «быстрые» обходные пути с EF, мы все должны избегать использования методов ToList и ToArray. Так же быстро можно вычислить дату и сравнить с этим значением, и это работает в запросе EF без создания экземпляра его результатов в памяти.
Isaac Llopis
92

Я думаю, что это то, что пытался предложить последний ответ, но вместо того, чтобы пытаться добавить дни в p.startdat (что-то, что не может быть преобразовано в оператор sql), почему бы не сделать что-то, что можно приравнять к sql:

var baselineDate = DateTime.Now.AddHours(-24);

something.Where(p => p.startdate >= baselineDate)
Андрей
источник
2
@Adrian Carr - Это может быть лучше для этого варианта использования, но не для такого количества, как EntityFunctionsрешение. Здесь второй операнд не извлекается из другого объекта в запросе и может быть вычислен до запроса. Если бы оба операнда были найдены в db, EntityFunctionsрешение все равно было бы подходящим, в то время как решение этого ответа больше не работало бы.
Фредерик
Это лучшее решение, чем решение, отмеченное как ответ. Ответ Джастина / отмеченный ответ вернет ненужные результаты из базы данных. Это решение фактически отправляет дату в SQL, где фильтруется, и использует SQL для фильтрации данных, что намного эффективнее.
Пол
3

Как насчет того, чтобы вычесть 2 дня из DateTime.Now:

Context.Article
.Where(p => p.StartDate < DateTime.Now)
.Where(p => p.StartDate > DateTime.Now.Subtract(new TimeSpan(2, 0, 0, 0)))

Честно говоря, я не уверен, чего вы пытаетесь достичь, но это может сработать

TimC
источник
Статьи будут отображаться в StartDate и заканчиваются через x (период) дней. Это то, что я пытаюсь сделать. Второе решение Джастина Нисснера очень хорошо сработало для того, что я хочу видеть
Yucel
3

Если вам нужно, чтобы ваше выражение было переведено на SQL, вы можете попробовать использовать

System.Data.Entity.Core.Objects.AddDays метод.

На самом деле отмечен как устаревший, но он работает. Его следует заменить на System.Data.Entity.DbFunctions.AddDays, но я не могу его найти ...

буби
источник
Это не добавляет ничего, кроме принятого ответа 4 года назад!
Эндрю Харрис
@AndrewHarris также мой ответ - ответ 4 года назад. В первой версии ответа перед Where был ToList (=> материализация данных) (см. Первый комментарий ответа).
bubi