EF'te DateTime'dan yalnızca tarih bileşenleri nasıl karşılaştırılır?


116

İki tarih değerim var, biri zaten veritabanında depolanıyor ve diğeri kullanıcı tarafından DatePicker kullanılarak seçilmiş. Kullanım durumu, veritabanından belirli bir tarihi aramaktır.

Veritabanına önceden girilen değer her zaman 12:00:00 saat bileşenine sahiptir, burada seçiciden girilen tarih farklı zaman bileşenine sahiptir.

Yalnızca tarih bileşenleriyle ilgileniyorum ve zaman bileşenini yok saymak istiyorum.

Bu karşılaştırmayı C # ile yapmanın yolları nelerdir?

Ayrıca, bunu LINQ'da nasıl yapmalı?

GÜNCELLEME: LINQ to Entities'de, aşağıdakiler iyi çalışır.

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

Yanıtlar:


121

NOT: Bu cevabı yazarken, EF-ilişkisi belirsizdi (bu yazıldıktan sonra soruda düzenlendi). EF ile doğru yaklaşım için Mandeeps cevabını kontrol edin .


DateTime.DateÖzelliği yalnızca tarih karşılaştırması yapmak için kullanabilirsiniz .

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

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

34
Tarihi karşılaştırmak kolaydır, ancak soru LINQ ile .Date özelliğini SQL'e dönüştüremeyen Varlıklar ile ilgilidir.
Michaël Carpentier

1
@ MichaëlCarpentier: iyi nokta. Görünüşe göre hala OP'nin sorununu çözdü.
Fredrik Mörk

6
Bu, veritabanını sorgulamaz, bunun yerine CLR / uygulama katmanındaki verileri olaydan sonra işler. Gerçek çözüm, sorguyu veritabanına gönderdiği ve işlemin depolama katmanında yapılmasına izin verdiği için aşağıdaki yanıtta belirtildiği gibi EntityFunctions.TruncateTime (..) işlevini kullanmaktır. Bu olmadan, Where / Count yan tümcelerindeki tarih karşılaştırma mantığını kullanamaz ve ardından filtrelenmiş veriler üzerinde daha fazla sorgulama yapamazsınız, çünkü önce kısmi sonuçları uygulama katmanına çekmeniz gerekir, bu da senaryolarda bir anlaşma kırıcı olabilir. büyük veri gövdelerini işlemek.
Marchy

