带有XmlSerializer的WCF:返回通用契约时的命名空间冲突

时间:2010-09-30 20:16:58

标签: c# .net xml-serialization xml-namespaces wcf-serialization

背景

我正在使用WCF为C#.NET Web应用程序开发REST API。我将其配置为使用XmlSerializer而不是其默认的DataContractSerializer,以便更好地控制XML格式。我创建了一个通用的ResponseContract<TResponse, TErrorCode>数据协定,它包含对<Api><Response>的响应,用于请求状态,错误消息和命名空间等通用数据。示例方法:

  

ResponseContract<ItemListContract, ItemListErrorCode> GetItemList(...)

上述方法的示例响应:

<?xml version="1.0" encoding="utf-8"?>
<Api xmlns="http://example.com/api/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
   <Response Status="OKAY" ErrorCode="OKAY" ErrorText="">
      <Data Template="ItemList">
         <Pages Template="Pagination" Size="10" Index="1" Count="13" Items="126" />
         <Items>
            <Item example="..." />
            <Item example="..." />
            <Item example="..." />
        </Items>
      </Data>
   </Response>
</Api>

问题

这对于方法都具有相同通用ResponseContract类型的服务非常有效。 WCF或XmlSerializer期望每个合同在其名称空间中具有唯一名称,但该服务现在返回具有相同XML根名称的不同类型的通用合同:

ResponseContract<ItemListContract, ItemListErrorCode> GetItemList(...)
ResponseContract<ItemContract, ItemErrorCode> GetItem(...)

导致异常:

The top XML element 'Api' from namespace 'http://example.com/api/' references distinct types Company.Product.ApiServer.Contracts.ResponseContract`2[Company.Product.ApiServer.Contracts.Items.ItemListContract,Company.Product.ApiServer.Interfaces.Items.ItemListErrorCode] and Company.Product.ApiServer.Contracts.ResponseContract`2[Company.Product.ApiServer.Contracts.Items.ItemContract,Company.Product.ApiServer.Items.ItemErrorCode]. Use XML attributes to specify another XML name or namespace for the element or types.

该服务必须允许不同的返回类型。这很难实现,因为ResponseContract<TResponse, TErrorCode>(设置名称和命名空间)是通用的,并且由所有API方法返回。我还需要保持WSDL metadata完整性,这意味着不使用反射进行动态更改。

尝试解决方案

  1. 以声明方式更改XML属性是不可能的,因为<Api>根元素及其属性是完全通用的(在ResponseContract中)。

  2. 使用反射在运行时更改属性命名空间(例如,'http://example.com/api/Items/GetItemList')无效。 获取属性是可能的,但对它们的更改不起作用。无论如何,这将打破WSDL。

  3. 实现IXmlSerializable时,在调用<Api>时,编写器已位于WriteXml()开始标记之后。只能覆盖<Api>的子节点的序列化,这无论如何都不会引起任何问题。无论如何这都行不通,因为在调用IXmlSerializable方法之前抛出了异常。

  4. 将常量命名空间与typeof()或类似连接起来使其唯一不起作用,因为命名空间必须是常量。

  5. 默认DataContractSerializer可以在名称中插入类型名称(如<ApiOfIdeaList>),但DataContractSerializer的输出是膨胀且不可读的,缺少属性,这是不可行的对于外部再利用者。

  6. 扩展XmlRootAttribute以不同方式生成命名空间。遗憾的是,调用时没有可用的类型信息,只有通用的ResponseContract数据。可以生成随机命名空间来规避问题,但动态更改架构会破坏WSDL元数据。

  7. 使ResponseContract基类而不是包装器合同应该有效,但会导致大量重复的通用数据。例如,上例中的<Pages><Item>也是合同,它们有自己的等效<Api><Response>元素。

  8. 结论

    有什么想法吗?

1 个答案:

答案 0 :(得分:0)

我为这个获得了风滚草徽章!

我放弃了所描述的方法,因为我找不到可行的解决方案。相反,每个合同都从通用QueryStatus<TErrorCode>继承可空的BaseContract<TContract, TErrorCode>属性。此属性将自动填充主合同,null用于转包。