'Tanggal' anggota jenis tertentu tidak didukung di LINQ untuk Pengecualian Entitas

105

Saya mendapat pengecualian saat menerapkan pernyataan berikut.

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

Pengecualian

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

Saya tahu apa arti pengecualian itu tetapi saya tidak tahu bagaimana cara menyingkirkannya. Ada bantuan?

nebula
sumber
Ini di EF6 dan lebih rendah. Dukungan inti EF .Date.
Gert Arnold

Jawaban:

102

LINQ ke Entitas tidak dapat menerjemahkan sebagian besar metode .NET Date (termasuk casting yang Anda gunakan) ke dalam SQL karena tidak ada SQL yang setara.

Solusinya adalah dengan menggunakan metode Tanggal di luar pernyataan LINQ dan kemudian memasukkan nilai. Tampaknya Convert.ToDateTime (rule.data) .Date yang menyebabkan kesalahan.

Memanggil Tanggal pada properti DateTime juga tidak dapat diterjemahkan ke SQL, jadi solusinya adalah membandingkan properti .Year .Month dan .Day yang dapat diterjemahkan ke LINQ karena hanya bilangan bulat.

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);
Judo
sumber
6
Bagaimana dengan j => j.JobDeadline.Date?
nebula
1
Apakah Date merupakan properti di JobDeadline? Ini seharusnya tidak menyebabkan kesalahan dengan sendirinya - mungkin konflik penamaan (tetapi tidak yakin tentang ini). Jika garis masih menyebabkan kesalahan, ubah namanya menjadi DeadlineDate atau serupa.
Judo
1
Tanggal adalah properti di JobDeadline. JobDeadline adalah tipe DateTime yang ingin saya ekstrak Date.
nebula
Kemudian, untuk mengerjakan ini dalam LINQ Anda hanya perlu membandingkan properti JobDeadline, misalnya j.JobDeadline> ruleDate. Ini membutuhkan sedikit pengujian tetapi dapat dibuat untuk bekerja. Atau bandingkan tiga properti .Month .Day dan .Year (j.Deadline.Year == ruleDate.Year && j j.Deadline.Month == ruleDate.Month && j.Deadline.Day == ruleDate.Day). Tidak elegan tetapi berfungsi karena ini hanya bilangan bulat.
Judo
Hmm. Ide ini berhasil. Kotor tapi berhasil. Jika Anda menulisnya sebagai jawaban saya bisa menandainya dengan benar.
nebula
230

Anda dapat menggunakan metode TruncateTime dari EntityFunctions untuk mendapatkan terjemahan Dateproperti yang benar ke dalam 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);


Pembaruan: EntityFunctions tidak digunakan lagi di EF6, GunakanDbFunctions.TruncateTime

Slauma
sumber
Nah, saya perhatikan itu ruleDataadalah DateTimetipe dan j.JobDeadlinememiliki waktu terpotong. Tidak terasa benar. Tidak mendapatkan pengecualian tetapi juga tidak mendapatkan hasil yang diharapkan.
nebula
@aneal: Ia mengembalikan semua catatan di mana JobDeadlinememiliki sama tanggal sebagai rule.data, tidak peduli apa waktu hari . Bukankah itu yang ingin Anda capai dengan pertanyaan Anda? Mengapa tidak terasa benar?
Slauma
1
+1 dan saya setuju dengan di atas, jelas merupakan jawaban yang lebih baik untuk 99% implementasi
jim tollan
26
Perhatikan bahwa EntityFunctionsEF6 sudah tidak digunakan lagi, sekarang Anda harus menggunakan DbFunctions.
Julien N
2
Namespace untuk DbFunctions di> EF6 adalah System.Data.Entity: msdn.microsoft.com/en-us/library/Dn220142(v=VS.113).aspx
GraehamF
39

Untuk EF6 gunakan DbFunctions.TruncateTime (mydate) sebagai gantinya.

KingOfHypocrites
sumber
9

"EntityFunctions.TruncateTime" atau "DbFunctions.TruncateTime" di ef6 Berfungsi tetapi memiliki beberapa masalah kinerja di Big Data.

Saya pikir cara terbaik adalah bertindak seperti ini:

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

itu lebih baik daripada menggunakan bagian dari tanggal untuk. karena kueri dijalankan lebih cepat dalam data besar.

Mahdi Shahbazi
sumber
1 untuk jawaban ini. EntityFunctions.TruncateTime(kemudian diganti dengan DbFunctions.TruncateTime) diimplementasikan dengan mengonversi ke SQL di mana tanggalwaktu diubah menjadi string dan dipotong. Ini membuat kueri berjalan jauh lebih lambat, sebanding dengan jumlah rekaman yang diproses.
urig
3

Perlu disertakan using System.Data.Entity;. Bekerja dengan baik bahkan denganProjectTo<>

var ruleDate = rule.data.Date;
return jobdescriptions.Where(j => DbFunctions.TruncateTime(j.Deadline) == ruleDate);
Omkar
sumber
Seperti yang sudah dijawab di sini
Gert Arnold
1

Artinya adalah LINQ ke SQL tidak tahu bagaimana mengubah Dateproperti menjadi ekspresi SQL. Ini karena Dateproperti DateTimestruktur tidak memiliki analog dalam SQL.

Adam Robinson
sumber
1

Itu berhasil untuk saya.

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

Sumber: Forum Asp.net

MRT2017
sumber
0

Saya memiliki masalah yang sama tetapi saya bekerja dengan DateTime-Ranges. Solusi saya adalah memanipulasi waktu mulai (dengan tanggal berapa pun) menjadi 00:00:00 dan waktu akhir menjadi 23:59:59 Jadi saya tidak boleh lagi mengubah DateTime saya menjadi Date, melainkan tetap DateTime.

Jika Anda hanya memiliki satu DateTime, Anda juga dapat menyetel waktu mulai (dengan tanggal apa pun) ke 00:00:00 dan waktu akhir ke 23:59:59. Kemudian Anda mencari seolah-olah itu adalah rentang waktu.

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

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

Anda juga dapat melakukannya dengan DateTime-Range:

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

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

Anda bisa mendapatkan Enum seperti:

DateTime todayDate = DateTime.Now.Date; var check = db.tableName.AsEnumerable().Select(x => new
        {
            Date = x.TodayDate.Date
        }).Where(x => x.Date == todayDate).FirstOrDefault();
Omid Soleiman
sumber
Ini secara efektif menghitung seluruh konten tabel sebelumnya untuk menerapkan filter tanggal ... sepertinya berpotensi menjadi ide yang sangat buruk!
Javier Rapoport
0

Seperti yang telah ditunjukkan oleh banyak orang di sini, penggunaan fungsi TruncateTime berjalan lambat.

Opsi termudah jika Anda bisa adalah menggunakan EF Core. Itu bisa melakukan ini. Jika Anda tidak bisa, maka alternatif yang lebih baik untuk memotong adalah dengan tidak mengubah bidang kueri sama sekali, tetapi memodifikasi batasnya. Jika Anda melakukan kueri tipe 'antara' normal di mana batas bawah dan atas adalah opsional, berikut ini akan melakukan triknya.

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

Pada dasarnya, daripada memangkas PoDate kembali ke hanya bagian Tanggal, kami menaikkan batas kueri atas dan pengguna <bukannya <=

statler
sumber