Solving the "disappearing data" issue when using Add Web Reference or Wsdl.exe with WCF services


A lot of people are reporting “mysterious” problems when using the Visual Studio “Add Web Reference” functionality or the wsdl.exe tool to consume WCF service. Let’s say you have the following service operation:


 


            [OperationContract]


            int GetShipmentQuote (Route r);


 


The operation is using the following data contract:


 


            [DataContract]


public class Route


{


            [DataMember]


            public int zipCodeFrom;


 


            [DataMember]


            public int zipCodeTo;


}


 


If you use “Add Web Reference” with this service, you will get a proxy along with a “Route” class. You may be tempted to use the proxy like this:


 


            Route r = new Route();


            r.zipCodeFrom = 98052;


            r.zipCodeTo = 98125;


            int price = proxy.GetShipmentQuote(r);


 


However, this won’t work – the zipCodeFrom and zipCodeTo items will be zero on the service side! If you look at the messages going over the wire, you will see that these items aren’t even transmitted from the client to the service. To discover the source of the problem, look at the generated Route class on the client side. It will look similar to the following (I am simplifying the class for clarity):


 


            public class Route


            {


                        public int zipCodeFrom { /* get/set code omitted… */ };


                        public bool zipCodeFromSpecified;


                        public int zipCodeTo { /* get/set code omitted… */ };


                        public bool zipCodeToSpecified;


 


                        //more code omitted…


            }


 


Where did these extra boolean “specified” members come from and what do they do? The answer 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 older web services stack, ASP.NET Web Services (“ASMX”), uses a different serializer, the XmlSerializer, which maintains full schema and XML fidelity. The XmlSerializer maps all optional elements to two members: one represents the data itself, and one specifies whether or not the data is actually present – this is the “xxxSpecified” member. These xxxSpecified members must be set to true to enable the serialization of the corresponding “actual data” members. So, the correct way to use the proxy is as follows:


 


            Route r = new Route();


            r.zipCodeFrom = 98052;


            r.zipCodeFromSpecified = true;


            r.zipCodeTo = 98125;


            r.zipCodeToSpecified = true;


            int price = proxy.GetShipmentQuote(r);


 


Now, even when you know about this, you may find it somewhat annoying to always have to write such code. Instead, you can prevent the xxxSpecified members from being generated by marking the data members as required instead of optional:


 


            [DataContract]


public class Route


{


            [DataMember(IsRequired=true)]


            public int zipCodeFrom;


 


            [DataMember(IsRequired=true)]


            public int zipCodeTo;


}


 


Keep in mind that WCF will then enforce the requirement constraint and 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 shipmentWeight” 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).


 


Another possible solution is to change the generated proxy code on the client side. You could specify default values of “true” for the xxxSpecified members, or you could modify the property setter for each member (e.g. zipCodeFrom) to set the corresponding xxxSpecified member (e.g. zipCodeFromSpecified) 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.


 


Of course, the real solution is to use WCF on the client side as well – this will completely eliminate the problem. If your client application cannot depend on .NET Framework 3.0, this is not an option for you. However, if the Framework version dependency is not a problem, you are strongly encouraged to use WCF on the client side – not only will the xxxSpecified problem go away, but you will also get all of the other WCF benefits such as higher performance in many cases. To use WCF on the client side, use “Add Service Reference” instead of “Add Web Reference” – if you do not have this option in Visual Studio, you should install the Visual Studio extensions for WCF from http://www.microsoft.com/downloads/details.aspx?familyid=F54F5537-CC86-4BF5-AE44-F5A1E805680D&displaylang=en (this feature will be greatly improved in the upcoming “Orcas” release). If you are using command-line tools, use svcutil.exe instead of wsdl.exe to generate the client proxy.


 


It is also worth mentioning that the xxxSpecified issue occurs at the parameter level as well, but for a slightly different reason. For example, if you have an operation with parameters as follows:


 


            [OperationContract]


            int GetShipmentQuote (int zipCodeFrom, int zipCodeTo);


 


The “Add Web Reference” proxy will look similar to the following:


 


            int GetShipmentQuote(int zipCodeFrom, bool zipCodeFromSpecified, int zipCodeTo, bool zipCodeToSpecified);


 


When using the proxy, make sure you set the xxxSpecified parameters to true:


 


            int price = GetShipmentQuote(98052,true,98125,true);


 


Again, you can use the partial class feature to add an overload of GetShipmentQuote to the generated proxy class that only takes zipCodeFrom and zipCodeTo and automatically sets the xxxSpecified parameters to “true”. And, again, if your client side uses WCF, the issue will not occur.


 


The reason the problem occurs at the parameter level is because our testing showed that parameters have to be marked as optional for maximum interoperability with 3rd-party web service stacks. Unlike with data members, there is no simple way to mark a parameter as required – and this is by design. If you really need to avoid xxxSpecified with parameters, there is a workaround involving [MessageContract(IsWrapped=false)] containing a data contract with required data members – but, again, consider the interoperability impact before going down this path.


 


Finally, I want to mention one other “solution” to the xxxSpecified issue – switching to the XmlSerializer on the service side. I am mentioning this only for completeness – this is by no means an approach I would recommend. There are various possible reasons why you may have to choose the XmlSerializer over the DataContractSerializer, but this is not one of them. The fact that the generated proxy in ASMX is somewhat less usable than expected should not influence your serialization technology choice. You get many benefits from the DataContractSerializer such as improved performance, improved versioning story, improved integration with other technologies including ones we are introducing in “Orcas”, and much more.


 


 


 


