This is my first post.
I am building a restful wcf service using post to accept an XML message (truncated from the real one). I am having trouble getting WCF to parse the XML due to the way the message is using namespaces. I cannot change the format of the XML message.
I have tried various combinations of namespace attributes on the Service and datacontract but either get a parsing error or segments that are missing or NULL.
If I was able to change the message I can get it to work by either removing the namespace or by applying the namespace prefix to all the fields. Unfortunately, it is not possible to get the vendor to change the format of the message being sent.
Is there a way to get this to work with the message being sent.
Sample Request
<m:MYMESSAGE xmlns:m="my.report">
<MESSAGEHEADER>
<MESSAGETYPE>GoodReport</MESSAGETYPE>
<MESSAGEDATE>20160203134445</MESSAGEDATE>
<MESSAGEACTION>UPDATE</MESSAGEACTION>
</MESSAGEHEADER>
<PATIENT>
<LASTNAME>Last</LASTNAME>
<FIRSTNAME>First</FIRSTNAME>
<MIDDLENAME>Middlename</MIDDLENAME>
</PATIENT>
</m:MYMESSAGE>
Sample Incorrect Response
<MYMESSAGE xmlns="my.report" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<MESSAGEHEADER i:nil="true"/>
<PATIENT i:nil="true"/>
</MYMESSAGE>
WCF Code
[ServiceContract]
public interface IPDF
{
[OperationContract ]
[WebInvoke(Method = "POST",
ResponseFormat = WebMessageFormat.Xml,
RequestFormat = WebMessageFormat.Xml
)]
MYMESSAGE GetPdf(MYMESSAGE mymessage);
}
[DataContract(Name = "MYMESSAGE", Namespace = "my.report")]
public class MYMESSAGE
{
[DataMember (Name ="MESSAGEHEADER",Order=0) ]
public _MESSAGEHEADER MESSAGEHEADER { get; set; }
[DataMember(Name = "PATIENT", Order = 1)]
public _PATIENT PATIENT { get; set; }
}
[DataContract(Namespace = "my.report")]
public class _MESSAGEHEADER
{
[DataMember(Name = "MESSAGETYPE", Order = 0)]
public string MESSAGETYPE { get; set; }
[DataMember(Name = "MESSAGEDATE", Order = 1)]
public string MESSAGEDATE { get; set; }
}
You can use Message Contracts to create the shape of the message you need. For eaxample:
[MessageContract]
public class BankingTransaction
{
[MessageHeader] public Operation operation;
[MessageHeader(Namespace="http://schemas.contoso.com/auditing/2005")] public bool IsAudited;
[MessageBodyMember(Name="transactionData")] public BankingTransactionData theData;
})
WCF uses Message based on SOAP but WCF internals can hide this by converting inbound messages to SOAP and outbound messages to what ever transport protocol you are using.
You can ultimately create your own message formatter.
"Message formatters are the component which do the translation between CLR operations and the WCF Message object – their role is to convert all the operation parameters and return values (possibly via serialization) into a Message on output, and deconstruct the message into parameter and return values on input."
Related
I have the following class I'd like to send from my WCF (C#) service to my client (WPF):
[DataContract]
public class OutputAvailableEventArgs
{
[DataMember]
public int ID { get; set; }
[DataMember]
public string Message { get; private set; }
[DataMember]
public bool IsError { get; private set; }
public OutputAvailableEventArgs(int id) : this(id, false, "") { }
public OutputAvailableEventArgs(int id, string output) : this(id, false, output) { }
public OutputAvailableEventArgs(int id, bool isError, string output)
{
ID = id;
IsError = isError;
Message = output;
}
}
It's used by the service as follows:
var channel = OperationContext.Current.GetCallbackChannel<IClientCallback>();
channel.OutputAvailable(new OutputAvailableEventArgs(1, false, "some message"));
At the client side, the members get their default values.
I tried marking them with IsRequired attribute but now the OutputAvailable at the client is not called. The code at the service side seems to run smoothly (I didn't notice anything with the debugger).
How can I transfer a DataContract class with WCF while maintaining the members' values?
(I saw solutions that suggested to use OnSerialized and OnDeserialized but I don't need just a default constructor.)
I saw many different solutions for this problem. For other people's sake I'll write some of them down + what worked for me:
It seems that in some cases specifying the items' order solves the problem. Please see this SO question for full details.
If it's some default initialization you're after, you can use OnSerialized and OnDeserialized methods to call your initialization methods.
I also tried using the IsRequired attribute on my DataMembers but still didn't get my objects.
What worked for me was adding NameSpace property in the DataContract attribute. Apparently, In order to have the contracts be considered equal, you must set the Namespace property on the DataContract to the same value on both sides.
I have WCF service that uses raw messages (Message class).
1) Service side:
[DataContract]
public class Person
{
[DataMember]
public int Id { get; set; }
[DataMember]
public string FirstName { get; set; }
[DataMember]
public string LastName { get; set; }
}
[ServiceContract]
public interface ITestService
{
[OperationContract(Action = TestService.RequestAction3)]
void AddNewPerson(Message newPerson);
public void AddNewPerson(Message newPerson)
{
Person personToAdd = newPerson.GetBody<Person>();
Employees.Persons.Add(personToAdd);
}
2) Client side:
TestServiceClient client = new TestServiceClient();
String RequestAction3 = "http://localhost:4249/Message_RequestAction3";
TestService.Person person = new TestService.Person
{
Id = 6,
FirstName = "Aleksey",
LastName = "Alekseyev"
};
Message request3 = Message.CreateMessage(MessageVersion.Default, RequestAction3, person);
string soapRequest = request3.ToString();
client.AddNewPerson(request3);
What's the problem here? I have Person class (data contract) on service side that is placed in TestService namespace: TestService.Person. Everything is fine on service side. But after I added service reference to client side by using "Add Service Reference..." option in VS2008, there's no such a type (TestService.Person) on client side. What I did to resolve this issue? I've simply copied the file with original data contract (TestService.Person) on client side, created object of Person type and passed it to the service method.
My question is - did I do it in correct way or there is another way to do this?
Thank you in advance.
Goran
Because Person class is not exposed in none of your service contracts their information is not shared via service metadata. That's why you get an error on the client side. If you copy the classes to your client with the same namespace that will do.
However a better solution is to place Person class in another assembly and reference this assembly from your client.
I have a WCF operation contract which looks like this:
public void SavePersons(List<Person> list, bool IsSelected)
{
}
I am passing it a strongly typed list of Person objects (List<Person>) in my client. However, I am getting a bad request 400 message when calling the service. What am I doing wrong?
May I suggest you create you create a contract to encapsulate the parameters like so:
public void SavePersons(PersonCollectionContract Request)
{
...
}
[DataContract]
public class PersonCollectionContract
{
[DataContract]
public List<Person> People { get; set; }
[DataContract]
public bool IsSelected { get; set; }
}
[DataContract]
public class Person
{
...
}
I was facing a similar problem in passing a List<Health> of class Health type as a parameter to a wcf service method. I created a data contract in wcf service as below:
[DataContract]
public class Health
{
...
}
Defined a method in wcf service class such as:
public string GetData(List<Health> healthValues)
In my client application, while configuring/updating the service, I followed these steps:
Add/Update URL
Under Data Type (in Advanced), selected option, Collection type: System.Collection.Generic.List
And finally, I created a list and added the code in client as follows:
List<WcfService.Health> listHealth = new List<WcfService.Health>();
WcfService.Health h = new WcfService.Health();
.
.
listHealth.Add(h);
WcfService.Service1Client s = new WcfService.Service1Client();
string str = s.GetData(listHealth);
This solved my purpose and I was able to send the data as a list through wcf service.
I'm trying to create a WCF SOAP service that has a service method that accepts bare parameters in the body but I just cant make it happen. At the moment, the method name element is being created under the body. I'm trying to use ws-addressing so that the method name is part of the header and the parameters are the direct children of the body.
Here's my service implementation:
[SoapDocumentService(Use = SoapBindingUse.Literal, ParameterStyle = SoapParameterStyle.Bare)]
public class Service1 : IService1
{
[SoapDocumentMethod(Use=SoapBindingUse.Literal, ParameterStyle = SoapParameterStyle.Bare)]
public void DoWork([XmlElement(Namespace = "http://www.contoso.com",
IsNullable = true)] MyClass wrapper)
{
}
}
[XmlRoot(Namespace = "http://www.contoso.com")]
public class MyClass
{
public int Value { get; set; }
public string MyProperty { get; set; }
}
[ServiceContract]
public interface IService1
{
[OperationContract]
void DoWork(MyClass wrapper);
}
The above implementation generates the soap client below. But I'm trying to have the wrapper element as the direct child on body (trying to remove DoWork). From what I've read, decorating the svc method to use bare parameters should remove the service method name (DoWork) and use ws-addressing.
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:tem="http://tempuri.org/" xmlns:web="http://schemas.datacontract.org/2004/07/WebApplication2">
<soap:Header/>
<soap:Body>
<tem:DoWork> <!-- I want to remove this svc method name element -- >
<tem:wrapper> <!-- I want this to be under the body -->
<!--Optional:-->
<web:MyProperty>?</web:MyProperty>
<!--Optional:-->
<web:Value>?</web:Value>
</tem:wrapper>
</tem:DoWork>
</soap:Body>
</soap:Envelope>
I've followed the guide from msdn to decorate the service method. MSDN Link
I think you should drop the wrapper. in .net 2 this would work, wcf should be similar:
[WebMethod]
[SoapDocumentMethod(ParameterStyle=SoapParameterStyle.Bare)]
public String EchoString(String s, String s1)
{
return s;
}
I had to create message contracts wrapper for the MyClass and specify the message body.
[MessageContract]
public class MyWrapper
{
[MessageBodyMember]
public int Value { get; set; }
[MessageBodyMember]
public string MyProperty { get; set; }
}
I am aware that there is a similar question here with no solution.
I'm working on a WCF streaming service over HTTP.
Here are my MessageContract
[MessageContract]
public class FileRequest
{
#region Message Header
[MessageHeader(MustUnderstand = true)]
public Credential Credentials { get; set; }
#endregion
#region Message body
[MessageBodyMember(Order = 1)]
public FileInfo FileInfo { get; set; }
#endregion
#region Ctor
// ...
#endregion
}
[MessageContract]
public class FileRequestResponse
{
#region Message Header
[MessageHeader(MustUnderstand = true)]
public FileInfo FileHeader { get; set; }
[MessageHeader(MustUnderstand = true)]
public OperationResult<bool> OperationResult { get; set; }
#endregion
#region Message Body
[MessageBodyMember]
public Stream FileStream { get; set; }
#endregion
#region Constructor
// ...
#endregion
}
Here is my ServiceContract
[ServiceContract(Namespace = "https://service.contract.example.com")]
public interface IUpdateService
{
[OperationContract(Action = "GetUpdates")]
OperationResult<List<FileInfo>> GetUpates(ApplicationInfo applicationInfo, Credential credential);
[OperationContract(Action = "GetFile")]
FileRequestResponse FileRequest(FileRequest fileRequest);
}
Now the question is why I am getting this error:
// CODEGEN: Generating message
contract since message FileRequest has
headers
When I add my service reference. The end result is that the service contract wraps the FileRequest operation into a wrapper which I do not want.
public FileInfo FileRequest(Credential Credentials, FileInfo, out OperationResult<bool> OperationResult, out System.IO.Stream FileStream)
NOTE:
I have not checked the "Always generate message contracts" in the service reference.
Set [MessageContract(IsWrapped=true)] for all the message contracts in the service and then try generating the proxy .
You might want to try to use the IsWrapped attribute on the message contract:
[MessageContract(IsWrapped=false)]
Not 100% sure which one you'll need (true or false) but that's one of the options you could try.
Also, another observation: I think it's a bit risky to have a method called FileRequest and a message contract which also is called FileRequest.
The generally accepted best practive would be to have a method GetFile, a request message for that called GetFileRequest and a response message type GetFileResponse. Do not use the same names for different things.