Làm cách nào để chỉ so sánh các thành phần ngày từ DateTime trong EF?


116

Tôi đang có hai giá trị ngày tháng, một giá trị đã được lưu trữ trong cơ sở dữ liệu và giá trị còn lại do người dùng sử dụng DatePicker chọn. Trường hợp sử dụng là tìm kiếm một ngày cụ thể từ cơ sở dữ liệu.

Giá trị được nhập trước đó trong cơ sở dữ liệu luôn có thành phần thời gian là 12:00:00, trong đó ngày được nhập từ bộ chọn có thành phần thời gian khác nhau.

Tôi chỉ quan tâm đến các thành phần ngày tháng và muốn bỏ qua thành phần thời gian.

Các cách để thực hiện so sánh này trong C # là gì?

Ngoài ra, làm thế nào để làm điều này trong LINQ?

CẬP NHẬT: Trên LINQ cho các thực thể, những điều sau đây hoạt động tốt.

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

1
Bạn cũng có thể xem qua câu hỏi SO này: stackoverflow.com/questions/683037/how-to-compare-dates-in-c/…
Quintin Robinson,

Câu trả lời:


121

LƯU Ý: tại thời điểm viết câu trả lời này, mối quan hệ EF không rõ ràng (đã được chỉnh sửa thành câu hỏi sau khi viết xong). Để biết cách tiếp cận đúng với EF, hãy kiểm tra câu trả lời Mandeeps .


Bạn có thể sử dụng thuộc DateTime.Datetính để thực hiện so sánh chỉ ngày.

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

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

34
Thật dễ dàng để so sánh ngày tháng nhưng câu hỏi liên quan đến LINQ với các Đối tượng không thể chuyển đổi thuộc tính .Date thành SQL.
Michaël Carpentier

1
@ MichaëlCarpentier: điểm tốt. Rõ ràng nó vẫn giải quyết được vấn đề của OP.
Fredrik Mörk

6
Điều này không truy vấn cơ sở dữ liệu mà là xử lý dữ liệu trong CLR / lớp ứng dụng sau thực tế. Giải pháp thực sự là sử dụng hàm EntityFunctions.TruncateTime (..) như được chỉ định trong câu trả lời bên dưới, vì nó sẽ gửi truy vấn đến cơ sở dữ liệu và cho phép xử lý được thực hiện ở lớp lưu trữ. Nếu không có điều này, bạn không thể sử dụng logic so sánh ngày trong mệnh đề Where / Count và sau đó truy vấn thêm trên dữ liệu được lọc, vì trước tiên bạn phải kéo các kết quả một phần vào lớp ứng dụng, điều này có thể là một yếu tố phá vỡ thỏa thuận trong các tình huống xử lý phần lớn dữ liệu.
Marchy

6
@Marchy Có, EntityFunctions.TruncateTimechắc chắn có vẻ là cách để đi vào những ngày này (nó đã có sẵn trong .NET 4 được phát hành vào năm sau khi câu hỏi này được hỏi).
Fredrik Mörk

1
sử dụng phương thức System.Data.Entity.DbFunctions.TruncateTime (). Bạn cần thêm tham chiếu đến
EntityFramework

132

Sử dụng lớp học EntityFunctionsđể cắt bớt phần thời gian.

using System.Data.Objects;    

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

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

CẬP NHẬT

Kể từ EF 6.0 trở lên EntityFunctions được thay thế bằng DbFunctions .


37
Chỉ có một ghi chú EntityFunctionsđã không được chấp nhận để ủng hộ System.Data.Entity.DbFunctions(ít nhất) EF6. Nó có thể đã sớm hơn thế này.
pquest

4
Tôi sẽ không được nhanh chóng để chuyển đến giải pháp này vì nó thực sự là chậm, thông tin thêm: stackoverflow.com/questions/22776843/...
pajics

Dường như không hoạt động với cơ sở dữ liệu SQLite. Tôi nhận được "Lỗi logic SQL hoặc thiếu cơ sở dữ liệu không có chức năng như vậy: TruncateTime".
shadowsora,

24

Tôi nghĩ rằng điều này có thể giúp bạn.

Tôi đã thực hiện một phần mở rộng vì tôi phải so sánh ngày tháng trong kho chứa đầy dữ liệu EF và vì vậy .Date không phải là một tùy chọn vì nó không được triển khai trong bản dịch LinqToEntities.

Đây là mã:

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

thì bạn có thể sử dụng nó theo cách này.

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

10

Nếu bạn sử dụng thuộc Datetính cho các Thực thể DB, bạn sẽ nhận được 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."

Bạn có thể sử dụng một cái gì đó như sau:

  DateTime date = DateTime.Now.Date;

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

8

Để thực hiện điều đó trong LINQ to Entities, bạn phải sử dụng các phương pháp được hỗ trợ :

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

Xấu xí, nhưng nó hoạt động và được thực hiện trên máy chủ DB.


8

Đây là một cách khác để làm điều đó, nhưng nó chỉ hữu ích nếu SecondDate là một biến bạn đang chuyển vào:

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

Tôi nghĩ rằng điều đó nên làm việc


1
Thông minh. Đã làm cho tôi. Đó là điều rõ ràng DateTime = x.Date;tôi đã thiếu. Nếu tôi đã sử dụng varhoặc có giá trị nội dòng trong phép so sánh thì nó không thành công với trường hợp ngoại lệ được báo cáo. Cảm ơn.
Tim Croydon

