Exposing a service to external systems - How should I design the contract? - wcf

I know this question is been asked before here but still I'm not sure what to select.
My service will be called from many 3 party system in the enterprise. I'm almost sure the information the service will collect (MyBigClassWithAllInfo) will change during the products lifetime. Is it still a good idea to expose objects?
This is basically what my two alternatives:
[ServiceContract]
public interface ICollectStuffService
{
[OperationContract]
SetDataResponseMsg SetData(SetDataRequestMsg dataRequestMsg);
}
// Alternative 1: Put all data inside a xml file
[DataContract]
public class SetDataRequestMsg
{
[DataMember]
public string Body { get; set; }
[DataMember]
public string OtherPropertiesThatMightBeHandy { get; set; } // ??
}
// Alternative 2: Expose the objects
[DataContract]
public class SetDataRequestMsg
{
[DataMember]
public Header Header { get; set; }
[DataMember]
public MyBigClassWithAllInfo ExposedObject { get; set; }
}
public class SetDataResponseMsg
{
[DataMember]
public ServiceError Error { get; set; }
}
The xml file would look like this:
<?xml version="1.0" encoding="utf-8"?>
<Message>
<Header>
<InfoAboutTheSender>...</InfoAboutTheSender>
</Header>
<StuffToCollectWithAllTheInfo>
<stuff1>...</stuff1>
</StuffToCollectWithAllTheInfo>
</Message>
Any thought on how this service should be implemented?
Thanks Larsi

If the information is going to change during the lifetime but you are under the gun to get something up there I would simply create a message that had a list of variant types that could be sent along with a message type version number.. Your bus can view the version number and route it appropriately. That way customers that are stuck with older versions of the message will not have to change the message interface they are using.

Related

.NET WebAPI Serialization k_BackingField Nastiness

When i serialize the following:
[Serializable]
public class Error
{
public string Status { get; set; }
public string Message { get; set; }
public string ErrorReferenceCode { get; set; }
public List<FriendlyError> Errors { get; set; }
}
I get this disgusting mess:
<ErrorRootOfstring xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/Printmee.Api">
<_x003C_Errors_x003E_k__BackingField>
An exception has occurred. Please contact printmee support
</_x003C_Errors_x003E_k__BackingField>
<_x003C_LookupCode_x003E_k__BackingField>988232ec-6bc9-48f3-8116-7ff7c71302dd</_x003C_LookupCode_x003E_k__BackingField>
</ErrorRootOfstring>
What gives? How can i make this pretty? JSON responses also contain the k_BackingField
By default you don't need to use neither [Serializable] nor [DataContract] to work with Web API.
Just leave your model as is, and Web API would serialize all the public properties for you.
Only if you want to have more control about what's included, you then decorate your class with [DataContract] and the properties to be included with [DataMember] (because both DCS and JSON.NET respsect these attributes).
If for some reason, you need the [Serializable] on your class (i.e. you are serializing it into a memory stream for some reason, doing deep copies etc), then you have to use both attributes in conjunction to prevent the backing field names:
[Serializable]
[DataContract]
public class Error
{
[DataMember]
public string Status { get; set; }
[DataMember]
public string Message { get; set; }
[DataMember]
public string ErrorReferenceCode { get; set; }
[DataMember]
public List<FriendlyError> Errors { get; set; }
}
There is a more general solution: you can configure the Json Serializer to ignore the [Serializable] attribute, so that you don't have to change the attributes in your classes.
You should make this configuration change in the application start, i.e. in Global.asax Application_Start event:
var serializerSettings =
GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings;
var contractResolver =
(DefaultContractResolver)serializerSettings.ContractResolver;
contractResolver.IgnoreSerializableAttribute = true;
You can also make other changes to the Json serialization, like specifying formats for serializing dates, and many other things.
This will only apply to the Web API JSON serialization. The other serializations in the app (Web API XML serialization, MVC JsonResult...) won't be affected by this setting.
Try using DataContract instead of Serializable for marking your class. For more detail on why, look at this good blog post on serializing automatic properties.
The [DataContract] attributes dosn't worked for me, so it was not an option.
XmlSerializer ignores [XmlAttribute] in WebApi
The above resolution solved it for me.
GlobalConfiguration.Configuration.Formatters.XmlFormatter.UseXmlSerializer = true;

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; }
}

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

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.....

WCF client side List<>

I got a WCF service with a method (GetUserSoftware)to send a List to a client.
the software I have defined like this:
[DataContract]
public class Software
{
public string SoftwareID { get; set; }
public string SoftwareName { get; set; }
public string DownloadPath { get; set; }
public int PackageID { get; set; }
}
the method is going through my db to get all software availeble to the clien, and generates a list of that to send back to the client.
problem is i on the client side the list is turned into an array. and every item in that array dont contain any of my software attributs.
i have debugged my way through the server side. and seen that the list its about to send is correct. with the expected software and attributs in it.
any one know how to work around this or know what i can do ?
Did you forget [DataMemeber] attribute on your properties?
When you use DataContract attribute for a type you have to use DataMember attribute for each property or field you want to serialize and transfer between service and client. Collections are by default created as arrays. If you don't like it you can change this behavior in Add Service Reference window -> Advanced settings where you can select which collection type should be used.
First off, each of the properties that you want to serialize should have the [DataMember] attribute:
[DataContract]
public class Software
{
[DataMember]
public string SoftwareID { get; set; }
[DataMember]
public string SoftwareName { get; set; }
[DataMember]
public string DownloadPath { get; set; }
[DataMember]
public int PackageID { get; set; }
}
Second, the translation to an Array would be handled by the client, not the server.
You can mantain List instead of array on the clien when you add the Service Reference: click the "advanced" button and change the collection type to the one you want.
I was suffering with same problem and now I solved it! It was a ServiceKnownType problem. If you have a in known type loader we have to add runtime Type like;
Type aaa = Type.GetType("System.Collections.Generic.List`1[[ProjectName.BusinessObjects.Bank, ProjectName.BusinessObjects, Version=4.0.0.0, Culture=neutral, PublicKeyToken=null]]");
knownTypes.Add(aaa);
Anyone having same problem can try this. It's working in my environment!

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;}
}