2013-10-05 98 views
0

我正在開發一個WCF RESTful服務與C#,.NET Framework 4.0和實體框架代碼第一作爲一個字符串比較DateTime作爲一個字符串到實體不識別DateTime.parse(字符串)

我有這個類(表示在數據庫中的表):

[DataContract] 
public class PostLine 
{ 
    public int PostLineId { get; set; } 

    [DataMember] 
    public int? UserId { get; set; } 

    [DataMember] 
    public string Description { get; set; } 

    [DataMember] 
    public string DateUtc { get; set; } 

    public User Author { get; set; } 
} 

我試圖做到這一點:

DateTime fourDaysAgo = DateTime.Now.Date.AddDays(-4); 

var postLines = 
    context.PostLines.Where(p => DateTime.Compare(DateTime.Parse(p.DateUtc), fourDaysAgo) > 0).Include("Author"); 

,但我得到了以下錯誤:

{System.NotSupportedException: LINQ to Entities doesn't recognize the method 'System.DateTime Parse(System.String)', which can not be converted into an expression of the repository.

我需要那PostLine.DateUtc是一個字符串becau我將在我的Web服務上使用它,並將它作爲JSON發送,所以它更適合我以字符串形式存儲它。

如果我使用DateTime類型,我會得到這樣的事情在JSON響應:

{ 
    "DateUtc": "/Date(1380924000000+0200)/", 
    "Description": "post_1", 
    "UserId": 1 
} 

你知道我怎麼可以在LINQ表達式比較字符串與一個DateTime?

+0

爲什麼'DateUtc'不是'DateTime'?你的JSON序列化程序應該處理這個解析。 –

+0

是的,但是如何在iOS和Android上分析'「/ Date(1380924000000 + 0200)/」'? – VansFannel

+0

相關鏈接:http://stackoverflow.com/questions/10286204/the-right-json-date-format所以我可以同情這裏使用的字符串屬性。 – hvd

回答

3

我認爲最好的辦法是將財產分成兩部分。

實體框架想要一個DateTime屬性。這很有道理。

對於序列化你想要一個string屬性。這也很有道理。

但是,您正在嘗試爲兩者使用單個屬性,但這沒有意義,並且不是必需的。

[DataContract] 
public class PostLine 
{ 
    ... 

    public DateTime DateUtcAsDateTime { get; set; } 

    [DataMember, NotMapped] 
    public string DateUtcAsString { 
     get { return DateUtcAsDateTime.ToString(); } 
     set { DateUtcAsDateTime = DateTime.Parse(value); } 
    } 

    ... 
} 

現在,DateUtcAsDateTime將實體框架使用,並DateUtcAsString將實體框架,因爲它有一個NotMapped屬性被忽略。

DateUtcAsString另一方面,這些屬性中只有一個具有DataMember屬性,所以應該是唯一被序列化的屬性。

如果需要,您當然可以將其中一個屬性重命名爲DateUtc

更新:正如馬特約翰遜指出的,一種改進是指定格式的方式總是導致完全相同的字符串。這可以確保您的字符串不會更改,只是因爲您的代碼被移動到恰好具有不同區域設置的另一臺服務器。

[DataMember, NotMapped] 
public string DateUtcAsString { 
    get { return DateUtcAsDateTime.ToString("o"); } 
    set { DateUtcAsDateTime = DateTime.Parse(value, "o", null, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal); } 
} 

請注意,我用的DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal的而不是他建議DateTimeStyles.RoundTripKind,因名DateUtc強烈建議您總是想UTC,從來沒有本地時間。而且我沒有明確指定任何文化,因爲"o"格式已經獨立於文化。

如果您的其他代碼更容易處理,則可以使用"r"而不是"o",具有相同的優點。

+1

-1序列化不應該影響類的實現。可以更改JsonDataContractSerializer和Newtonsoft的序列化程序的行爲。 – Aron

+0

@Aron我同意這可能會更好,如果你發佈了一個可以工作的答案,只要它以沒有意外的副作用的方式完成(包括其他可能依賴於其他'DateTime'的代碼]屬性按照他們現在的方式序列化),它會得到我的投票。我在稍微不同的情況下對自己的回答採取了類似的方法,因爲我本來需要的可擴展性完全缺失,但也許這不是這個問題的情況。 – hvd

+0

@Aron BTW,感謝解釋,這總比沒有評論的downvote更好。 – hvd

1

我不知道你是如何實例化你的DataContractJsonSerializer。如果您直接實例化它...您可以傳遞DataContractJsonSerializerSettingsDateTimeFormat用於設置DateTime的串行化方式。

