Bagaimana cara membandingkan hanya komponen tanggal dari DateTime di EF?


116

Saya memiliki dua nilai tanggal, satu sudah disimpan dalam database dan yang lainnya dipilih oleh pengguna menggunakan DatePicker. Kasus penggunaan adalah untuk mencari tanggal tertentu dari database.

Nilai yang sebelumnya dimasukkan ke dalam database selalu memiliki komponen waktu 12:00:00, sedangkan tanggal yang dimasukkan dari alat pilih memiliki komponen waktu yang berbeda.

Saya hanya tertarik pada komponen tanggal dan ingin mengabaikan komponen waktu.

Apa cara untuk melakukan perbandingan ini di C #?

Juga, bagaimana melakukan ini di LINQ?

UPDATE: Pada LINQ ke Entitas, berikut ini berfungsi dengan baik.

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

Jawaban:


121

CATATAN: pada saat menulis jawaban ini, hubungan EF tidak jelas (yang diedit ke dalam pertanyaan setelah ini ditulis). Untuk pendekatan yang benar dengan EF, periksa jawaban Mandeeps .


Anda dapat menggunakan DateTime.Dateproperti untuk melakukan perbandingan khusus tanggal.

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

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

34
Sangat mudah untuk membandingkan tanggal tetapi pertanyaannya terkait dengan LINQ dengan Entitas yang tidak dapat mengonversi properti .Date menjadi SQL.
Michaël Carpentier

1
@ MichaëlCarpentier: poin yang bagus. Rupanya itu masih menyelesaikan masalah OP.
Fredrik Mörk

6
Ini tidak meminta database tetapi memproses data di CLR / application layer setelah fakta. Solusi sebenarnya adalah dengan menggunakan fungsi EntityFunctions.TruncateTime (..) seperti yang ditentukan dalam jawaban di bawah ini, karena ia mengirimkan kueri ke database dan memungkinkan pemrosesan dilakukan di lapisan penyimpanan. Tanpa ini, Anda tidak dapat menggunakan logika perbandingan tanggal di klausa Di mana / Hitung dan kemudian kueri lebih lanjut pada data yang difilter, karena Anda harus menarik hasil parsial ke dalam lapisan aplikasi terlebih dahulu, yang dapat menjadi pemecah kesepakatan dalam skenario yang memproses banyak data.
Marchy

6
@Marki Ya, EntityFunctions.TruncateTimetampaknya pasti cara untuk pergi hari ini (itu menjadi tersedia di .NET 4 yang dirilis setahun setelah pertanyaan ini diajukan).
Fredrik Mörk

1
menggunakan metode System.Data.Entity.DbFunctions.TruncateTime (). Anda perlu menambahkan referensi ke EntityFramework
adeel41

132

Gunakan kelas EntityFunctionsuntuk memangkas porsi waktu.

using System.Data.Objects;    

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

Sumber: http://social.msdn.microsoft.com/Forums/en-US/csharpgeneral/thread/84d4e18b-7545-419b-9826-53ff1a0e2a62/

MEMPERBARUI

Mulai EF 6.0 dan yang lebih baru, EntityFunctions digantikan oleh DbFunctions .


37
Hanya catatan yang EntityFunctionssudah usang dan mendukung System.Data.Entity.DbFunctionsuntuk (setidaknya) EF6. Mungkin lebih awal dari ini.
kalahkan

4
Saya tidak akan cepat melompat ke solusi ini karena sangat lambat, info lebih lanjut: stackoverflow.com/questions/22776843/…
pajics

Sepertinya tidak berfungsi dengan database SQLite. Saya mendapatkan "kesalahan logika SQL atau database yang hilang tidak ada fungsi seperti itu: TruncateTime".
shadowsora

24

Saya pikir ini bisa membantu Anda.

Saya membuat perpanjangan karena saya harus membandingkan tanggal di repositori yang diisi dengan data EF dan sebagainya. Tanggal bukanlah pilihan karena tidak diterapkan dalam terjemahan LinqToEntities.

Ini kodenya:

        /// <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);
    }

maka Anda bisa menggunakannya dengan cara ini.

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

10

Jika Anda menggunakan Dateproperti untuk Entitas DB, Anda akan mendapatkan pengecualian:

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

Anda dapat menggunakan sesuatu seperti ini:

  DateTime date = DateTime.Now.Date;

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

8

Untuk melakukannya di LINQ ke Entitas, Anda harus menggunakan metode yang didukung :

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

Jelek, tetapi berhasil, dan itu dilakukan di server DB.


8

Berikut cara berbeda untuk melakukannya, tetapi ini hanya berguna jika SecondDate adalah variabel yang Anda teruskan:

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

Saya pikir itu seharusnya berhasil


1
Luar biasa. Bekerja untuk saya. Itu adalah eksplisit yang DateTime = x.Date;saya lewatkan. Jika saya menggunakan var, atau memiliki nilai sebaris dalam perbandingan itu gagal dengan pengecualian yang dilaporkan. Terima kasih.
Tim Croydon

