I spent a few hair-pulling hours on this issue, and unfortunately still only have the workaround. It appears there are other people who've run unto similar thing, so I will try to capture the details.
The Symptoms:
1. An application consuming a WCF service throws an exception when the call from the service returns:
This means: I got a hash table that contains an array of strings as one of its values, and I was not expecting that. You need to add KnownType attribute to the contract description, so I know how to handle it.
2. The second symptom I discovered later, and it is actually the cause of the message above. The References.cs file, that is auto-generated by Visual Studio, does not include the 'ServiceKnownTypeAttribute' decorations as specified by the service interface.
Note, that inside the service, there is a 'ServiceKnownType' attribute applied to the interface. However, when the service reference is generated by Visual Studio, the attribute is not propagated.
The Code:
The code is very simple for the example. The function 'GetHashtable' in the service, simply returns a hash table. The catch is that it is a Hashtable that contains List<string> as its value (hence reference to 'ArrayOfstring' in the exception).
Points for Consideration:
Note, that is NOT an exception in the service caused by the lack of 'ServiceKnownType' attributes. When that is the case, you get the exception in the service, not the client, and it looks like this:
This one is having trouble serializing, not deserializing. This exception IS the result of missing 'ServiceKnownType' attributes on the service interface. To mitigate, the interface definition should look like this:
namespace Company.MyServiceNameSpace { [ServiceContract] [ServiceKnownType(typeof(List<object>))] [ServiceKnownType(typeof(List<string>))] [ServiceKnownType(typeof(Hashtable))] public interface IMyServiceLibrary { [OperationContract] Hashtable GetHashtable(); } }
Specifically, adding '[ServiceKnownType(typeof(List<object>))]' and '[ServiceKnownType(typeof(List<string>))]' tells the serializer to expect arrays of objects and arrays of strings.
So, this is NOT the issue discussed here. On the service end, everything is fine. It is the client the fails.
Auto-Generated Code:
When the service is added as a reference to a client project, in my case, a Windows Forms application, it automatically 'discovers' the service and generates a proxy class for the service. The proxy class contains all types and functions exposed by the service.
After a few hours (unfortunately) of debugging/googling this, I finally got into the auto-generated code. Only to notice that it contained no KnownType information at all. Here is an excerpt:
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")] [System.ServiceModel.ServiceContractAttribute( ConfigurationName="MyServiceLibraryProxy.IMyServiceLibrary")] public interface IMyServiceLibrary { [System.ServiceModel.OperationContractAttribute( Action="http://tempuri.org/IMyServiceLibrary/GetHashtable", ReplyAction="http://tempuri.org/IMyServiceLibrary/GetHashtableResponse")] System.Collections.Hashtable GetHashtable(); }
So it auto-generated the function definition, but the KnownType attribute is mysteriously absent. And therein lies the problem. The client subsequently does not expect any Lists or arrays of strings as values in the Hashtable.
Things I've tried:
- Adding ServiceKnownType attribtue to the function and the interface defintion.
- Adding KnownType attribute to the class (derrived from interface) and function definition.
- Adding ServiceKnownType to the class definition.
- Encapsulating a Hashtable into a DataContract object (class that has one member which is a Hashtable)
Clearly a lot of just unreasonable trial-and-error. Nothing.. I keep Updating Service Reference, and it keeps missing the KnownType attribute, and keeps throwing the exception in the client.
Workarounds:
1. (BAD) Manually adding KnownType attributes to the auto-generated Refereces.cs file as follows:
[System.ServiceModel.OperationContractAttribute( Action="http://tempuri.org/IMyServiceLibrary/GetHashtable", ReplyAction="http://tempuri.org/IMyServiceLibrary/GetHashtableResponse")] [System.ServiceModel.ServiceKnownTypeAttribute(typeof(System.Collections.Hashtable))] [System.ServiceModel.ServiceKnownTypeAttribute(typeof( System.Collections.Generic.List<string>))] [System.ServiceModel.ServiceKnownTypeAttribute(typeof( System.Collections.Generic.List<object>))] System.Collections.Hashtable GetHashtable();
It works, but it an awful solution as that code will get overwritten next time a service reference is updated. Furthermore, you can't expect other consumers of your service to have to do this!
2. I do not know why this works, but changing the service function return type to a generic seems to fix the auto-generation issue. That is, change the function prototype from
Hashtable GetHashtable() to object GetHashtable()
Interface code now looks like this:
namespace Company.MyServiceNameSpace { [ServiceContract] [ServiceKnownType(typeof(List<object>))] [ServiceKnownType(typeof(List<string>))] [ServiceKnownType(typeof(Hashtable))] public interface IMyServiceLibrary { [OperationContract] object GetHashtable(); } }
Obviously, the associated class needs to be modified as well. Compile, Update Service Reference in the client, and what do you know, References.cs looks like this without manual manipulation:
[System.ServiceModel.OperationContractAttribute( Action="http://tempuri.org/IMyServiceLibrary/GetHashtable", ReplyAction="http://tempuri.org/IMyServiceLibrary/GetHashtableResponse")] [System.ServiceModel.ServiceKnownTypeAttribute(typeof(System.Collections.Hashtable))] [System.ServiceModel.ServiceKnownTypeAttribute(typeof( System.Collections.Generic.List<string>))] [System.ServiceModel.ServiceKnownTypeAttribute(typeof( System.Collections.Generic.List<object>))] object GetHashtable();
The client now needs to cast the service call return as a Hashtable, but it seems more livable.
If anyone can shed any more light on this, or if you see that I am doing something wrong, please let me know!
For now, file under WTF.
The problem occurs when Hashtable/Dictionary is used. If you return a List of objects - it will be working fine.
Anyway, I don't know why this happens.
One more related source:
http://social.msdn.microsoft.com/Forums/ar/wcf/thread/3f37cdae-4525-4ed5...
Thanks for this workaround, but have anyone found a way to get these ServiceKnownTypeAttributes autogenerated by the svcutil?
Just update the service reference and that would sove this issue.
That was certainly one of the very first things I tried along with removing the reference completely and adding it back in.
Thanks A LOT! i have spent a hole day to fix it!!!
I didn't understand why it can serialize it and not deserialize it!! The first solution worked perfectly form me ;)
Thanks A LOT! i have too many hours breaking my head against that issue. The second work around worked perfectly form me.
Thanks again, Cheers!
Was not able to change the reference.cs because VS does not create Reference.cs for a website project.
But, the second work aorund worked for me. :) Thanks and cheers.
hey i've the same problem over here. the client side reference.vb isn't generating the knowntypes :( any idea?
You can make use of the sample Application
You can download from the Below Link
Sample Application.zip