Castle Windsor integration with SignalR - how should I approach this?

I get started with SignalR and it works great when everything is set up. However, almost all the applications I'm working on use Castle Windsor, so it would be great to use them together. The reason I want to do this is because I can use Castle dependencies / services inside a persistent connection.

I dug into the source code, and it looks like I could either replace DependencyResolver with a castle based on one (i.e. Castle implementation of IDependencyResolver), or I could change the use of DependencyResolver in Castle.

Which one is the best idea? Is there any other approach I could use to combine Castle and SignalR?

Thanks Erick

+6
source share
3 answers

August 2016 Update

Following the comment, I no longer use this approach below, but now I use GlobalHost.DependencyResolver

So in Global.asax.cs I initialize 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 RESPONSE from 2012

I went with the first setting option of our own DependencyResolver

 AspNetHost.SetResolver(new SignalResolver(_container)); 

I can provide a SignalResolver if necessary, but not yet readable.

Another important point: hubs must have an empty constructor, so our lock container enters through properties, for example.

 public class NotificationHub : Hub, INotificationHub { public INotificationService NotificationService { get; set; } 

and the requested resolver

 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; } } } 
+6
source

Here is what I ended up doing. First, I went along with the Wiki Windsor to get the 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 was not sure if the WTF that the HubsInstaller was trying to make from the same project, but I made my own, which seems to work fine (I'm certainly open to any suggestions why this might suck):

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

This is also for new versions of SignalR 0.5 +

+3
source

The dve answer is fine, but it's a bit confusing by adding another more specific answer. My main goal is 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, you want 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 Resendver Dependency (in the SignalR namespace) that it uses to get its dependencies, you can add material to it, but we want Windsor to remember? Thus, we are going to change the way IHubActivator creates hubs, we will not use SignalR, 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 the SignalR container, you should do something like:

 // Get an instance of the hub creator (see note below) var _hubActivator = new SignalRHubActivator(container); // Get the SignalR 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 for this, you must 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 SignalRHubActivator as a component because the Startup class that I use gets the activator as a dependency:

 container.Register(Component.For<SignalRHubActivator>(). DependsOn(Dependency.OnValue("container", container))); 
+1
source

Source: https://habr.com/ru/post/915240/


All Articles