Як порівняти лише компоненти дати від DateTime в EF?


116

У мене є два значення дати, одне вже зберігається в базі даних, а друге вибране користувачем за допомогою DatePicker. Випадок використання - пошук певної дати з бази даних.

Раніше введене в базу значень завжди має часовий компонент 12:00:00, де як дата, введена з вибору, має різну часову складову.

Мене цікавлять лише компоненти дати і хотів би ігнорувати компонент часу.

Які способи зробити це порівняння в C #?

Також, як це зробити в LINQ?

ОНОВЛЕННЯ: На LINQ для сутностей наступне працює добре.

e => DateTime.Compare(e.FirstDate.Value, SecondDate) >= 0

1
Ви також можете подивитися на це питання ТАК: stackoverflow.com/questions/683037/how-to-compare-dates-in-c/…
Квінтін Робінсон,

Відповіді:


121

ПРИМІТКА: на момент написання цієї відповіді співвідношення EF було незрозумілим (це було відредаговано у запитанні після написання цього запису). Для правильного підходу з EF перевірити відповідь Mandeeps .


Ви можете використовувати DateTime.Dateвластивість для порівняння лише дати.

DateTime a = GetFirstDate();
DateTime b = GetSecondDate();

if (a.Date.Equals(b.Date))
{
    // the dates are equal
}

34
Порівнювати дату легко, але питання стосується LINQ для сутностей, які не в змозі перетворити властивість .Date в SQL.
Michaël Carpentier

1
@ MichaëlCarpentier: хороший момент. Мабуть, це все-таки вирішило проблему ОП.
Фредрік Мьорк

6
Це не запитує базу даних, а обробляє дані в рівні CLR / додатку після факту. Справжнє рішення полягає у використанні функції EntityFunctions.TruncateTime (..), як зазначено у відповіді нижче, оскільки вона надсилає запит до бази даних та дозволяє обробку здійснювати на рівні зберігання. Без цього ви не могли б використовувати логіку порівняння дат у пунктах Де / Злічити, а потім подавати запит на відфільтровані дані, оскільки вам доведеться спочатку витягнути часткові результати в рівень програми, який може бути вимикачем угоди в сценаріях, які обробляють великі масиви даних.
березень

6
@Marchy Так, EntityFunctions.TruncateTimeбезумовно, здається, що саме цим шляхом можна дійти в наші дні (він став доступним у .NET 4, який був випущений через рік після того, як було задано це питання).
Фредрік Морк

1
використовувати метод System.Data.Entity.DbFunctions.TruncateTime (). Вам потрібно додати посилання на EntityFramework
adeel41

132

Використовуйте клас EntityFunctionsдля обрізки часової частини.

using System.Data.Objects;    

var bla = (from log in context.Contacts
           where EntityFunctions.TruncateTime(log.ModifiedDate) ==  EntityFunctions.TruncateTime(today.Date)
           select log).FirstOrDefault();

Джерело: http://social.msdn.microsoft.com/Forums/en-US/csharpgeneral/thread/84d4e18b-7545-419b-9826-53ff1a0e2a62/

ОНОВЛЕННЯ

Станом на EF 6.0 і пізніших EntityFunctions замінюється DbFunctions .


37
Лише нотатка EntityFunctionsзастаріла на користь System.Data.Entity.DbFunctions(принаймні) EF6. Це, можливо, було раніше цього.
pquest

4
Я не був би швидко перейти до цього рішення , оскільки це дуже повільно, більше інформації: stackoverflow.com/questions/22776843 / ...
pajics

Схоже, це не працює з базою даних SQLite. Я отримую "логічну помилку SQL або відсутність бази даних такої функції: TruncateTime".
тіньсора

24

Я думаю, що це може вам допомогти.

Я зробив розширення, оскільки мені доводиться порівнювати дати в сховищах, заповнених даними EF, і так.

