views:

603

answers:

2

I'm experimenting with WCF RESTful web services and I'm having a problem with Auto-Implemented properties.

I have a class called DeviceDescriptor, defined as follows:

public class DeviceDescriptor
{
    public string DeviceId { get; set; }
    public string DisplayName { get; set; }
}

I have a RESTful WCF service that is supposed to return a List of DeviceDescriptors - here's my service contract:

[ServiceContract]
public interface IChooser
{
[WebGet(UriTemplate="/Chooser/RegisteredDevices")]
[OperationContract]
List<DeviceDescriptor> RegisteredDevices();

[WebGet(UriTemplate = "/Chooser/Ping")]
[OperationContract]
string Ping();
}

Well, it sort of works, except that in the XML output, the property names don't come out right, it looks like the serializer is using the "unutterable names" of the auto-generated backing fields instead of the property names. My output comes out like this:

<DeviceDescriptor>
  <_x003C_DeviceId_x003E_k__BackingField>Pipe.Dome</_x003C_DeviceId_x003E_k__BackingField> 
  <_x003C_DisplayName_x003E_k__BackingField>Pipe diagnostic tool</_x003C_DisplayName_x003E_k__BackingField> 
</DeviceDescriptor>

So, is there a way out of this? Why doesn;t WCF use the property names?

+1  A: 

It uses reflection to get them IIRC, if you want more control you should try to use a DataContract, it allows you to specify the exact names (using [DataMember(Name = "DeviceID")]). See also the documentation on DataMemberAttribute

Anteru
That did it. I had to put a [DataContract] attribute on the class and then [DataMember(Name="xxx")] attributes on the properties, shazamm.It's a bit smelly though. I wonder why WCF finds the supposedly-private auto-implemented backing fields and uses them instead of the public property names? The C# compiler must be doing some dodgy code generation.
Tim Long
As I said, it probably uses reflection to get the location where the property value is stored, not the name of the property itself; but I'm no expert on WCF, so you should maybe ask a question how WCF actually discovers the names ;)
Anteru
(you've fully answered the main question; but to follow up on the comments I touch briefly on those points below)
Marc Gravell
+4  A: 

To add some detail to the question you ask in the comments... in the good old (3.0) days WCF/DataContractSerializer was pretty strict. If your type wasn't explicitly marked as a [DataContract] (or IXmlSerializable for fallback) then it wouldn't serialize. It used the members marked as [DataMember], using the explicit name on the attribute if specified, or the member-name otherwise. Life was good.

More recently, the code was changed allowing "regular" types to be serialized - meaning: those without data-contracts. It does this using the same approach as BinaryFormatter uses - i.e. it works at the field-level. This is IMO bad:

  • fields are the implementation, not the contract
  • it is brittle if you change... anything!
  • it doesn't hold much water against obfuscation
  • something as innocent as switch to/from an auto-prop break it (auto-props have truly bizarre field names, that can't be emulated in regular C#)

I know why this was tempting (to allow WCF to transport arbitrary types), but every sinew in my body says this was a net loss. It is much better to make people use the right tools (data-contracts) than it is to let their broken classes work. In the original 3.0, there would have been an exception thrown telling you how to fix this correctly: mark it as a [DataContract] and tell it which [DataMember]s you want to serialize.

See also: Obfuscation, serialization and automatically implemented properties

Marc Gravell
I have to agree with you -- not only is this change bad in practice but "feels" wrong as it goes against the opt-in philosophy that has been present for so long.
Tuzo