Integrating Castle Windsor with SignalR - how should I approach this?
Asked Answered
H

3

8

I am getting started with SignalR, and it works great once everything is configured. However, almost all the applications that I work on use Castle Windsor, so it would be great to be able to use them together. The reason that I want to do this is so that I can use Castle dependencies/services inside of a persistent connection.

I dug around in the source code, and it looks like I could either replace DependencyResolver with a Castle based one (i.e., Castle implementing IDependencyResolver), or I could change the usage of DependencyResolver to Castle.

Which one of these is a better idea? Is there another approach that I could use to combine Castle and SignalR?

Thanks, Erick

Hatpin answered 8/5, 2012 at 23:22 Comment(0)
R
6

August 2016 update

Following from a comment I no longer use the approach below but now use the GlobalHost.DependencyResolver

So in Global.asax.cs I initialise things

public static void Init(IWindsorContainer container)
{
    var conn = configurationManager.ConnectionStrings["SRSQL"].ConnectionString;
    GlobalHost.DependencyResolver.Register(typeof(IHubActivator), 
                                      () => new SignalHubActivator(container));
    GlobalHost.DependencyResolver.Register(typeof(ILoggingService), 
                                      container.Resolve<ILoggingService>);
    //etc or you could just pass your existing container to the resolver
    GlobalHost.DependencyResolver.UseSqlServer(conn);    
}

and then in the hub

private ILoggingService LoggingService{ get; set; }

    public NotificationHub()
    {
        LoggingService = GlobalHost.DependencyResolver.Resolve<ILoggingService>();
    }

and for completeness

public class SignalHubActivator: IHubActivator
{
    private readonly IWindsorContainer _container;

    public SignalHubActivator(IWindsorContainer container)
    {
        _container = container;
    }


    public IHub Create(HubDescriptor descriptor)
    {
                var result=  _container.Resolve(descriptor.HubType) as IHub;

        if (result is Hub)
        {
            _container.Release(result);
        }

    return result;
    }

}

OLD ANSWER from 2012

I went with the first option of setting our own DependencyResolver

AspNetHost.SetResolver(new SignalResolver(_container));

I can provide SignalResolver if desired but leaving out for readability for now.

Another important note is that the hubs must have an empty constructor so our castle container injects through properties, e.g.

public class NotificationHub : Hub, INotificationHub
    { 

public INotificationService NotificationService { get; set; }

and the resolver requested

public class SignalResolver : DefaultDependencyResolver
    {
        private readonly IWindsorContainer _container;

        public SignalResolver(IWindsorContainer container) 
        {
            if (container == null)
            {
                throw new ArgumentNullException("container");
            }
            _container = container;
        }

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

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

        private object TryGet(Type serviceType)
        {
            try
            {
                return _container.Resolve(serviceType);
            }
            catch (Exception)
            {
                return null;
            }
        }

        private IEnumerable<object> TryGetAll(Type serviceType)
        {
            try
            {
                var array = _container.ResolveAll(serviceType);
                return array.Cast<object>().ToList();
            }
            catch (Exception)
            {
                return null;
            }
        }
    }
Rebellion answered 11/5, 2012 at 10:31 Comment(13)
Thanks. I am going to go with something similar, but I'm finding some challenges in combining the way that Castle and SignalR manage lifestyles. For some reason my PersistentConnections keep getting messed up. It's good to hear that someone was able to do it! I will keep chugging along (unless your solution allowed for persistent connections from the Windsor container?)Hatpin
what version of IIS are you running? what kind of lifestyle issues do you have, singleton? It would be interesting to figure out how Castle would do a per web request lifestyle for a persistant connection. It might be best to keep things transient.Rebellion
@Rebellion where, the one I found in my answer?Lateen
@Lateen no, added it to my answer. though you're probably happy with yours?Rebellion
Oh duh, didn't see the add. My bad.Lateen
@ErickT @rdove I was going to use your resolver code as I need the ability to inject dependencies into hub constructors. However my understanding of Castle is that whenever you call resolve a transient using container.Resolve you must release that object when you've finished with it. Clearly the resolver isn't going to do this, and I can't think where you could release objects (the hub's Dispose() perhaps?). Have any of you guys come across this issue?Diffusive
@AndrewStephens I would prefer you to update the answer and add your desired disposal implementation.Inheritrix
@Inheritrix I've updated my answer as things have changedRebellion
Thanks dove, but what is SignalHubActivator for you? I have the same done, eventually it does var resolve = ioc.Resolve(hubType) as IHub inside Create method. Yet this causes an error when a client is disconnecting (not connecting, thats fine). Instead of ILoggingService I have something similar to this: asp.net/signalr/overview/getting-started/… (StockTicker class). That class eventually has other interfaces to talk to that need to be resolvedInheritrix
added SignalHubActivator, my container resolves any dependency as that is what is resolving the ILoggingService. Note that the container has already be configured with all my dependenciesRebellion
My tuppence-worth:- I use a custom IHubActivator class, which was identical to yours initially. As it stands though, Windsor will track each Hub instance that gets created, preventing them from being GC'd, i.e. memory leaks (remember the Windsor rule: "you must release what you explicitly resolve"). To avoid this I call Resolve then immediately call Release. Read last comment (and link to other issues) here: github.com/SignalR/SignalR/issues/3208. The downside to this trick is that your hubs can't implement IDisposable as Release results in Dispose() being called.Diffusive
thanks Andrew, I've taken that on board, have updated the answer, be interested to hear if that's what you meantRebellion
@AndrewStephens nice suggestion! Unfortunately it didn't help for me. I register my hub now like so: Container.Register(Component.For<TService>().LifestyleTransient()); (TService would be my Hub, through some interfaces) and I have the IHubActivator like dove has. Still onReconnect or onDisconnect when it is trying to resolve other interfaces, it gives the same error. These are mostly registered like so: Container.Register(Component.For<TService>().ImplementedBy<TService>();Inheritrix
L
3

