Вказаний член типу "Дата" не підтримується в LINQ для винятків об'єктів


105

Я отримав виняток під час реалізації наступних тверджень.

 DateTime result;
 if (!DateTime.TryParse(rule.data, out result))
     return jobdescriptions;
 if (result < new DateTime(1754, 1, 1)) // sql can't handle dates before 1-1-1753
     return jobdescriptions;
 return jobdescriptions.Where(j => j.JobDeadline.Date == Convert.ToDateTime(rule.data).Date );

Виняток

The specified type member 'Date' is not supported in LINQ to Entities. Only initializers, entity members, and entity navigation properties are supported.

Я знаю, що означає виняток, але я не знаю, як його позбутися. Будь-яка допомога?


Це в EF6 і нижче. Підтримка ядра EF .Date.
Герт Арнольд

Відповіді:


102

LINQ до сутностей не може перевести більшість методів .NET Date (включаючи використовувані вами кастинги) в SQL, оскільки немає еквівалентного SQL.

Рішення полягає у використанні методів Date за межами оператора LINQ, а потім передачі значення. Схоже, що Convert.ToDateTime (rule.data) .Date викликає помилку.

Дата виклику у властивості DateTime також не може бути переведена в SQL, тому вирішення полягає в порівнянні властивостей .Year .Month і .Day, які можна перевести в LINQ, оскільки вони є лише цілими числами.

var ruleDate = Convert.ToDateTime(rule.data).Date;
return jobdescriptions.Where(j => j.Deadline.Year == ruleDate.Year 
                       && j.Deadline.Month == ruleDate.Month 
                       && j.Deadline.Day == ruleDate.Day);

6
Що про j => j.JobDeadline.Date?
туманність

1
Чи є власність Date на JobDeadline? Це не повинно викликати помилку само по собі - можливо, конфлікт імен (але не впевнений у цьому). Якщо рядок все ще викликає помилку, просто перейменуйте її в DeadlineDate або подібну.
Джудо

1
Дата є власністю на JobDeadline. JobDeadline - це тип DateTime, з якого я хочу витягнути Date.
туманність

Тоді для роботи в межах LINQ вам слід просто порівняти властивість JobDeadline, наприклад, j.JobDeadline> ruleDate. Для цього потрібно трохи тестувати, але можна змусити працювати. Крім того, порівняйте три властивості .Month .Day та .Year (j.Deadline.Year == ruleDate.Year && j j.Deadline.Month == ruleDate.Month && j.Deadline.Day == ruleDate.Day). Не елегантно, але це працює, оскільки це лише цілі числа.
Джудо

Хм. Ця ідея спрацьовує. Брудно, але працює. Якщо ви пишете це як відповідь, я можу позначити його правильним.
туманність

230

Ви можете використовувати TruncateTime метод в EntityFunctions для досягнення правильного перекладу в Dateвласність в SQL:

using System.Data.Objects; // you need this namespace for EntityFunctions

// ...

DateTime ruleData = Convert.ToDateTime(rule.data).Date;
return jobdescriptions
    .Where(j => EntityFunctions.TruncateTime(j.JobDeadline) == ruleData);


Оновлення: EntityFunctions застаріло в EF6, ВикористовуватиDbFunctions.TruncateTime


Ну, я помітив, що ruleDataце DateTimeтип і j.JobDeadlineмає усічений час. Не відчуває себе правильно. Не отримав винятку, але також не отримав очікуваного результату.
туманність

@aneal: Він повертає всі записи , в яких JobDeadlineмають ту ж дату , як rule.data, незалежно від того , що за час доби . Хіба це не те, чого ви хочете досягти зі своїм запитом у питанні? Чому це не так?
Слаума

1
+1, і я згоден з вище, безумовно, краща відповідь на 99% реалізацій
jim tollan

26
Зауважте, що EntityFunctionsце застаріле в EF6, тепер ви повинні використовувати DbFunctions.
Жульєн N

2
Простір імен для DbFunctions у> EF6 є System.Data.Entity: msdn.microsoft.com/en-us/library/Dn220142(v=VS.113).aspx
GraehamF