Ось код:

        /// <summary>
    /// Check if two dates are same
    /// </summary>
    /// <typeparam name="TElement">Type</typeparam>
    /// <param name="valueSelector">date field</param>
    /// <param name="value">date compared</param>
    /// <returns>bool</returns>
    public Expression<Func<TElement, bool>> IsSameDate<TElement>(Expression<Func<TElement, DateTime>> valueSelector, DateTime value)
    {
        ParameterExpression p = valueSelector.Parameters.Single();

        var antes = Expression.GreaterThanOrEqual(valueSelector.Body, Expression.Constant(value.Date, typeof(DateTime)));

        var despues = Expression.LessThan(valueSelector.Body, Expression.Constant(value.AddDays(1).Date, typeof(DateTime)));

        Expression body = Expression.And(antes, despues);

        return Expression.Lambda<Func<TElement, bool>>(body, p);
    }

тоді ви можете використовувати це таким чином.

 var today = DateTime.Now;
 var todayPosts = from t in turnos.Where(IsSameDate<Turno>(t => t.MyDate, today))
                                      select t);

10

Якщо ви використовуєте Dateвластивість для об'єктів DB, ​​ви отримаєте виняток:

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

Ви можете використовувати щось подібне:

  DateTime date = DateTime.Now.Date;

  var result = from client in context.clients
               where client.BirthDate >= date
                     && client.BirthDate < date.AddDays(1)
               select client;

8

Щоб зробити це в LINQ Entities, ви повинні використовувати підтримувані методи :

var year = someDate.Year;
var month = ...
var q = from r in Context.Records
        where Microsoft.VisualBasic.DateAndTime.Year(r.SomeDate) == year 
              && // month and day

Некрасиво, але це працює, і це робиться на сервері БД.


8

Ось інший спосіб зробити це, але він корисний лише в тому випадку, якщо SecondDate є змінною, яку ви передаєте:

DateTime startDate = SecondDate.Date;
DateTime endDate = startDate.AddDays(1).AddTicks(-1);
...
e => e.FirstDate.Value >= startDate && e.FirstDate.Value <= endDate

Я думаю, що це має працювати


1
Відмінно. Працювали для мене. Це явне DateTime = x.Date;я бракував. Якщо я використовував varабо мав значення, вбудоване в порівнянні, воно не вдалося зі звітом про виключення. Дякую.
Тім Кройдон

Радий, що працював, Тіме. Вибачте за затримку з реагуванням - я фактично не ввійшов до SO на деякий час.
Джон Кастер

1
Якщо ви змінюєте e.FirstDate.Value <= endDateдо e.FirstDate.Value < endDateвам можна видалити .AddTicks(-1).
Марко де Зеув

@MarcodeZeeuw Ви праві, це, безумовно, також спрацювало б. Показаний умовний вираз призначений для включного порівняння дат точних дати початку та кінця (якщо припустити, що значення діапазону дат передаватимуться умові, а не встановлюватись у фрагменті коду.) IOW, умовне вважається окремим від значень дати .
Джон Кастер


4

ви можете використовувати для цього метод DbFunctions.TruncateTime ().

e => DbFunctions.TruncateTime(e.FirstDate.Value) == DbFunctions.TruncateTime(SecondDate);

3

Просто завжди порівнюйте властивість Date з DateTime, а не повний час дати.

Коли ви робите свій LINQ-запит, використовуйте date.Date у запиті, тобто:

var results = from c in collection
              where c.Date == myDateTime.Date
              select c;

10
Я отримую помилку "Вказаний член типу" Дата "не підтримується в LINQ для сутностей. Підтримуються лише ініціалізатори, члени сутності та властивості навігації сутності." Будь-які думки?
олівцем

Так - ваш постачальник не обробляє властивість .Date безпосередньо. Вам доведеться витягнути його та порівняти дати пізніше.
Рід Копсі