6
@Marchy Evet, EntityFunctions.TruncateTimebu günlerde kesinlikle gidilecek yol gibi görünüyor (bu soru sorulduktan bir yıl sonra yayınlanan .NET 4'te kullanıma sunuldu).
Fredrik Mörk

1
System.Data.Entity.DbFunctions.TruncateTime () yöntemini kullanın. EntityFramework'a bir referans eklemeniz gerekiyor
adeel41

132

EntityFunctionsZaman bölümünü kırpmak için sınıfı kullanın .

using System.Data.Objects;    

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

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

GÜNCELLEME

EF 6.0 ve sonraki EntityFunctions itibariyle değiştirilir DbFunctions .


37
Sadece (en azından) EF6 EntityFunctionslehine bir not kullanımdan kaldırıldı System.Data.Entity.DbFunctions. Bundan daha önce olmuş olabilir.
pquest

4
Gerçekten yavaş olduğu için bu çözüme
hızlıca geçemeyeceğim

SQLite veritabanıyla çalışmıyor gibi görünüyor. "SQL mantık hatası veya eksik veritabanı böyle bir işlev yok: TruncateTime" alıyorum.
shadowsora

24

Bunun size yardımcı olabileceğini düşünüyorum.

EF verileriyle dolu depolardaki tarihleri ​​karşılaştırmak zorunda olduğum için bir uzantı yaptım ve bu nedenle .Date, LinqToEntities çevirisinde uygulanmadığı için bir seçenek değildi.

İşte kod:

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

o zaman bu şekilde kullanabilirsiniz.

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

10

DateÖzelliği DB Varlıkları için kullanırsanız, istisna alırsınız:

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

Bunun gibi bir şey kullanabilirsiniz:

  DateTime date = DateTime.Now.Date;

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

8

Bunu LINQ to Entities'de yapmak için desteklenen yöntemleri kullanmanız gerekir :

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

Çirkin, ama işe yarıyor ve DB sunucusunda yapılıyor.


8

İşte bunu yapmanın farklı bir yolu, ancak bu yalnızca SecondDate'in gönderdiğiniz bir değişken olması durumunda yararlıdır:

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

Bence bu işe yaramalı


1
Mükemmel. Benim için çalıştı. Açıkça DateTime = x.Date;kaçırdığım şey buydu. Karşılaştırmada kullandıysam varveya değeri satır içi olarak aldıysam, bildirilen istisna ile başarısız oldu. Teşekkürler.
Tim Croydon

İşe yaradığına sevindim, Tim. Yanıt vermedeki gecikme için özür dilerim - aslında bir süredir SO'da oturum açmadım.
John Kaster

1
Eğer değiştirirseniz e.FirstDate.Value <= endDateiçin e.FirstDate.Value < endDatesize kaldırabilirsiniz .AddTicks(-1).
Marco de Zeeuw

@MarcodeZeeuw haklısın, bu kesinlikle işe yarar. Gösterilen koşullu ifade, tam başlangıç ​​ve bitiş tarih zamanlarının kapsamlı tarih karşılaştırmaları için tasarlanmıştır (tarih aralığı değerlerinin bir kod parçacığında ayarlanmak yerine koşula aktarılacağı varsayılır.) IOW, koşullu tarih saat değerlerinden ayrı kabul edilir .
John Kaster


4

bunun için DbFunctions.TruncateTime () yöntemini kullanabilirsiniz.

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

3

Tam tarih saati yerine her zaman DateTime'ın Date özelliğini karşılaştırın .

LINQ sorgunuzu oluşturduğunuzda, sorguda date.Date kullanın, yani:

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

10
"Belirtilen tür üyesi 'Tarih', LINQ to Entities'de desteklenmiyor. Yalnızca başlatıcılar, varlık üyeleri ve varlık gezinme özellikleri destekleniyor." Hatası alıyorum. Düşüncesi olan var mı?
pencilslate

Evet, sağlayıcınız .Date özelliğini doğrudan işlemiyor. Onu çıkarmanız ve tarihleri ​​daha sonra karşılaştırmanız gerekecek.
Reed Copsey

.Date maalesef Linq To Entities'de kullanılamaz. Umarım MS bu aşırı yük desteğini yakında ekleyecektir
John Kaster

1
Her zaman Date özelliği karşılaştırılsın mı? Bu yorumu Google'da araştırdım çünkü bunun en iyi uygulama olup olmadığını merak ettim, yani. için her zaman tarihi özelliği gibi bile 's şey kullanın candidate.Date >= base.Date. Teorik olarak, candidate.Datesaat> = 12:00:00 olmalıdır, bu nedenle Date özelliğini kullanmak gereksizdir, ancak Reed'in tavsiyesine bağlı kalacağım.
Stephen Hosking

3

Ben bunu böyle yapıyorum.

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 Kullanıcıları / Kodlayıcıları için Not

Bu size, örneğin bir kullanıcı - tarih seçiciden gelen girdiyle çalışırken bir tarihin aralık dahilinde olup olmadığını kontrol etmek için tam bir karşılaştırma sağlamalıdır:

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

burada başlangıç ​​tarihi ve bitiş tarihi, bir tarih seçiciden alınan değerlerdir.


1

Zaman olmadan şunu deneyin:

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

2 tarihi zaman olmadan karşılaştırmak için aşağıdaki bağlantıyı kullanabilirsiniz:

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

Karşılaştırma işlevi 3 farklı değer döndürür: -1 0 1 yani dt1> dt2, dt1 = dt2, dt1


Neden DateTime.Compare (dt1.Date, dt2.Date) döndürmüyorsunuz? Bu, ihtiyacınız olan her şeyi yapar.
Johnny Graber

0

Bunu deneyin ... İki DateTimes türü arasında Date özelliklerini karşılaştırmak sorunsuz çalışır:

PS. Geçici bir çözümdür ve gerçekten kötü bir uygulamadır, veritabanının binlerce kayıt getirebileceğini bildiğiniz zaman asla kullanılmamalıdır ...

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

1
Not: DateTimes zaman değerine sahip olduğunda genellikle bu yolu kullanırım ve sadece Date'i karşılaştırmak istiyorum.
Raskunho

2
bu çok kötü bir çözüm, sorgu tüm kayıtları alacak ve ancak daha sonra tarihleri ​​filtreleyecektir. Veritabanında milyonlarca kayıt varsa, bu tüm kayıtları yakalayacak ve ancak o zaman tarihleri ​​filtreleyecektir. ÇOK KÖTÜ UYGULAMA.
Dementic

1
Geçici bir çözümdür ve gerçekten kötü bir uygulamadır, veritabanının binlerce kayıt getirebileceğini bildiğinizde asla kullanılmamalıdır.
Raskunho

Yorumunuzu cevabınıza eklerseniz, olumsuz oyumu kaldırırım. Bu sayfayı ziyaret eden herkes için önerdiğiniz çözümün kötü olduğu, yorumları okumak zorunda kalmadan açıkça anlaşılmalıdır.
Dementic

Genel olarak kötü bir fikir olsa da, bu yaklaşım, EF'nin tarih karşılaştırmalarını SQL'e çevirmesinin aptalca yolu nedeniyle küçük kayıt kümeleri (<1000 kayıt veya benzeri) için büyük ölçüde geliştirilmiş performansla sonuçlanır. SQL EF'nin ürettiği şey yerine bellekte tarih karşılaştırması yaparak sorguların bir dakikadan bir saniyenin altına indiğini gördüm.
Extragorey
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.