Thành viên loại được chỉ định 'Ngày' không được hỗ trợ trong LINQ cho Đối tượng ngoại lệ


105

Tôi có một ngoại lệ khi triển khai các câu lệnh sau.

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

ngoại lệ

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

Tôi biết ngoại lệ có nghĩa là gì nhưng tôi không biết làm thế nào để loại bỏ nó. Bất kỳ giúp đỡ?


Đây là EF6 và thấp hơn. Hỗ trợ lõi EF .Date.
Gert Arnold

Câu trả lời:


102

LINQ to Entities không thể dịch hầu hết các phương thức .NET Date (bao gồm cả việc truyền bạn đã sử dụng) sang SQL vì không có SQL tương đương.

Giải pháp là sử dụng các phương thức Ngày bên ngoài câu lệnh LINQ và sau đó chuyển vào một giá trị. Có vẻ như Convert.ToDateTime (rule.data) .Date đang gây ra lỗi.

Gọi Ngày trên thuộc tính DateTime cũng không thể được dịch sang SQL, do đó, cách giải quyết là so sánh các thuộc tính .Year .Month và .Day có thể được dịch sang LINQ vì chúng chỉ là số nguyên.

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
Còn j => j.JobDeadline.Date?
tinh vân

1
Ngày có phải là thuộc tính trên JobDeadline không? Điều này không tự gây ra lỗi - có thể là xung đột đặt tên (nhưng không chắc chắn về điều này). Nếu dòng vẫn gây ra lỗi, chỉ cần đổi tên nó là DeadlineDate hoặc tương tự.
Judo

1
Ngày tháng là tài sản trên JobDeadline. JobDeadline là kiểu DateTime mà tôi muốn trích xuất Date.
tinh vân

Sau đó, để làm việc này trong LINQ, bạn sẽ chỉ cần so sánh thuộc tính JobDeadline, ví dụ: j.JobDeadline> ruleDate. Điều này cần một chút thử nghiệm nhưng có thể được thực hiện để hoạt động. Ngoài ra, hãy so sánh ba thuộc tính của .Month .Day và .Year (j.Deadline.Year == ruleDate.Year && j j.Deadline.Month == ruleDate.Month && j.Deadline.Day == ruleDate.Day). Không thanh lịch nhưng nó hoạt động vì đây chỉ là những số nguyên.
Judo

Hừ! Ý tưởng này hoạt động. Bẩn nhưng hoạt động. Nếu bạn viết nó dưới dạng câu trả lời, tôi có thể đánh dấu nó đúng.
tinh vân

230

Bạn có thể sử dụng phương thức TruncateTime của EntityFunctions để đạt được bản dịch chính xác của thuộc Datetính sang 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);


Cập nhật: EntityFunctions không được dùng trong EF6, Sử dụngDbFunctions.TruncateTime


Vâng, tôi nhận thấy rằng đó ruleDataDateTimeloại và j.JobDeadlinecó thời gian cắt ngắn. Cảm thấy không ổn. Không ngoại lệ nhưng cũng không nhận được kết quả như mong đợi.
tinh vân

@aneal: Nó trả về tất cả hồ sơ, nơi JobDeadlinecó cùng một ngày như rule.data, không có vấn đề gì là thời gian trong ngày . Đó không phải là những gì bạn muốn đạt được với truy vấn của bạn trong câu hỏi? Tại sao nó không cảm thấy đúng?
Slauma

1
+1 và tôi đồng ý với ở trên, chắc chắn là câu trả lời tốt hơn cho 99% việc triển khai
jim phonean

26
Lưu ý rằng EntityFunctionsEF6 không được dùng nữa, bây giờ bạn nên sử dụng DbFunctions.
Julien N

2
Không gian tên cho DbFunctions trong> EF6 là System.Data.Entity: msdn.microsoft.com/en-us/library/Dn220142(v=VS.113).aspx
GraehamF


9

"EntityFunctions.TruncateTime" hoặc "DbFunctions.TruncateTime" trong ef6 Đang hoạt động nhưng nó có một số vấn đề về hiệu suất trong Dữ liệu lớn.

Tôi nghĩ cách tốt nhất là hành động như sau:

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

tốt hơn là sử dụng các phần của ngày tháng. vì truy vấn được chạy nhanh hơn trong dữ liệu lớn.


+1 cho câu trả lời này. EntityFunctions.TruncateTime(sau đó được thay thế bằng DbFunctions.TruncateTime) được thực hiện bằng cách chuyển đổi sang SQL trong đó ngày giờ được chuyển đổi thành một chuỗi và được cắt ngắn. Điều này làm cho truy vấn chạy chậm hơn đáng kể, tương ứng với số lượng bản ghi được xử lý.
urig

3

Cần phải bao gồm using System.Data.Entity;. Hoạt động tốt ngay cả vớiProjectTo<>

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

Như đã được trả lời ở đây
Gert Arnold

1

Điều đó có nghĩa là LINQ to SQL không biết cách biến thuộc Datetính thành biểu thức SQL. Điều này là do thuộc Datetính của DateTimecấu trúc không có tương tự trong SQL.


1

Nó đã làm việc cho tôi.

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

Nguồn: Diễn đàn Asp.net


0

Tôi gặp vấn đề tương tự nhưng tôi làm việc với DateTime-Ranges. Giải pháp của tôi là điều chỉnh thời gian bắt đầu (với bất kỳ ngày nào) thành 00:00:00 và thời gian kết thúc thành 23:59:59 Vì vậy, tôi không phải chuyển đổi DateTime của mình thành Date nữa, thay vào đó nó vẫn là DateTime.

Nếu bạn chỉ có một DateTime, bạn cũng có thể đặt thời gian bắt đầu (với bất kỳ ngày nào) thành 00:00:00 và thời gian kết thúc thành 23:59:59 Sau đó, bạn tìm kiếm như thể đó là một khoảng thời gian.

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

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

Bạn cũng có thể làm điều đó với 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

bạn có thể nhận được Enum như:

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

Điều này có hiệu quả là liệt kê toàn bộ nội dung của bảng trước để áp dụng bộ lọc ngày ... có vẻ như là một ý tưởng rất tồi!
Javier Rapoport

0

Như đã được nhiều người chỉ ra ở đây, việc sử dụng hàm TruncateTime là chậm.

Tùy chọn dễ nhất nếu bạn có thể là sử dụng EF Core. Nó có thể làm điều này. Nếu bạn không thể thì giải pháp thay thế tốt hơn cho việc cắt ngắn là không thay đổi trường được truy vấn mà hãy sửa đổi các giới hạn. Nếu bạn đang thực hiện truy vấn loại 'between' bình thường trong đó giới hạn dưới và giới hạn trên là tùy chọn, thì thao tác sau sẽ thực hiện thủ thuật.

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

Về cơ bản, thay vì cắt bỏ PoDate trở lại chỉ phần Ngày, chúng tôi tăng truy vấn giới hạn trên và người dùng <thay vì <=

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.