Protobuf.net和DateTimeOffset的序列化

时间:2016-08-22 19:47:49

标签: c# serialization protobuf-net

我一直在使用Protobuf-net作为胖客户端应用程序的序列化程序,该应用程序使用服务堆栈通过HTTP进行通信。我们的第一个拥有大量数量的客户在反序列化时已经开始出现错误。我们在一些模型中发送DateTimeOffset类型,因此我们创建了一个代理,将值序列化为字符串。从我们的日志中,我可以看到错误发生的时间,这是它尝试反序列化的日期值在重复时区偏移的末尾有额外的六个字符:

2016/8/9 12:02:37 AM-7:00 -7:00

这是我们代理人的代码。

[ProtoContract]
public class DateTimeOffsetSurrogate
{
    [ProtoMember(1)]
    public string DateTimeString { get; set; }

    public static implicit operator DateTimeOffsetSurrogate(DateTimeOffset value)
    {
        return new DateTimeOffsetSurrogate { DateTimeString = value.ToString() };
    }

    public static implicit operator DateTimeOffset(DateTimeOffsetSurrogate value)
    {
        try
        {
            return DateTimeOffset.Parse(value.DateTimeString);
        }
        catch (Exception ex)
        {
            throw new Exception("Unable to parse date time value: " + value.DateTimeString, ex);
        }
    }
}

一旦发生此日期错误,它将无法正确序列化/反序列化,直到PC重新启动。我们无法以允许我们调试和查看消息的其余部分的方式重现此错误。这是某人熟悉的情况吗?我们使用的是版本2.0.0.640,由于这个问题我更新到2.0.0.668,但问题仍然存在。

1 个答案:

答案 0 :(得分:2)

看起来好像CultureInfo.CurrentCulture.DateTimeFormat.LongTimePattern在客户端的机器上搞砸了。我可以通过将"K"格式添加到LongTimePattern

来重现该问题
var dateTime = DateTimeOffset.Parse(@"8/9/2016 12:02:37 AM-7:00");

var myCI = new CultureInfo("en-US");
myCI.DateTimeFormat.LongTimePattern = myCI.DateTimeFormat.LongTimePattern + " K";
Console.WriteLine(dateTime.ToString(myCI)); // Prints 8/9/2016 12:02:37 AM -07:00 -07:00

写的字符串是8/9/2016 12:02:37 AM -07:00 -07:00,这正是您所看到的。

可能是您的应用程序中存在将LongTimePattern设置为某处的错误。我也可以通过这样做来重现这个问题:

Thread.CurrentThread.CurrentCulture = myCI;
Console.WriteLine(dateTime.ToString());     // Prints 8/9/2016 12:02:37 AM -07:00 -07:00

或者可能是客户端以某种方式修改“区域和语言”中的“长时间:”字符串 - > “其他设置...”对话框,看起来像(Windows 7):

enter image description here

如果客户端以某种方式执行此操作,并且计算机位于域中,则格式可能会reset back on reboot,这正是您所看到的。

客户端可能正在手动执行此操作(但是,从实验中,尝试在Windows上手动附加K会在UI中生成错误弹出然后失败),或者可能有一些错误的第三方应用程序在做通过致电SetLocaleInfo,您或他们都不知道。

您可以记录LongTimePattern的值以尝试跟踪问题,但无论您是否应修改DateTimeOffsetSurrogate,以便它以文化不变格式序列化DateTimeOffset,最好由How to: Round-trip Date and Time Values: To round-trip a DateTimeOffset value指定:

[ProtoContract]
public class DateTimeOffsetSurrogate
{
    [ProtoMember(1)]
    public string DateTimeString { get; set; }

    public static implicit operator DateTimeOffsetSurrogate(DateTimeOffset value)
    {
        return new DateTimeOffsetSurrogate { DateTimeString = value.ToString("o") };
    }

    public static implicit operator DateTimeOffset(DateTimeOffsetSurrogate value)
    {
        try
        {
            return DateTimeOffset.Parse(value.DateTimeString, null, DateTimeStyles.RoundtripKind);
        }
        catch (Exception ex)
        {
            throw new Exception("Unable to parse date time value: " + value.DateTimeString, ex);
        }
    }
}

这不仅可以修复您看到的错误,还可以确保您的应用程序在一个区域(例如英国)生成的协议缓冲区可以在其他地方(例如,美国)进行解析,具有不同的日期文化格式和时间。