天天看點

特殊需求:EF 6.x如何比較TimeSpan格式的字元串?EF Core實作方式是否和EF 6.x等同?

前言

我們知道C#中的TimeSpan對應SQL Server資料庫中的Time類型,但是如果因為特殊需求資料庫存儲的不是Time類型,而是作為字元串,那麼我們如何在查詢資料時對資料庫所存儲的字元串類型進行比較呢?

TimeSpan類型比較

首先我們來看看正常情況下屬性為TimeSpan類型進行比較的情況,給出如下實體模型。

public class TestA         {             public int Id { get; set; }             public string StrartEnd { get; set; }             public string EndTime { get; set; }             public TimeSpan TimeSpanStartEnd { get; set; }             public TimeSpan TimeSpanEndTime { get; set; }         }      
特殊需求:EF 6.x如何比較TimeSpan格式的字元串?EF Core實作方式是否和EF 6.x等同?
using (var ctx = new EfDbContext())                 {                     ctx.Database.Log = Console.WriteLine;                     var testA = new TestA()                     {                         StrartEnd = "10:00:00",                         EndTime = "22:59:00",                         TimeSpanStartEnd = new TimeSpan(10, 00, 00),                         TimeSpanEndTime = new TimeSpan(22, 59, 00)                     };                     ctx.TestAs.Add(testA);                     ctx.SaveChanges();                 };      
特殊需求:EF 6.x如何比較TimeSpan格式的字元串?EF Core實作方式是否和EF 6.x等同?
特殊需求:EF 6.x如何比較TimeSpan格式的字元串?EF Core實作方式是否和EF 6.x等同?

如上正常流程添加資料到資料庫中,接下來我們來查詢資料利用TimeSpanStartEnd和TimeSpanEndTime,如下:

using (var ctx = new EfDbContext())                 {                     ctx.Database.Log = Console.WriteLine;                     var test = ctx.TestAs.Where(d => d.TimeSpanStartEnd >= DateTime.Now.TimeOfDay && DateTime.Now.TimeOfDay <= d.TimeSpanStartEnd).ToList();                 };      
特殊需求:EF 6.x如何比較TimeSpan格式的字元串?EF Core實作方式是否和EF 6.x等同?

居然不支援TimeOfDay,需要初始化值設定項,那我們接下來初始化TimeSpan看看:

using (var ctx = new EfDbContext())                 {                     ctx.Database.Log = Console.WriteLine;                     var time = new TimeSpan(9, 00, 00);                     var test = ctx.TestAs.Where(d => d.TimeSpanStartEnd >= time && time <= d.TimeSpanStartEnd).ToList();                 };      
特殊需求:EF 6.x如何比較TimeSpan格式的字元串?EF Core實作方式是否和EF 6.x等同?

到此我們可以下一結論:在EF 6.x中對TimeSpan進行查詢比較需要初始化TimeSpan值才行,否則抛出異常。問題來了,有一位園友問我,如果資料庫存儲的類型不是Time,而是字元串那麼我們該如何查詢比較呢?當時心裡第一想法明明可以存儲Time,為何要搞個字元串類型,結果問其原因是特殊需求,好吧,那沒辦法,那我們接下來探讨一下如何,請往下看,我們利用屬性StartEnd和EndTime來查詢。

EF 6.x如何比較TimeSpan格式的字元串?

此時我們首先需要将資料庫中的字元串即StartEnd和EndTime轉換為TimeSpan然後進行過濾,如下:

using (var ctx = new EfDbContext())                 {                     ctx.Database.Log = Console.WriteLine;                     var time = new TimeSpan(9, 00, 00);                     var test = ctx.TestAs.Where(d => TimeSpan.Parse(d.StrartEnd) >= time && time <= TimeSpan.Parse(d.EndTime)).ToList();                 };      
特殊需求:EF 6.x如何比較TimeSpan格式的字元串?EF Core實作方式是否和EF 6.x等同?

抛出異常無法支援TimeSpan中的Parse解析,也就是說EF 6.x無法将Parse進行翻譯。在EF 6.x中對日期相關操作有SqlFunctions和DbFunctions,最終經過對相關APi的嘗試,我們可通過如下改造就行,不知是否有更好的辦法,一個簡單的過濾,其代碼實在有點多,直接上代碼:

using (var ctx = new EfDbContext())                 {                     ctx.Database.Log = Console.WriteLine;                     var time = new TimeSpan(9, 00, 00);                     var test = ctx.TestAs.Where(d => ((                            DbFunctions.CreateTime(                            SqlFunctions.DatePart("hh", d.StrartEnd),                            SqlFunctions.DatePart("mi", d.StrartEnd),                            SqlFunctions.DatePart("ss", d.StrartEnd)))                           >= DbFunctions.CreateTime(                            SqlFunctions.DatePart("hh", time),                            SqlFunctions.DatePart("mi", time),                            SqlFunctions.DatePart("ss", time)))                            &&                            ((                            DbFunctions.CreateTime(                            SqlFunctions.DatePart("hh", time),                            SqlFunctions.DatePart("mi", time),                            SqlFunctions.DatePart("ss", time)))                           <= DbFunctions.CreateTime(                            SqlFunctions.DatePart("hh", d.EndTime),                            SqlFunctions.DatePart("mi", d.EndTime),                            SqlFunctions.DatePart("ss", d.EndTime)))                            ).ToList();                 };      
特殊需求:EF 6.x如何比較TimeSpan格式的字元串?EF Core實作方式是否和EF 6.x等同?
特殊需求:EF 6.x如何比較TimeSpan格式的字元串?EF Core實作方式是否和EF 6.x等同?

好了,到了這裡算是給出了我在EF 6.x中實作的解決方案,Jeff在看待EF Core和EF 6.x習慣将二者拿來比較,看看EF Core是否真的強大,我們知道EF Core中沒有SqlFunctions和DbFunctions兩個APi,是不是就無法實作了呢,我們實踐便知,同樣是進行上述查詢。

EF Core比較TimeSpan格式的字元串

using (var context = new EFCoreDbContext())                 {                     var test = context.TestAs.Where(d => d.TimeSpanStartEnd >= DateTime.Now.TimeOfDay && DateTime.Now.TimeOfDay <= d.TimeSpanStartEnd).ToList();                 }      
特殊需求:EF 6.x如何比較TimeSpan格式的字元串?EF Core實作方式是否和EF 6.x等同?
特殊需求:EF 6.x如何比較TimeSpan格式的字元串?EF Core實作方式是否和EF 6.x等同?

我們能夠直接比較TimeSpan類型,此時我們完全不用初始化TimeSpan直接比較就行,從最終翻譯出來的SQL來看以及我在github上向EF Core團隊送出的ISSUE得出利用DaTime.Now.TimeOfDay會在用戶端被評估即在記憶體中查詢,但是在EF Core 2.1會被正确翻譯,詳情請見我提的ISSUE(https://github.com/aspnet/EntityFrameworkCore/issues/12187)。接下來我們再來比較字元串即StartEnd和EndTime,如下:

using (var context = new EFCoreDbContext())                 {                     var time = new TimeSpan(9, 00, 00);                     var test = context.TestAs.Where(d => TimeSpan.Parse(d.StrartEnd) >= time && time <= TimeSpan.Parse(d.EndTime)).ToList();                 }      
特殊需求:EF 6.x如何比較TimeSpan格式的字元串?EF Core實作方式是否和EF 6.x等同?
特殊需求:EF 6.x如何比較TimeSpan格式的字元串?EF Core實作方式是否和EF 6.x等同?

最終依然能夠正常查詢沒有出現任何異常,相比較EF 6.x實作方式而言,代碼沒有那麼冗長,但是依然翻譯錯誤,無論第一種方式還是第二種方式查詢在翻譯成SQL時都沒有添加篩選條件。

總結 

在EF 6.x中對于TimeSpan類型的查詢必須通過TimeSpan初始化值,否則抛出異常,如果是字元串類型的TimeSpan那麼在查詢時需要借助SqlFunctions和DbFunctions來實作,比較複雜,而對EF Core而言就和我們正常查詢一樣,沒有任何異同,EF Core目前已經很穩定,還是推薦大家早早使用EF Core和.NET Core,感謝您的閱讀。

所有的選擇不過是為了下一次選擇做準備