How to setup a DataContract to match the complex XML input in WCF 4.0 REST Service - wcf

I have a XML structure like this:
<Message>
<Messagehead>
<OSType>Android</OSType>
<RouteDest>SiteServerName</RouteDest>
<ActionType>Enroll</ActionType>
</Messagehead>
<MessageBody>
<Raw>
<![CDATA[OrienginalMessageContent]]>
</Raw>
</MessageBody>
</Message>
and I want upload this XML to WCF 4.0 my rest service:
public string Enroll(Message instance)
{
// TODO: Add the new instance of SampleItem to the collection
return "success";
}
the Message is a DataContract type, I setup it like below:
[DataContract(Namespace = "")]
public class Message
{
[DataMember]
public MessageHead MessageHead { get; set; }
[DataMember]
public MessageBody MessageBody { get; set; }
}
public class MessageHead
{
public OSType OSType { get; set; }
public string RouteDest { get; set; }
public Action Action { get; set; }
}
public class MessageBody
{
public string RawRequestContent { get; set; }
}
but when I get the Message instance from the server side, all the property is null, except the OSType, can anybody tell me why? How could I solve this problem?

Besides being a really bad name for a class (since it's already used in the WCF runtime), your Message class also has some flaws:
<Message>
<Messagehead>
....
</Messagehead>
Your <Messagehead> has a lower-case h in the middle - yet your class defines it to be upper case:
[DataContract(Namespace = "")]
public class Message
{
[DataMember]
public MessageHead MessageHead { get; set; }
This will not work - case is important and relevant in a WCF message! If your XML has a lower-case h, so must your DataContract class!
Your XML also requires a <Raw> tag inside your <MessageBody>
<MessageBody>
<Raw>
<![CDATA[OriginalMessageContent]]>
</Raw>
</MessageBody>
yet your data contract doesn't respect that:
public class MessageBody
{
public string RawRequestContent { get; set; }
}
Again - those don't line up! Names are important - and they must match between your XML representation of the message, and the C# class representing that message.....

Related

WCF with abstract base class with implemented interfaces does not serialize properly?

On the service side I have an abstract base class like so:
[DataContract]
public abstract class EntityBase : IObjectState, IDatabaseMetaData
{
[NotMapped]
[DataMember]
public ObjectState ObjectState { get; set; }
#region IDatabaseMetaData Members
[DataMember] public DateTime InsertDatetime { get; set; }
[DataMember] public int InsertSystemUserId { get; set; }
[DataMember] public DateTime? UpdateDatetime { get; set; }
[DataMember] public int? UpdateSystemUserId { get; set; }
public virtual SystemUser InsertSystemUser { get; set; }
public virtual SystemUser UpdateSystemUser { get; set; }
#endregion
}
Here is an implementing class (data contract):
[DataContract(Namespace = Constants.MyNamespace)]
public class AccountClass : EntityBase
{
[DataMember] public int AccountClassId { get; set; }
[DataMember] public string AccountClassCode { get; set; }
[DataMember] public string AccountClassDesc { get; set; }
}
On the client side I have essentially duplicated contracts. Here is the Client.AccountClass:
public class AccountClass : ObjectBase
{
private int _accountClassId;
private string _accountClassCode;
private string _accountClassDesc;
public int AccountClassId
{
get { return _accountClassId;}
set
{
if (_accountClassId == value) return;
_accountClassId = value;
OnPropertyChanged(() => AccountClassId);
}
}
public string AccountClassCode
{
get { return _accountClassCode; }
set
{
if (_accountClassCode == value) return;
_accountClassCode = value;
OnPropertyChanged(() => AccountClassCode);
}
}
public string AccountClassDesc
{
get { return _accountClassDesc; }
set
{
if (_accountClassDesc == value) return;
_accountClassDesc = value;
OnPropertyChanged(() => AccountClassDesc);
}
}
}
..and here is the parts of ObjectBase that matter:
public abstract class ObjectBase : IObjectState, IDatabaseMetaData
{
public ObjectState ObjectState { get; set; }
#region IDatabaseMetaData Members
public DateTime InsertDatetime { get; set; }
public int InsertSystemUserId { get; set; }
public DateTime? UpdateDatetime { get; set; }
public int? UpdateSystemUserId { get; set; }
#endregion
}
When I debug the service in my WcfMessageInspector.BeforeSendReply, I can see the message correctly sending the IObjectState and IDatabaseMetaData values. However, on the client side, they are always null (or default values). I have tried using KnownTypes, applying the namespace to the abstract class. The only way I can serialize everything correctly is to get rid of the interfaces and base classes all together and put the properties directly on the Client/Server AccountClass object. What am I missing here? Thanks.
Update 1
This seems to be a namespace thing. If I move my EntityBase and ObjectBase into the same CLR Namespace, everything works (with no KnownType attributes). In my client contract's AssemblyInfo.cs file I have this:
[assembly: ContractNamespace(Constants.MyNamespace, ClrNamespace = "Project.Name.Client.Entities")]
I tried adding ContractNamespaces here to no avail. Like I said, unless the EntityBase and ObjectBase are in the same namespace, it won't work. However, this is a problem for me because it creates a circular reference, unless I move a lot of stuff around.
Any idea how I can see what the full data contract (namespaces, DataMembers, etc) looks like just before/after serialization on the client/server? I tried intercepting the OnSerializing event without much luck. Thanks again.
This was a namespace issue.
I explicitly add the correct namespace to all parties involved and everything works great. One thing I notice is that the ContractNamespace's ClrNamespace in your AssemblyInfo.cs file should match the AssemblyTitle. Also, putting more than one ContractNamespace in the AssemblyInfo.cs does nothing. For example, I was doing this:
[assembly: ContractNamespace(Constants.MyNamespace, ClrNamespace = "Company.Project.Client.Entities")]
[assembly: ContractNamespace(Constants.MyNamespace, ClrNamespace = "Company.Project.Client.Entities.Core")]
Any POCO in the Company.Project.Client.Entities.Core would not serialize correctly until I explicitly put the DataContract namespace on it like so
[DataContract(Namespace = Constants.MyNamespace)]
public class SomeObject
{
[DataMember] public string SomeProperty { get; set; }
//..etc
}
Alternatively, I could have restructured the project so SomeObject was in the Company.Project.Client.Entities namespace and that would have worked.
Finally, the most helpful thing to debugging this was looking at the WSDL, and then using a custom IDispatchMessageInspector to see the actual messages AfterReceiveRequest and BeforeSendReply. Hopefully this helps someone.

Different SOAP Headers in request/response using Message Contracts

I'm building some services which have a common header. This header has a certain layout in the request, and a different one in the response (i.e., there are two classes).
However, when I add a reference or use svcutil, the proxy is generated with the same header in both request and response types.
For instance:
[MessageContract]
class Contract<THeader, TBody>
{
[MessageHeader] public THeader Header { get; set; }
[MessageBodyMember] public TBody Body { get; set; }
}
class MyRequestHeader
{
public string RequestorId { get; set; }
}
class MyResponseHeader
{
public string ErrorMessage { get; set; }
}
The OperationContract is something like:
[OperationContract]
public Contract<MyResponseHeader, ResponseBody> Process(Contract<MyRequestHeader, RequestBody> data);
The proxy becomes something like:
var client = new ...;
var header = new MyRequestHeader();
var body = new RequestBody();
**ResponseBody**
response = client.Process(ref header, body);
As you can see, the header (Request) is passed as ref; That probably means WCF is having this header as the same in request and response. And the MyResponseHeader disappears.
Anyone can shed some light on the subject?
There is something strange going on here.
I tried to reproduce your problem, and got the following results (i had to mark some types public, and added [DataContract] to your header classes).
Here is a view of the WSDL:
The generated code (svcutil 4.0.30319.18046) uses MyRequestHeader in the Response message as well:
This is caused by the following XSD:
As you can see there is only one instance generated for the "Header" class.
I tried creating types for the generic classes, as follows:
[MessageContract]
public abstract class Contract<THeader, TBody>
{
[MessageHeader]
public THeader Header { get; set; }
[MessageBodyMember]
public TBody Body { get; set; }
}
[DataContract(Name="RequestHeader")]
public class MyRequestHeader
{
public string RequestorId { get; set; }
}
[DataContract(Name = "ResponseHeader")]
public class MyResponseHeader
{
public string ErrorMessage { get; set; }
}
[MessageContract]
public class RequestContract : Contract<MyRequestHeader, string>
{ }
[MessageContract]
public class ResponseContract : Contract<MyResponseHeader, string>
{ }
[ServiceContract]
public interface IService1
{
[OperationContract]
ResponseContract Process(RequestContract data);
}
But that did not fix the problem, the generated client ResponseContract still is generated using a Header of type RequestHeader.
Even changing the service code to use two diffrent message contracts:
[DataContract(Name="RequestHeader")]
public class MyRequestHeader
{
public string RequestorId { get; set; }
}
[DataContract(Name = "ResponseHeader")]
public class MyResponseHeader
{
public string ErrorMessage { get; set; }
}
[MessageContract]
public class RequestContract<TBody>
{
[MessageHeader]
public MyRequestHeader Header { get; set; }
[MessageBodyMember]
public TBody Body { get; set; }
}
[MessageContract]
public class ResponseContract<TBody>
{
[MessageHeader]
public MyResponseHeader Header { get; set; }
[MessageBodyMember]
public TBody Body { get; set; }
}
[ServiceContract]
public interface IService1
{
[OperationContract]
ResponseContract<string> Process(RequestContract<string> data);
}
does not solve the problem:
Even removing all shared inheritance and generics as follows:
[DataContract(Name="RequestHeader")]
public class MyRequestHeader
{
public string RequestorId { get; set; }
}
[DataContract(Name = "ResponseHeader")]
public class MyResponseHeader
{
public string ErrorMessage { get; set; }
}
[MessageContract(WrapperName="RequestMessage")]
public class RequestContract
{
[MessageHeader]
public MyRequestHeader Header { get; set; }
[MessageBodyMember]
public string Body { get; set; }
}
[MessageContract(WrapperName = "ResponseMessage")]
public class ResponseContract
{
[MessageHeader]
public MyResponseHeader Header { get; set; }
[MessageBodyMember]
public string Body { get; set; }
}
[ServiceContract]
public interface IService1
{
[OperationContract]
ResponseContract Process(RequestContract data);
}
still results in the RequestHeader being used in the ResponseMessage.
I think the answer lies somewhere in this documentation:
WSDL Considerations
When generating a Web Services Description
Language (WSDL) contract from a service that uses message contracts,
it is important to remember that not all message contract features are
reflected in the resulting WSDL [sic]. Consider the following points: WSDL
cannot express the concept of an array of headers. When creating
messages with an array of headers using the
MessageHeaderArrayAttribute, the resulting WSDL reflects only one
header instead of the array.
The resulting WSDL document may not reflect some protection-level
information.
The message type generated in the WSDL has the same name as the class
name of the message contract type.
When using the same message contract in multiple operations, multiple
message types are generated in the WSDL document. The names are made
unique by adding the numbers "2", "3", and so on, for subsequent uses.
When importing back the WSDL, multiple message contract types are
created and are identical except for their names.

Send a list with appointments through WCF

I would like to send a list of Appointments through WCF. My Interface looks like this:
[ServiceContract]
public interface IServices
{
[OperationContract]
string addAppointments(List<Appointment> appointmentList);
}
If I call my WCF Service I'm always getting the following error:
Type 'Microsoft.Exchange.WebServices.Data.Appointment' cannot be
serialized. Consider marking it with the DataContractAttribute
attribute, and marking all of its members you want serialized with the
DataMemberAttribute attribute. See the Microsoft .NET Framework
documentation for other supported types.
My Service currently looks like this:
class Service : IServices
{
public string addAppointments(List<Appointment> appointmentList)
{
foreach (Appointment app in appointmentList)
{
Console.WriteLine(app.Organizer.Name);
}
return "true";
}
}
It's not your service that's at fault, it's the class your passing, Appointment.
Start by adding [DataContract] to your class. then [DataMember] to each of the properties you'd like to pass.
For example, if you started with:
public class Appointment{
public DateTime Date { get; set; }
public string Name { get; set; }
}
You can make it serializable by WCF's DataContractSerializer by adding those attributes:
[DataContract]
public class Appointment{
[DataMember]
public DateTime Date { get; set; }
[DataMember]
public string Name { get; set; }
}

Solve a circular reference in WCF from my POCOs

I have a couple of classes (for now) and I'm trying to clear up a circular reference between the two since it is killing WCF's serialization.
I am using EF with POCOs in a WCF REST service is that helps. I have simplified my problem down to bare bones for an easy example here:
[ServiceContract]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class Groups
{
[WebGet(UriTemplate = "")]
public Message GetCollection()
{
var message = new Message { Body = "Test message" };
var group = new Group { Title = "Title of group" };
message.Group = group;
group.Messages = new List<Message> { message };
return message;
}
}
public class Message
{
public string Body { get; set; }
public Group Group { get; set; }
}
[DataContract(IsReference = true)]
public class Group
{
public string Title { get; set; }
public ICollection<Message> Messages { get; set; }
}
I have added the [DataContract(IsReference = true)] to the Group class so that the circular reference is cleaned up however my returned results end up like this:
<Message xmlns="http://schemas.datacontract.org/2004/07/LmApi" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<Body>Test message</Body>
<Group z:Id="i1" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/"/>
</Message>
Where are the properties of the Group and how can I get them?
BritishDeveloper,
There are no properties associated with Group. That's why all you see is the ID of 1.
The reason is that as soon as you annotate the Group class with [DataContract(IsReference = true)], you are telling the DataContract serializer that it's no longer a POCO type. It's a DataContract type.
So, to serialize Group with properties, you now need to go ahead and annotate the Title and Message properties with DataMemberAttribute.
An alternative would be use the "preserveObjectReferences", which you can pass as a parameter to DataContractSerializer, DataContractSerializerOperationBehavior, and other classes.
Hope this helps!
I decided to make my own smaller classes that have a constructor that takes an entity and sets all of this lighterweight properties correctly.
Basically it is a very small copy of the class that has just the properties needed in the payload. (Obviously I have excluded the problem navigation properties)
This is similar to Circular References and WCF Here is my answer modified for this case
I had the same problem and resolved it by excluding the navigation property back to the parent from the DataContract
[DataContract]
public partial class Message
{
[DataMember]
public virtual string Body { get; set; }
[DataMember]
public virtual Group Group { get; set; }
}
[DataContract]
public partial class Group
{
[DataMember]
public virtual string Title { get; set; }
public virtual ICollection<Message> Messages {get; set;}
}

RIA DomainService not generating client code using DataContracts

I'm working on a Silverlight 4.0 application and am using RIA services. I have created a class on the server-side which has DataContract and DataMember attributes applied to it.
A DomainService exposes this class as a query result and as such, generates code for it on the client. But somehow it doesn't generate code for all properties of the class. Primitive properties of type guid, string, int, bool etc are generated just fine, but if I have a property of my own complex type, that property isn't created on the client.
Here's the class in question:
[DataContract]
[KnownType(typeof(SummaryGroup))]
public class SummaryDataSet
{
public SummaryDataSet()
{
}
[KeyAttribute]
[DataMember]
public Guid Guid { get; set; }
[DataMember]
public SummaryGroup SummaryGroup { get; set; }
}
The Guid property is created on the client just fine. The SummaryGroup property isn't created on the client. Here's the code for the SummaryGroup:
[DataContract]
public class SummaryGroup
{
public SummaryGroup()
{
}
[KeyAttribute]
[DataMember]
public Guid Guid { get; set; }
[DataMember]
public string Name { get; set; }
[DataMember]
public int Sequence { get; set; }
}
Both classes are in the same namespace.
Question: why isn't the SummaryGroup property of SummaryDataSet created on the client and what should I do to fix this?
WCF RIA cannot handle complex types, but you could try this:
[DataContract]
[KnownType(typeof(SummaryDataSet))]
public class SummaryDataSet
{
public SummaryDataSet()
{ }
[KeyAttribute]
[DataMember]
public Guid Guid { get; set; }
[DataMember]
[Association("SummarySet_SummaryGrp_FK", "Guid", "Guid")]
public SummaryGroup SummaryGroup { get; set; }
}
This gives RIA the information to connect from the SummaryDataSet to the SummaryGroup.
This assumes that you can request both the SummaryDataSet and SummaryGroup from the serverside service.