.Дату, на жаль, не можна використовувати в Linq To Entities. Сподіваємось, MS незабаром додасть, що підтримка перевантаження
Джон Кастер

1
Завжди порівнювати властивість Date? Я поглибився в цей коментар, тому що я задумався, чи це найкраща практика, тобто. щоб завжди використовувати властивість Date, навіть якщо це що - щось на зразок candidate.Date >= base.Date. Теоретично candidate.Dateчас повинен бути> = 12:00:00, тому використання властивості Date є зайвим, але я буду дотримуватися поради Рида.
Стівен Хоскінг

3

Ось як я це роблю.

DateTime date_time_to_compare = DateTime.Now;
//Compare only date parts
context.YourObject.FirstOrDefault(r =>
                EntityFunctions.TruncateTime(r.date) == EntityFunctions.TruncateTime(date_to_compare));

2

// Примітка для користувачів / кодерів Linq

Це повинно дати вам точне порівняння для перевірки, чи не потрапляє дата в межах діапазону при роботі із введенням даних, вибраного користувачем, що вибирає дату, наприклад:

((DateTime)ri.RequestX.DateSatisfied).Date >= startdate.Date &&
        ((DateTime)ri.RequestX.DateSatisfied).Date <= enddate.Date

де початковий і кінцевий термін - значення з вибору дати.


1

Без часу, ніж спробуйте так:

TimeSpan ts = new TimeSpan(23, 59, 59);
toDate = toDate.Add(ts);
List<AuditLog> resultLogs = 
    _dbContext.AuditLogs
    .Where(al => al.Log_Date >= fromDate && al.Log_Date <= toDate)
    .ToList();
return resultLogs;

1

Ви можете скористатися посиланням нижче, щоб порівняти 2 дати без часу:

private bool DateGreaterOrEqual(DateTime dt1, DateTime dt2)
        {
            return DateTime.Compare(dt1.Date, dt2.Date) >= 0;
        }

private bool DateLessOrEqual(DateTime dt1, DateTime dt2)
        {
            return DateTime.Compare(dt1.Date, dt2.Date) <= 0;
        }

функція порівняння повертає 3 різних значення: -1 0 1, що означає dt1> dt2, dt1 = dt2, dt1


Чому ви просто не повернете DateTime.Compare (dt1.Date, dt2.Date)? Це робить все необхідне.
Джонні Грабер

0

Спробуйте це ... Добре працює для порівняння властивостей дати між двома типами DateTimes:

PS. Це рішення щодо зупинки і справді погана практика, ніколи не слід використовувати, коли ви знаєте, що база даних може принести тисячі записів ...

query = query.ToList()
             .Where(x => x.FirstDate.Date == SecondDate.Date)
             .AsQueryable();

1
PS: Я зазвичай використовую цей спосіб, коли в DateTimes є значення Time, і я хочу порівнювати лише Date.
Раскунхо

2
це дуже погане рішення, запит отримає всі записи, і лише після цього відфільтруйте дати. якщо в базі даних є мільйони записів, це захопить усіх, і лише тоді фільтрувати дати. ДУЖЕ БАГА ПРАКТИКА.
Дементік

1
Це рішення щодо зупинки і справді погана практика, ніколи не слід застосовувати, коли ви знаєте, що база даних може принести тисячі записів.
Раскунхо

якщо ви додасте свій коментар до своєї відповіді, я зніму свій голос. Кожному, хто відвідує цю сторінку, має бути зрозуміло, що запропоноване вами рішення є поганим без необхідності читати коментарі.
Дементік

Хоча в цілому погана ідея, такий підхід призводить до значно поліпшеної продуктивності для невеликих наборів записів (<1000 записів або близько того), через те, як EF перекладає порівняння дат у SQL. Я бачив, як запити переходять від хвилини до секунди, лише роблячи порівняння дат у пам'яті, а не в SQL EF, що генерується.
Екстрагорей
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.