WCF: Returning a derived object for a contract with base object (DataContractResolver) - wcf

I have have a WCF derived/base contract issue. I have a server interface/contract that returns a BaseThing object:
[OperationContract]
BaseThing Get_base_thing();
The server that implements this has a DerivedThing (derived from BaseThing) and wants to return this as a BaseThing. How to tell WCF that I only want to transport the BaseThing part of DerivedThing?
If in Get_base_thing I just return a reference to a DerivedThing then I get a SerializationException server side.
I think I need to define a DataContractResolver and I looked at the MSDN article Using a Data Contract Resolver but this is not 100% clear (to me a least).
How should my DataContractResolver look to tell WCF to only transport the base part of the derived object I pass it?
Is there some way to do this more simply just with KnownType attribue?

KnownType will not resolve this issue.
It sounds as if you have a serious divergence between the object model you're using at the server and the service contracts you're using. There seems to be 3 possible solutions:
1) Data Contract Resolver as you've identified to make it automatic across all your operations. There are a number of examples out there including this one: http://blogs.msdn.com/b/youssefm/archive/2009/06/05/introducing-a-new-datacontractserializer-feature-the-datacontractresolver.aspx.
2) Align your object model to better match your service contracts. That is, use containment rather than inheritance to manage the BaseThing-DerivedThing relationship. That way you work with DerivedThing at the server and simply return DerivedThing.BaseThing over the wire. If BaseThing needs to get transmitted from client to server, this will also work better.
3) Use explicit conversion using something like AutoMapper so it is obvious in your operations that there is a divergence between the objects being used at the server and those exposed to the outside world.

