Introduction
This post helps us to understand the mechanism behind creating a proxy class while adding a Web Reference. This post mainly concentrates on the parameter values that get missed while calling a web service method.
Background
While adding a Web Reference to our project or using wsdl.exe to consume WCF, and while calling the methods exposed in the reference with parameters; we generally get to the state where we don’t understand why our data is not sent or rather sent as zero to the service even when we provide it.
For example; let's consider the following:
[OperationContract]
int GetEmployeeDetails (Consumer c);
[DataContract]
public class Consumer
{
[DataMember]
public int consumerId;
[DataMember]
public int consumerZipCode;
}
If you use “Add Web Reference” to this service, you will get a proxy along with a “Consumer
” class. You may use the proxy like this:
Consumer c = new Consumer();
c.consumerId = 19089;
c.consumerZipCode = 48397;
Employee emp = proxy.GetEmployeeDetails(c);
However, this won’t work – the consumerId
and consumerZipCode
items will be zero on the service side! This is because the items are not transmitted from the client to the service.
A Closer Look
To see it even more closely, let us look at the generated consumer
class on the client side. It will look similar to the following:
public class Consumer
{
public int consumerId { };
public bool consumerIdSpecified;
public int consumerZipCode { };
public bool consumerZipCodeSpecified;
}
Did you notice these extra booleans? Where do they come from and what do they do?
The answer is; this is the schema that the WCF data contract serializer generates by default. Because of the way its versioning model works, the serializer generates all data members as optional elements. The right way to use the proxy is as follows:
Consumer c = new Consumer();
c.consumerId = 19089;
c.consumerIdFromSpecified = true;
c.consumerZipCode = 48397;
c.consumerZipCodeSpecified = true;
Employee emp = proxy.GetEmployeeDetails(c);
Now, even when you know about this, you may find this disturbing to always have to write such code. Instead, you can prevent the —Specified members from being generated by marking the data members as required instead of optional:
[DataContract]
public class consumer
{
[DataMember(IsRequired=true)]
public int consumerId;
[DataMember(IsRequired=true)]
public int consumerZipCode;
}
Always have in mind that WCF will throw an exception if a required data member is missing. It is usually fine to mark all data members as required in a first version of a data contract. However, if you add a new data member (e.g. if we also want to add an “int consumerPhone
” to our contract in v2), it should not be marked as required – otherwise, v1 clients will not be able to talk to v2 services (or vice versa, depending on where the data contract is used).
This is otherwise called as lax versioning in WCF Versioning Terms.To know more about WCF Versioning, visit http://www.bloggingbunk.com/2011/08/wcf-versioning/.
Another solution apart from the above is to change the generated proxy code on the client side. You could specify default values of “true
” for the —Specified members, or you could modify the property setter for each member (e.g. consumerId
) to set the corresponding —Specified member (e.g. consumerIdSpecified
) to true
whenever that property is set.
There are many circumstances in which modifying the generated proxy code is unacceptable – e.g. when you expect the service to change and thus expect to have to regenerate the proxy code often. In this situation, you can take advantage of the “partial class” feature to add a helper method to do the property setting work.
Conclusion
Therefore the recommendations will be to Add Service Reference while consuming a WCF service.
History
- 28th September, 2011: Initial version