WCF Datacontractserializer not being used. Guid is string - wcf

I Have the following basicHttpBinding in my service config. When I consume this service any guids are serialised as strings. On closer inspection of the reference file i see that my classes and objects are being serialised with the system.xml.serialization not the datacontractserializer.
I have another service in the same project which shares the endpoint behaviours & configs and this service treats the guids as expected.
Any pointers greatly appreciated!
<service behaviorConfiguration="DefaultServiceBehaviour" name="ExtranetService.Repositories.Client.Submission.PSPSubmission.GoalRepository.GoalRepositoryService">
<endpoint address="basic"
behaviorConfiguration="basicBehaviorConfig"
binding="basicHttpBinding"
bindingConfiguration="basicBindingConfig"
name="basicEndpoint"
bindingName="basicBinding"
contract="ExtranetService.Repositories.Client.Submission.PSPSubmission.GoalRepository.GoalRepositoryService"
listenUriMode="Explicit" />
<host>
<baseAddresses>
<add baseAddress="https://blah.com/Repositories/client/submission/pspsubmission/GoalRepositoryService.svc"/>
</baseAddresses>
</host>
</service>
<basicHttpBinding>
<binding name="basicBindingConfig"
closeTimeout="00:05:00"
maxBufferPoolSize="2147483646" maxBufferSize="2147483646"
maxReceivedMessageSize="2147483646">
<readerQuotas maxDepth="32"
maxStringContentLength="2147483646"
maxArrayLength="2147483646" />
<security mode="Transport">
<message clientCredentialType="Certificate" />
</security>
</binding>
</basicHttpBinding>
<serviceBehaviors>
<behavior name="DefaultServiceBehaviour">
<serviceMetadata />
<serviceDebug />
<dataContractSerializer />
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="basicBehaviorConfig">
<dataContractSerializer />
</behavior>
</endpointBehaviors>
<System.CodeDom.Compiler.GeneratedCodeAttribute("System.Xml", "4.0.30319.34234"), _
System.SerializableAttribute(), _
System.Diagnostics.DebuggerStepThroughAttribute(), _
System.ComponentModel.DesignerCategoryAttribute("code"), _
System.Xml.Serialization.XmlTypeAttribute([Namespace]:="http://schemas.datacontract.org/2004/07/ExtranetService.Repositories.Client.Submission.PSPSubmission.GoalRepository")> _
Partial Public Class GetGoalByIdRequestObject
Inherits Object
Implements System.ComponentModel.INotifyPropertyChanged
Private idField As String
<System.Xml.Serialization.XmlElementAttribute(Order:=0)> _
Public Property Id() As String
Get
Return Me.idField
End Get
Set
Me.idField = value
Me.RaisePropertyChanged("Id")
End Set
End Property
Public Event PropertyChanged As System.ComponentModel.PropertyChangedEventHandler Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged
Protected Sub RaisePropertyChanged(ByVal propertyName As String)
Dim propertyChanged As System.ComponentModel.PropertyChangedEventHandler = Me.PropertyChangedEvent
If (Not (propertyChanged) Is Nothing) Then
propertyChanged(Me, New System.ComponentModel.PropertyChangedEventArgs(propertyName))
End If
End Sub
End Class
Edit
This is code from another service in the same project showing the guids as expected
<System.Diagnostics.DebuggerStepThroughAttribute(), _
System.CodeDom.Compiler.GeneratedCodeAttribute("System.Runtime.Serialization", "4.0.0.0"), _
System.Runtime.Serialization.DataContractAttribute(Name:="GetClientRecommendationListRequestObject", [Namespace]:="http://schemas.datacontract.org/2004/07/ExtranetService.Repositories.Client.Submi"& _
"ssion.PSPSubmission"), _
System.SerializableAttribute()> _
Partial Public Class GetClientRecommendationListRequestObject
Inherits Object
Implements System.Runtime.Serialization.IExtensibleDataObject, System.ComponentModel.INotifyPropertyChanged
<System.NonSerializedAttribute()> _
Private extensionDataField As System.Runtime.Serialization.ExtensionDataObject
<System.Runtime.Serialization.OptionalFieldAttribute()> _
Private CLNT_CLIENT_IDField As System.Guid
<Global.System.ComponentModel.BrowsableAttribute(false)> _
Public Property ExtensionData() As System.Runtime.Serialization.ExtensionDataObject Implements System.Runtime.Serialization.IExtensibleDataObject.ExtensionData
Get
Return Me.extensionDataField
End Get
Set
Me.extensionDataField = value
End Set
End Property
<System.Runtime.Serialization.DataMemberAttribute()> _
Public Property CLNT_CLIENT_ID() As System.Guid
Get
Return Me.CLNT_CLIENT_IDField
End Get
Set
If (Me.CLNT_CLIENT_IDField.Equals(value) <> true) Then
Me.CLNT_CLIENT_IDField = value
Me.RaisePropertyChanged("CLNT_CLIENT_ID")
End If
End Set
End Property
Public Event PropertyChanged As System.ComponentModel.PropertyChangedEventHandler Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged
Protected Sub RaisePropertyChanged(ByVal propertyName As String)
Dim propertyChanged As System.ComponentModel.PropertyChangedEventHandler = Me.PropertyChangedEvent
If (Not (propertyChanged) Is Nothing) Then
propertyChanged(Me, New System.ComponentModel.PropertyChangedEventArgs(propertyName))
End If
End Sub
End Class