After posting I also found this SO identical question How to serialize a derived type as base. The unaccepted second answer by marc for me is the easiest way to resolve this issue. That is:
Decorate the derived class with [DataContract(Name="BaseClass")]
Note that this solution means that derived will transport as base for all every case of transport of this object. For me that was not an issue if it is then you need to go the DataContractResolver route.
Some notes on the DataContractResolver route:
1. This enables you to pass the derived as derived on some calls but as base on other - if you need to do that - if not use about Name= approach.
2. I get an exception using the DeserializeAsBaseResolver from the datacontractrsolver article as it stands because the knownTypeResolver returns false. To fix that I ignor the return value of that call and always return true from TryResolveType. That seems to work.
3. I initially thought that because we were serializing as base that I didnt need [DataContract] on the derived class. That was wrong. The object is serialized as the derived object and derserialized as a base object - so you must decorate the derived with [DataContract] but don't mark any fields as [DataMembers] to avoid them being unnecessarily serialize.
4. If you have a command line host and a service host then you need the code to insert the contract resolver in both. I found it useful to put this as a static in my resolver.
5. Note that that the "Get_gateway_data" string in the call to cd.Operations.Find("Get_gateway_data") is the name of the contract method that returns the object concerned. You will need to do this for each call that you want this behaviour.
Final code for this approach:
public class DeserializeAsBaseResolver : DataContractResolver {
public static void Install(ServiceHost service_host) {
// Setup DataContractResolver for GatewayProcessing to GatewayData resolution:
ContractDescription cd = service_host.Description.Endpoints[0].Contract;
OperationDescription myOperationDescription = cd.Operations.Find("Get_gateway_data");
DataContractSerializerOperationBehavior serializerBehavior = myOperationDescription.Behaviors.Find<DataContractSerializerOperationBehavior>();
if (serializerBehavior == null) {
serializerBehavior = new DataContractSerializerOperationBehavior(myOperationDescription);
myOperationDescription.Behaviors.Add(serializerBehavior);
}
serializerBehavior.DataContractResolver = new DeserializeAsBaseResolver();
}
public override bool TryResolveType(Type type, Type declaredType,
DataContractResolver knownTypeResolver,
out XmlDictionaryString typeName,
out XmlDictionaryString typeNamespace) {
bool ret = knownTypeResolver.TryResolveType(type, declaredType, null, out typeName, out typeNamespace);
//return ret; // ret = false which causes an exception.
return true;
}
public override Type ResolveName(string typeName, string typeNamespace,
Type declaredType, DataContractResolver knownTypeResolver) {
return knownTypeResolver.ResolveName(typeName, typeNamespace, declaredType, null) ?? declaredType;
}
Host code (service or command line):
using (ServiceHost service_host = new ServiceHost(typeof(GatewayServer))) {
// Setup DataContractResolver for GatewayProcessing to GatewayData resolution:
DeserializeAsBaseResolver.Install(service_host);
// Open the host and start listening for incoming messages.
try { service_host.Open(); }

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.

DataContractException in hosted WCF service

I am trying a host a service where there is a method that returns the following type:
[DataContract]
[Obfuscation(ApplyToMembers = true, Exclude = true)]
[Serializable]
public class Output
{
[DataMember]
public DataSet dsOutput{get;set;}
}
The method signature is as follows:
[OperationContract]
[WebGet]
function Output matchData(DataSet pDSSide1,DataSet pDSSide2)
{
return new Output();
}
On browsing the service I encounter the following exception:
System.InvalidOperationException: An exception was thrown in a call to a WSDL export extension:System.ServiceModel.Description.DataContractSerializerOperationBehavior
contract: http://tempuri.org/:TesterTool ---->
System.Runtime.Serialization.InvalidDataContractException: Type 'System.Data.DataRow' cannot be serialized. Consider marking it with the DataContractAttribute attribute, and marking all of its members you want serialized with the DataMemberAttribute attribute. If the type is a collection, consider marking it with the CollectionDataContractAttribute. See the Microsoft .NET Framework documentation for other supported types.
`
Any suggestions.
It's not a good practice to send a DataTable over a service.
As stated by govindaraj here:
The best way is to use custom collection of custom object.
If you're using 2.0, then you can use generic collection instead of
custom collection to reduce code.
How?
Create a custom data object (containing only private fields and public properties for each field) that is similar to each row in the
datatable.
Create a layer that will do all database (in this case, dataset) access and translation to the custom object.
All client code will access that layer.

DataContract properties names during proxy generation with svcutil

Is there any switch that instructs svcutil to generate DataContract properties with their names as defined in code? For example when I create a proxy which uses the following DataContract:
[DataContract(Namespace = "http://schemas.mynamespace.com/2012/08")]
public class MyDataContract
{
[DataMember(IsRequired = true, Order = 0)]
private int _id;
public int Id
{
get { return _id; }
set { _id = value; }
}
}
I get this DataContract on the proxy generated class:
public partial class MyDataContract : object
{
private int _idField;
[System.Runtime.Serialization.DataMemberAttribute(IsRequired=true)]
public int _id
{
get
{
return this._idField;
}
set
{
this._idField = value;
}
}
}
The order property of the DataMemberAttribute is always ommited as well for the first 3 properties and a MessageContract ommits an IDisposable implementation.
Well, I cannot comment on the omitted order, but I may be able to help on the rest:
One usually specifies the DataMember attribute on the property, not on the field. The data-contract itself does not distinguish between a property and a field, but it knows the name, type, if it is mandatory, etc. etc.
Added: What Chris said: With [DataMember(Name="whateveryouwant")] you'll be able to set a name different from the field/property name. I do not like such usage, though, but it is helpful when refactoring code, but still keeping the API compatible.
Only other DataContract (and some intrinsically supported) types are serialized to/from messages. IDisposable seems not to be one.
Serializing the inherited IDisposable of a MessageContract would not make any sense. A message-contract is the .NET representation of a SOAP message. It literally has nothing else to do but to provide a 1:1 mapping between what is in the SOAP message XML, and the accessible .NET types.
A message is part of a ServiceContract, in that it specifies which kind of message must be sent to a certain operation to be a valid invocation, and another (response-)message contract specifies how the data, that the operation returns, will be structured. Nothing else; it is a data-aggregate.
If you want to capture the result of a service-operation on the client, and for convenience automatically send a message back upon going out of scope (or for instance unregistering from a service), you will have to implement this on the client-side. Be aware, however, that the service must not require this to happen (due to lost connections, crashes, etc.).
Use the name property on DataMember attribute
Such as:
[DataMember(Name="myname")]

IronPython Lists, Tuples, Dictionaries crash WCF communications

I am attempting to use WCF to execute IronPython remotely inside of C#. Everything in my system is functioning beautifully as long as it is local.
I have isolated the problem to passing certain objects to the client via WCF:
If you try to pass these to a WCF client from a WCF server, the communications channel crashes:
PythonDictionaries containing values that are Tuples or Lists
Tuples of any kind
...Strangely, dictionaries containing dictionaries are ok (as long as the nested dictionary doesn't meet these 2 conditions). Here is my example code:
try
{
PythonFlow localPython = new PythonFlow();
IPythonFlow remotePython = new IronTesterWcfClient("localhost", "8000");
string tuple = "(1,2,3)";
string list = "[1,2,3]";
string complexDict0 = "{'a':'b','c':{'d':'f'}}";
string complexDict1 = "{'a':'b','c':(1,2,3),'e':'e'}";
string complexDict2 = "{'a':'b','c':[1,2,3],'d':'e'}";
string complexDict3 = "{'a':'b','c':[1,2,3],'d':(1,2,3),'e':{'a':'b','c':[1,2,3],'d':(1,2,3)}}";
localPython.OpenFlow(args[2]);
//OK
IronPython.Runtime.List list1 = localPython.PythonListFromString(list);
//OK
IronPython.Runtime.PythonDictionary dict0 = localPython.PythonDictionaryFromString(complexDict0);
//OK
IronPython.Runtime.PythonDictionary dict1 = localPython.PythonDictionaryFromString(complexDict1);
//OK
IronPython.Runtime.PythonDictionary dict2 = localPython.PythonDictionaryFromString(complexDict2);
//OK
IronPython.Runtime.PythonDictionary dict3 = localPython.PythonDictionaryFromString(complexDict3);
//OK
IronPython.Runtime.PythonTuple tuple1 = localPython.PythonTupleFromString(tuple);
remotePython.OpenFlow(args[2]);
//OK
IronPython.Runtime.List list2 = remotePython.PythonListFromString(list);
//OK
IronPython.Runtime.PythonDictionary dict5 = remotePython.PythonDictionaryFromString(complexDict0);
//Fail!!!
IronPython.Runtime.PythonDictionary dict6 = remotePython.PythonDictionaryFromString(complexDict1);
//Fail!!!
IronPython.Runtime.PythonDictionary dict7 = remotePython.PythonDictionaryFromString(complexDict2);
//Fail!!!
IronPython.Runtime.PythonDictionary dict8 = remotePython.PythonDictionaryFromString(complexDict3);
//Fail!!!
IronPython.Runtime.PythonTuple tuple2 = remotePython.PythonTupleFromString(tuple);
}
catch (Exception ex)
{
//The communication object, System.ServiceModel.Channels.ServiceChannel, cannot be used for communication because it is in the Faulted state.
Console.WriteLine(ex.ToString());
}
I am using NetTcpBinding with SecurityMode.None on the WCF server side... I should also mention that the python call is ultimately accessing a simple object in python which returns the result of eval()
It's basically making it impossible to use Python with WCF. Any ideas?
More info... I was finally able to extract the exceptions inside WCF when this happens:
Outer Exception:
There was an error while trying to serialize parameter http://Intel.ServiceModel.Samples:TestResult.
The InnerException message was 'Type 'IronPython.Runtime.PythonTuple' with data contract name 'ArrayOfanyType:http://schemas.microsoft.com/2003/10/Serialization/Arrays' is not expected. Consider using a DataContractResolver 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 DataContractSerializer.'. Please see InnerException for more details.
Inner Exception:
Type 'IronPython.Runtime.PythonTuple' with data contract name 'ArrayOfanyType:http://schemas.microsoft.com/2003/10/Serialization/Arrays' is not expected. Consider using a DataContractResolver 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 DataContractSerializer.
You're getting a SerializationException, indicating that .NET doesn't know how to deserialize some chunk of the data you're sending. In this case, it's choking on ArrayOfanyType, which is any kind on non-generic collection (an ArrayList or plain array, for instance).
I've reviewed the source for IronPython 2.7.1 (what version are you using?), looking at the implementation of List and PythonTuple. Both contain an Object array, pretty much identically declared; List has a few other random instance fields.
// IronPython.Runtime.List
internal volatile object[] _data;
private const int INITIAL_SIZE = 20;
internal int _size;
// IronPython.Runtime.PythonTuple
internal readonly object[] _data;
I don't know why the serializer isn't happy with the PythonTuple class, when it's fine with List. What this probably indicates, however, is that .NET's type resolver can't resolve some element of the serialized object.
There are two ways to resolve this, that I know of.
You can try to convince .NET to consider a given type during deserialization, using the KnownTypes attribute. From MSDN:
When data arrives at a receiving endpoint, the WCF runtime attempts to deserialize the data into an instance of a common language runtime (CLR) type. The type that is instantiated for deserialization is chosen by first inspecting the incoming message to determine the data contract to which the contents of the message conform. The deserialization engine then attempts to find a CLR type that implements a data contract compatible with the message contents. The set of candidate types that the deserialization engine allows for during this process is referred to as the deserializer's set of "known types."
You'd want to apply this attribute to the class being transferred over the wire, and this isn't convenient when you don't control the class, as is the case here. So this is probably a non-starter.
You can specify a custom DataContractResolver to resolve your problematic types:
A data contract resolver allows you to configure known types dynamically. Known types are required when serializing or deserializing a type not expected by a data contract.
You can do this without controlling the class to be serialized, but it takes a bit more work. This MSDN blog post has a great writeup.
In summary, you'd create a DataContractResolver and override its two methods, TryResolveType and ResolveName. The first is used during serialization, and the second during deserialization. From the MSDN sample, with my comments:
public class MyCustomerResolver : DataContractResolver
{
public override bool TryResolveType(Type dataContractType, Type declaredType, DataContractResolver knownTypeResolver, out XmlDictionaryString typeName, out XmlDictionaryString typeNamespace)
{
if (dataContractType == typeof(Customer)) // a type I recognize
{
XmlDictionary dictionary = new XmlDictionary();
typeName = dictionary.Add("SomeCustomer");
typeNamespace = dictionary.Add("www.FPSTroller.com");
return true;
}
else // I don't know what this is; defer to the inbuilt type resolver
{
return knownTypeResolver.TryResolveType(dataContractType, declaredType, null, out typeName, out typeNamespace);
}
}
public override Type ResolveName(string typeName, string typeNamespace, DataContractResolver knownTypeResolver)
{
// my type
if (typeName == "SomeCustomer" && typeNamespace == "http://www.FPSTroller.com")
{
return typeof(Customer);
}
else // I don't know what this is; defer to the inbuilt type resolver
{
return knownTypeResolver.ResolveName(typeName, typeNamespace, null);
}
}
}
The blog post I mentioned above has some sample resolvers that might give .NET a better shot and handling your classes without writing anything custom (look for the "Useful resolvers" heading).
You'd use DataContractSerializerOperationBehavior. To plug your resolver into WCF; see the sample in the MSDN documentation.
Finally, before going down this path, you might consider changing your WCF operations interface. Do you really need to pass these custom, non-generic types over the wire? What I've read implies that non-generic types run into this kind of issue often. Consider using a plain old System.Collections.Generic.Dictionary<K,V> and (if you're using .NET 4+) System.Tuple. Lock down your types; don't make the resolver guess.

WCF and interfaces

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.