Udostępnij za pośrednictwem


Why your NetCF apps fail to call some web services

Here's the scenario: You are writing an NetCF app and trying to call a web service from that app.  You generated the code for the client proxy class using Visual Studio's "Add Web Reference" command.  Code is generated, you call into it, and you run your app.  The call fails with a cryptic error from the web service saying something about a malformed message.  If you try the same thing from a desktop app it works perfectly.  Sound familiar?  Read on for the reason and solution.

I'll address the common cause of xml element mis-ordering here.

Background

Web services and serializers

Calling web services involves serializing objects and sending the resulting xml to the web service, and deserializing the xml response back into objects.  This is usually totally transparent to the developer, who just invokes a method and the magic happens behind the scenes.  The serialized objects come from classes that are usually generated for you by Visual Studio or command-line tools like wsdl.exe or svcutil.exe.  They are constructed based on the service WSDL in such a way that their serialized format matches what the service is expecting, and such that they can be deserialized from the xml the service will respond with.  These classes are called "client proxy classes."

Both the desktop .NET Framework and the .NET Compact Framework use the XmlSerializer class for serializing and deserializing these objects.  When using the WCF stack, the desktop framework will use their recently added DataContractSerializer instead of the XmlSerializer.  Both of these serializers rely on reflection to query the generated client proxy classes and to generate the required xml. 

Reflection

The .NET runtime does not ever care about the order a given type's elements are declared.  For example, the class:

 class Fruit {
   int seeds;
   string color;
}

Is equivalent to

 class Fruit {
   string color;
   int seeds;
}

This makes sense.  Unfortunately though, because of this when you use reflection to query the members of a type, the order of those members is not guaranteed to be in declaration order, or even to be consistent as you switch between versions of the .NET Framework.  In fact, the order in which reflection returns members did in fact change between versions 1.1 and 2.0 of the framework. 

How non-deterministic ordering affects web services

Because the serializers in .NET rely on reflection, they are affected by the non-deterministic ordering of members that reflection provides.  The order of elements serialized will change with whatever order reflection provides members in.

The .NET designers anticipated this problem, and provided a way to force a specific ordering of serialized elements.  The xml serializer attributes that you can decorate your serializable classes with support an Order attribute that you can use to guarantee some desired ordering of elements.  For example, you could force the Fruit class used earlier to put <seeds> before <color> no matter what order reflection provides by changing the class to read like this:

 class Fruit {
   [XmlElementAttribute(Order=1)] int seeds;
   [XmlElementAttribute(Order=2)] string color;
}

The attributes will retain the Order property's value regardless of reflection order, and that information can be used within the serializer to keep the order to the way the app developer intended.

The problem: why it "works" on desktop's framework and not on NetCF

Although neither desktop nor NetCF's framework guarantees ordering, it so happens that desktop's serializer preserves the order better than NetCF's.  Desktop's serializer isn't perfect either, but it's predictable enough that developers take its order for granted and then wonder why NetCF's serializer doesn't behave the same way. 

Unfortunately, the wsdl.exe and Visual Studio IDE developers were among those developers that seem to have forgotten that ordering is not guaranteed unless explicitly defined, and so neither generate the code in the client proxy classes to set the Order properties necessary to guarantee the correct ordering.  It seems they assumed that declaration order is the default, since the desktop framework (mostly) works that way. 

The wsdl.exe tool does offer an "/order" switch that will set order explicitly, but unfortunately this command line tool generates code that won't compile on NetCF projects because of the limited API exposed by NetCF. 

The workaround

So until the code is fixed in Visual Studio and/or wsdl.exe, you have a couple of options to get your NetCF projects to call these web services that require specific order:

  1. Use wsdl.exe /order to generate your client proxy class, and then remove all the code not supported by NetCF until your project compiles. 
  2. Use Visual Studio to generate your client proxy class, then run wsdl.exe /order in some other directory, and copy just those lines of source code from the resulting class into your project source file that give the explicit ordering.

The ultimate fix

There are a few fixes I'm personally working on to help alleviate this inconvenience for app developers.

  1. I'm trying to get Visual Studio Orcas to include explicit ordering for all generated proxy client classes.
  2. I have changed NetCF's XmlSerializer to preserve declaration order serialization as closely as possible (still not guaranteed).  You'll have to be running on NetCF 2.0 SP2 or later to get this fix. 
  3. The svcutil.exe tool (which deprecates wsdl.exe) automatically generates explicit ordering code where required with no extra steps for the app developer.

Summary

The XmlSerializer cannot guarantee element order either on desktop .NET Framework or the .NET Compact Framework unless the developer gives the order explicitly, although in some cases the "default" ordering will behave as you expect and in some cases it won't. 

The bottom line: where element order is important, use XmlElementAttribute.Order, XmlArrayAttribute.Order, and the other ordering attributes as necessary.

Comments

  • Anonymous
    February 05, 2007
    I ran into exactly this problem just last week when trying to access a WCF service from NetCF 2.0.  I was just about to give up on accessing WCF from NetCF when I saw your post.  Thanks Andrew...

  • Anonymous
    February 19, 2007
    There's no original content in this post, but here's what people are saying about WCF and ASMX interoperability

  • Anonymous
    June 15, 2007
    I now have three blogs that I post to: this one, JMPInline , and NetCFTeam . Which posts will I put where?