0

我正在使用 C#、.NET Framework 4.0 和Entity Framework Code First开发 WCF RESTful 服务开发 WCF RESTful 服务。

我有这个类(代表数据库中的一个表):

[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将其作为字符串,因为我将在我的 Web 服务上使用它,并将其作为 JSON 发送,因此将其存储为字符串对我来说更好。

如果我使用DateTime类型,我将在 JSON 响应中得到类似的信息:

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

您知道如何在 LINQ 表达式上将字符串与 DateTime 进行比较吗?

4

4 回答 4

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"具有相同的好处。

于 2013-10-05T12:58:37.440 回答
2

如果我见过,这肯定是一个XY 问题。您正在询问将日期时间与实体框架中的字符串进行比较,而真正的问题似乎是您不喜欢DataContractJsonSerializerWCF 默认使用的默认日期格式。

部分问题是您正在混合本地和UTC。您将得到/Date(1380924000000+0200)/,其中包含服务器的本地时区偏移量。这是因为您从 开始,DateTime.Now其中有一个.KindDateTimeKind.Local

如果您改为使用DateTime.UtcNow,它将具有.Kindof DateTimeKind.Utc,并且将被序列化为/Date(1380924000000)/。是的,格式的数字部分是相同的。即使指定了偏移量,数字部分仍然与 UTC 相关。

这只是这种格式的一个问题。另一个是DataContractJsonSerializer在序列化过程中写入本地偏移量,在反序列化过程中没有正确使用它。它只是假设如果提供了任何偏移量,则时间应该是本地的 -即使进行反序列化的计算机具有完全不同的偏移量

在 JSON 中使用的更好的格式是 ISO8601 格式。例如,这个 UTC 值看起来像2013-10-04T22:00:00.000Z. 如果您直接使用它,您可以轻松地将不同的日期格式传递给DataContractJsonSerializer它,但 WCF 并不容易将其公开给您。

您可以DataContractJsonSerializer通过自定义 WCF 消息格式化程序更改设置,例如此处此处此处所述,但它很快就会变得复杂。如果你这样做,请小心,并确保彻底测试!

另一个想法是编写一个使用JSON.Net而不是DataContractJsonSerializer. JSON.Net 默认使用 ISO8601 格式,因此您需要进行设置。

但老实说,最好的解决方案是不要尝试使用 WCF 来构建您的 REST 端点。日期格式问题只是一个开始。在此过程中还会出现各种其他问题。相反,请使用为此目的而设计的现代框架,例如ASP.Net WebAPIServiceStack

ASP.Net WebAPI 使用 JSON.Net,ServiceStack 有自己的 JSON 序列化器,称为 ServiceStack.Text。JSON.Net 使用 ISO8601 作为它的默认日期格式。如果您使用服务堆栈,则需要设置JsConfig.DateHandler = JsonDateHandler.ISO8601;

总而言之,你有两个XY 问题:

  • 首先,您选择了 WCF 来构建您的 REST 端点,这非常有问题,以至于业界开发了其他解决方案。
  • 其次,您无法DateTime发出正确的格式,因此您决定将其视为字符串,然后您无法将其DateTime与 EF 查询中的 a 进行比较。

是的,我意识到我没有回答您关于如何将DateTime输入与实体框架中的字符串属性进行比较的问题,但我认为现在您可以明白原因了。如果您真的想走这条路,您可能会在SqlFunctionsor中找到一些有用的东西EntityFunctions——但即便如此,您将如何在数据库中的该列上建立一个有效的索引?如果您获得大量数据,查询将非常慢。我建议不要这样做。

于 2013-10-06T01:03:50.497 回答
1

我不知道你是如何实例化你的DataContractJsonSerializer. 如果您直接实例化它...您可以传递一个DataContractJsonSerializerSettings,DateTimeFormat用于设置如何DateTime序列化。

如果您使用行为来实例化您的序列化程序,事情会稍微复杂一些。

于 2013-10-05T18:12:50.690 回答
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

这是一个真正的痛苦。

于 2013-10-05T18:54:52.690 回答