GUIDs aren't a valid XML data type so they can't be used as a member type in the XSD schema. No matter which serializer you use, if you want to use one of the Http bindings, GUIDs will be serialized as strings and defined as strings in the schema.
Any proxy generated from this schema will create a string-typed property because that's what the XSD defines.
The duplicate question shows how to pass the GUID as a byte array, although this will hurt interoperability.
Another option is to put all data contract classes in a separate library that is shared by both client and server code. The classes in the DTO assembly will be used when generating the proxy instead of creating new ones.
Another alternative is to add another property to the proxy DTO class eg IdAsGuid() that parses the string value and returns a GUID. All proxy classes are partial so you can add new properties without modifying the generated files

Related

Extension method not being recognized

I've got a web application project in VB.NET and am trying to add an extension method for use in in a view.
Extensions.vb
Imports System.Runtime.CompilerServices
Namespace MyApp
Public Module Extensions
<Extension()> _
Public Function GetValOrDefault(ByVal dict As Dictionary(Of String, String), ByVal key As String, ByVal defaultVal As String) As String
Dim val As String
If (dict.TryGetValue(key, val)) Then
Return val
End If
Return defaultVal
End Function
End Module
End Namespace
View.cshtml
#Code
Dim msgs As Dictionary(Of String, String) = New Dictionary(Of String, String)
msgs("Foo") = "Bar"
Dim val As String = msgs.GetValOrDefault("Foo", "Bar")
End Code
However, this doesn't work, showing the following error:
'GetValOrDefault' is not a member of 'System.Collections.Generic.Dictionary(Of String, String)'.
This is a fairly straightforward extension method, and I'm not sure why it's not being picked up. Also tried adding an even simpler extension taking just a string and returning a string, with the same problem, so it seems like the extension methods aren't being picked up.
Tried compiling, also with no luck. Note, the module is in the same project as the view, but I don't think that should make any difference (I've done this in C# projects with no problems).
Is there a step or something else I'm missing that needs to be done to get this to work?
It's an old question, but, in case others are also scratching their heads, make sure you check that the Build Action of the file containing the extension is set to Compile. After two hours of going quietly insane and wondering why the extension only worked in certain places, I realised it was set to Content. A quick change to Compile and it worked. :)
Did you include the namespace "MyApp" in your web.config within the views folder?
<system.web.webPages.razor>
<host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
<pages pageBaseType="System.Web.Mvc.WebViewPage">
<namespaces>
<add namespace="System.Web.Mvc" />
<add namespace="System.Web.Mvc.Ajax" />
<add namespace="System.Web.Mvc.Html" />
<add namespace="System.Web.Routing" />
<add namespace="MyApp"/>
</namespaces>
</pages>
</system.web.webPages.razor>
Note: After adding to your web.config you may have to close and re-open your view to have the extension be available with Intellisense.
The other option is to import your MyApp namespace directly into the view: #Import MyApp
You can define the type for the extension. I just tried it with no problems.
Public Module Extensions
<Runtime.CompilerServices.Extension()> _
Public Function GetValOrDefault(Of dict As Dictionary(Of String, String))(ByVal d As dict, ByVal key As String, ByVal defaultVal As String) As String
Dim val As String = ""
If (d.TryGetValue(key, val)) Then
Return val
End If
Return defaultVal
End Function
End Module
For me this was because my extension method relied on a reference that my calling project didn't contain.
This was made visible by calling the method statically which caused vs to bring up the error. Referencing the required dll fixed the issue and allowed me to call it as an extension.

