จะเปรียบเทียบเฉพาะวันที่จาก DateTime ใน EF ได้อย่างไร?


116

ฉันมีค่าวันที่สองค่าค่าหนึ่งเก็บไว้ในฐานข้อมูลแล้วและค่าอื่นที่ผู้ใช้เลือกโดยใช้ DatePicker กรณีการใช้งานคือการค้นหาวันที่ที่ต้องการจากฐานข้อมูล

ค่าที่ป้อนก่อนหน้านี้ในฐานข้อมูลมักจะมีองค์ประกอบของเวลาคือ 12:00:00 น. โดยที่วันที่ที่ป้อนจากตัวเลือกจะมีส่วนประกอบของเวลาที่แตกต่างกัน

ฉันสนใจเฉพาะส่วนประกอบวันที่และต้องการละเว้นองค์ประกอบเวลา

การเปรียบเทียบนี้ใน C # มีวิธีใดบ้าง

นอกจากนี้วิธีการทำใน LINQ

อัปเดต: ใน LINQ ถึงเอนทิตีสิ่งต่อไปนี้ใช้งานได้ดี

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

1
คุณสามารถดูคำถาม SO นี้ได้ที่ stackoverflow.com/questions/683037/how-to-compare-dates-in-c/…
Quintin Robinson

คำตอบ:


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: จุดดี เห็นได้ชัดว่ามันยังแก้ปัญหาของ OP ได้
Fredrik Mörk

6
สิ่งนี้ไม่ได้สอบถามฐานข้อมูล แต่จะประมวลผลข้อมูลในชั้น CLR / แอปพลิเคชันหลังจากข้อเท็จจริง วิธีแก้ปัญหาที่แท้จริงคือการใช้ฟังก์ชัน EntityFunctions.TruncateTime (.. ) ตามที่ระบุไว้ในคำตอบด้านล่างเนื่องจากจะส่งแบบสอบถามไปยังฐานข้อมูลและอนุญาตให้ดำเนินการประมวลผลที่ชั้นจัดเก็บข้อมูล หากไม่มีสิ่งนี้คุณจะไม่สามารถใช้ตรรกะการเปรียบเทียบวันที่ในส่วนคำสั่ง Where / Count แล้วสอบถามเพิ่มเติมเกี่ยวกับข้อมูลที่กรองได้เนื่องจากคุณจะต้องดึงผลลัพธ์บางส่วนลงในเลเยอร์แอปพลิเคชันก่อนซึ่งอาจเป็นตัวทำลายข้อตกลงในสถานการณ์ที่ ประมวลผลข้อมูลขนาดใหญ่
มีนาคม

6
@Marchy ใช่EntityFunctions.TruncateTimeดูเหมือนว่าจะเป็นหนทางไปในทุกวันนี้ (มีให้บริการใน. NET 4 ซึ่งเปิดตัวในปีหลังจากที่มีการถามคำถามนี้)
Fredrik Mörk

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/

UPDATE

ในฐานะของ EF 6.0 และ EntityFunctions ต่อมาถูกแทนที่ด้วยDbFunctions


37
EntityFunctionsมีการเลิกใช้โน้ตเพื่อสนับสนุนSystem.Data.Entity.DbFunctions(อย่างน้อย) EF6 มันอาจจะเร็วกว่านี้
pquest

4
ฉันจะไม่รีบไปที่โซลูชันนี้เนื่องจากมันช้ามากข้อมูลเพิ่มเติม: stackoverflow.com/questions/22776843/…
pajics

ดูเหมือนจะไม่ทำงานกับฐานข้อมูล SQLite ฉันได้รับ "ข้อผิดพลาดตรรกะ SQL หรือฐานข้อมูลที่ขาดหายไปไม่มีฟังก์ชันดังกล่าว: TruncateTime"
shadowsora

24

ฉันคิดว่านี่สามารถช่วยคุณได้

ฉันสร้างส่วนขยายเนื่องจากฉันต้องเปรียบเทียบวันที่ในที่เก็บที่เต็มไปด้วยข้อมูล EF ดังนั้นวันที่ไม่ใช่ตัวเลือกเนื่องจากไม่มีการใช้งานในการแปล LinqToEntities

นี่คือรหัส:

        /// <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 Entities คุณจะได้รับข้อยกเว้น:

"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 ถึงเอนทิตีคุณต้องใช้วิธีการที่รองรับ :

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

น่าเกลียด แต่มันใช้งานได้และทำบนเซิร์ฟเวอร์ DB


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หรือมีค่าแบบอินไลน์ในการเปรียบเทียบจะล้มเหลวโดยมีรายงานข้อยกเว้น ขอบคุณ
Tim Croydon

ดีใจที่ได้ผลทิม ขออภัยในความล่าช้าในการตอบกลับ - ฉันไม่ได้ลงชื่อเข้าใช้ SO มาระยะหนึ่งแล้ว
John Kaster

1
หากคุณเปลี่ยนe.FirstDate.Value <= endDateเป็นe.FirstDate.Value < endDateคุณสามารถลบไฟล์.AddTicks(-1).
Marco de Zeeuw