如果你正在使用一個行爲來實例化你的串行器,事情會更復雜一些。

+0

WCF自動創建'DataContractJsonSerializer',並使用不提供任何'DataContractorJsonSerializerSettings'或' DateTimeFormat'對象。如果你深入挖掘,你可以在'System.ServiceModel.Dispatcher.SingleBodyParameterDataContractMessageFormatter.CreateSerializer'中找到它。 –

+0

理論上你可以用行爲注入自己。不過,我同意你真的想使用WebAPI,它足夠奇怪地使用Newtonsoft Json.NET(他們大肆宣揚'DataContractJsonSerializer')。 – Aron

0

如果您真的想在將數據傳輸到客戶端時使用字符串類型的路線,那麼您應該有一個單獨的DTO類。

然後,您可以使用像AutoMapper這樣的庫將PostLine類映射到PostLineDto並添加一個表達式。

然而,你將面對那麼接下來的問題是,你Expression<Func<PostLine,PostLineDto>>將包含Expression.MethodCall(DateTime.Parse),你將有注入的ExpressionVisitor,可以轉換...

p => p.DateUtc > DateTime.Parse("2013 Aug 1") - bool 

p => p.DateUtc > new DateTime(1, Aug, 2013) - bool 

哪個真正的痛苦。

2

這當然是一個XY problem,如果我見過一個。您在詢問如何將實體框架中的日期時間作爲字符串進行比較,但實際問題似乎是您不喜歡WCF默認使用的默認日期格式DataContractJsonSerializer

問題的一部分是你混合本地和UTC。你得到/Date(1380924000000+0200)/,其中包含服務器的本地時區偏移量。這是因爲你從DateTime.Now開始,.KindDateTimeKind.Local

如果改爲使用DateTime.UtcNow,這將有一個的DateTimeKind.Utc.Kind,並且將被序列化爲/Date(1380924000000)/。是的,格式的數字部分將是相同的。即使指定了偏移量,數字部分仍與UTC有關。

這只是這種格式的一個問題。另一個是,雖然DataContractJsonSerializer在序列化過程中寫入本地偏移量,但在反序列化過程中並未正確使用它。它只是假定如果提供了任何偏移量,即使計算機進行的反序列化具有完全不同的偏移量,時間應該是本地的 -

在JSON中使用的更好的格式是ISO8601格式。例如,這個UTC值看起來像2013-10-04T22:00:00.000Z。雖然如果直接使用它,您可以輕鬆地將不同的日期格式傳遞給DataContractJsonSerializer,但WCF不會輕易地將此內容暴露給您。

你可以去下通過自定義WCF消息格式化,如描述hereherehere改變DataContractJsonSerializer設置的路線,但它就會很快變得複雜。如果你這樣做要小心,並且一定要徹底測試!

另一個想法是編寫一個使用JSON.Net而不是DataContractJsonSerializer的自定義WCF消息格式器。 JSON.Net默認使用ISO8601格式,因此您將被設置。

但說實話,最好的解決方案是不要嘗試使用WCF來構建您的REST端點。日期格式問題僅僅是開始。還有各種各樣的其他問題可能會一路出現。相反,請使用專爲此目的而設計的現代框架,例如ASP.Net WebAPIServiceStack

ASP.Net WebAPI使用JSON.Net,而ServiceStack有它自己的名爲ServiceStack.Text的JSON序列化程序。 JSON.Net使用ISO8601作爲默認日期格式。如果您使用的服務棧,你需要設置JsConfig.DateHandler = JsonDateHandler.ISO8601;

所以回顧一下,你有2個 XY問題:

  • 首先,你選擇了WCF建立自己的休息終點,這是如此困難該行業開發了其他解決方案。
  • 其次,你不能得到DateTime發出正確的格式,所以你決定把它作爲一個字符串,然後你不能比較回EF75查詢DateTime

是的,我意識到我沒有回答你如何比較DateTime輸入與實體框架中的字符串屬性的問題,但我想現在你可以看到爲什麼。如果你真的想要走這條路,你可能會發現在SqlFunctionsEntityFunctions有用的東西 - 但即使如此,你將如何建立一個有效的索引在你的數據庫的這一列?如果您獲得大量數據,查詢將非常慢。我會建議反對它。

+0

我檢查了EF功能。沒有'DateTime.Parse',但想到它,你可能只能使用'轉換'或不安全的轉換。仍然在一天結束時,分離關心的人。您的序列化邏輯和持久性邏輯應該是分開的!但是,是的......輝煌的呃......不是一個答案。 – Aron