NetTCP and binary transfer

I have a WCF Service with HTTP bindings which returns dataset on 500k size.
When using WCF default logging I can see the messages and data being transfered with each message
<system.serviceModel>
<!-- add trace logging -->
<diagnostics wmiProviderEnabled="true">
<messageLogging
logEntireMessage="true"
logMalformedMessages="true"
logMessagesAtServiceLevel="true"
logMessagesAtTransportLevel="true"
maxMessagesToLog="3000"
/>
</diagnostics>
....
<system.diagnostics>
<sources>
<source name="System.ServiceModel.MessageLogging">
<listeners>
<add type="System.Diagnostics.DefaultTraceListener" name="Default">
<filter type="" />
</add>
<add initializeData="c:\nettcpTestLOCALToTEST.xml" type="System.Diagnostics.XmlWriterTraceListener"
name="messages">
<filter type="" />
</add>
</listeners>
</source>
</sources>
</system.diagnostics>
Point is, I am looking for a way to reduce the traffic between server and client, and I have been told that NetTCP is transferring the data binary? Is that correct?
I have set up a test scenario with a NetTCPBinding and when I read the WCF on the client side, the Response Message includes the whole dataset schema and data in XML format. Is it just serialized so the can be written to a log, or was this message transfered binary?
Is the amount of data being transfered with a NetTCP binding smaller than with HTTPBinding? Is it text or binary?
thanks in advance
yes the message will be transfered binary but the Serializer (Datacontractserializer I assume) will serialize the data in XML format:
Use the DataContractSerializer class to serialize and deserialize instances of a type into an XML stream or document
DataContractSerializer
From the docu:
The NetTcpBinding generates a run-time communication stack by default, which uses transport security, TCP for message delivery, and a binary message encoding. This binding is an appropriate system-provided choice for communicating over an Intranet.
NetTcpBinding MSDN
If you opt to implement ISerializable you can use WCF too but you have to implement an DataContractResolver to resolve the types: if the client "knows" the Types (for example you put them into a dll and add them to the client-app) you can use the following example-code (sorry I only have this in F# around but you should find it easy to translate)
This should yield the serialization in more compact form.
type internal SharedTypeResolver() =
inherit System.Runtime.Serialization.DataContractResolver()
let dict = new Xml.XmlDictionary()
override this.TryResolveType(t : Type, declaredT : Type, knownTypeResolver : System.Runtime.Serialization.DataContractResolver, typeName : Xml.XmlDictionaryString byref, typeNamespace : Xml.XmlDictionaryString byref) =
typeNamespace = dict.Add(t.Assembly.FullName)
typeName = dict.Add(t.FullName)
true
override this.ResolveName(typeName : string, typeNamespace : string, declaredType : Type, knownTypeResolver : System.Runtime.Serialization.DataContractResolver) =
let res = knownTypeResolver.ResolveName(typeName, typeNamespace, declaredType, null)
if res = null then Type.GetType(typeName + ", " + typeNamespace) else res
PS: found the same in C#:
public class SharedTypeResolver : DataContractResolver
{
#region Overrides of DataContractResolver
///
/// Override this method to map a data contract type to an xsi:type name and namespace during serialization.
///
///
/// true if mapping succeeded; otherwise, false.
///
/// The type to map.The type declared in the data contract.The known type resolver.The xsi:type name.The xsi:type namespace.
public override bool TryResolveType(Type type, Type declaredType, DataContractResolver knownTypeResolver, out XmlDictionaryString typeName, out XmlDictionaryString typeNamespace)
{
if (!knownTypeResolver.TryResolveType(type, declaredType, null, out typeName, out typeNamespace))
{
var dict = new XmlDictionary(); // nice trick to get the right type for typeName
if (type != null)
{
typeNamespace = dict.Add(type.Assembly.FullName);
typeName = dict.Add(type.FullName);
}
else
{
typeNamespace = dict.Add("noAss");
typeName = dict.Add("noType");
}
}
return true;
}
///
/// Override this method to map the specified xsi:type name and namespace to a data contract type during deserialization.
///
///
/// The type the xsi:type name and namespace is mapped to.
///
/// The xsi:type name to map.The xsi:type namespace to map.The type declared in the data contract.The known type resolver.
public override Type ResolveName(string typeName, string typeNamespace, Type declaredType, DataContractResolver knownTypeResolver)
{
return knownTypeResolver.ResolveName(typeName, typeNamespace, declaredType, null) ??
Type.GetType(typeName + ", " + typeNamespace);
}
(Please note: stackoverflow don't like the assignmentoperator "<-" from F# and i don't know how to circumvent - therefore I used "=")
oh well - I guess I have to say how to add those resolvers to your host:
private static void AddResolver(OperationDescription operationDescription)
{
if (operationDescription == null)
throw new ArgumentNullException();
var serializationBehavior = operationDescription.Behaviors.Find();
if (serializationBehavior == null)
{
serializationBehavior = new DataContractSerializerOperationBehavior(operationDescription);
operationDescription.Behaviors.Add(serializationBehavior);
}
serializationBehavior.DataContractResolver = new SharedTypeResolver();
}
use this with:
var contrDescription = _host.Description.Endpoints[0].Contract;
var description= contrDescription.Operations.Find("MyServiceMethod");
AddResolver(description);
replacing "MyServiceMethod" by the name of your service-method (on call per method or you iterate over all of them)

When passing an object as an argument to a method, string properties are populated, but integers are null

I'm not sure WCF being involved is significant or not.
I have a class and method exposed to a client asp.net app. The class looks like
<DataContract()> _
Public Class Class1
Private v_string As String
Private v_integer As Integer
Public Sub New()
v_string = ""
v_integer = -1
End Sub
<DataMember()> _
Public Property P_String() As String
Get
Return v_string
End Get
Set(ByVal value As String)
v_string = value
End Set
End Property
<DataMember()> _
Public Property P_Integer() As Integer
Get
Return v_integer
End Get
Set(ByVal value As Integer)
v_integer = value
End Set
End Property
End Class
The method is declared as
<OperationContract()> _
Function GetStuff(ByVal bar As Class1) As String
In the client code I create an instance of Class1 and set the values for v_string and v_integer, but using Wireshark to look at the xml being sent to the server only a value for v_string is being sent as part of Class1. I'm guessing this is because it considers the value of v_integer to be null/not set. Here is an example of the client code.
Dim MyService as New Service1.ServiceClient
Dim test as New Service1.Class1
test.P_integer = 1
test.P_string = "hello"
Dim result as String = MyService.GetStuff(test)
I'm guessing this is a problem with how different types are passed/used since Integer is a intergral type and String is a class, but can't seem to work out what to do to fix the problem.
WCF will only ever transmit data - you cannot expose a "method" via WCF.
WCF is a serialized messaging system - only serialized data travels between client and server - there is no other connection (no "remote object" or something like that) going on.
But both string and int should be handled no problem....
Maybe you are stumbling over this behavior?? I don't fully understand where your problem is, too - what are you expecting, and what are you seeing instead?? What does your service method actually do in its implementation??
Update: when I do the same thing as you have, and then use WCF Tracing to see what's happening, this is the request going into the service:
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Header>
<To s:mustUnderstand="1" xmlns="http://schemas.microsoft.com/ws/2005/05/addressing/none">http://localhost:8433/Services/GetStuff</To>
<Action s:mustUnderstand="1" xmlns="http://schemas.microsoft.com/ws/2005/05/addressing/none">http://tempuri.org/IGetStuffService/GetStuff</Action>
</s:Header>
<s:Body>
<GetStuff xmlns="http://tempuri.org/">
<data xmlns:a="http://schemas.datacontract.org/2004/07/wcf_test"
xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<a:P_Integer>5</a:P_Integer>
<a:P_String>Where to go for holidays...</a:P_String>
</data>
</GetStuff>
</s:Body>
</s:Envelope>
So what is it you're not seeing that should be there??..... I clearly see both the integer and the string value in that SOAP message - as expected.
Could it be you're not seeing your value of 1 because that's hidden away in the byte stream somewhere?? Try specifying some other value, like 4711067 or something - that won't get lost in your message....
To enable WCF tracing, put these two sections into your web.config or app.config of your WCF service:
<system.diagnostics>
<sources>
<source name="UserTraceSource" switchValue="Warning, ActivityTracing" >
<listeners>
<add name="xml"
type="System.Diagnostics.XmlWriterTraceListener"
initializeData="C:\logs\UserTraces.svclog" />
</listeners>
</source>
</sources>
<trace autoflush="true" />
</system.diagnostics>
(make sure the C:\logs directory exists before hand!!), and
<system.serviceModel>
<diagnostics>
<messageLogging maxMessagesToLog="30000"
logEntireMessage="true" logMalformedMessages="true"
logMessagesAtServiceLevel="false"
logMessagesAtTransportLevel="true">
<filters>
<clear/>
</filters>
</messageLogging>
</diagnostics>
</system.serviceModel>
Now you'll get *.svclog files in C:\logs - open Windows Explorer, double-click on them, and you should be taken into the WCF Trace Viewer for analysis.
A client of ours is having the same problem and we have not found a fix, the encapsulated a WCF service in a vb.net 4.0 dll and then from thier exe they showed me from thier machine they are passing strings and integers, with a packet sniffer on my server, the only soap xml tags i see are the string tags.
I have written a client in c# 4.0 and it works fine, even from thier machine.
Michael Evanchik
This doesn't quite make sense to me:
<DataMember()> _
Public Property P_Integer() As Integer
Get
Return v_integer
End Get
Set(ByVal value As Integer)
v_string = value
End Set
End Property
Shouldn't that Set function be:
v_integer = value
A colleague of mine found that everthing works properly we change the decorations for the data members from:
<DataMember()>_
Public Property P_Integer() As Integer
...
End Property
To:
<DataMember(IsRequired:=True)> _
Public Property P_Integer() As Integer
...
End Property
for all value types. Class types such as string work fine without this.

Why is WCF serializing a Enum as a string?

Code on the server
<DataContract(Namespace:="http://schema.aam.us.com/2010/6", Name:="TradeStatus")>
Public Enum TradeStatus
NewOrder = 100
SendToProvider = 101
ProviderSubmitted = 102
ProviderAccepted = 103
ExecutionPending = 104
Executed = 105
TicketsCreated = 106 'TERMINAL STATE
End Enum
<DataContract(Namespace:="http://schema.aam.us.com/2010/6", Name:="StatusUpdate")> _
Public Class StatusUpdate
Public Sub New(ByVal tradeStatus As TradeStatus, ByVal additionalInformation As String)
Me.TradeStatus = tradeStatus
Me.AdditionalInforamtion = additionalInformation
End Sub
<DataMember(IsRequired:=True)> _
Public Property AdditionalInforamtion() As String
<DataMember(IsRequired:=True)> _
Public Property TradeStatus() As TradeStatus
End Class
Generated code
<System.Diagnostics.DebuggerStepThroughAttribute(), _
System.CodeDom.Compiler.GeneratedCodeAttribute("System.Runtime.Serialization", "4.0.0.0"), _
System.Runtime.Serialization.DataContractAttribute(Name:="StatusUpdate", [Namespace]:="http://schema.aam.us.com/2010/6"), _
System.SerializableAttribute()> _
Partial Public Class StatusUpdate
Inherits Object
Implements System.Runtime.Serialization.IExtensibleDataObject, System.ComponentModel.INotifyPropertyChanged
<System.NonSerializedAttribute()> _
Private extensionDataField As System.Runtime.Serialization.ExtensionDataObject
Private AdditionalInforamtionField As String
Private TradeStatusField As String
<Global.System.ComponentModel.BrowsableAttribute(false)> _
Public Property ExtensionData() As System.Runtime.Serialization.ExtensionDataObject Implements System.Runtime.Serialization.IExtensibleDataObject.ExtensionData
Get
Return Me.extensionDataField
End Get
Set
Me.extensionDataField = value
End Set
End Property
<System.Runtime.Serialization.DataMemberAttribute(IsRequired:=true)> _
Public Property AdditionalInforamtion() As String
Get
Return Me.AdditionalInforamtionField
End Get
Set
If (Object.ReferenceEquals(Me.AdditionalInforamtionField, value) <> true) Then
Me.AdditionalInforamtionField = value
Me.RaisePropertyChanged("AdditionalInforamtion")
End If
End Set
End Property
<System.Runtime.Serialization.DataMemberAttribute(IsRequired:=true, EmitDefaultValue:=false)> _
Public Property TradeStatus() As String
Get
Return Me.TradeStatusField
End Get
Set
If (Object.ReferenceEquals(Me.TradeStatusField, value) <> true) Then
Me.TradeStatusField = value
Me.RaisePropertyChanged("TradeStatus")
End If
End Set
End Property
Public Event PropertyChanged As System.ComponentModel.PropertyChangedEventHandler Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged
Protected Sub RaisePropertyChanged(ByVal propertyName As String)
Dim propertyChanged As System.ComponentModel.PropertyChangedEventHandler = Me.PropertyChangedEvent
If (Not (propertyChanged) Is Nothing) Then
propertyChanged(Me, New System.ComponentModel.PropertyChangedEventArgs(propertyName))
End If
End Sub
End Class
Enums are serialized by default. Like primites and Collection classes you do not need to mark them with [DataContract]. However, that does not mean that WCF does not let you customize the serialization behavior, so in the spirit of interoperability you can change how the enum will be serialized. As part of that customizability if you mark it with DataContract but do not mark EnumMembers you are changing the default serialization scheme. See more on Enum serialization here Enum Serialization
EDIT: Got to thinking a bit more about this and now I started wondering about the underlying cause... turns out it is the WSDL's fault.
By default if you don't put [DataContract] then WCF by default serializes the enum as if it had the [DataContract] and [EnumMembers] attribute. So if you take the following example
[DataContract]
public enum FileType {
[EnumMember]
Text,
[EnumMember]
Pdf,
[EnumMember]
Word
}
it will generate the following WSDL
<xs:simpleType name="FileType">
<xs:restriction base="xs:string">
<xs:enumeration value="Text" />
<xs:enumeration value="Pdf" />
<xs:enumeration value="Word" />
</xs:restriction>
</xs:simpleType>
<xs:element name="FileType" nillable="true" type="tns:FileType" />
So now if you take out the [EnumMember] attributes like so
[DataContract]
public enum FileType {
Text,
Pdf,
Word
}
your WSDL will look like this:
<xs:simpleType name="FileType">
<xs:restriction base="xs:string" />
</xs:simpleType>
<xs:element name="FileType" nillable="true" type="tns:FileType" />
So the second one looks just like the first one except without the enumeration elements. Now what is the difference between the second one and just a WSDL describing a simple string? None. That is why the WCF Proxy gen gives you a string instead of the Enum.
Simple - this is the way it's meant to work.
XML Schema has no concept of an enum, in the sense of a name/value pair. The closest it has is the ability to indicate that a particular simple type may have one of several values - string values in this case.
Note that ASMX web services and the XML Serializer do exactly the same thing.
OK, Jonathan and I are both right and both wrong.
When serializing an enum, WCF adds .NET-specific information to the XML Schema. This permits another .NET implementation to treat the enum as an enum, complete with preservation of the enum values.
However, no other platform is going to understand this information. As a result, an enum will simply be treated as a string which can only take on one of several values.
If you include the DataContract attribute, then you need to tag at least one value with the EnumMember attribute. Otherwise it can't see any of the values and turns the whole field into a string.
If you do not include the DataContract attribute, then you don't need the EnumMember attribute either.
EDIT: Example of correct code
<DataContract(Namespace:="http://schema.aam.us.com/2010/6", Name:="TradeStatus")>
Public Enum TradeStatus
<EnumMember> NewOrder = 100
<EnumMember> SendToProvider = 101
<EnumMember> ProviderSubmitted = 102
<EnumMember> ProviderAccepted = 103
<EnumMember> ExecutionPending = 104
<EnumMember> Executed = 105
<EnumMember> TicketsCreated = 106 'TERMINAL STATE
End Enum
Because there is a defined Name for that specific value.
When there is not a defined name for a given value, then the serializer will write out an integer and the deserializer will read it just fine:
TradeStatus didntMakeCompileCutoffDate = (TradeStatus)dbRecord.TS; // value 999
It would be nice if the serializer allowed an option to write a particular enum field as an integer because this would allow the list of valid enum names to increase without having to recompile the deserializer code. Currently the deserializer will throw an exception (and reject the whole contract) due to the unrecognized enum name.
It may be possible to work around this issue by using the [EnumMember(Value = "123")] and specify the equivalent integers for each of the enum names known at compile time. From MSDN Enumeration Types in Data Contracts
A 2nd work around is to specify the contract enum as a property that uses an integer backing store and serialize the backing store. SO Link

In the WCF web programming model, how can one write an operation contract with an array of query string parameters (i.e. with the same name)?

Using the WCF web programming model one can specify an operation contract like so:
[OperationContract]
[WebGet(ResponseFormat = WebMessageFormat.Xml, UriTemplate = "SomeRequest?qs1={qs1}&qs2={qs2}")]
XElement SomeRequest1(string qs1, string qs2);
Now if we had to make a contract that accepts an array of parameters with the same name (in this case qs1) contract like so...
[OperationContract]
[WebGet(ResponseFormat = WebMessageFormat.Xml, UriTemplate = "SomeRequest?qs1={qs1}&qs1={qs2}")]
XElement SomeRequest2(string qs1, string qs2);
We get the error message at run time when we make the invocation to the method:
the query string must have 'name=value' pairs with unique names. Note that the names are case-insensitive. See the documentation for UriTemplate for more details.
How does one define an HTTP service that exposes a resource with an array of parameters without resorting to a loosey-goosey interface?
I've implemented a simple custom QueryStringConverter so that you can make qs1 an string[] then have the query string variable be comma delimited (e.g. http://server/service/SomeRequest?qs1=val1,val2,val3,val4)
[OperationContract]
[WebGet(ResponseFormat = WebMessageFormat.Xml,
UriTemplate = "SomeRequest?qs1={qs1}")]
XElement SomeRequest2(string[] qs1);
First you need a class that inherits from WebHttpBehavior so that we can inject our custom QueryStringConverter:
public class CustomHttpBehavior : System.ServiceModel.Description.WebHttpBehavior
{
protected override System.ServiceModel.Dispatcher.QueryStringConverter GetQueryStringConverter(System.ServiceModel.Description.OperationDescription operationDescription)
{
return new CustomQueryStringConverter();
}
}
Then our CustomQueryStringConverter that handles string[] parameters:
public class CustomQueryStringConverter : System.ServiceModel.Dispatcher.QueryStringConverter
{
public override bool CanConvert(Type type)
{
if (type == typeof(string[]))
{
return true;
}
return base.CanConvert(type);
}
public override object ConvertStringToValue(string parameter, Type parameterType)
{
if (parameterType == typeof(string[]))
{
string[] parms = parameter.Split(',');
return parms;
}
return base.ConvertStringToValue(parameter, parameterType);
}
public override string ConvertValueToString(object parameter, Type parameterType)
{
if (parameterType == typeof(string[]))
{
string valstring = string.Join(",", parameter as string[]);
return valstring;
}
return base.ConvertValueToString(parameter, parameterType);
}
}
The last thing you need to do is create a behavior configuration extension so that the runtime can get an instance of the CustomWebHttpBehavior:
public class CustomHttpBehaviorExtensionElement : System.ServiceModel.Configuration.BehaviorExtensionElement
{
protected override object CreateBehavior()
{
return new CustomHttpBehavior();
}
public override Type BehaviorType
{
get { return typeof(CustomHttpBehavior); }
}
}
Now we add the element to our configuration extensions so that our CustomWebHttpBehavior is used, we use the Name of that extension instead of <webHttp /> in our behavior:
<system.serviceModel>
<services>
<service name="NameSpace.ServiceClass">
<endpoint address="" behaviorConfiguration="MyServiceBehavior"
binding="webHttpBinding" contract="NameSpace.ServiceClass" />
</service>
</services>
<behaviors>
<endpointBehaviors>
<behavior name="MyServiceBehavior">
<customWebHttp/>
</behavior>
</endpointBehaviors>
</behaviors>
<extensions>
<behaviorExtensions>
<add name="customWebHttp" type="NameSpace.CustomHttpBehaviorExtensionElement, MyAssemblyName" />
</behaviorExtensions>
</extensions>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" />
</system.serviceModel>
You can now also extend your CustomQueryStringConverter to handle other types that the default one doesn't, such as nullable value types.
NOTE: There is a bug logged at microsoft connect that directly relates to this code. The code does not actually work in almost all circumstances where you attempt to Query Convert different types.
http://connect.microsoft.com/VisualStudio/feedback/details/616486/bug-with-getquerystringconverter-not-being-called-by-webservicehost#tabs
Please make sure you read this carefully before wasting hours of your time creating custom REST query string converters that cannot possibly work. (Applies to Framework 4.0 and below).
To respond to your comment on my other answer:
You can do a wildcard parameter at the end of the querystring like
[WebGet(ResponseFormat = WebMessageFormat.Xml,
UriTemplate = "SomeRequest?qs1={*qs1}")]
XElement SomeRequest2(string qs1);
This way the qs1 string parameter will be the whole raw querystring after the qs1=, you could then parse that manually in your code.
The QueryStringConverter relies on the formatting of the querystring so doing something exactly how you want is not possible without possibly rewriting QueryStringConverter instead of the little overrides we did in the other answer.
From MSDN:
Wildcard segments must follow the following rules:
There can be at most one named wildcard segment for each template string.
A named wildcard segment must appear at the right-most segment in the path.
A named wildcard segment cannot coexist with an anonymous wildcard segment within the same template string.
The name of a named wildcard segment must be unique.
Named wildcard segments cannot have default values.
Named wildcard segments cannot end with “/”.
Be aware that in WCF 3.5 you must specify the full qualified assembly name in:
<extensions>
<behaviorExtensions>
<add name="customWebHttp" type="NameSpace.CustomHttpBehaviorExtensionElement, MyAssemblyName, NOT SUFFICIENT HERE" />
</behaviorExtensions>
</extensions>
Just like this: SampleService.CustomBehavior, SampleService, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
Otherwise you will get exception:
Configuration Error
Description: An error occurred during the processing of a configuration file required to service this request. Please review the specific error details below and modify your configuration file appropriately.
Parser Error Message: Invalid element in configuration. The extension name 'CustomWebHttp' is not registered in the collection at system.serviceModel/extensions/behaviorExtensions.