I thought using my own IoC would be pretty straight forward with SignalR and maybe it is; most likely I'm doing something wrong. Here's my code I have so far:
private static void InitializeContainer(Container container)
{
container.Register<IMongoHelper<UserDocument>, MongoHelper<UserDocument>>();
// ... registrations like about and then:
var resolver = new SimpleInjectorResolver(container);
GlobalHost.DependencyResolver = resolver;
}
and then my class:
public class SimpleInjectorResolver : DefaultDependencyResolver
{
private Container _container;
public SimpleInjectorResolver(Container container)
{
_container = container;
}
public override object GetService(Type serviceType)
{
return _container.GetInstance(serviceType) ?? base.GetService(serviceType);
}
public override IEnumerable<object> GetServices(Type serviceType)
{
return _container.GetAllInstances(serviceType) ?? base.GetServices(serviceType);
}
}
What ends up happening is I get an error that IJavaScriptProxyGenerator can't be resolved, so I think, well I'll add the registration:
container.Register<IJavaScriptProxyGenerator, DefaultJavaScriptProxyGenerator>(
ConstructorSelector.MostParameters);
but then there are a bunch of others! I get to:
container.Register<IDependencyResolver, SimpleInjectorResolver>();
container.Register<IJavaScriptMinifier, NullJavaScriptMinifier>();
container.Register<IJavaScriptProxyGenerator, DefaultJavaScriptProxyGenerator>(
ConstructorSelector.MostParameters);
container.Register<IHubManager, DefaultHubManager>();
container.Register<IHubActivator, DefaultHubActivator>();
container.Register<IParameterResolver, DefaultParameterResolver>();
container.Register<IMessageBus, InProcessMessageBus>(ConstructorSelector.MostParameters);
Which still gives me "No registration for type ITraceManager
could be found." ... but now I'm wondering if I'm doing this right at all as I hoping I wouldn't need to re-wire everything SignalR is doing...right? Hopefully? If not I'll keep trudging along but I'm a SignalR and Simple Injector newb so thought I'd ask first. :)
Additional: https://cuttingedge.it/blogs/steven/pivot/entry.php?id=88 since SignalR had multiple constructors.
The following worked for me. In addition, you will need to register a delegate with the container for your hub class before instantiating the dependency resolver.
UPDATE This answer has been updated for SignalR version 1.0
This is how to build a SignalR
IDependencyResolver
for Simple Injector:Unfortunately, there is an issue with the design of the
DefaultDependencyResolver
. That's why the implementation above does not inherit from it, but wraps it. I created an issue about this on the SignalR site. You can read about it here. Although the designer agreed with me, unfortunately the issue hasn't been fixed in version 1.0.I hope this helps.
Want to throw my 2 cents in here with the other answers, which can be helpful in finding your own way with dependency injection in SignalR, either using SimpleInjector or another IoC.
Using @Steven's answer
If you decide to use Steven's answer, make sure you register your hub routes before you compose the root. The
SignalRRouteExtensions.MapHubs
extension method (a.k.a.routes.MapHubs()
) will callRegister(Type, Func<object>)
on theGlobalHost.DependencyResolver
when mapping the hub routes, so if you swap out theDefaultDependencyResolver
with Steven'sSimpleInjectorResolver
before the routes are mapped, you will run into hisNotSupportedException
.Using @Nathanael Marchand's answer
This is my favorite. Why?
SimpleInjectorDependencyResolver
.DefaultDependencyResolver
(a.k.a.GlobalHost.DependencyResolver
), which means even less code.DefaultDependencyResolver
, it will "just work".Like Nathanael said though, this is only if you care about the dependencies on your
Hub
classes, which will probably be the case for most. If you want to mess around with injecting other dependencies into SignalR, you might want to go with Steven's answer.Problems with per-web-request dependencies in a
Hub
There is an interesting thing about SignalR... when a client disconnects from a hub (for example by closing their browser window), it will create a new instance of the
Hub
class in order to invokeOnDisconnected()
. When this happens,HttpContext.Current
is null. So if thisHub
has any dependencies that are registered per-web-request, something will probably go wrong.In Ninject
I tried out SignalR dependency injection using Ninject and the ninject signalr dependency resolver on nuget. With this configuration, dependencies that are bound
.InRequestScope()
will be created transiently when injected into aHub
during a disconnect event. SinceHttpContext.Current
is null, I suppose Ninject just decides to ignore it and create transient instances without telling you. Maybe there was a configuration setting to tell ninject to warn about this, but it was not the default.In SimpleInjector
SimpleInjector on the other hand will throw an exception when a
Hub
depends on an instance that is registered withWebRequestLifestlyle
:...note this exception will only bubble up when
HttpContext.Current == null
, which as far as I can tell, only happens when SignalR requests aHub
instance in order to invokeOnDisconnected()
.Solutions for per-web-request dependencies in a
Hub
Note that none of these are really ideal, it will all depend on your application requirements.
In Ninject
If you need non-transient dependencies, just don't override
OnDisconnected()
or do anything custom there with the class dependencies. If you do, each dependency in the graph will be a separate (transient) instance.In SimpleInjector
You need a hybrid lifestyle between
WebRequestLifestlye
and eitherLifestyle.Transient
,Lifestyle.Singleton
, orLifetimeScopeLifestyle
. WhenHttpContext.Current
is not null, dependencies will only live as long as the web request as you would normally expect. However whenHttpContext.Current
is null, dependencies will either be injected transiently, as singletons, or within a lifetime scope.More about
LifetimeScopeLifestyle
In my case, I have an EntityFramework
DbContext
dependency. These can be tricky because they can expose problems when registered transiently or as singletons. When registered transiently, you can end up with exceptions while trying to work with entities attached to 2 or moreDbContext
instances. When registered as a singleton, you end up with more general exceptions (don't ever register aDbContext
as a singleton). In my case I needed theDbContext
to live within a specific lifetime in which the same instance can be reused across many nested operations, which means I needed theLifetimeScopeLifestyle
.Now if you used the hybrid code above with the
falseLifestyle: new LifetimeScopeLifestyle()
line, you will get another exception when your customIHubActivator.Create
method executes:The way you have set up a lifetime scoped dependency goes like this:
Any dependencies that are registered with lifetime scope must be resolved within this
using
block. Furthermore, if any of those dependencies implementIDisposable
, they will be disposed of at the end of theusing
block. Don't be tempted to do something like this:I asked Steven (who also happens to be the SimpleInjector author in case you didn't know) about this, and he said:
You can't use
IHubActivator
to scope the dependencies because it does not live as long as theHub
instance it creates. So even if you wrapped theBeginLifetimeScope()
method in ausing
block, your dependencies would be disposed immediately after theHub
instance is created. What you really need here is another layer of indirection.What I ended up with, with much thanks to Steven's help, is a command decorator (and a query decorator). A
Hub
cannot depend on per-web-request instances itself, but must instead depend on another interface whose implementation depends on the per-request instances. The implementation that is injected into theHub
constructor is decorated (via simpleinjector) with a wrapper that begins and disposes of the lifetime scope.... it is the decorated
ICommandHandler<T>
instances that depend on per-web-request instances. For more information on the pattern used, read this and this.Example registration
From SignalR 2.0 (and the beta's) there is a new way of setting the dependency resolver. SignalR moved to OWIN startup to do the configuration. With Simple Injector you'd do it like this:
You'd have to explicitly inject your hubs like so:
This config is running live on a high traffic website without problems.
Well, I tried yesterday and I've found a solution. According to me, the only moment where I want dependency injection in SignalR is for my hubs: I don't care about how SignalR is working inside ! So instead of replacing the DependencyResolver, I created my own implementation of IHubActivator :
That I can register like this (in Application_Start) :