Here's what I ended up doing. First I followed along with the Windsor wiki to get my ASP.NET MVC3 setup. My Global.asax.cs:

private static IWindsorContainer _container;

    protected void Application_Start()
    {
        BootstrapContainer();
        RegisterRoutes(RouteTable.Routes);
        AreaRegistration.RegisterAllAreas();
        RegisterGlobalFilters(GlobalFilters.Filters);
    }

    protected void Application_End()
    {
        _container.Dispose();
    }

    private static void BootstrapContainer()
    {
        _container = new WindsorContainer().Install(FromAssembly.This());
        RouteTable.Routes.MapHubs(new CastleWindsorDependencyResolver(_container));
        var controllerFactory = new WindsorControllerFactory(_container.Kernel);
        ControllerBuilder.Current.SetControllerFactory(controllerFactory);
    }
    ...

CastleWindsorDependencyResolver came from here

Copied:

public class CastleWindsorDependencyResolver : DefaultDependencyResolver
{
    private readonly IWindsorContainer _container;

    public CastleWindsorDependencyResolver(IWindsorContainer container)
    {
        if (container == null)
        {
            throw new ArgumentNullException("container");
        }

        _container = container;

        // perform the lazy registrations
        foreach (var c in _lazyRegistrations)
            _container.Register(c);

        _lazyRegistrations.Clear();
    }

    public override object GetService(Type serviceType)
    {
        if (_container.Kernel.HasComponent(serviceType))
            return _container.Resolve(serviceType);
        return base.GetService(serviceType);
    }

    public override IEnumerable<object> GetServices(Type serviceType)
    {
        IEnumerable<object> objects;
        if (_container.Kernel.HasComponent(serviceType))
            objects = _container.ResolveAll(serviceType).Cast<object>();
        else
            objects = new object[] { };

        var originalContainerServices = base.GetServices(serviceType);
        if (originalContainerServices != null)
            return objects.Concat(originalContainerServices);

        return objects;
    }

    public override void Register(Type serviceType, Func<object> activator)
    {
        if (_container != null)
            // cannot unregister components in windsor, so we use a trick
            _container.Register(Component.For(serviceType).UsingFactoryMethod<object>(activator, true).OverridesExistingRegistration());
        else
            // lazy registration for when the container is up
            _lazyRegistrations.Add(Component.For(serviceType).UsingFactoryMethod<object>(activator));

        // register the factory method in the default container too
        //base.Register(serviceType, activator);
    }

    // a form of laxy initialization is actually needed because the DefaultDependencyResolver starts initializing itself immediately
    // while we now want to store everything inside CastleWindsor, so the actual registration step have to be postponed until the
    // container is available
    private List<ComponentRegistration<object>> _lazyRegistrations = new List<ComponentRegistration<object>>();
}

