I have searched multiple resources on the internet but all I have encountered is 'Hello World' and 'Calculator' like examples explaining Message Contract in WCF. I would like to know the practical usage of Message Contract in real-world enterprise applications and also when it should be preferred over Data Contract. Any help regarding this would be highly appreciated.
A real live scenario is for example, when a Java client has a WSDL file which doesn’t change (technical reason). So the Java client has a fixed WSDL file. Then the web service (.net) must have to provide this method with the exact name, namespace, action and message string.
Example Java client:
<operation name="remove">
<input wsam:Action="remove" message="tns:remove"/>
<output wsam:Action="removeResponse" message="tns:removeResponse"/>
</operation>
Default is in the web service automatic generated, something like this:
<wsdl:operation name="remove">
<wsdl:input message="tns:IService1_remove_InputMessage" wsaw:Action="http://tempuri.org/IService1/remove"/>
<wsdl:output message="tns:IService1_remove_OutputMessage" wsaw:Action="http://tempuri.org/IService1/removeResponse"
</wsdl:operation>
Then server and client get a mismatch error, because the client cannot find the method. To solve this problem you have to change the action, replyAction and message string. The first and second one you must change with:
<OperationContractAttribute(Action:="remove", name:="remove" ReplyAction:="removeResponse")> _
Function remove(key As string) As Boolean
new WSDL file(server):
<wsdl:operation name="remove">
<wsdl:input message="tns:IService1_remove_InputMessage" wsaw:Action="remove"/>
<wsdl:output message="tns:IService1_remove_OutputMessage" wsaw:Action="removeResponse"
</wsdl:operation>
Now you still get the same mismatch error, because the message string isn’t the same. To solve this you need Message Contract. This enable you to manipulate the WSDL file/SOAP Message.
For this the syntax of the method changes inside the IService class.
<OperationContractAttribute(Action:="remove", name:="remove" ReplyAction:="removeResponse")> _
Function remove(key As remove) As removeResponse
and the Message Contract definite the ‘type’:
<MessageContract()> _
Public Class removeResponse
Private return1 As Boolean()
<DataMember(Name:="return")> _
Public Property returnP() As Boolean ()
Get
Return Me.return1
End Get
Set(ByVal value As Boolean ())
Me.return1 = value
End Set
End Property
End Class
<MessageContract()> _
Public Class remove
Private key1 As String()
<DataMember(Name:="key")> _
Public Property keyP() As String ()
Get
Return Me.key1
End Get
Set(ByVal value As String ())
Me.key1 = value
End Set
End Property
End Class
Now the communication between client and server is ok.
WSDL(server):
<wsdl:operation name="remove">
<wsdl:input message="remove" wsaw:Action="remove"/>
<wsdl:output message="removeResponse" wsaw:Action="removeResponse"
</wsdl:operation>
Related
I've added a service reference to my WCF webservice which generated all of my datacontract objects. I'm using BasicHttpBinding. Using a partial class, I've made one of these objects inherit from another class that adds some properties to it. Now it throws an error when making a call to the service:
Test method CP.Exg2010.Tests.UnitTest1.TestWCF threw exception:
System.ServiceModel.Dispatcher.NetDispatcherFaultException: The
formatter threw an exception while trying to deserialize the message:
There was an error while trying to deserialize parameter
http://tempuri.org/:RunResult. The InnerException message was 'Error
in line 1 position 283. 'Element' 'CommandResult' from namespace
'uri://mycomp.corp/line/exg2010' is not expected. Expecting element
'_EngineTracingData'.'. Please see InnerException for more details.
---> System.Runtime.Serialization.SerializationException: Error in
line 1 position 283. 'Element' 'CommandResult' from namespace
'uri://mycomp.corp/line/exg2010' is not expected. Expecting element
'_EngineTracingData'.
CommandResult is a property that is part of the WSDL. _EngineTracingData is the private field used by a property in a base class.
<XmlIgnore()> <SoapIgnore()> <Newtonsoft.Json.JsonIgnore()> _
Private _EngineTracingData As String = String.Empty
<XmlIgnore()> <SoapIgnore()> <Newtonsoft.Json.JsonIgnore()>
Public Property EngineTracingData As String Implements Interfaces.ICPMasterBaseInfo.EngineTracingData
Get
Return Me._EngineTracingData
End Get
Set(ByVal value As String)
Me._EngineTracingData = value
End Set
End Property
I read something about the deserialization happening in alphabetic order, which would explain why _EngineTracingData is first. But, that field/property shouldn't even be used in deserialization!
Any help would be appreciated!
Ah, I found it!
Adding
<NonSerialized()>
to the private fields in the base class fixed my issue!
I have developed a WCF Service Application hosted in IIS 7.5 targeting .NET 3.5 configured with only a basicHttpBinding endpoint. The OperationContract signature consists of a Composite type where one of its properties is a custom type. When this property is not initialized by the consuming client, the deserializer on the service appears to ignore the property leaving it null/nothing. I would like to initialize this custom type if it null/nothing and I realize that WCF serialization doesn't call constructors so I've used the deserialization callback. The callback executes and intializes the type but immediately after the callback completes this property returns to null/nothing. Stepping through the code, the ExtensionData property setter executes immediately after the callback and it is at this point where I notice that the property is reset to null/nothing. What am I missing? Here is my sample code
<DataContract(Name:="Request")> _
Public Class Request
Implements IExtensibleDataObject
<DataMember(Name:="MyCustomType")>
Public MyCustomType As CustomType
Private _ExtensionDataObject As ExtensionDataObject
Public Overridable Property ExtensionData() As ExtensionDataObject Implements IExtensibleDataObject.ExtensionData
Get
Return _ExtensionDataObject
End Get
Set(value As ExtensionDataObject)
_ExtensionDataObject = value
End Set
End Property
<OnDeserializing()>
Sub OnDeserializing(c As StreamingContext)
Me.myCustomType = New CustomType()
End Sub
End Class
If the client didn't initialize the property, then it's value is actually Nothing, and the fact that it is null/Nothing will be present in the serialized Request object. So prior to the deserialization happening, your OnDeserializing method is called, and it initializes the variable; but then the deserialization happens, and since there is a value for the property (which happens to be Nothing/null), it will override it.
I think what you want is to have an OnDeserializ*ed* callback, which will initialize the member after the deserialization happened, if it's value is Nothing:
<OnDeserialized()>
Sub OnDeserialized(ByVal c as StreamingContext)
If Me.myCustomType Is Nothing Then
Me.myCustomType = new CustomType()
End If
End Sub
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.
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
I am trying to make a Delphi client (Delphi 2006) to communicate with a service written using WCF. Service is damn simple with just one function. Technically like below:
[ServiceContract (Namespace = "http://www.company.com/sample/")]
public interface IService
{
[OperationContract]
string GetNumber (string name);
}
I have hosted this service on IIS and exposed it using basicHttpBinding with mex end point. I am able to use it in .NET clients.
I tried to run WSDLImp.exe and it generated a source code unit (btw, it generates wierd classes to encapsulate string type. Why cant it be same as Delphi string type?). When I try to call this service, I get the exception:
The message with Action '' cannot be processed at the receiver, due to a ContractFilter mismatch at the EndpointDispatcher. This may be because of either a contract mismatch (mismatched Actions between sender and receiver) or a binding/security mismatch between the sender and the receiver. Check that sender and receiver have the same contract and the same binding (including security requirements, e.g. Message, Transport, None).
I don't see any way to configure the Delphi Win32 client to changing the binding or security parameters. How can I fix this problem?
I've had the exact same problem. Delphi just has hard time importing the WSDL exposed by WCF. One solution is to add an ASMX wrapper to your service and use that with Delphi clients.
Here's an example:
[ServiceContract (Namespace = "http://www.company.com/sample/")]
public interface IService
{
[OperationContract]
string GetNumber (string name);
}
public class Service : IService
{
public string GetNumber (string name)
{
return Repository.GetNumber(name);
}
}
[WebService(
Namespace = "http://www.company.com/sample/",
Name = "wstest",
Description = "description")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class AsmxService : WebService
{
[WebMethod]
public string GetNumber(string name)
{
return Repository.GetNumber(name);
}
}
You need to look at the network traffic between the client and service to see what's going on. Alternatively, turn on WCF tracing on the service, possibly including message tracing. You should be able to see what's going on, in great detail.