@MarcodeZeeuw คุณพูดถูกแน่นอนว่าจะได้ผลเช่นกัน นิพจน์เงื่อนไขที่แสดงมีไว้สำหรับการเปรียบเทียบวันที่รวมของวันที่เริ่มต้นและวันที่สิ้นสุดที่แน่นอน (สมมติว่าค่าช่วงวันที่จะถูกส่งผ่านไปยังเงื่อนไขแทนที่จะตั้งค่าในส่วนของโค้ด) IOW เงื่อนไขจะถูกพิจารณาแยกจากค่าวันที่และเวลา .
John Kaster



3

เพียงแค่เปรียบเทียบคุณสมบัติDateของ DateTime เสมอแทนที่จะเป็นวันที่แบบเต็ม

เมื่อคุณสร้างแบบสอบถาม LINQ ของคุณให้ใช้ date.Date ในแบบสอบถามกล่าวคือ:

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

10
ฉันได้รับข้อผิดพลาด "ไม่สนับสนุนสมาชิกประเภทที่ระบุ 'วันที่' ใน LINQ ถึงเอนทิตีรองรับเฉพาะตัวเริ่มต้นสมาชิกเอนทิตีและคุณสมบัติการนำทางของเอนทิตีเท่านั้น" ความคิดใด ๆ ?
pencilslate

ใช่ - ผู้ให้บริการของคุณไม่ได้จัดการคุณสมบัติ. วันที่โดยตรง คุณจะต้องดึงมันออกมาและเปรียบเทียบวันที่ในภายหลัง
Reed Copsey

ไม่สามารถใช้วันที่ใน Linq To Entities ได้ หวังว่า MS จะเพิ่มการรองรับโอเวอร์โหลดเร็ว ๆ นี้
John Kaster

1
เปรียบเทียบคุณสมบัติ Date เสมอ ? ฉันเข้าสู่ความคิดเห็นนี้เพราะฉันสงสัยว่านั่นเป็นแนวทางปฏิบัติที่ดีที่สุดหรือไม่กล่าวคือ เพื่อใช้คุณสมบัติ Date เสมอแม้ว่าจะเป็นเช่นcandidate.Date >= base.Dateนั้นก็ตาม ตามทฤษฎีแล้วcandidate.Dateเวลาต้องเป็น> = 12:00:00 ดังนั้นการใช้คุณสมบัติ Date จึงซ้ำซ้อน แต่ฉันจะยึดตามคำแนะนำของ Reed
Stephen Hosking

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 / Coders

สิ่งนี้ควรให้การเปรียบเทียบที่แน่นอนสำหรับการตรวจสอบว่าวันที่อยู่ในช่วงเมื่อทำงานกับอินพุตจากผู้ใช้ตัวเลือกวันที่เช่น:

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

โดยที่ startdate และ enddate เป็นค่าจากเครื่องมือเลือกวันที่



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) สิ่งนี้ทำให้คุณต้องการทั้งหมด
Johnny Graber

0

ลองสิ่งนี้ ... มันใช้งานได้ดีในการเปรียบเทียบคุณสมบัติ Date ระหว่าง DateTimes สองประเภท:

PS มันเป็นวิธีแก้ปัญหาและเป็นแนวทางปฏิบัติที่ไม่ดีจริงๆไม่ควรใช้เมื่อคุณรู้ว่าฐานข้อมูลสามารถนำมาซึ่งบันทึกหลายพันรายการ ...

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

1
PS: ฉันมักจะใช้วิธีนี้เมื่อ DateTimes มีค่า Time และฉันต้องการเปรียบเทียบเฉพาะ Date
Raskunho

2
นี่เป็นวิธีแก้ปัญหาที่แย่มากแบบสอบถามจะได้รับระเบียนทั้งหมดจากนั้นกรองเฉพาะวันที่ออก หากฐานข้อมูลมีระเบียนหลายล้านรายการสิ่งนี้จะดึงข้อมูลทั้งหมดและจะกรองวันที่เท่านั้น การปฏิบัติที่ไม่ดีมาก
Dementic

1
มันเป็นวิธีแก้ปัญหาและเป็นแนวทางปฏิบัติที่ไม่ดีจริงๆไม่ควรใช้เมื่อคุณรู้ว่าฐานข้อมูลสามารถนำมาซึ่งระเบียนหลายพันรายการ
Raskunho

หากคุณจะเพิ่มความคิดเห็นของคุณในคำตอบของคุณฉันจะลบการลงคะแนนของฉัน ทุกคนที่เข้าชมหน้านี้ควรชัดเจนว่าวิธีแก้ปัญหาที่คุณเสนอนั้นไม่ดีโดยไม่ต้องอ่านความคิดเห็น
Dementic

แม้ว่าโดยทั่วไปแล้วจะเป็นความคิดที่ไม่ดี แต่วิธีนี้ส่งผลให้ประสิทธิภาพที่ดีขึ้นอย่างมากสำหรับชุดระเบียนขนาดเล็ก (<1,000 ระเบียนหรือมากกว่านั้น) เนื่องจากวิธีที่บ้าบิ่น EF แปลการเปรียบเทียบวันที่เป็น SQL ฉันเคยเห็นข้อความค้นหาเปลี่ยนไปจากหนึ่งนาทีถึงไม่ถึงหนึ่งวินาทีเพียงแค่ทำการเปรียบเทียบวันที่ในหน่วยความจำแทนที่จะเป็นสิ่งที่ SQL EF สร้างขึ้น
Extragorey
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.