65.9K
CodeProject 正在变化。 阅读更多。
Home

WCF/ASMX 互操作性 – 删除向 WCF 服务添加 Web 引用时令人讨厌的 xxxSpecified

starIconstarIconstarIconstarIconstarIcon

5.00/5 (1投票)

2012 年 2 月 1 日

CPOL

3分钟阅读

viewsIcon

19265

WCF/ASMX 互操作性 – 删除向 WCF 服务添加 Web 引用时令人讨厌的 xxxSpecified

今天,我回答了希伯来语 MSDN 论坛上的一个问题,关于使用 Visual Studio 的“添加 Web 引用”选项从 .NET 2 客户端使用 WCF。

为了以防您不懂希伯来语,我为您总结一下 – 当向 WCF 服务添加 Web 引用时,该服务公开了以下类型的方法

int UseScalarTypes(int value1, int value2)

客户端应用程序中生成的方法签名将如下所示

public void UseScalarTypes(
  int value1, bool value1Specified, 
  int value2, bool value2Specified, 
  out int UseScalarTypesResult, out bool UseScalarTypesResultSpecified)

问题是为什么会发生这种情况以及如何修复它以使其看起来像服务的原始方法签名。

在我们继续了解为什么会发生这种情况以及如何修复它之前,简短的答案是 – 是的,您可以使用消息协定使其看起来像原始协定。继续阅读以了解如何操作。

为什么会发生这种情况

在 WCF 中,为上述方法生成的 WSDL 如下所示

<xs:element name="UseScalarTypes">
  <xs:complexType>
    <xs:sequence>
      <xs:element minOccurs="0" name="value1" type="xs:int" /> 
      <xs:element minOccurs="0" name="value2" type="xs:int" /> 
    </xs:sequence>
  </xs:complexType>
</xs:element>

注意 minOccurs 吗? int 是一种值类型,这意味着它不接受 null 值,但 WCF 仍然将其标记为可选。 当您在 Visual Studio 中使用 WCF 的“添加服务引用”选项时,生成器会忽略该属性,并以与在服务协定中声明的相同方式在客户端创建方法声明。 但是,如果您使用旧的“添加 Web 引用”选项,生成器会检查 minOccurs,意识到该变量是可选的,并且由于这是一种值类型,因此它会将可选变量转换为一组变量: value + xxxSpecified

顺便说一句,这是在 ASMX 样式的 Web 服务中使用相同方法声明时创建 WSDL 的方式

<s:element name="UseScalarTypes">
  <s:complexType>
    <s:sequence>
      <s:element minOccurs="1" 
      maxOccurs="1" name="value1" type="s:int" /> 
      <s:element minOccurs="1" 
      maxOccurs="1" name="value2" type="s:int" /> 
  </s:sequence>
  </s:complexType>
</s:element>

注意 1:对于 ASMX Web 服务,minOccurs/maxOccurs 设置正确,因为“添加 Web 引用”需要它,因此我们在 ASMX Web 服务 + 添加 Web 引用时没有看到这种行为。

注意 2:由于 WCF 忽略 minOccurs,因此使用“添加服务引用”来使用该 ASMX Web 服务将导致客户端方法具有与服务中声明的相同签名(没有 xxxSpecified)。

现在我们知道了为什么会发生这种情况,让我们看看如何修复它。

步骤 1

创建一组消息协定,一个用于请求,一个用于响应(如果您有的话)。

第二步

在请求消息协定类中,应用 MessageContract 属性,并将 IsWrapped 参数设置为 false,如下所示

[MessageContract(IsWrapped = false)]
public class UseScalarTypesRequest

IsWrapped 设置为 false 将创建没有包装元素的 XML,使属性看起来像是实际的方法参数。

步骤 3

将方法的每个参数作为属性添加到类中,并将 MessageBodyMember 属性应用于它。 您可以使用此步骤通过使用属性中的 Name 参数来重命名该属性,以使用参数的命名约定。 结果应如下所示

[MessageContract(IsWrapped = false)]
public class UseScalarTypesRequest
{
  [MessageBodyMember(Name = "value1")]
  public int Value1 { get; set; }
  [MessageBodyMember(Name = "value2")]
  public int Value2 { get; set; }
}

步骤 4

对响应消息协定执行相同的操作,这次您只需要一个属性用于方法的返回类型,例如

[MessageContract(IsWrapped = false)]
public class UseScalarTypesResponse
{
  [MessageBodyMember]
  public int Result { get; set; }
}

步骤 5

更改协定和服务中的方法签名,从使用参数更改为使用您创建的消息协定,如下所示

UseScalarTypesResponse UseScalarTypes(UseScalarTypesRequest parameters)

当然,紧随其后的步骤是更改代码的实现,以反映参数已移动到包装器对象 – 为了方便起见,您可以将现有代码保留在服务中,更改协定,然后创建新的方法,这些方法将简单地调用旧的方法,如下所示

UseScalarTypesResponse UseScalarTypes(UseScalarTypesRequest parameters)
{
  UseScalarTypesResponse result = new UseScalarTypesResponse();
  result.Result = UseScalarTypes(parameters.Value1, parameters.Value2);
  return result;
}

就是这样,更新客户端中的 Web 引用,并观察客户端中的方法签名如何没有 xxxSpecified

© . All rights reserved.