9

"EntityFunctions.TruncateTime" або "DbFunctions.TruncateTime" в ef6 працює, але у Big Data є деякі проблеми з продуктивністю.

Я думаю, що найкращий спосіб - це діяти так:

DateTime ruleDate = Convert.ToDateTime(rule.data);

DateTime  startDate = SearchDate.Date;

DateTime  endDate = SearchDate.Date.AddDay(1);

return jobdescriptions.Where(j.Deadline >= startDate 
                       && j.Deadline < endDate );

це краще, ніж використовувати частини дати до. оскільки запит запускається швидше у великих даних.


+1 за цю відповідь. EntityFunctions.TruncateTime(пізніше замінено на DbFunctions.TruncateTime) реалізуються шляхом перетворення в SQL, де час дату перетворюється в рядок і врізається. Це робить запит значно повільнішим, пропорційно кількості оброблених записів.
уріг

3

Необхідно включити using System.Data.Entity;. Добре працює навіть зProjectTo<>

var ruleDate = rule.data.Date;
return jobdescriptions.Where(j => DbFunctions.TruncateTime(j.Deadline) == ruleDate);


1

Це означає, що LINQ в SQL не знає, як перетворити Dateвластивість у вираз SQL. Це тому, що Dateвластивість DateTimeструктури не має аналога в SQL.


1

Це працювало для мене.

DateTime dt = DateTime.Now.Date;
var ord = db.Orders.Where
      (p => p.UserID == User && p.ValidityExpiry <= dt);

Джерело: Форуми Asp.net


0

У мене така ж проблема, але я працюю з DateTime-Ranges. Моє рішення - маніпулювати початковим часом (з будь-якою датою) до 00:00:00, а кінцевий час - до 23:59:59. Тому я більше не повинен перетворювати DateTime у Date, а він залишається DateTime.

Якщо у вас є лише один DateTime, ви також можете встановити час початку (з будь-якою датою) на 00:00:00, а кінцевий - на 23:59:59. Потім ви шукаєте, як би це був часовий проміжок часу.

var from = this.setStartTime(yourDateTime);
var to = this.setEndTime(yourDateTime);

yourFilter = yourFilter.And(f => f.YourDateTime.Value >= from && f.YourDateTime.Value <= to);

Ви можете зробити це також за допомогою DateTime-Range:

var from = this.setStartTime(yourStartDateTime);
var to = this.setEndTime(yourEndDateTime);

yourFilter = yourFilter.And(f => f.YourDateTime.Value >= from && f.YourDateTime.Value <= to);

0

Ви можете отримати Enum на зразок:

DateTime todayDate = DateTime.Now.Date; var check = db.tableName.AsEnumerable().Select(x => new
        {
            Date = x.TodayDate.Date
        }).Where(x => x.Date == todayDate).FirstOrDefault();

Це ефективно перераховує весь вміст таблиці заздалегідь, щоб застосувати фільтр дат ..., здається, потенційно дуже погана ідея!
Хав'єр Рапопорт

0

Як вже вказували багато тут, використання функції TruncateTime відбувається повільно.

Найпростіший варіант, якщо ви можете, це використовувати EF Core. Він може це зробити. Якщо ви не можете, то кращою альтернативою для врізання є взагалі не змінювати запитуване поле, а змінювати межі. Якщо ви робите звичайний запит типу "між", де нижня та верхня межі необов’язкові, наступне виконає трюк.

    public Expression<Func<PurchaseOrder, bool>> GetDateFilter(DateTime? StartDate, DateTime? EndDate)
    {
        var dtMinDate = (StartDate ?? SqlDateTime.MinValue.Value).Date;
        var dtMaxDate = (EndDate == null || EndDate.Value == SqlDateTime.MaxValue.Value) ? SqlDateTime.MaxValue.Value : EndDate.Value.Date.AddDays(1);
        return x => x.PoDate != null && x.PoDate.Value >= dtMinDate && x.PoDate.Value < dtMaxDate;
    }

В основному, замість обрізання PoDate лише на частину Дати, ми збільшуємо верхню межу запиту та користувача <замість <=

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.