public static class WindsorTrickyExtensions
{
    /// <summary>
    /// Overrideses the existing registration:
    /// to overide an existiong component registration you need to do two things:
    /// 1- give it a name.
    /// 2- set it as default.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="componentRegistration">The component registration.</param>
    /// <returns></returns>
    public static ComponentRegistration<T> OverridesExistingRegistration<T>(this ComponentRegistration<T> componentRegistration) where T : class
    {
        return componentRegistration
        .Named(Guid.NewGuid().ToString())
        .IsDefault();
    }
}

I wasn't sure WTF the HubsInstaller was trying to do from that same project but I made my own which seems to work fine (I am of course open to any suggestions why this could suck):

public class HubsInstallers : IWindsorInstaller
{
    public void Install(IWindsorContainer container, IConfigurationStore store)
    {
        container.Register(Classes.FromThisAssembly()
                            .BasedOn<IHub>()
                            .LifestyleTransient());
    }
}

Also this is for the newer SignalR versions 0.5+

Lateen answered 13/7, 2012 at 21:34 Comment(4)
I made mine singleton, but guess that depends on what your hubs are doing.Rebellion
Care to share the code? What are the pro's and con's of doing it this way?Lateen
used LifestyleSingleton instead of transient, that's all i meant about that comment. anything else you meant?Rebellion
Well I don't know if I grok the reasons why to really even use LifestyleTransient other than it worked. Just wondering what the differences were.Lateen
C
1

dove answer is fine but it is a bit confusing, adding another more specific answer. My main goal is this to work:

[HubName("MyHub")]
public class MyHub : Hub
{
    public IJobRepository JobRepository { get; }

    public MyHub(IJobRepository jobRepository)
    {
        JobRepository = jobRepository ?? throw new ArgumentNullException(nameof(jobRepository));
    }
...
}

Of course what you want is your Hubs to be created for you, they are usually created by SignalR but now that they have some dependencies SignalR cannot create them. SignalR itself has a Dependency Resolver (in SignalR namespace) which uses to get its own dependencies, you can add stuff to it, but we want Windsor remember? So we are going to change just how the IHubActivator creates hubs, we are not going to use SignalR's but this one:

public class SignalRHubActivator : IHubActivator
{
    private readonly IWindsorContainer _container;

    public SignalRHubActivator(IWindsorContainer container)
    {
        _container = container;
    }

    public IHub Create(HubDescriptor descriptor)
    {
        var result = _container.Resolve(descriptor.HubType) as IHub;
        if (result is Hub)
        {
            _container.Release(result);
        }
        return result;
    }
}

To replace this in SignalR container you have to do something like:

// Get an instance of the hub creator (see note below)
var _hubActivator = new SignalRHubActivator(container);

// Get the SignalR's Default Dependency Resolver
var signalRResolver = new Microsoft.AspNet.SignalR.DefaultDependencyResolver();
// Override the IHubActivator service
signalRResolver.Register(typeof(IHubActivator), () => _hubActivator);
// now map SignalR with this configuration
appBuilder.MapSignalR(new HubConfiguration { Resolver = signalRResolver });

And that's it, you should also register all your Hubs with Windsor

container.Register(Classes.FromThisAssembly()
    .BasedOn(typeof(Microsoft.AspNet.SignalR.Hub)));
...
container.Register(Component.For<IJobRepository>()).ImplementedBy<JobRepository>());

Note: I registered the SignalRHubActivator as a component too, this is because the Startup class I use receives the activator as a dependency:

container.Register(Component.For<SignalRHubActivator>().
    DependsOn(Dependency.OnValue("container", container)));
Casmey answered 29/9, 2017 at 16:34 Comment(2)
Can you detail a bit where you have the container in the Startup class ? I tried injecting it using your suggestion, but it doesn't seem to work.Marijo
I'm making my startup class dependant on the hubActivator, not the Windsor container, that's why I do the latest registration there where I register the SignalRHubActivator. Also my Startup class is registered as a component in Windsor, then I have yet another component which is the root component, this one has a dependency on the Startup class, and in the app entry point I setup the container, then get the root component and then I access the startup class injected and then I call Start. Btw, my Startup class is called WebAppServiceCasmey

© 2022 - 2024 — McMap. All rights reserved.