I Implemented the answer on Using Simple Injector with SignalR and my services are successfully resolved, not until OnDisconnected is called on the Hub class. Then I had to follow this question Simple Injector per-web-api-request dependency in SignalR hub as a workaround but get an exception whenever the hub instance is requested.
I get the exception saying:
[SimpleInjector.ActivationException] The registered delegate for type ChatHub threw an exception. The ChatHub is registered as 'Hybrid Web Request / Execution Context Scope' lifestyle, but the instance is requested outside the context of a Hybrid Web Request / Execution Context Scope.
StackTrace:
at SimpleInjector.InstanceProducer.GetInstance()
at SimpleInjector.Container.GetInstance(Type serviceType)
at QuickChat.Hubs.SimpleInjectorHubActivator.Create(HubDescriptor descriptor) in c:\Users\Peter\Documents\Visual Studio 2013\Projects\QuickChat\QuickChat\Hubs\SimpleInjectorHubActivator.cs:line 21
at Microsoft.AspNet.SignalR.Hubs.DefaultHubManager.ResolveHub(String hubName)
at Microsoft.AspNet.SignalR.Hubs.HubDispatcher.CreateHub(IRequest request, HubDescriptor descriptor, String connectionId, StateChangeTracker tracker, Boolean throwIfFailedToCreate)
InnerException:
at SimpleInjector.Scope.GetScopelessInstance[TService,TImplementation](ScopedRegistration`2 registration)
at SimpleInjector.Scope.GetInstance[TService,TImplementation](ScopedRegistration`2 registration, Scope scope)
at SimpleInjector.Advanced.Internal.LazyScopedRegistration`2.GetInstance(Scope scope)
at lambda_method(Closure )
at SimpleInjector.InstanceProducer.GetInstance()
See below my current code configs.
Hub activator:
public class SimpleInjectorHubActivator : IHubActivator
{
private readonly Container _container;
public SimpleInjectorHubActivator(Container container)
{
_container = container;
}
public IHub Create(HubDescriptor descriptor)
{
return (IHub)_container.GetInstance(descriptor.HubType);
}
}
SimpleInjector service registration:
public class SimpleInjectorConfig
{
public static void Register()
{
// Create the container as usual.
var container = new Container();
var hybrid = Lifestyle.CreateHybrid(
() => container.GetCurrentExecutionContextScope() != null,
new SimpleInjector.Integration.Web.WebRequestLifestyle(),
new ExecutionContextScopeLifestyle());
// Register types:
container.RegisterSingle<MembershipRebootConfiguration>(MembershipRebootConfig.Create);
container.Register<DefaultMembershipRebootDatabase>(() => new CustomMembershipRebootDatabase());
container.Register<UserAccountService>(() => new UserAccountService(container.GetInstance<MembershipRebootConfiguration>(), container.GetInstance<IUserAccountRepository>()));
container.Register<AuthenticationService, SamAuthenticationService>();
container.RegisterPerWebRequest<IUserAccountQuery, DefaultUserAccountRepository>();
container.RegisterPerWebRequest<IUserAccountRepository, DefaultUserAccountRepository>();
container.Register(() => new DataAccess.EF.DataContext(), hybrid);
container.Register<IUnitOfWork, UnitOfWork>(hybrid);
container.Register<IUserService, UserService>(hybrid);
//Register SimpleAuthentication callback provider class
container.RegisterPerWebRequest<IAuthenticationCallbackProvider, SimpleAuthenticationProviderController>();
//Register SimpleAuthentication MVC controller.
container.RegisterPerWebRequest<SimpleAuthenticationController>(
() => new SimpleAuthenticationController(container.GetInstance<IAuthenticationCallbackProvider>(), null));
// This is an extension method from the integration package.
container.RegisterMvcControllers(Assembly.GetExecutingAssembly());
// This is an extension method from the integration package as well.
container.RegisterMvcIntegratedFilterProvider();
//Enable injections to SignalR Hubs
var activator = new SimpleInjectorHubActivator(container);
container.Register<ChatHub, ChatHub>(hybrid);
GlobalHost.DependencyResolver.Register(typeof(IHubActivator), () => activator);
container.Verify();
//Set dependency resolver for MVC
DependencyResolver.SetResolver(new SimpleInjectorDependencyResolver(container));
}
}
Global.asax.cs:
protected void Application_Start()
{
SimpleInjectorConfig.Register();
// Register the default hubs route: ~/signalr/hubs
RouteTable.Routes.MapHubs();
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimTypes.NameIdentifier;
}
Your hybrid lifestyle is wrong. You should turn the predicate around:
var hybrid = Lifestyle.CreateHybrid(
() => container.GetCurrentExecutionContextScope() != null,
new ExecutionContextScopeLifestyle(),
new SimpleInjector.Integration.Web.WebRequestLifestyle());
Tip: instead of reusing that hybrid variable throughout your composition root, you can also set it as the default scoped lifestyle like this:
container.Options.DefaultScopedLifestyle = hybrid;
This way you can change your registrations to the following:
container.Register<ChatHub, ChatHub>(Lifestyle.Scoped);
This makes your registrations easier and cleaner.
Like Steven said in his answer, my hybrid lifestyle is wrong. I had to turn the predicate around:
var hybrid = Lifestyle.CreateHybrid(
() => container.GetCurrentExecutionContextScope() != null,
new ExecutionContextScopeLifestyle(),
new SimpleInjector.Integration.Web.WebRequestLifestyle());
After a few discussions with Steven GitHub I arrived at a conclusion.
I had to make my ChatHub a Humble Object and extract all logic from it and put into a separate class that implements a service interface consisting of methods that expose all the logic contained initially within the ChatHub
public interface IChatService
{
void OnConnected(Guid userId, string connectionId, HubConnectionContext clients);
void OnDisconnected(Guid userId, string connectionId);
void Broadcast(string message, string username, HubConnectionContext clients);
}
public class ChatService : IChatService
{
private readonly IUnitOfWork _unitOfWork;
public UserChatService(IUnitOfWork unitOfWork) {
_unitOfWork = unitOfWork;
}
public void OnConnected(Guid userId, string connectionId, HubConnectionContext clients) {
//Perform other operation using _unitOfWork
}
public void OnDisconnected(Guid userId, string connectionId) {
//Perform other operation using _unitOfWork
}
public void Broadcast(string message, string username, HubConnectionContext clients) {
//Perform other operation using _unitOfWork and HubConnectionContext
//broadcast message to other connected clients
clients.others.broadcast(message);
}
}
The HubConnectionContext really seems like runtime data to me, so I decided to pass it as a params to the methods.
With this, my hub ChatHub looks lightweight, delegating the calls to the ChatService object, and I no longer experience errors when the hub's OnDisconnected() is called .
And don't forget to register your service with the container:
container.Register<IChatService, ChatService>(hybrid);
Related
Trying to do some DI on Web API 2 without third-party tools.
So, from some examples I've got custom dependency resolver (why there's no integrated one? Strange, even Microsoft.Extensions.DependencyInjection provides nothing):
public class DependencyResolver : IDependencyResolver
{
protected IServiceProvider _serviceProvider;
public DependencyResolver(IServiceProvider serviceProvider)
{
this._serviceProvider = serviceProvider;
}
public IDependencyScope BeginScope()
{
return this;
}
public void Dispose()
{
}
public object GetService(Type serviceType)
{
return this._serviceProvider.GetService(serviceType);
}
public IEnumerable<object> GetServices(Type serviceType)
{
return this._serviceProvider.GetServices(serviceType);
}
public void AddService()
{
}
}
then created this class:
public class ServiceConfig
{
public static void Register(HttpConfiguration config)
{
var services = new ServiceCollection();
services.AddScoped<IMyService, MyServiceClient>();
var resolver = new DependencyResolver(services.BuildServiceProvider());
config.DependencyResolver = resolver;
}
}
and registered it:
protected void Application_Start()
{
GlobalConfiguration.Configure(WebApiConfig.Register);
GlobalConfiguration.Configure(ServiceConfig.Register);
}
But when I'm trying to use it:
public class TestController : ApiController
{
private IMyService _myService = null;
public TestController(IMyService myService)
{
_myService = myService;
}
public void Get()
{
_myService.DoWork();
}
}
I'm getting error:
An error occurred when trying to create a controller of type 'TestController'. Make sure that the controller has a parameterless public constructor.
How to cook this one in right way?
What you see happening is related to this problem. In short, Web API will call its default IHttpControllerActivator implementation to request a new controller instance. That instance will call into your DependencyResolver.GetService method. That method will forward the call to MS.DI's GetService method. However, since you didn't register your controllers into the MS.DI container, it will return null. This will cause the default IHttpControllerActivator to try to create the controller using reflection, but this requires a default constructor. Since the controller doesn't have one, this results in the rather cryptic exception message.
The quick solution, therefore, is to register your controllers, e.g.:
services.AddTransient<TestController>();
This, however, will only partly solve your problem because your IDependencyResolver implementation is broken. It is broken in an ugly way, because it might seem to work at first, but will result in memory leaks, because you always resolve from the root container, instead of resolving from a scope. This will cause your resolved controller instances (and other disposable transient components) to stay referenced for the lifetime of your application.
To fix this, you should change your IDependencyResolver implementation to the following:
public class DependencyResolver : IDependencyResolver
{
private readonly IServiceProvider provider;
private readonly IServiceScope scope;
public DependencyResolver(ServiceProvider provider) => this.provider = provider;
internal DependencyResolver(IServiceScope scope)
{
this.provider = scope.ServiceProvider;
this.scope = scope;
}
public IDependencyScope BeginScope() =>
new DependencyResolver(provider.CreateScope());
public object GetService(Type serviceType) => provider.GetService(serviceType);
public IEnumerable<object> GetServices(Type type) => provider.GetServices(type);
public void Dispose() => scope?.Dispose();
}
This implementation will ensure a new IServiceScope is created on each web request and services are always resolved from a request; not from the root IServiceProvider.
Although this will fix your problems, another implementation might still be benificial.
The IDependencyResolver contract is problematic, because it is forced to return null when a call to GetService doesn't result in the correct resolution of a registration. This means that you will end up with these annoying "Make sure that the controller has a parameterless public constructor" errors when you forget to register your controllers.
It is, therefore, much easier to create a custom IHttpControllerActivator instead. In that case you can call GetRequiredService which will never return null:
public class MsDiHttpControllerActivator : IHttpControllerActivator
{
private readonly ServiceProvider provider;
public MsDiHttpControllerActivator(ServiceProvider provider) =>
this.provider = provider;
public IHttpController Create(
HttpRequestMessage request, HttpControllerDescriptor d, Type controllerType)
{
IServiceScope scope = this.provider.CreateScope();
request.RegisterForDispose(scope); // disposes scope when request ends
return (IHttpController)scope.ServiceProvider.GetRequiredService(controllerType);
}
}
This MsDiHttpControllerActivator implementation can be added to the Web API pipeline as follows:
GlobalConfiguration.Configuration.Services
.Replace(typeof(IHttpControllerActivator),
new MsDiHttpControllerActivator(services.BuildServiceProvider(true)));
This removes the need to have an IDependencyResolver implementation. You still need to register your controllers, though:
services.AddTransient<TestController>();
Also note that I changed this:
services.BuildServiceProvider()
To this:
services.BuildServiceProvider(true)
This is a really important change; it protects you (for some part) against Captive Dependencies, which are one of the major problems when using DI Containers. For some obscure reason, the BuildServiceProvider() overload defaults to false, which means it will not validate your scopes.
I have a simple service that contains a List<Foo>. In Startup.cs, I am using the services.addScoped<Foo, Foo>() method.
I am inject the service instance in two different places (controller and middleware), and for a single request, I would expect to get the same instance. However, this does not appear to be happening.
Even though I am adding a Foo to the List in the Controller Action, the Foo list in the Middleware is always empty. Why is this?
I have tried changing the service registration to a singleton, using AddSingleton() and it works as expected. However, this has to be scoped to the current request. Any help or ideas are greatly appreciated!
FooService.cs
public class FooService
{
public List<Foo> Foos = new List<Foo>();
}
Startup.cs
...
public void ConfigureServices(IServiceCollection services)
{
...
services.AddScoped<FooService, FooService>();
}
[Below are the two places where I am injecting the service, resulting in two different instances]
MyController.cs
public class MyController : Controller
{
public MyController(FooService fooService)
{
this.fooService = fooService;
}
[HttpPost]
public void TestAddFoo()
{
//add foo to List
this.fooService.Foos.Add(new Foo());
}
}
FooMiddleware.cs
public AppMessageMiddleware(RequestDelegate next, IServiceProvider serviceProvider)
{
this.next = next;
this.serviceProvider = serviceProvider;
}
public async Task Invoke(HttpContext context)
{
context.Response.OnStarting(() =>
{
var fooService = this.serviceProvider.GetService(typeof(FooService)) as FooService;
var fooCount = fooService.Foos.Count; // always equals zero
return Task.CompletedTask;
});
await this.next(context);
}
That's because when you inject IServiceProvider into your middleware - that's "global" provider, not request-scoped. There is no request when your middleware constructor is invoked (middleware is created once at startup), so it cannot be request-scoped container.
When request starts, new DI scope is created, and IServiceProvider related to this scope is used to resolve services, including injection of services into your controllers. So your controller resolves FooService from request scope (because injected to constructor), but your middleware resolves it from "parent" service provider (root scope), so it's different. One way to fix this is to use HttpContext.RequestServices:
public async Task Invoke(HttpContext context)
{
context.Response.OnStarting(() =>
{
var fooService = context.RequestServices.GetService(typeof(FooService)) as FooService;
var fooCount = fooService.Foos.Count; // always equals zero
return Task.CompletedTask;
});
await this.next(context);
}
But even better way is to inject it into Invoke method itself, then it will be request scoped:
public async Task Invoke(HttpContext context, FooService fooService)
{
context.Response.OnStarting(() =>
{
var fooCount = fooService.Foos.Count; // always equals zero
return Task.CompletedTask;
});
await this.next(context);
}
First of all you shouldn't be using GetService, use the proper DI system that is in place by passing it into the Invoke method as a parameter.
Secondly, the reason you are getting a different object is because the constructor of the middleware is called outside of the scope of any request, during the app initialisation phase. So the container used there is the global provider. See here for a good discussion.
public class AppMessageMiddleware
{
private readonly RequestDelegate _next;
public AppMessageMiddleware(RequestDelegate next, IServiceProvider serviceProvider)
{
_next = next;
}
//Note the new parameter here: vvvvvvvvvvvvvvvvvvvvv
public async Task Invoke(HttpContext context, FooService fooService)
{
context.Response.OnStarting(() =>
{
var fooCount = fooService.Foos.Count;
return Task.CompletedTask;
});
await _next(context);
}
}
We have a websolution with autofac. Now we want to reuse things in a windows service/console app where things are only available when a message comes in from an enterprise bus.
I have the following service to reuse
public SettingsService : ISettingsService {
public readonly ITenantIdentifer _tenantIdentifier
public SettingsService (ITenantIdentifier tenantIdentifier) {
this._tenantIdentifier =tenantIdentifier;
}
// do other stuff
}
The current working setup
the ITenantIdentifier for the webcontext is simply registered for the webapplication using builder.RegisterType<WebTenantIdentifier>().As<ITenantIdentifier>();.
Evething works fine.
Our enterprise bus
The enterprise bus can not resolve the ITenantIdentifier until the message is available. So we created a MessageTenantIdentifier and registered a factory.
public class MessageTenantIdentifier : ITenantIdentifier
{
public delegate MessageTenantIdentifier Factory(int tenantId);
public MessageTenantIdentifier(int tenantId, IOtherDependency stuff)
{
_tenantId = tenantId;
// ...
}
}
// somewhere else the this is registered
builder.RegisterType<MessageTenantIdentifier >().As<ITenantIdentifier>().AsSelf();
builder.RegisterGeneratedFactory<MessageTenantIdentifier.Factory>();
The problem
The factory can only be used when the message is being handled in a
public class MsgTypeHandler : IHandleMessages<MsgType>
{
public MsgTypeHandler(ISettingsService settingsService, MessageTenantIdentifier factory) { ...}
public async Task Handle(MsgType message)
{
var tenantId = message.TenantId;
// THIS IS THE MOMENT I CAN CONFIGURE THE MessageTenantIdentifier
var tenantIdentifier = factory.Invoke(tenantId);
// but this factory is not used against the ISettingsService. The service to be reused. <== THE REAL PROBLEM
}
}
The question
So, how can I solve this issue? E.g. how should I setup the registration of the MessageTenantIdentifier in the servicebus?
Or is my dependency setup just plain wrong?
If the MsgTypeHandler class needs an ISettingsService, but the entire object graph can't be resolved until the tenant ID is available, that means that the MsgTypeHandler is the Composition Root. That's OK, but that means that this is where you resolve your entire object graph, so don't inject individual services here; instead, inject the factory you need:
public class MsgTypeHandler : IHandleMessages<MsgType>
{
public MsgTypeHandler(ISettingsServiceFactory factory) {...}
public async Task Handle(MsgType message)
{
var tenantId = message.TenantId;
ISettingsService svc = this.factory.Create(tenantId);
// User svc here...
}
}
I am trying to create a UserService that I can inject in my classes, that will hold the user currently logged in to my system.
I am using CastleWindsor as my container.
Now my problem is that I am trying to make my UserService disposable, so that the databaseconnection fetching the user on creating will also be disposed when the object is destroyed.
I added the following setup in my Global.asax.cs:
private static void BootstrapContainer()
{
_container = new WindsorContainer().Install(FromAssembly.This());
var controllerFactory = new WindsorControllerFactory(_container.Kernel);
ControllerBuilder.Current.SetControllerFactory(controllerFactory);
GlobalConfiguration.Configuration.DependencyResolver = new WindsorDependencyResolver(_container.Kernel);
_container.Register(Component.For<IUserService>()
.LifestylePerWebRequest()
.ImplementedBy<UserService>());
_container.Register(Component.For<IPrincipal>()
.LifeStyle.PerWebRequest
.UsingFactoryMethod(() => HttpContext.Current.User));
}
Which is called in my Application_Start.
My UserService code is as follows:
public interface IUserService
{
OrganisationBruger User { get; }
int UserId { get; }
}
public class UserService : IUserService, IDisposable
{
private readonly IPrincipal _principal;
private OrganisationBruger _user;
private readonly DatabaseDataContext _db;
public UserService(IPrincipal principal, IDatabaseDataContextFactory dataContextFactory)
{
_principal = principal;
_db = dataContextFactory.GetDataContext();
}
public OrganisationBruger User => _user ?? (_user = GetUser());
public int UserId => Convert.ToInt32(_principal.Identity.Name);
private OrganisationBruger GetUser()
{
return _db.OrganisationBrugers.Single(u => u.ID == UserId);
}
public void Dispose()
{
_db.Dispose();
}
}
When I Debug my code I can see on the very first request I fire it correctly creates the class UserService.cs and then disposes it after the webrequest. Now my problem is the second web request does not seem to call the constructor anymore thus just reusing the formerly created object. This leads to the DatabaseContext already being disposed of.
I thought that LifestylePerWebRequest meant that the UserService would get recreated on every request. Can anyone help me understand this?
So first of all I had overlook the "registration of the module"-part in the documentation. You need to add the following to your web.config:
<httpModules>
<add name="PerRequestLifestyle" type="Castle.MicroKernel.Lifestyle.PerWebRequestLifestyleModule, Castle.Windsor"/>
</httpModules>
Second of all I was not a hundred percent sure how the dependency resolver worked. The problem was that one of the modules using my UserService as a dependency had its lifecycle set to Singleton which is default behavior when you specify nothing about the lifecycle when registering your module with the container.
I fixed the problem by making sure that every module that is using my UserService as dependency is also registered with a lifecycle of LifestylePerWebRequest() or LifestyleTransient().
You should double check if you have any other interface which use IUserService overwrites the lifestyle.
In that case, Castle windsor will not resolve IUserService for each request because ITest is set to singleton.
For example
_container.Register(Component.For<ITest>()
.LifestyleSingleton()
.ImplementedBy<Test>());
public interface ITest
{
}
public class Test: ITest
{
private readonly IUserService _ser;
public Test(IUserService ser)
{
_ser= ser;
}
}
I have a WindsorHttpControllerActivator that implements IHttpControllerActivator. It registers the controller for disposal which ensures a new controller gets created on each request by destroying the old one. This occurs when .LifestylePerWebRequest() completes each request.
public IHttpController Create(
HttpRequestMessage request,
HttpControllerDescriptor controllerDescriptor,
Type controllerType)
{
var controller =
(IHttpController)_container.Resolve(controllerType);
// Controller disposal ensures new controller for each request, hence DbContexts are fresh and pull fresh data from the DB.
request.RegisterForDispose(
new Release(
() => _container.Release(controller)));
return controller;
}
private class Release : IDisposable
{
private readonly Action _release;
public Release(Action release)
{
_release = release;
}
public void Dispose()
{
_release();
}
}
According to this post, it should be possible to inject per-web-request dependencies into SignalR hubs (although with some limitations like problem with OnDisconnected() method). In my case it is ASP Web API (not MVC) and it does not work for some reason.
Here are relevant parts:
container.RegisterWebApiControllers(httpConfiguration);
container.RegisterWebApiRequest<DbContext, MyDbContext>();
container.RegisterWebApiRequest<ISampleRepository, SampleRepository>(); //DbContext injected to SampleRepository
//Enable injections to SignalR Hubs
var activator = new SimpleInjectorHubActivator(container);
GlobalHost.DependencyResolver.Register(typeof(IHubActivator), () => activator);
This class makes possible to inject into hubs:
public class SimpleInjectorHubActivator : IHubActivator
{
private readonly Container _container;
public SimpleInjectorHubActivator(Container container)
{
_container = container;
}
public IHub Create(HubDescriptor descriptor)
{
return (IHub)_container.GetInstance(descriptor.HubType);
}
}
And Hub itself:
[HubName("sample")]
public class SampleHub : Hub
{
public ActiveBetsHub(ISampleRepository repository)
{
}
//Irrelevant methods here. OnDisconnected() NOT implemented!
}
With this setup I get exception:
No registration for type SampleHub could be found and
an implicit registration could not be made.
The ISampleRepository is registered as 'Web API Request'
lifestyle, but the instance is requested outside the context of a Web API Request.
Which is expected as I understand. However I get exactly same exception when I change Lifestyle of repository to Transient:
var transientHybrid = Lifestyle.CreateHybrid(() => HttpContext.Current != null, new WebApiRequestLifestyle(), Lifestyle.Transient);
container.Register<ISampleRepository, SampleRepository>(transientHybrid);
I suspect the problem could lie in HttpContext.Current != null check that is not working for Web API the same way as for MVC.
SignalR 2.2
Simple Injector 2.8.3
What do I miss?
UPDATE:
This is stack trace on how SignalR creates Hubs:
at SimpleInjector.InstanceProducer.GetInstance()
at SimpleInjector.Container.GetInstance(Type serviceType)
at MyWebAbi.WebApiApplication.SimpleInjectorHubActivator.Create(HubDescriptor descriptor) in Global.asax.cs:line 108
at Microsoft.AspNet.SignalR.Hubs.DefaultHubManager.ResolveHub(String hubName)
at Microsoft.AspNet.SignalR.Hubs.HubDispatcher.CreateHub(IRequest request, HubDescriptor descriptor, String connectionId, StateChangeTracker tracker, Boolean throwIfFailedToCreate)
So the proper solution would be to use ExecutionContextScope for a Hubs but this scope needs to be explicitly closed which makes things more complicated...
Your definition of your hybrid lifestyle is incorrect. The WebApiRequestLifestyle does not depend in any way on the HttpContext so checking whether HttpContext.Current != null will not work. You will have to check if there is an active Web API request lifestyle scope (or execution context scope, which is basically the same) by calling container.GetCurrentExecutionContextScope():
var transientHybrid = Lifestyle.CreateHybrid(
() => container.GetCurrentExecutionContextScope() != null,
new WebApiRequestLifestyle(),
Lifestyle.Transient);
Do note however that you should be very careful composing a hybrid lifestyle of a scoped lifestyle and transient, because this will easily yield in wrong results. This is actually the default behavior of some DI libraries, but this is a design flaw IMO. I assume you very consciously registered your MyDbContext with the scoped lifestyle, because you need to make sure that the same instance is used throughout the request. Using the Transient lifestyle means that you might get multiple MyDbContext during the request. This might not be a problem, because in your hubs you might currently only have one reference to your MyDbContext, but your code might break once your object graph changes and a second reference to MyDbContext is added.
So instead, I would advice not using this combination of lifestyles. Instead, just use either the WebApiRequestLifestyle or the ExecutionContextScopeLifestyle (they are the same) and make sure that such a execution context scope is started before your hub is resolved.
And by the way, don't forget to register your hubs explicitly in Simple Injector. This allows Simple Injector to analyze the complete object graph for you including your hub classes.
Recently I faced the same problem and found the following working quite well, hope this will help someone:
public class SignalRDependencyResolver : DefaultDependencyResolver
{
public SignalRDependencyResolver(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public override object GetService(Type serviceType)
{
return _serviceProvider.GetService(serviceType) ?? base.GetService(serviceType);
}
public override IEnumerable<object> GetServices(Type serviceType)
{
var #this = (IEnumerable<object>) _serviceProvider.GetService(typeof (IEnumerable<>).MakeGenericType(serviceType));
var #base = base.GetServices(serviceType);
return #this == null ? #base : #base == null ? #this : #this.Concat(#base);
}
private readonly IServiceProvider _serviceProvider;
}
public class SignalRHubDispatcher : HubDispatcher
{
public SignalRHubDispatcher(Container container, HubConfiguration configuration) : base(configuration)
{
_container = container;
}
protected override Task OnConnected(IRequest request, string connectionId)
{
return Invoke(() => base.OnConnected(request, connectionId));
}
protected override Task OnReceived(IRequest request, string connectionId, string data)
{
return Invoke(() => base.OnReceived(request, connectionId, data));
}
protected override Task OnDisconnected(IRequest request, string connectionId, bool stopCalled)
{
return Invoke(() => base.OnDisconnected(request, connectionId, stopCalled));
}
protected override Task OnReconnected(IRequest request, string connectionId)
{
return Invoke(() => base.OnReconnected(request, connectionId));
}
private async Task Invoke(Func<Task> method)
{
using (_container.BeginExecutionContextScope())
await method();
}
private readonly Container _container;
}
public class Startup
{
public void Configuration(IAppBuilder app)
{
var container = new Container();
container.Options.DefaultScopedLifestyle = new ExecutionContextScopeLifestyle();
container.Register<DbContext, MyDbContext>(Lifestyle.Scoped);
container.Register<ISampleRepository, SampleRepository>(Lifestyle.Scoped);
// if you want to use the same container in WebApi don't forget to add
app.Use(async (context, next) => {
using (container.BeginExecutionContextScope())
await next();
});
// ... configure web api
var config = new HubConfiguration
{
Resolver = new SignalRDependencyResolver(container)
}
// ... configure the rest of SignalR
// pass SignalRHubDispatcher
app.MapSignalR<SignalRHubDispatcher>("/signalr", config);
}
}