Comments (19)

  1. .Netfavourite says:

    Wonderful article.. Helped me solve my problem

  2. 吴頔 says:

    首先,问题的具体描述在博客园的一篇 Blog 中. 在.NET2.0中,加入了DataTable序列化的支持,现在WCF中,我们可以直接使用DataTable/DataSet作为参数在服务器端和客户端进行传递(当然,基于SOA互操作性的原因,最好不要在OperationContract中使用.NET平台独有的DataTable/DataSet作为参数).但是,当我们选用DataTable/DataSet作为OperationContract的参数的时候,要千万注意对DataContract的影响

  3. Shan says:

    How do i consume WCF service from .net framework 1.1 client (web and windows)?

  4. DT blogi says:

    Seoses WCF teenuste katsetamisega oli mul vaja teenused arendustööde arvutis käima saada IIS-i all. Vaikimisi Visual Studio poolt loodav WCF teenuste teek IIS-i tuge endas automaatselt ei sisalda. Tekkis veel pisikesi tõrkeid, mille suutsin kõiges…

  5. How to create WCF web services. Code example for client and server.

  6. How to create WCF web services. Code example for client and server.

  7. Nicolas Webb says:

    Thank you so much – we were scratching our heads over this for quite a while here.

    Time to see if we can get our clients to switch from ASMX :).

  8. Liam says:

    Thanks. This problem was going to frustrate me no end.

  9. Anubhav says:

    Thanks, this article was really helpful…..i was wondering why my unit tests were sending the "default values" for the date param to the web service…….

    i was tryin to install the client app, but was nt having the svcutil.exe tool, and so added the service as a Web Reference rather than as a Service reference……will check it by adding as a SR, and see what happens…….

    anyways it was really good….thanks again 🙂

  10. Chandra says:

    It is really a nice article. By the way, Can’t we resolve the XXXSpecified(optional) parameters issue by marking the Service Contract with XmlSerializerFormat attribute?

    [XmlSerializerFormat]

       public partial interface ITestContract

       {

             [OperationContract]

               int GetShipmentQuote (int zipCodeFrom, int zipCodeTo);

      }

    The “Add Web Reference” proxy will look similar to the following:

                int GetShipmentQuote(int zipCodeFrom, int zipCodeTo);

    It won’t show the XXXSpecified flag in the proxy method.

    You got any concerns with the above approach?

    Thanks,

    Chandra

  11. WCF web services and the Business Data Catalog (BDC)

  12. Shailendra says:

    When i use VS2008 Add Service Reference utility, it gives me dofferent issue. It creates a DataContract twice, whiich gives me comiple time err. Here I am using FaultContract in the service defination. When i remove falut contract it works fine. Any help?

    [System.Diagnostics.DebuggerStepThroughAttribute()]

    [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Runtime.Serialization", "3.0.0.0")]

    [System.Runtime.Serialization.DataContractAttribute(Name="DataContract", Namespace=schemas.datacontract.org/…/DataDefinition.XXXXXXXXX)]

    [System.SerializableAttribute()]

    public partial class DataContract : object, System.Runtime.Serialization.IExtensibleDataObject, System.ComponentModel.INotifyPropertyChanged {

    }

    [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Xml", "2.0.50727.3053")]

    [System.SerializableAttribute()]

    [System.Diagnostics.DebuggerStepThroughAttribute()]

    [System.ComponentModel.DesignerCategoryAttribute("code")]

    [System.Xml.Serialization.XmlTypeAttribute(Namespace=schemas.datacontract.org/…/DataDefinition.XXXXXXXXX)]

    public partial class DataContract : object, System.ComponentModel.INotifyPropertyChanged {

    }

  13. Michel says:

    Thanks a lot. I was wondering why this was happening.

    I’m constructing the proxy for the .NEt compact framwork 3.5 on the command prompt with ‘NetCFSvcUtil.exe’ and it also generates the XXXSpecified members.

    But now i know what to do, just set the XXXspecified members to true when necessary.

  14. anujkiran says:

    Thanks a ton

    A very frustrating problem finally resolved!

    Anuj

  15. Curt says:

    Like Michel, I too am using NetCFSvcUtil.exe to generate my proxy classes for my Windows Mobile app.  In reading this article, I was hopeful that specifying <DataMember(IsRequired:=True)> for each property would cause NetCFSvcUtil to not generate the XXXSpecified members.  Unfortunately, this is not what happens.  The xxxSpecified members are still generated.

    Does anyone know a way to prevent the xxxSpecified members from being generated using NetCFSvcUtil?  I’d really like to not have to set xxxSpecified to true every time I set a value on a property.

  16. Curt says:

    Update: Nevermind.  I’m not sure what I missed, perhaps one of the projects with all of my custom classes was not saved/compiled when I ran netcfsvcutil.  Anywho…now the proxy classes are being created without the xxxSpecified members.

    Glad that one is behind me.

  17. Manfred says:

    Hi Eugene,

    please moderate your comments on your Blogspot blog (http://eugeneos.blogspot.com/). You have some quite good content there as well but some of the comments is outright spam and diminishes the value of your posts significantly.

    Cheers, Manfred.

  18. IH says:

    I still don’t understand why the client side has to ‘manually’ set the ‘specified’ fields. if they are not null’s, aren’t they ‘specified’?

  19. Bashar says:

    What about (out double xxxResult) and (out bool xxxResultSpecified)