WCF 对 xs:date 的支持
WCF 与其他框架在传输日期值时的互操作性
引言
尽管 WCF 技术强大且灵活,但在某些方面仍有改进的空间。在本文中,我将展示与 Web 服务的日期格式互操作性相关的问题以及如何解决这些问题。
背景
我的公司是一家金融机构,有许多不同的应用程序交换数据。其中大多数应用程序都是用 Java 实现的,而我的应用程序公开了用 WCF .NET 实现的 Web 服务。
为了交换日期,我提出了一个 xs:dateTime
格式。 这是因为那时我还不了解这种类型的特殊性。
第一个问题:UTC 格式。 当我使用 soapUI 测试 Web 服务时,我手动输入了 1969-03-17T00:00:00.000+01:00
作为 1969-03-17
出生日期的输入参数。 一切都很好。 然而,当实际的 Web 服务客户端发送 1969-03-16T23:00:00.000Z
(UTC 格式的等效日期时间)时,.NET 将其视为 1969-03-16
日期(偏移一天)。
补充说明:我在波兰工作,所以冬天是 GMT+1,夏天是 GMT+2 CET 时区,具有夏令时。
因此,看起来 UTC 时间没有被正确处理。 幸运的是,存在一个简单的解决方法
if (date.Kind == DateTimeKind.Utc)
date = date.ToLocalTime()
第二个更困难的问题如下:对于某些日期值,不同的框架有不同的表示形式。 例如,我的 WCF Web 服务接收到 1941-12-05T00:00:00.000+02:00
。 Java 客户端的意图是发送 1941-12-05
日期,而 .NET 服务将其视为 1941-12-04
。 我们发现大约 5% 的日期存在类似的问题。 这些日期大多来自 40 年代,但不仅仅如此。 一种可能的解释是 夏令时 在 40 年代没有使用。 或者也许一个框架使用了 德国的夏令时 用于二战时期?
如果您开始考虑框架之间的夏令时差异,那么以这种方式表示日期的想法就会变得疯狂。 那么框架是否必须记住夏令时期的历史日期? 他们是否考虑了历史事故,例如改变边界(和时区)? 哪里有记载? 如果算法不同怎么办? 哪些参数会影响时区解释? 我们知道最重要的是 Windows 上的区域设置。 Linux、AIX 呢? 所有服务器都具有正确且兼容的设置吗?
好的,所以 xs:dateTime
不是一个好的选择,因为框架之间存在差异。 还有哪些其他选择? xs:string
呢? 不幸的是,这也不是一个好的选择:目前使用着许多不同的格式:dd-MM-yyyy、yyyy-MM-dd、yyyyMMdd、… 。 在某些情况下,这是可以的——例如一个 Web 服务对应一个客户端。 但在某些情况下,这是不够的:当来自多个系统的 Web 服务在 ServiceBus
上重用以创建新的 Web 服务时,所有 Web 服务都必须同意一种格式。 但是,有时无法更改格式,因为许多客户端都在使用它。
最好的选择是使用没有时区的 xs:date
(xs:date
可用的选项)。 但是在 WCF 中,不支持此选项。 我在 Google 上搜索了很多,但没有找到令人满意的解决方案。 好的,我发现可以强制 WCF 使用能够使用 xs:date
的 XmlSerializer
。 但我更喜欢使用 DataContractSerializer
,因为其他原因。 最重要的是:XmlSerializer
不会处理简单服务参数的 xs:date
。 XmlSerializer
要求日期必须是对象的属性。
那么我的建议是什么? 我找到了以下解决方案:定制的 xs:date
。 它并不像看起来那么复杂。
Using the Code
我定义了自己的类型 – WcfDate
,它实现了 IXmlSerializable
接口。 在服务合同中,使用该类型代替 .NET 代码中的 DateTime
就足够了。
例如
[ServiceContract]
public interface IService1
{
[OperationContract]
string GetData(WcfDate value);
[OperationContract]
CompositeType GetDataUsingDataContract(CompositeType composite);
}
[DataContract]
public class CompositeType
{
bool boolValue = true;
string stringValue = "Hello ";
WcfDate dateValue = new WcfDate(DateTime.Today);
[DataMember]
public bool BoolValue
{
get { return boolValue; }
set { boolValue = value; }
}
[DataMember]
public string StringValue
{
get { return stringValue; }
set { stringValue = value; }
}
[DataMember]
public WcfDate DateValue
{
get { return dateValue; }
set { dateValue = value; }
}
}
在运行时,WcfDate
在 soap envelop 中呈现为 xs:date
。 我还添加了一些扩展方法,以便轻松地在 WcfDate
和 DateTime
之间进行转换。 例如,可以在一行中递增传入的日期
composite.DateValue = composite.DateValue.GetDate().AddDays(1).ToWcfDate();
所有详细信息都在附加的代码中:WcfCommon
项目提供了 WcfDate
类型和 WsdlDocumentation
属性(从 Microsoft 示例复制)以及 WcfSampleService
展示了如何使用 WcfDate
。
缺点
当您生成代理时,客户端不支持 xs:date
。 然后 xs:date
元素被视为 xs:string
。 客户端必须自己解析 date
,但这仍然比以前好,因为 xs:date
强制执行标准的 date
布局。
进一步的结论
Date
类型非常有用。 它存在于大多数数据库(包括 SQL Server)以及许多框架和语言中(尽管并非全部)。 如果 Microsoft 在 WCF 和 .NET Framework 中原生支持它,我们的生活会更轻松。
历史
- 2011 年 4 月 15 日:初版