Rất vui vì nó đã thành công, Tim. Xin lỗi vì sự chậm trễ trong việc phản hồi - tôi đã không thực sự đăng nhập vào SO trong một thời gian.
John Kaster 22/10/12

1
Nếu bạn thay đổi e.FirstDate.Value <= endDatethành e.FirstDate.Value < endDatebạn có thể xóa .AddTicks(-1).
Marco de Zeeuw

@MarcodeZeeuw bạn nói đúng, điều đó chắc chắn cũng sẽ hoạt động. Biểu thức điều kiện được hiển thị nhằm mục đích so sánh ngày bao gồm cả ngày bắt đầu và ngày kết thúc chính xác (giả sử giá trị phạm vi ngày sẽ được chuyển vào điều kiện thay vì được thiết lập trong một đoạn mã.) IOW, điều kiện được coi là tách biệt với các giá trị ngày giờ .
John Kaster


4

bạn có thể sử dụng phương thức DbFunctions.TruncateTime () cho việc này.

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

3

Chỉ cần luôn so sánh thuộc tính Ngày của DateTime, thay vì ngày giờ đầy đủ.

Khi bạn thực hiện truy vấn LINQ, hãy sử dụng date.Date trong truy vấn, tức là:

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

10
Tôi gặp lỗi "Thành viên loại được chỉ định 'Ngày' không được hỗ trợ trong LINQ tới Thực thể. Chỉ hỗ trợ trình khởi tạo, thành viên thực thể và thuộc tính điều hướng thực thể.". Có suy nghĩ gì không?
pencilslate, 25/09/09

Vâng - nhà cung cấp của bạn không trực tiếp xử lý thuộc tính .Date. Bạn sẽ phải lấy nó ra và so sánh các ngày sau đó.
Reed Copsey

Rất tiếc, không thể sử dụng .Date trong Linq To Entities. Hy vọng rằng MS sẽ bổ sung thêm rằng sự ủng hộ quá tải sớm
John Kaster

1
Luôn so sánh thuộc tính Ngày? Tôi đã truy cập vào bình luận này vì tôi đã tự hỏi liệu đó có phải là phương pháp hay nhất hay không. để luôn sử dụng thuộc tính Ngày, ngay cả khi nó giống như candidate.Date >= base.Date. Về mặt lý thuyết, candidate.Datethời gian phải> = 12:00:00, vì vậy việc sử dụng thuộc tính Date là thừa, nhưng tôi sẽ tuân theo lời khuyên của Reed.
Stephen Hosking

3

Đây là cách tôi làm điều này.

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

// Lưu ý cho Người dùng / Người viết mã Linq

Điều này sẽ cung cấp cho bạn so sánh chính xác để kiểm tra xem một ngày có nằm trong phạm vi không khi làm việc với đầu vào từ bộ chọn ngày của người dùng, ví dụ:

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

trong đó ngày bắt đầu và ngày kết thúc là các giá trị từ bộ chọn ngày.


1

Nếu không có thời gian, hãy thử như thế này:

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

Bạn có thể sử dụng liên kết dưới đây để so sánh 2 ngày không tính thời gian:

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

hàm So sánh trả về 3 giá trị khác nhau: -1 0 1 có nghĩa là dt1> dt2, dt1 = dt2, dt1


Tại sao bạn không trả về DateTime.Compare (dt1.Date, dt2.Date)? Điều này làm cho tất cả những gì bạn cần.
Johnny Graber

0

Hãy thử điều này ... Nó hoạt động tốt khi so sánh các thuộc tính Ngày giữa hai loại DateTimes:

Tái bút. Đó là một giải pháp stopgap và một thực tiễn thực sự tồi tệ, không bao giờ nên sử dụng khi bạn biết rằng cơ sở dữ liệu có thể mang đến hàng nghìn bản ghi ...

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

1
Tái bút: Tôi thường sử dụng cách này khi DateTimes có giá trị Thời gian và tôi chỉ muốn so sánh Ngày.
Raskunho

2
đây là một giải pháp rất tệ, truy vấn sẽ lấy tất cả các bản ghi và chỉ sau đó lọc ra các ngày tháng. nếu cơ sở dữ liệu có hàng triệu bản ghi, điều này sẽ lấy tất cả chúng và chỉ sau đó mới lọc ngày. THỰC HÀNH RẤT XẤU.
Dementic

1
Đó là một giải pháp stopgap và một thực tiễn thực sự tồi tệ, không bao giờ được sử dụng khi bạn biết rằng cơ sở dữ liệu có thể mang đến hàng nghìn bản ghi.
Raskunho

nếu bạn thêm nhận xét của bạn vào câu trả lời của bạn, tôi sẽ loại bỏ phiếu bầu của mình. Cần phải nói rõ với bất kỳ ai truy cập trang này rằng giải pháp bạn đề xuất là tệ mà không cần phải đọc các bình luận.
Dementic

Nói chung là một ý tưởng tồi, cách tiếp cận này dẫn đến hiệu suất được cải thiện đáng kể cho các tập hợp bản ghi nhỏ (<1000 bản ghi hoặc lâu hơn), vì cách EF chuyển các phép so sánh ngày sang SQL. Tôi đã thấy các truy vấn đi từ hơn một phút đến dưới một giây chỉ bằng cách thực hiện so sánh ngày trong bộ nhớ thay vì trong bất kỳ thứ gì SQL EF tạo ra.
Extragorey
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.