IronPython Lists, Tuples, Dictionaries crash WCF communications - wcf

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.

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.

Why does Json.NET not include $type for the root object when TypeNameHandling is Auto?

When I set Json.NET to serialize with TypeNameHandling set to TypeNameHandling.Auto, it correctly sets $type for child properties of an object but does not do so for the root object being serialized. Why?
Please consider the following repro:
public class Animal
{
public Animal[] Offspring { get; set; }
}
public class Dog : Animal {}
Animal fido = new Dog
{
Offspring = new Animal[] { new Dog() }
};
var json = JsonConvert.SerializeObject(fido,
new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Auto
});
The Json emitted into the json variable is:
{
"Offspring": [{
"$type": "MyApp.Dog, MyApp",
"Offspring": null
}]
}
The Json.NET Documentation says that for TypeNameHandling.Auto the behavior is:
Include the .NET type name when the type of the object being serialized is not the same as its declared type.
My question is - Why does fido not have
"$type": "MyApp.Dog, MyApp", like its puppy? :)
UPDATE: I've found out from the accepted answer to this question that I can force $type to be added by doing this:
var json = JsonConvert.SerializeObject(fido,
typeof(Animal),
new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Auto,
Formatting = Formatting.Indented
});
But my question still holds - Why does Json.NET not do this by itself as per the documentation?
Short answer: it doesn't because it can't.
As you stated in your question, setting TypeNameHandling to Auto directs Json.Net to include the .NET type name when the actual (run-time) type of the object being serialized is not the same as its declared (compile-time) type. In order to do that, Json.Net needs to know both types for every object.
For everything inside the root object, this is straightforward: just get the runtime type of the root object via GetType(), then use reflection to get all of its declared properties and their types, and for each one compare the declared type to the actual type to see if they differ. If they do, output the type name.
But for the root object itself, Json.Net doesn't have access to both types. All the information it has is the object referenced by fido, whose runtime type is Dog. There's no way for Json.Net to discover that the fido variable was declared as Animal, unless you provide that context somehow. And that is exactly why Json.Net provides overloads of SerializeObject which allow you to specify the compile-time type of the object being serialized. You must use one of these overloads if you want the TypeNameHandling.Auto setting to work for the root object.
Brian is absolutely correct, Json.NET has no way of knowing the compile-time declared type of the object it's being passed as the value parameter is declared as an object. The easy fix for this was if Json.NET added generic serialize methods so that the compile-time declared type would automatically flow over to Json.NET but the library's author has decided against my proposal for this here.
As an alternative, I've wrapped all my json (de)serialization needs in a JsonHelper class with generic serialize methods which use the typeof expression to automatically pass the compile-time declared type of the value to be serialized.
Newer versions of Json.Net allow you to pass the expected type to the serialize method
ser.Serialize(stream, rootObject, typeof(BaseClass));
You can pass the base class to the serialize method and TypeNameHandling.Auto will write the $type if the object and expected type do not match.

WCF deserialization of type object Properties

I am having trouble achieving the following scenario.
We currently have a method which expects a list of 'context' key value pairs. The value however can be of any type.
the goal is to make this method available using WCF. So I created a
public List<Element> Transform(List<Element> elements)
{
... Transformation of elements takes place
}
[DataContract(Namespace = Constants.NAMESPACE)]
public struct Element
{
[DataMember(Order = 0, IsRequired = true)]
public string Key;
[DataMember(Order = 1, IsRequired = true)]
public object Value;
}
When I use a .Net test project everything works fine.
However, when I call this service using SOAP UI I get an error message:
The formatter threw an exception while trying to deserialize the
message: There was an error while trying to deserialize parameter
elements. The InnerException message was 'Element Value cannot have
child contents to be deserialized as an object. Please use XmlNode[]
to deserialize this pattern of XML.'.
I am having trouble figuring out what to do. any help appreciated.
the xml i use is this:
<ws:Transform>
<ws:elements>
<ws:Element>
<ws:Key>Key1</ws:Key>
<ws:Value>A</ws:Value>
</ws:Element>
<ws:Element>
<ws:Key>Key2</ws:Key>
<ws:Value>B</ws:Value>
</ws:Element>
<ws:ScriptName>SetVariable</ws:ScriptName>
</ws:elements>
</ws:Transform>
In this case, SoapUI uses .Net technology which does not understand target type for object.
sending object is not valid across all platforms. In fact you might get an error with a .Net client as well. Your best bet is create a generic xml representation of the Value and have all clients inflate the object from the xml

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

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

WCF Serialization error with "ArrayOfstring" Data Contract

Am getting an error mentioned below when I call my WCF service?How do i get rid of it?
There was an error while trying to serialize parameter http://tempuri.org/:MyWCFSvc.svc
The InnerException message was 'Type 'System.String[]' with data contract name 'ArrayOfstring:http://schemas.microsoft.com/2003/10/Serialization/Arrays'
is not expected. 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.*
I tried using [ServiceKnownType(typeof(string[]))] in my WCF service interface but no luck
A year late, but I had the same issue and here is what you need to do
List<SomeClass> mylist = new List<SomeClass>();
DataContractSerializer dcs = new DataContractSerializer(mylist.GetType());
XmlWriter writer = XmlWriter.Create(sb, XWS);
dcs.WriteObject(writer, query);
writer.Close();
The problem is when you construct your serializer with the typeof your class, the serialzer does not see it as an arrray, it only sees a single object.
If found it by doing this first:
DataContractSerializer dcs = new DataContractSerializer(SomeClass.GetType());
XmlWriter writer = XmlWriter.Create(sb, XWS);
dcs.WriteObject(writer, query[0]); // Only get the first record from linq to sql
writer.Close();
I too had the same issues but after qualifiying the OperationContract with [ServiceKnownType(typeof(string[]))] and [ServiceKnownType(typeof(int[]))] fixed the issue.
Eg:
[ServiceContract]
public interface IReportService
{
[OperationContract]
[ServiceKnownType(typeof(string[]))]
[ServiceKnownType(typeof(int[]))]
bool GenerateReport(int clientId, int masterId, string reportType, int[] vtIds, DateTime initialDate, DateTime finalDate,
bool descending, string userName, string timeZoneId, bool embedMap,
object[] vtExtraParameters, object[] vtScheduleParameters, string selectedCriteria,
out long reportID, out int scheduleID, out string message);
There's no reason for you to have to KnownType an array of strings. The serializer should already know about that, and arrays are not a problem. I'm moving Lists of things around in WCF without an issue. Could you post a representative sample of what you're doing?
Configuring service references on your client provides "Data Type" options that allow you to specify different types for Collection/Dictionary Types. What settings do you have in there?