Click here to Skip to main content
Click here to Skip to main content
Technical Blog

Tagged as

WCF/ASMX Interoperability – Removing the Annoying xxxSpecified when Adding a Web Reference to a WCF Service

, 1 Feb 2012 CPOL
Rate this:
Please Sign up or sign in to vote.
Today I answered a question in the Hebrew MSDN forums about consuming WCF from a .NET 2 client, using the "Add Web Reference” option of Visual Studio.Just in case you don't know Hebrew I'll sum it up for you – when adding a web reference to a WCF service that exposes a method of the following...

Today I answered a question in the Hebrew MSDN forums about consuming WCF from a .NET 2 client, using the “Add Web Reference” option of Visual Studio.

Just in case you don’t know Hebrew I’ll sum it up for you – when adding a web reference to a WCF service that exposes a method of the following sort:

int UseScalarTypes(int value1, int value2)

The generated method signature in the client app will look like so:

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

The question was why this happens and how this can be fixed to look like the service’s method signature.

Before we continue to see why this happens and how to fix it, the short answer is – Yes, you can make it look like the original contract by using message contracts. Continue reading to see how.

Why this happens:

In WCF the WSDL generated for the above method looks like so:

<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>

Notice the minOccurs? int is a value type which means it doesn’t accept null values, but still WCF marks it as optional. When you use the “Add Service Reference” option of WCF in Visual Studio, the generator ignores that attribute and creates the method declaration in the client side the same way it is declared in the service contract. However, if you use the old “Add Web Reference” option, the generator checks the minOccurs, realizes that the variable is optional, and since this is a value type, it translates the optional into a set of variables: value + xxxSpecified.

By the way, this is how the WSDL is created for the same method declaration when it is used in an ASMX-style web service:

<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>

Note 1: With ASMX web service, the minOccurs/maxOccurs is set properly because the “Add Web Reference” expects that, and therefore we don’t see this behavior with ASMX web service + add web reference.

Note 2: Since WCF ignores the minOccurs, using the “Add Service Reference” to consume that ASMX web service will result in a client-side method with the same signature as declared in the service (without the xxxSpecified).

So now that we know why this happens, let’s see how to fix it.

Step 1: Create a set of message contracts, one for the request and one for the response (if you have one).

Step 2: In the request message contract class, apply the MessageContract attribute and set the IsWrapped parameter to false, like so:

[MessageContract(IsWrapped = false)]
public class UseScalarTypesRequest

Setting the IsWrapped to false will create the XML without a wrapping element, making the properties look like they are actually method parameters.

Step 3: Add each parameter of the method to the class as a property, and apply the MessageBodyMember attribute to it. You can use this step to rename the property to use the naming convention of parameters by using the Name parameter in the attribute. The result should look like so:

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

Step 4: Do the same for the response message contract, this time you just need one property for the return type of the method, for example:

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

Step 5: Change the method signature in the contract and service from using parameters to using the message contracts you’ve created, like so:

UseScalarTypesResponse UseScalarTypes(UseScalarTypesRequest parameters)

Of course the immediate step will be to also change the implementation of your code to reflect the parameters being moved to a wrapper object - for convenience, you can leave your existing code in the service as is, change the contract, and then create the new methods which will simply call the older ones, like so:

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

That’s it, update your web reference in the client side, and watch how the method signature in the client side is without the xxxSpecified.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Ido Flatow
Architect Sela Group
Israel Israel
Web developer since 1997. I'm a senior architect at Sela Group in Israel. I'm a consultant, trainer (Microsoft MCT), and a speaker in conferences worldwide.
My main fields are WCF, ASP.NET, Windows Azure, IIS, Entity Framework, and Silverlight.
Follow on   Twitter

Comments and Discussions

 
-- There are no messages in this forum --
| Advertise | Privacy | Terms of Use | Mobile
Web03 | 2.8.1411022.1 | Last Updated 1 Feb 2012
Article Copyright 2012 by Ido Flatow
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid