WCF and interfaces - wcf

I need to have 2 families of classes (one on server and one on client side) which are identical in data structure but differs in behavior. Also I suppose that these fmailies will be enough big, thus I don't want to implement intermediate level of DTO and transformations into and from it.
I decided to move in following manner: declare shared assembly with declaration of data and services interfaces like these ones:
public interface ITest
{
string Title { get; set; }
int Value { get; set; }
}
public interface IService
{
ITest GetData();
}
Having these declarations I can implement these interfaces on server side for example basing on Entity Framework (data) and WCF (services). On the client side I can use for example Dependency Properties (data) and WCF (service).
When I started trying to implement this, I met several troubes.
First one was about server side of WCF - it simply do not want to work with interfaces as return parameters. Thanks to StackOverflow this issue was resolved like here
.
Next problem is that XML rendered by server side includes qulified assembly name of serialized on the server class.
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<GetDataResponse xmlns="http://tempuri.org/">
<Test z:Id="1" z:Type="Dist.Server.Model.Test" z:Assembly="Dist.Server, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" xmlns="http://schemas.datacontract.org/2004/07/Dist.Server.Model" xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/">
<Title z:Id="2">Test</Title>
<Value>123</Value>
</Test>
</GetDataResponse>
</s:Body>
</s:Envelope>
Thus during deserialization on client side there was an attempt to load this type. As this type is inaccessible on client side, I had to implement some kind of type mapping. I found that this is quite easy as NetDataContractSerializer used for serialization supports Binder property. Thus I override this property on client side and return correct value (hardcode in meantime, but it's OK for tests).
public class NetBinder : SerializationBinder
{
public override Type BindToType(string assemblyName, string typeName) {
var type = Type.GetType("Client.Test, Client, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null");
return type;
}
}
Now I have following picture:
- Server uses NetDataContractSerializer to serialize response. It uses actual value (calss) during serialization instead of type used in declaration of service (interface).
- Client side receives XML and starts deserialization. To resolve type, NetDataContractSerializer calls my Binder that returns correct type.
- NetDataContractSerializer creates instance of correct type and starts loading of its properties.
And here I got a trouble that I don't know how to resolve. Values of properties are not deserialized. It means that instance of class is created correctly (uninitialized instance created through reflection services), but all properties are in their default values (0 or null).
I tried to play with declaration of class on client side: mark it as [Serializable], implement ISerializable, etc., but nohing is helpful. NetDataContractSerializer requires class to be marked as [DataContract] or [Serializable]. First option leaves properties empty, second one causes exceptions like "</Test> is unexpected, expected is bla-bla-bla_Value_Ending_bla-bla-bla".
Does anybody have any suggestions on how to resolve this last step?
I can provide full sources for better understanding, but I don't know ifI can attach them here...
Thanks in advance.

You could have a look at frameworks like AutoMapper that would take care the transformation to and from DTO. This would make your life much easier.
Instead of using an interface why not create a base class containing only the data and inherit it on both sides by sharing the assembly containing this class. Playing around with the ServiceKnownType should help you fix the last issues.
You may also share the same base classes on both sides and implement the specific logic as extension methods.

Seems that problem was solved enough easily. I created own serializer and used it instead of NetDataContractSerializer. Code is quite simple:
public class MySerializer: XmlObjectSerializer
{
public override void WriteStartObject(XmlDictionaryWriter writer, object graph) {
}
public override void WriteObjectContent(XmlDictionaryWriter writer, object graph) {
var formatter = new XmlSerializer(graph.GetType());
formatter.Serialize(writer, graph);
}
public override void WriteEndObject(XmlDictionaryWriter writer) {
}
public override object ReadObject(XmlDictionaryReader reader, bool verifyObjectName) {
var realType = Type.GetType("Client.Test, Client, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"); //temporary solution
var formatter = new XmlSerializer(realType);
return formatter.Deserialize(reader);
}
public override bool IsStartObject(XmlDictionaryReader reader) {
return true;//temporary solution
}
}
I checked SOAP that goes from server to client and it's almost the same as NetDataSerializer renders. The only difference is in attribute xmlns="".
Kai, Johann thanksfor your tries.

Related

SerializationException when returning custom classes from a WCF service

I have the following classes...
public abstract class Fallible<T> {
}
public class Success<T> : Fallible<T> {
public Success(T value) {
Value = value;
}
public T Value { get; private set; }
}
The background to this can be found in a previous question of mine, but you don't need to read that post as the classes above are all that's needed to see the problem.
If I have a simplified WCF service call like this...
[OperationContract]
public Fallible<Patient> GetPatient(int id) {
return new Success<Patient>(new Patient {ID = 1,FirstName = "Jim",Surname = "Spriggs"});
}
...then when I try to call the service from the WPF app that consumes it (or the WCF test client), I get a CommunicationException exception...
There was an error while trying to serialize parameter :GetPatientResult. The
InnerException message was 'Type 'PhysioDiary.Entities.FallibleClasses.Success`1[[PhysioDiary.Entities.Patient,
PhysioDiary.Entities, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]'
with data contract name > 'SuccessOfPatient0yGilFAm:http://schemas.datacontract.org/2004/07/PhysioDiary.Entities.FallibleClasses'
is not expected. Consider using a DataContractResolver if you are using
DataContractSerializer or add any types not known statically to the list of
known types - for example, by using the KnownTypeAttribute attribute or by
adding them to the list of known types passed to the serializer.'. Please
see InnerException for more details.
...with an inner SerializationException exception of...
Type 'PhysioDiary.Entities.FallibleClasses.Success`1[[PhysioDiary.Entities.Patient,
PhysioDiary.Entities, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]'
with data contract name > 'SuccessOfPatient0yGilFAm:http://schemas.datacontract.org/2004/07/PhysioDiary.Entities.FallibleClasses'
is not expected. Consider using a DataContractResolver if you are using
DataContractSerializer or add any types not known statically to the list
of known types - for example, by using the KnownTypeAttribute attribute
or by adding them to the list of known types passed to the serializer.
I've tried adding [DataContract] to the class and [DataMember] to each property, as well as adding a [KnownType] attribute for all four classes involved, and adding a [ServiceKnownType] for each of them on the service contract, but nothing helps.
I've read countless answers to the same question, but have not found anything that works. My services return other custom classes, and they all get serialised without a problem.
Anyone able to explain what the problem is here? Please let me know if I've not supplied enough information.
Turns out all I needed to do was decorate the service method with [ServiceKnownType] attributes for the base type, and each derived type...
[OperationContract]
[ServiceKnownType(typeof(Fallible<Patient>)]
[ServiceKnownType(typeof(Success<Patient>)]
[ServiceKnownType(typeof(BadIdea<Patient>)]
[ServiceKnownType(typeof(Failure<Patient>)]
public Fallible<Patient> GetPatient(int id) {
return new Success<Patient>(new Patient {ID = 1,FirstName = "Jim",Surname = "Spriggs"});
}
Although it's a pain to have to add four attributes to every call, it works. I'd like to know if there is a way to combine them into one attribute, but at least I have a working service now.
Hope this helps someone.

how to set attributes in soap format WCF

how to set attributes for soap message:
for example my soap messg look like following
<doPaymentResult xmlns:a="http://schemas.datacontract.org/2004/07/MemoService" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<a:errors i:nil="true"/>
<a:messages>
<a:MessageEntity>
<a:codeField>Payment Request Successful</a:codeField>
<a:textField i:nil="true" xmlns:b="http://schemas.microsoft.com/2003/10/Serialization/Arrays"/>
</a:MessageEntity>
</a:messages>
<a:requestTrackCode>20130430T125904R14646</a:requestTrackCode>
<a:status i:nil="true"/>
</doPaymentResult>
</doPaymentResponse>
but i need a soap message which take attributes not elements
like following
<doPaymentResult xmlns:a="http://schemas.datacontract.org/2004/07/MemoService" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<a:errors i:nil="true"/>
<a:messages>
<a:MessageEntity codeField="Payment Request Successful">
some text here
</a:MessageEntity>
</a:messages>
<a:requestTrackCode>20130430T125904R14646</a:requestTrackCode>
<a:status i:nil="true"/>
</doPaymentResult>
</doPaymentResponse>
I am using datacontract in class.
It’s a common problem – you want to return an object from a WCF service as XML, but you either want, or need, to deliver some or all of the property values as XML Attributes instead of XML Elements; but you can’t because the DataContractSerializer doesn’t support attributes (you’re most likely to have seen this StackOverflow QA if you’ve done a web search). Most likely you’ve then migrated all your WCF service code to using the XmlSerializer (with all the XmlElement/XmlAttribute/XmlType attributes et al) – and you’ve cursed loudly.
Well, I’m here to rescue you, because it is possible – and the answer to the problem is actually inferred from the MSDN article entitled ‘Types supported by the Data Contract Serializer’.
The example I’m going to give is purely for illustration purposes only. I don’t have a lot of time, so work with me!
•Create a new Asp.Net WCF service application, you can use Cassini as your web server (probably easier – otherwise you might have to enable Asp.Net compatibility mode).
•Open the web.config and delete the element that was created for the new service.
•The interface and implementation model for this example is overkill. Move the [ServiceContract] and [OperationContract] declarations from the interface that was created for you new service to the class that was also created. Delete the interface.
•Open the .svc markup file and add the following at the end: Factory="System.ServiceModel.Activation.WebServiceHostFactory" – this enables the zero-configuration WCF model for this service (we’re going to create a RESTful service).
•Paste the following class declarations into your svc codebehind:
public interface IExampleData
{
string Description { get; set; }
string Name { get; set; }
int ID { get; set; }
}
public class ExampleData : IExampleData
{
public string Description { get; set; }
public string Name { get; set; }
public int ID { get; set; }
}
public class ExampleDataAttributed : ExampleData, IXmlSerializable
{
#region IXmlSerializable Members
public System.Xml.Schema.XmlSchema GetSchema()
{
return null;
}
public void ReadXml(System.Xml.XmlReader reader)
{
//implement if remote callers are going to pass your object in
}
public void WriteXml(System.Xml.XmlWriter writer)
{
writer.WriteAttributeString("id", ID.ToString());
writer.WriteAttributeString("name", Name);
//we'll keep the description as an element as it could be long.
writer.WriteElementString("description", Description);
}
#endregion
}
Just to demonstrate the point, the class that will be part-serialized to attributes simply derives from one that will be serialized as normal.
•Now add the following two methods to your service class:
[OperationContract]
[WebGet(UriTemplate = "/test1")]
public ExampleData Test1()
{
return new ExampleData() { ID = 1,
Name = "Element-centric",
Description =
"The contents of this item are entirely serialized to elements - as normal" };
}
[OperationContract]
[WebGet(UriTemplate = "/test2")]
public ExampleDataAttributed Test2()
{
return new ExampleData_Attributed() { ID = 2,
Name = "Mixed",
Description =
"Everything except this description will be serialized to attributes" };
}
Cover, and bake for 40 minutes (that is – Build it).
If you left your service as Service1.svc, then run it and open up IE and browse to http://localhost:[port of cassini]/test1
The result should look something like this:
<JSLabs.ExampleData
xmlns="http://schemas.datacontract.org/2004/07/ExampleNamespace"
xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<Description>
The contents of this item are entirely serialized to elements - as normal
</Description>
<ID>
1
</ID>
<Name>
Element-centric
</Name>
</JSLabs.ExampleData>
Now browse to http://localhost:[port of cassini]/test2
<JSLabs.ExampleDataAttributed id="2" name="Mixed"
xmlns="http://schemas.datacontract.org/2004/07/JobServe.Labs.Web">
<description>Everything except this description will be
serialized to attributes</description>
</JSLabs.ExampleDataAttributed>
It’s made a little less impressive by that nasty ‘orrible “xmlns=” attribute that the WCF data contract serializer automatically puts on the type – but, as you can see, the ‘ID’ and ‘Name’ properties have indeed been pushed out as attributes!
We could have made both methods return IExampleData and then used the KnownType attribute on that interface in order to get it to support either (according to what the code of the methods returned).
To support deserializing an object from the attributes, all you have to do is to implement the IXmlSerializable.ReadXml method.
Finally, as the aforementioned MSDN article says about the supported types – you should also be able to use XmlElement/XmlNode types as a way of representing XML directly – the DataContractSerializer, like in this case, take the short route and simply gets the Xml.
This also shouldn’t affect JSON formatting if you’re dual-outputting objects for either XML or JSON clients.
Check the source of this article

Why can't NServiceBus create a proxy for my message when using XML serialization?

I've defined a message, IGetQuote, for use with NServiceBus. It's shown below, with the other types on which it depends;
public interface IGetQuote : ICommand
{
IRisk Risk { get; set; }
}
public interface IRisk
{
IProposer Proposer { get; }
}
public interface IProposer : IDriver
{
string Postcode { get; set; }
}
public interface IDriver
{
string Name { get; set; }
void DoSomething();
}
When using the fluent API to get an IBus, I get the following exception when calling CreateBus();
System.TypeLoadException: "Method 'DoSomething' in type 'Contracts.IProposer_impl' from assembly 'Contracts_impl, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' does not have an implementation.":"Contracts.IProposer__impl"
From what I understand of the way NServiceBus works, it's trying to create types that implement IGetQuote and any associated interfaces such as IProposer, in order to make the XML serialization work when the IGetQuote message is sent.
If I adjust my interfaces so that IProposer no longer inherits from IDriver, the problem goes away. My initial conclusion was that the DoSomething method must be the problem, but if I make IProposer inherit IDriver again, and move the DoSomething method onto IProposer, the exception is not thrown (although I do get a warning message "Interface IProposer contains methods and therefore cannot be mapped. Be aware that non-mapped interfaces can't be used to send messages").
So it seems like the exception occurs when you have a method declared on an interface, and that interface is extended by one of the types upon which the message type depends. It's almost like the dynamic proxy generation doesn't account for interface inheritance.
Does anyone know what is going on here?
The messages sent by NSB define the contract or schema between two endpoints. This does not include behaviour, but simply data. NSB will handle interface inheritance, but rightfully so it warns you about adding behvaviour to your interfaces.

WCF with shared objects and derived classes on client

I have a WCF service and I'm sharing types with a client in a shared assembly.
If the client create a derived class will it be possible to pass back the derived type to the service so that I can read the added properties through reflection ?
I tried but having issues with KnownTypes since the service don't know how to deserialize the derived type.
[Serializable]
public abstract class Car : ICar
{........
//on the client :
[Serializable]
public class MyCar : Car
{......
when passing myCar to Service I get the exception complaining about knownType but I cant add this on the server since I wont know what the client will be sending through and I want to handle extra properties through reflection.
Possible to register client types as knowntypes at runtime ?
Is this maybe the solution ?
http://blogs.msdn.com/b/sowmy/archive/2006/03/26/561188.aspx
This is not possible. Both service and client has to know what types will be sent in messages. If you want to use known type you have to define that relation to parent type on the service.
Why do you need to know added properties on the server?
I think there is a way.
I vaguely remember that when I studied WCF, I met ExtensionData which should be a mechanism to get everything that does not match the serialization of the class. for example, if you enable ExtensionData and you are in this situation
//Server
public class GenericRQ
{
public string GenericProperty {get;set;}
}
public Service GenericService
{
Public void GenericMethod(GenericRQ RQ)
{
}
}
// client
Public class MoreSpecificRQ : GenericRQ
{
public string SpecificProperty {get;set;}
}
At
Public void GenericMethod(GenericRQ RQ)
{
// the serializer adds automatically in RQ.ExtensionData everything that has come and that does not match the class GenericRQ.
}
On how to enable ExtensionData you to easily search on the web

Is it possible to serialize objects without a parameterless constructor in WCF?

I know that a private parameterless constructor works but what about an object with no parameterless constructors?
I would like to expose types from a third party library so I have no control over the type definitions.
If there is a way what is the easiest? E.g. I don't what to have to create a sub type.
Edit:
What I'm looking for is something like the level of customization shown here: http://msdn.microsoft.com/en-us/magazine/cc163902.aspx
although I don't want to have to resort to streams to serialize/deserialize.
You can't really make arbitrary types serializable; in some cases (XmlSerializer, for example) the runtime exposes options to spoof the attributes. But DataContractSerializer doesn't allow this. Feasible options:
hide the classes behind your own types that are serializable (lots of work)
provide binary formatter surrogates (yeuch)
write your own serialization core (a lot of work to get right)
Essentially, if something isn't designed for serialization, very little of the framework will let you serialize it.
I just ran a little test, using a WCF Service that returns an basic object that does not have a default constructor.
//[DataContract]
//[Serializable]
public class MyObject
{
public MyObject(string _name)
{
Name = _name;
}
//[DataMember]
public string Name { get; set; }
//[DataMember]
public string Address { get; set; }
}
Here is what the service looks like:
public class MyService : IMyService
{
#region IMyService Members
public MyObject GetByName(string _name)
{
return new MyObject(_name) { Address = "Test Address" };
}
#endregion
}
This actually works, as long as MyObject is either a [DataContract] or [Serializable]. Interestingly, it doesn't seem to need the default constructor on the client side. There is a related post here:
How does WCF deserialization instantiate objects without calling a constructor?
I am not a WCF expert but it is unlikely that they support serialization on a constructor with arbitrary types. Namely because what would they pass in for values? You could pass null for reference types and empty values for structs. But what good would a type be that could be constructed with completely empty data?
I think you are stuck with 1 of 2 options
Sub class the type in question and pass appropriate default values to the non-parameterless constructor
Create a type that exists soley for serialization. Once completed it can create an instance of the original type that you are interested in. It is a bridge of sorts.
Personally I would go for #2. Make the class a data only structure and optimize it for serialization and factory purposes.