0

的背景

我正在使用 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="..." />
        &lt;/Items>
      </Data>
   </Response>
</Api>

问题

ResponseContract这对于方法都具有相同泛型类型的服务非常有效。WCF orXmlSerializer期望每个合约在其命名空间中具有唯一名称,但服务现在返回具有相同 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 元数据的完整性,这意味着不会使用反射进行动态更改。

尝试的解决方案

  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>元素。

结论

有任何想法吗?

4

1 回答 1

0

我得到了这个的风滚草徽章!

我放弃了所描述的方法,因为我找不到可行的解决方案。相反,每个合约都QueryStatus<TErrorCode>从 generic 继承一个可为空的属性BaseContract<TContract, TErrorCode>。该属性会自动为主合同和分合同填充null

于 2010-10-13T19:32:37.217 回答