0

I'm using SignalR 2.2.2 (Latest release) with .NET 4.5.2

I've this error:

Could not create an instance of type DF.ApplicationBlocks.IPropertyValueData. Type is an interface or abstract class and cannot be instantiated. Path 'Properties[0].Id', line 1, position 881.

The problem is :

I've a SignalR client (written in .NET, using Signal.Client package) that send a custom object, on this object there is an array of object typed as an interface (IPropertyValueData). The values in the array is an implementation of this interface.

In the signalR client, I've configured to use TypeNameHandling.Auto :

        this._connection.JsonSerializer.TypeNameHandling = TypeNameHandling.Auto;
        this._connection.JsonSerializer.PreserveReferencesHandling = PreserveReferencesHandling.Objects;
        this._connection.JsonSerializer.ReferenceLoopHandling = ReferenceLoopHandling.Serialize;

and it does it correctly, as here is an extract of what's sent by the client:

"Properties":[  
    {  
       "$id":"20",
       "$type":"DF.MailFlow.Services.Contract.Data.MailDataProperty, DF.MailFlow.Services.Contract",
       "Id":"5588ce9e-30fb-45b1-afbe-1e6ad0c3e8d4",

So the $type property is defined and the value is correct. But anyway, the server still trigger the error when the message is received.

I've also configured the server to use TypeNameHandling.Auto, here is how the hub is configured (I use Castle Windsor as IoC):

        var resolver = new WindsorDependencyResolver(WebAPILoader.Container);

        var hubConfig = new HubConfiguration()
        {
            EnableJSONP = true,
            EnableDetailedErrors = true,
            EnableJavaScriptProxies = true,
            Resolver = resolver
        };


        GlobalHost.DependencyResolver = resolver;

        // Configure signalR and run it
        app.UseCors(CorsOptions.AllowAll);
        app.Map("/live", map =>
        {
            map.UseCors(CorsOptions.AllowAll);
            map.RunSignalR(hubConfig);
        });

        var serializerSettings = new JsonSerializerSettings
        {
            TypeNameHandling = TypeNameHandling.Auto,
            PreserveReferencesHandling = PreserveReferencesHandling.Objects,
            ReferenceLoopHandling = ReferenceLoopHandling.Serialize
        };
        GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings = serializerSettings;
        GlobalConfiguration.Configuration.Formatters.JsonFormatter.UseDataContractJsonSerializer = false;

        // Register in Castle.Windsor container
        WebAPILoader.Container.Register(Castle.MicroKernel.Registration.Component
                              .For<JsonSerializer>()
                              .UsingFactoryMethod(() => JsonSerializer.Create(serializerSettings)));

I've checked that when the 1st message is received, the the factory method is called and the setting still have the correct TypeNameHandling.Auto.

I also tested to register the serializer this way:

GlobalHost.DependencyResolver.Register(typeof(JsonSerializer), () => JsonSerializer.Create(serializerSettings));

But no way, the server always trigger the same error when the message is received.

Adding to the complexity, the application run both signalr and webapi component (in differnt url). I've checked that the webapi doesn't register any JsonSerializer, and Windsor container used is the same for both component.

Any idea ?

Edit #1:

I just ran on this ticket https://github.com/SignalR/SignalR/issues/3304
Seems just incredible that a bug is listed there without any fix for more than 3 years, even when someone made a PR (https://github.com/SignalR/SignalR/pull/3343)
So now I'm trying to find a workaround...

Fabske
  • 2,106
  • 18
  • 33

2 Answers2

1

I had the same issue with SignalR showing an error with a custom object. I accidentally fixed it by changing the object's data type to 'dynamic' in all SignalR-related code, for example:

// Bad
public void Receive_SendBoardObject(Board myboard)

// Good
public void Receive_SendBoardObject(dynamic myboard)

Now SignalR sends the object correctly, no errors!

Jeremy Caney
  • 7,102
  • 69
  • 48
  • 77
Vilis2017
  • 11
  • 1
0

This problem is clearly due to a bug in SignalR

The workaround posted in this link works, i.e. register a custom resolver:

private class Resolver : DefaultParameterResolver
{
    private readonly JsonSerializer _serializer;

    public Resolver(JsonSerializer serializer)
    {
        _serializer = serializer;
    }

    private FieldInfo _valueField;
    public override object ResolveParameter(ParameterDescriptor descriptor, Microsoft.AspNet.SignalR.Json.IJsonValue value)
    {
        if (value.GetType() == descriptor.ParameterType)
        {
            return value;
        }

        if (_valueField == null)
            _valueField = value.GetType().GetField("_value", BindingFlags.Instance | BindingFlags.NonPublic);

        var json = (string)_valueField.GetValue(value);
        using (var reader = new StringReader(json))
            return _serializer.Deserialize(reader, descriptor.ParameterType);
    }
}

The discussion on how to resolve this bug is now on this ticket, so you could find there when it'll (hopefully) be resolved

Fabske
  • 2,106
  • 18
  • 33