2

I'm working with SignalR trying to get a .net Client to calls methods to a .net on a SignalR Hub. My situation is that the objects being transmitted aren't necessarily simple value types (string, int, double, etc); they can be DTOs such as generic<< T >> or other non-trivial things. I need them to be deserialized with the same object type as when they where serialized. To accomplish this, I believe I need to set JSON.Net TypeNameHandling to "All". I believe I need to do this on both the client and the server.

Making the change to TypeNameHandling on the client seems to work smoothly, but when I make the change on the server, I get an error on the server at connection time, before any of my requests are processed. Here is how I change the JSON.Net settings on the server:

class Startup
{
    public void Configuration(IAppBuilder app)
    {
        var serializer = new JsonSerializer() 
        { 
            TraceWriter = new ConsoleTraceWriter(),
            TypeNameHandling = TypeNameHandling.All,
    };

    // register it so that signalr can pick it up
    GlobalHost.DependencyResolver.Register(typeof(JsonSerializer), () => serializer);

    app.UseCors(CorsOptions.AllowAll);
    app.MapSignalR();
}

Using a custom TraceWriter that outputs to the console, I see the following on the client:

01:36:55.8596332 - null - ChangeState(Disconnected, Connecting)
-> Msg: Started serializing System.Linq.Enumerable+WhereSelectEnumerableIterator`2[System.Collections.Generic.KeyValuePa
ir`2[System.String,Microsoft.AspNet.SignalR.Client.Hubs.HubProxy],Microsoft.AspNet.SignalR.Client.Hubs.HubRegistrationDa
ta]. Path ''.
-> Msg: Writing type name 'System.Linq.Enumerable+WhereSelectEnumerableIterator`2[[System.Collections.Generic.KeyValuePa
ir`2[[System.String, mscorlib],[Microsoft.AspNet.SignalR.Client.Hubs.HubProxy, Microsoft.AspNet.SignalR.Client]], mscorl
ib],[Microsoft.AspNet.SignalR.Client.Hubs.HubRegistrationData, Microsoft.AspNet.SignalR.Client]], System.Core' for Syste
m.Linq.Enumerable+WhereSelectEnumerableIterator`2[System.Collections.Generic.KeyValuePair`2[System.String,Microsoft.AspN
et.SignalR.Client.Hubs.HubProxy],Microsoft.AspNet.SignalR.Client.Hubs.HubRegistrationData]. Path ''.
-> Msg: Started serializing Microsoft.AspNet.SignalR.Client.Hubs.HubRegistrationData. Path '$values'.
-> Msg: Writing type name 'Microsoft.AspNet.SignalR.Client.Hubs.HubRegistrationData, Microsoft.AspNet.SignalR.Client' fo
r Microsoft.AspNet.SignalR.Client.Hubs.HubRegistrationData. Path '$values[0]'.
-> Msg: Finished serializing Microsoft.AspNet.SignalR.Client.Hubs.HubRegistrationData. Path '$values[0]'.
-> Msg: Finished serializing System.Linq.Enumerable+WhereSelectEnumerableIterator`2[System.Collections.Generic.KeyValueP
air`2[System.String,Microsoft.AspNet.SignalR.Client.Hubs.HubProxy],Microsoft.AspNet.SignalR.Client.Hubs.HubRegistrationD
ata]. Path ''.
-> Msg: Serialized JSON:
{
  "$type": "System.Linq.Enumerable+WhereSelectEnumerableIterator`2[[System.Collections.Generic.KeyValuePair`2[[System.St
ring, mscorlib],[Microsoft.AspNet.SignalR.Client.Hubs.HubProxy, Microsoft.AspNet.SignalR.Client]], mscorlib],[Microsoft.
AspNet.SignalR.Client.Hubs.HubRegistrationData, Microsoft.AspNet.SignalR.Client]], System.Core",
  "$values": [
    {
      "$type": "Microsoft.AspNet.SignalR.Client.Hubs.HubRegistrationData, Microsoft.AspNet.SignalR.Client",
      "Name": "MyHub"
    }
  ]
}
01:36:56.4606332 - null - Disconnected
01:36:56.4616332 - null - Transport.Dispose()
01:36:56.4626332 - null - Closed

And the server shows this:

-> Msg: Error deserializing System.Collections.Generic.IEnumerable`1[Microsoft.AspNet.SignalR.Hubs.HubDispatcher+ClientH
ubInfo]. Error resolving type specified in JSON 'System.Linq.Enumerable WhereSelectEnumerableIterator`2[[System.Collecti
ons.Generic.KeyValuePair`2[[System.String, mscorlib],[Microsoft.AspNet.SignalR.Client.Hubs.HubProxy, Microsoft.AspNet.Si
gnalR.Client]], mscorlib],[Microsoft.AspNet.SignalR.Client.Hubs.HubRegistrationData, Microsoft.AspNet.SignalR.Client]],
System.Core'. Path '$type', line 1, position 333.
-> ex: Newtonsoft.Json.JsonSerializationException: Error resolving type specified in JSON 'System.Linq.Enumerable WhereS
electEnumerableIterator`2[[System.Collections.Generic.KeyValuePair`2[[System.String, mscorlib],[Microsoft.AspNet.SignalR
.Client.Hubs.HubProxy, Microsoft.AspNet.SignalR.Client]], mscorlib],[Microsoft.AspNet.SignalR.Client.Hubs.HubRegistratio
nData, Microsoft.AspNet.SignalR.Client]], System.Core'. Path '$type', line 1, position 333. ---> Newtonsoft.Json.JsonSer
ializationException: Could not find type 'System.Linq.Enumerable WhereSelectEnumerableIterator`2[[System.Collections.Gen
eric.KeyValuePair`2[[System.String, mscorlib],[Microsoft.AspNet.SignalR.Client.Hubs.HubProxy, Microsoft.AspNet.SignalR.C
lient]], mscorlib],[Microsoft.AspNet.SignalR.Client.Hubs.HubRegistrationData, Microsoft.AspNet.SignalR.Client]]' in asse
mbly 'System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.
   at Newtonsoft.Json.Serialization.DefaultSerializationBinder.GetTypeFromTypeNameKey(TypeNameKey typeNameKey)
   at Newtonsoft.Json.Utilities.ThreadSafeStore`2.AddValue(TKey key)
   at Newtonsoft.Json.Utilities.ThreadSafeStore`2.Get(TKey key)
   at Newtonsoft.Json.Serialization.DefaultSerializationBinder.BindToType(String assemblyName, String typeName)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.ResolveTypeName(JsonReader reader, Type& objectType, Js
onContract& contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, String
 qualifiedTypeName)
   --- End of inner exception stack trace ---
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.ResolveTypeName(JsonReader reader, Type& objectType, Js
onContract& contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, String
 qualifiedTypeName)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.ReadMetadataProperties(JsonReader reader, Type& objectT
ype, JsonContract& contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember,
 Object existingValue, Object& newValue, String& id)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateObject(JsonReader reader, Type objectType, JsonCo
ntract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object exis
tingValue)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal(JsonReader reader, Type objectType,
 JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Obje
ct existingValue)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize(JsonReader reader, Type objectType, Boolean
 checkAdditionalContent)

It looks like JSON.Net can't find class System.Linq.Enumerable WhereSelectEnumerableIterator`2 . What can I do to fix this?

sevzas
  • 701
  • 2
  • 5
  • 13
  • 1
    Show the code that sends the data from the client. Looks like you are trying to serialize a query and not the data. Even if you were able to deserialize it how would it work if you have just a query but don't have the data to use the query on? – Pawel Mar 21 '15 at 04:00
  • The server shows that error at connection time, so the error is in response to the client call "Connection.Start().Wait();" - none of my functions have been caled yet. Note how you see "ChangeState(Disconnected, Connecting)" in the client log, but you do not see a state change from Connecting to Connected. – sevzas Mar 21 '15 at 13:05
  • It's interesting to note that the client shows the missing class name as "System.Linq.Enumerable+WhereSelectEnumerableIterator`2" but the error message on the server says that "Error resolving type specified in JSON 'System.Linq.Enumerable WhereSelectEnumerableIterator`2". If you compare the two class names carefully, they are slightly different: the class name on the server is missing the "+" plus sign between ".Enumerable" and "WhereSelect". – sevzas Mar 22 '15 at 13:09
  • WhereSelectEnumerableIterator is a private class inside the System.Linq.Enumerable static class that inherits from Iterator. This explains why JSON.NET cannot instantiate it. I suppose with TypeNameHandling=None, JSON.Net substitutes its own classes when deserializing. This leads me to believe that SignalR 2.2.0 cannot work when JSON.Net has TypeNameHandling=All unless I can find some way to substitute WhereSelectEnumerableIterator with a different class during Deserialization. – sevzas Mar 23 '15 at 00:08
  • See http://stackoverflow.com/questions/22197842/signalr-and-customtypeconverter - you can register a custom type converter to shim in some code where you can create the object yourself. I'm doing this, with customised JSON.Net contract resolvers, to great effect with SignalR hubs. – Ian Yates Jun 01 '15 at 05:49

0 Answers0