Senang berhasil, Tim. Maaf atas keterlambatan dalam menanggapi - Saya belum benar-benar masuk ke SO beberapa lama.
John Kaster

1
Jika Anda berubah e.FirstDate.Value <= endDatemenjadi e.FirstDate.Value < endDateAnda dapat menghapus .AddTicks(-1).
Marco de Zeeuw

@MarcodeZeeuw Anda benar, itu pasti akan berhasil juga. Ekspresi bersyarat yang ditampilkan dimaksudkan untuk perbandingan tanggal inklusif dari waktu mulai dan akhir yang tepat (dengan asumsi nilai rentang tanggal akan diteruskan ke kondisi daripada disiapkan dalam fragmen kode.) IOW, kondisional dianggap terpisah dari nilai-nilai waktu .
John Kaster


4

Anda bisa menggunakan metode DbFunctions.TruncateTime () untuk ini.

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

3

Selalu bandingkan properti Date dari DateTime, bukan waktu tanggal penuh.

Saat Anda membuat kueri LINQ, gunakan tanggal.Date dalam kueri, yaitu:

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

10
Saya mendapatkan pesan kesalahan "Anggota jenis tertentu 'Tanggal' tidak didukung di LINQ untuk Entitas. Hanya penginisialisasi, anggota entitas, dan properti navigasi entitas yang didukung.". Ada pemikiran?
pelat pensil

Ya - penyedia Anda tidak menangani properti .Date secara langsung. Anda harus menariknya keluar, dan membandingkan tanggalnya nanti.
Reed Copsey

.Tanggal tidak dapat digunakan di Linq To Entities, sayangnya. Mudah-mudahan MS akan segera menambahkan dukungan yang berlebihan itu
John Kaster

1
Selalu bandingkan properti Date? Saya telah mencari di Google komentar ini karena saya bertanya-tanya apakah itu praktik terbaik, yaitu. untuk selalu menggunakan properti Tanggal, bahkan ketika itu seperti candidate.Date >= base.Date. Secara teoritis, candidate.Datewaktunya harus> = 12:00:00, jadi menggunakan properti Date berlebihan, tapi saya akan tetap mengikuti saran Reed.
Stephen Hosking

3

Beginilah cara saya melakukan ini.

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

// Catatan untuk Pengguna / Pembuat Kode Linq

Ini akan memberi Anda perbandingan yang tepat untuk memeriksa apakah suatu tanggal berada dalam kisaran saat bekerja dengan masukan dari pemilih tanggal pengguna misalnya:

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

dengan tanggal mulai dan tanggal akhir adalah nilai dari pemilih tanggal.


1

Tanpa waktu selain mencoba seperti ini:

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

Anda dapat menggunakan tautan di bawah ini untuk membandingkan 2 tanggal tanpa waktu:

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;
        }

Fungsi Compare mengembalikan 3 nilai berbeda: -1 0 1 yang berarti dt1> dt2, dt1 = dt2, dt1


Mengapa Anda tidak mengembalikan DateTime.Compare (dt1.Date, dt2.Date) saja? Ini membuat semua yang Anda butuhkan.
Johnny Graber

0

Coba ini ... Ini berfungsi dengan baik untuk membandingkan properti Tanggal antara dua jenis DateTimes:

PS. Ini adalah solusi sementara dan praktik yang sangat buruk, tidak boleh digunakan ketika Anda tahu bahwa database dapat membawa ribuan catatan ...

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

1
PS: Saya biasanya menggunakan cara ini ketika DateTimes memiliki nilai Waktu dan saya hanya ingin membandingkan Tanggal.
Raskunho

2
ini adalah solusi yang sangat buruk, kueri akan mendapatkan semua catatan, dan baru kemudian memfilter tanggal. jika database memiliki jutaan record, ini akan mengambil semuanya dan hanya kemudian akan memfilter tanggal. PRAKTIK SANGAT BURUK.
Dementik

1
Ini adalah solusi sementara dan praktik yang sangat buruk, tidak boleh digunakan ketika Anda tahu bahwa database dapat membawa ribuan catatan.
Raskunho

jika Anda menambahkan komentar Anda ke dalam jawaban Anda, saya akan menghapus suara negatif saya. harus jelas bagi siapa pun yang mengunjungi halaman ini bahwa solusi yang Anda usulkan buruk tanpa harus membaca komentar.
Dementik

Meskipun ide yang buruk secara umum, pendekatan ini menghasilkan performa yang sangat meningkat untuk kumpulan record kecil (<1000 record atau lebih), karena cara EF menerjemahkan perbandingan tanggal ke SQL. Saya telah melihat kueri berubah dari lebih dari satu menit menjadi kurang dari satu detik hanya dengan melakukan perbandingan tanggal dalam memori, bukan dalam apa pun yang dihasilkan SQL EF.
Extragorey
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.