Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SignalR 2.0 Injecting Interfaces with Ninject

I have the following simple Hub Class to list users using a injected User Service. A startup class that uses the NinjectSignalRDependencyResolver. And very simple client script.

hub

[HubName("dashboardHub")]
public class DashboardHub : Hub
{
    private readonly IUserService _users;

    public DashboardHub(IUserService users)
    {
        _users = users;
    }

    public void Initialize()
    {
        var users = _users.ListUsers();

        Clients.All.UpdateStatus(users);
    }
}

startup.cs

  var kernel = new StandardKernel();
  var resolver = new NinjectSignalRDependencyResolver(kernel);

  var config = new HubConfiguration()
  {
        Resolver = resolver,
  };

  app.MapSignalR(config);

NinjectSignalRDependencyResolver

public class NinjectSignalRDependencyResolver : DefaultDependencyResolver
{
    private readonly IKernel _kernel;

    public NinjectSignalRDependencyResolver(IKernel kernel)
    {
        _kernel = kernel;
    }

    public override object GetService(Type serviceType)
    {
        return _kernel.TryGet(serviceType) ?? base.GetService(serviceType);
    }

    public override IEnumerable<object> GetServices(Type serviceType)
    {
        return _kernel.GetAll(serviceType).Concat(base.GetServices(serviceType));
    }
}

JavaScript

<script>
    $(function () {
        var dashboard = $.connection.dashboardHub;

        dashboard.client.updateStatus = function (users) {
            var elem= $('.userList');

            elem.html('');
            $.each(users, function (i, user) {
                parent.append("<p>" user.Name +  "<p>");
            });
        };

        $.connection.hub.start().done(function () {
            dashboard.server.initialize();
        });
    });
</script>

I register the Interface with Ninject in the stadard way inside RegisterServices

private static void RegisterServices(IKernel kernel)
{
    kernel.Bind<IUserService >().To<UserService>().InSingletonScope();

    ...
}

When open client with the script. The code runs and there are no exceptions or script errors generated. The Constructor of Dashboard is never called and neither is the Initialize() method - even though the object exists in the JavaScript and so does the method. The the user list is never populated as client.updateStatus is never called.

If I add a default constructor to the Hub class then this is called and so too is the Initialize method - but it obviously now falls over as the private IUserService variable is null.

public DashboardHub()
{
}

How do you configure SignalR 2.0 and Ninject to allow Hubs to have dependency Injected constructor arguments?

like image 871
Code Uniquely Avatar asked Dec 23 '13 11:12

Code Uniquely


1 Answers

The easiest way to achieve this is to get rid of the Custom Dependency resolver entirely and just use a very simple IHubActivator instead.

startup.cs

public void Configuration(IAppBuilder app)
{
    app.MapSignalR();
}

Hubactivator.cs

public class HubActivator : IHubActivator
{
    private readonly IKernel container;

    public HubActivator(IKernel container)
    {
        this.container = container;
    }

    public IHub Create(HubDescriptor descriptor)
    {
        return (IHub)container.GetInstance(descriptor.HubType);
    }
}

RegisterServices

private static void RegisterServices(IKernel kernel)
{
    GlobalHost.DependencyResolver.Register(typeof(IHubActivator), () => new HubActivator(kernel));    

    kernel.Bind<IUserService >().To<UserService>().InSingletonScope();

    ...
}

Simple as that - everything works and Interfaces are injected into the constructors as you would expect...

like image 144
Code Uniquely Avatar answered Oct 23 '22 13:10

Code Uniquely