次の方法で共有


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 https://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

  • Anonymous
    March 23, 2007
    Wonderful article.. Helped me solve my problem

  • Anonymous
    June 18, 2007
    The comment has been removed

  • Anonymous
    June 27, 2007
    How do i consume WCF service from .net framework 1.1 client (web and windows)?

  • Anonymous
    July 06, 2007
    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...

  • Anonymous
    September 12, 2007
    How to create WCF web services. Code example for client and server.

  • Anonymous
    September 12, 2007
    How to create WCF web services. Code example for client and server.

  • Anonymous
    October 10, 2007
    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 :).

  • Anonymous
    October 23, 2007
    Thanks. This problem was going to frustrate me no end.

  • Anonymous
    February 09, 2008
    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 :)

  • Anonymous
    April 29, 2008
    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

  • Anonymous
    May 12, 2008
    WCF web services and the Business Data Catalog (BDC)

  • Anonymous
    November 20, 2008
    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 { }

  • Anonymous
    December 01, 2008
    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.

  • Anonymous
    December 10, 2008
    Thanks a ton A very frustrating problem finally resolved! Anuj

  • Anonymous
    February 13, 2009
    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.

  • Anonymous
    February 13, 2009
    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.

  • Anonymous
    March 07, 2009
    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.

  • Anonymous
    September 07, 2009
    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'?

  • Anonymous
    November 15, 2009
    What about (out double xxxResult) and (out bool xxxResultSpecified)