I've recently upgraded to the new version of the excellent SignalR library, and moved all my Dependency Injection from StructureMap to Ninject, as Ninject seemed to be better supported.
I've got the dependency injection working fine for Server-side notifications using the "Broadcasting over a Hub from outside of a Hub" described here: https://github.com/SignalR/SignalR/wiki/Hubs.
The problem I'm getting is that all SignalR messages originating from the Javascript hub don't seem to be triggering the dependency injection.
I'm also using MVC4 WebAPI which also takes some shoe-horning to get dependency injection working.
Here's my Hub:
public class PresenceHub : Hub, IPresenceHub
{
private readonly IUserRepository _userRepository;
private readonly IFormsAuthenticationProvider _formsAuthenticationProvider;
public PresenceHub(IFormsAuthenticationProvider formsAuthenticationProvider, IUserRepository userRepository)
{
_userRepository = userRepository;
_formsAuthenticationProvider = formsAuthenticationProvider;
}
public void PresenceChange(string presence)
{
var user = _userRepository.FindById(_formsAuthenticationProvider.GetUserId());
var rosterEntry = Mapper.Map<User, RosterEntryDto>(user);
rosterEntry.Presence = presence;
Clients.updatePresence(rosterEntry);
}
}
Here's my Ninject Bootstrapper:
Namespace SLx.Web.App_Start
{
using System;
using System.Web;
using Microsoft.Web.Infrastructure.DynamicModuleHelper;
using Ninject;
using Ninject.Web.Common;
public static class NinjectWebCommon
{
private static readonly Bootstrapper bootstrapper = new Bootstrapper();
public static void Start()
{
DynamicModuleUtility.RegisterModule(typeof(OnePerRequestHttpModule));
DynamicModuleUtility.RegisterModule(typeof(NinjectHttpModule));
bootstrapper.Initialize(CreateKernel);
}
public static void Stop()
{
bootstrapper.ShutDown();
}
private static IKernel CreateKernel()
{
var kernel = new StandardKernel();
kernel.Bind<Func<IKernel>>().ToMethod(ctx => () => new Bootstrapper().Kernel);
kernel.Bind<IHttpModule>().To<HttpApplicationInitializationHttpModule>();
// SignalR Ninject Resolver
GlobalHost.DependencyResolver = new SignalR.Ninject.NinjectDependencyResolver(kernel);
// WebApi Ninject Resolver
GlobalConfiguration.Configuration.DependencyResolver = new NinjectDependencyResolver(kernel);
RegisterServices(kernel);
return kernel;
}
private static void RegisterServices(IKernel kernel)
{
}
}
I'm notifying clients on the serverside via a PresenceProxy defined as follows:
public class PresenceHubProxy : IPresenceHubProxy
{
private readonly IHubContext _hubContext;
public PresenceHubProxy()
{
_hubContext = GlobalHost.ConnectionManager.GetHubContext<PresenceHub>();
}
public void NotifyLogin(RosterEntryDto user)
{
_hubContext.Clients.updatePresence(user);
}
public void NotifyLogout(RosterEntryDto user)
{
_hubContext.Clients.updatePresence(user);
}
}
The Proxy works fine, injected into Controllers or their dependencies, and can send messages to the clients.
When the clients try to call SignalR via Javascript I get the following error:
No parameterless constructor defined for this object.
It looks like Ninject is not being invoked because the dependencies are not being injected into the constructor. What do I need to do to get Dependency Injection working for Javascript calls too?
Update --
Following advice from DFowler, I've replaced the Resolver in PostApplicationStart. Debugging I can see in the Immediate Window that SignalR.GlobalHost.Resolver is of type NinjectDependencyResolver but its still not working I get the same error - no paramaterless constructor.
I've then removed the NinjectDependencyResolver NuGet Library and added the source file to my solution and am using that for debugging purposes. Debugging on GetService and GetServices shows that neither method is ever called in NinjectDependencyResolver.
Any Ideas?
Problem was I hadn't called RouteTable.Routes.MapHubs:
GlobalHost.DependencyResolver = new SignalRNinjectResolver(NinjectWebCommon.Kernel);
RouteTable.Routes.MapHubs(new SignalRNinjectResolver(NinjectWebCommon.Kernel));
From the docs https://github.com/SignalR/SignalR/wiki/Extensibility:
NOTE: DO NOT override the global resolver in PreApplicationStart, it will not work, or it'll work only sometimes. Do it in PostApplicationStart (using WebActivator) or in Global.asax.
Related
I am new to SignalR and building a live chat by using .NET 5 and angular 13. While establishing the connection it is giving this error-- Connection closed with an error. NullReferenceException
After multiple debagging I found out what is the root cause of the problem. When I inject any dependency to my message hub. NullReferenceException error is happened. without dependency the connection is established properly.
here is my MessageHub.cs code (with dependency)
public class MessageHub : Hub
{
private readonly IMessageRepository _messageRepository;
private readonly IUserRepository _userRepository;
public MessageHub(IMessageRepository messageRepository, IUserRepository userRepository)
{
_messageRepository = messageRepository;
_userRepository = userRepository;
}
public override async Task OnConnectedAsync()
{
var httpContext = Context.GetHttpContext();
var otherUser = httpContext.Request.Query["user"].ToString();
var groupName = GetGroupName(Context.User.GetUsername(), otherUser);
await Groups.AddToGroupAsync(Context.ConnectionId, groupName);
}
public override async Task OnDisconnectedAsync(Exception exception)
{
await base.OnDisconnectedAsync(exception);
}
}
Startup.cs (ConfigureServices)
services.AddSignalR(e=>
{
e.EnableDetailedErrors = true;
});
Startup.cs (Configure)
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapHub<MessageHub>("hubs/message");
});
I extremely need this dependency to complete my functionality. I got stack with this. If I remove the Dependency it works fine. I tried to find some solution from various sources but didn't work for me. if anyone help me. I will be really appreciate for this.
After doing some study, a solution I applied that worked for me. We Can Retrieve service object. so in startup.cs what I did given below
Startup.cs (ConfigureServices)
//we can retrieve service object
public static IServiceProvider serviceProvider;
services.AddSignalR()
services.AddScoped<IUserRepository, UserRepository>();
services.AddScoped<IMessageRepository, MessageRepository>();
In my MessageHub.cs instead of using Dependency Injection I Retrieve Required Service and define it in the filed like below
MessageHub.cs
public class MessageHub : Hub
{
private readonly IMessageRepository _messageRepository;
private readonly IUserRepository _userRepository;
public MessageHub()
{
_messageRepository = (IMessageRepository)Startup.serviceProvider.GetRequiredService(typeof(IMessageRepository));
_userRepository = (IUserRepository)Startup.serviceProvider.GetRequiredService(typeof(IUserRepository));
}
// other code....
}
It worked for me. Now I can implement my necessary method from IRepository and SignalR also working properly.
I am trying to use Ninject to inject an EventLogger instance into a custom ExceptionFilterAttribute. Whenever I run the code, the EventLogger instance is null. I have implemented an IFilterProvider to resolve dependencies in a similar manner for my custom AuthorizationFilterAttribute, and that works fine. Any ideas?
Not Working
public class ErrorHandlingAttribute : ExceptionFilterAttribute
{
[Inject]
public IEventLogger EventLogger { get; set; }
public override void OnException(HttpActionExecutedContext actionExecutedContext)
{
EventLogger.LogException(actionExecutedContext.Exception);
actionExecutedContext.Response = actionExecutedContext.Request.
CreateResponse(HttpStatusCode.BadRequest,
new ServiceErrorResponseDTO("An unhandled exception occurred while calling " +
actionExecutedContext.Request.RequestUri.ToString() +
". This event has been logged. If you continue to receive this error contact Weichert"));
}
}
Working
public class RequireAuthorizationAttribute : AuthorizationFilterAttribute
{
[Inject]
public IServiceRepository ServiceRepository { get; set; }
public override void OnAuthorization(HttpActionContext actionContext)
{
#region Header Authentication
var authHeader = actionContext.Request.Headers.Authorization;
if (authHeader != null)
{
Custom IFilterProvider
public class NinjectWebApiFilterProvider : IFilterProvider
{
private IKernel _kernel;
public NinjectWebApiFilterProvider(IKernel kernel)
{
_kernel = kernel;
}
public IEnumerable<FilterInfo> GetFilters(HttpConfiguration configuration, HttpActionDescriptor actionDescriptor)
{
var controllerFilters = actionDescriptor.ControllerDescriptor.GetFilters().Select(instance => new FilterInfo(instance, FilterScope.Controller));
var actionFilters = actionDescriptor.GetFilters().Select(instance => new FilterInfo(instance, FilterScope.Action));
var filters = controllerFilters.Concat(actionFilters);
foreach(var filter in filters)
{
_kernel.Inject(filter.Instance);
}
return filters;
}
}
NinjectWebCommon CreateKernel Method
private static IKernel CreateKernel()
{
var kernel = new StandardKernel();
kernel.Bind<Func<IKernel>>().ToMethod(ctx => () => new Bootstrapper().Kernel);
kernel.Bind<IHttpModule>().To<HttpApplicationInitializationHttpModule>();
// Ad Ninject support for Web API.
GlobalConfiguration.Configuration.DependencyResolver = new NinjectDependencyResolver(kernel);
GlobalConfiguration.Configuration.Services.Add(typeof(IFilterProvider),
new NinjectWebApiFilterProvider(kernel));
RegisterServices(kernel);
return kernel;
}
NinjectWebCommon Bindings
private static void RegisterServices(IKernel kernel)
{
kernel.Bind<ServiceDbContext>().To<ServiceDbContext>();
kernel.Bind<IServiceRepository>().To<ServiceRepository>();
kernel.Bind<CareerDevelopmentDbContext>().To<CareerDevelopmentDbContext>();
kernel.Bind<ICareerDevelopmentRepository>().To<CareerDevelopmentRepository>();
kernel.Bind<ICareerDevelopmentService>().To<CareerDevelopmentService>();
kernel.Bind<IEventLogger>().To<ServiceEventLogger>();
kernel.Bind<IFilterProvider>().To<NinjectWebApiFilterProvider>().WithConstructorArgument("kernel", kernel);
}
I had the same problem and was configuring my error handler the same way by adding it to the filter collection in WebApiConfig.cs which meant it wasn't getting handled by the FilterProvider implementation I had added. So I did this instead:
public class LoggingExceptionFilterAttribute : ExceptionFilterAttribute, IExceptionFilter
{
// this is what I wanted injected
private IEmailService emailService;
public LoggingExceptionFilterAttribute(IEmailService service)
{
emailService = service;
}
public override void OnException(HttpActionExecutedContext actionExecutedContext)
{
// my implementation here
}
}
Then I registered this in NinjectWebCommon like so:
kernel.Bind<System.Web.Http.Filters.IExceptionFilter>().To<LoggingExceptionFilterAttribute>().InSingletonScope();
And then in WebApiConfig I realized that I could get a hold of the DependencyResolver so I did this:
config.Filters.Add((IFilter)config.DependencyResolver.GetService(typeof(IExceptionFilter)));
Now Ninject handles constructing my exception filter and I can even do constructor injection instead of needing [Inject] attributes and I don't have to add my ExceptionFilterAttribute to every API controller.
Ok, you have to make sure you are binding your custom IFilterProvider as well. As of writing the Ninject.Web.WebApi Nuget package is unstable and would do that automatically for you, if you were using it. Just in the same fashion Ninject.MVC3 does this for your regular controllers.
Just make sure you have this binding, and the replaced DependencyResolver will look for IFilterProvider implementation via your Ninject kernel as well:
kernel.Bind<IFilterProvider>().To<NinjectWebApiFilterProvider>();
Then your NinjectWebApiFilterProvider will kick in and inject dependencies into your filters as per your code.
I got this error when using Ninject with Web API, but it works with MVC Controller:
Type 'App.Web.Controllers.ProductController' does not have a default constructor
NinjectControllerFactory :
public class NinjectControllerFactory : DefaultControllerFactory
{
private IKernel ninjectKernel;
public NinjectControllerFactory()
{
ninjectKernel = new StandardKernel();
AddBindings();
}
protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
return controllerType == null ? null : (IController)ninjectKernel.Get(controllerType);
}
public void AddBindings()
{
ninjectKernel.Bind<IProductRepository>().To<EFProductRepository>();
}
}
Global.asax.cs :
BundleConfig.RegisterBundles(BundleTable.Bundles);
ControllerBuilder.Current.SetControllerFactory(new NinjectControllerFactory());
ProductController :
public class ProductController : ApiController
{
private IProductRepository repository;
public ProductController(IProductRepository ProducteRepository)
{
this.repository = ProductRepository;
}
public IEnumerable<Product> GetAllProducts()
{
return repository.Products.AsEnumerable();
}
}
You have overriden the DefaultControllerFactory. But this is used to instantiate ASP.NET MVC controllers (one deriving from System.Web.Mvc.Controller). It has strictly nothing to do with ASP.NET Web API controllers (the ones deriving from System.Web.Http.ApiController).
So basically what you have done here is dependency injection into ASP.NET MVC. If you want to use this for the Web API you may take a look at the following guides:
http://www.strathweb.com/2012/05/using-ninject-with-the-latest-asp-net-web-api-source/
http://www.peterprovost.org/blog/2012/06/19/adding-ninject-to-web-api/
You should use the latest Ninject Web API package, which solves these problems already. See here: http://nuget.org/packages/Ninject.Web.WebApi.WebHost/
You need to set DependencyResolver property of the HttpConfiguration. What you have done was for ASP.NET MVC and not ASP.NET Web API.
So get the NuGet package and set DependencyResolver:
var kernel = new StandardKernel();
// use kernel to register your dependencies
var dependencyResolver = new NInjectResolver(kernel);
config.DependencyResolver = dependencyResolver; // config is an instance of HttpConfiguration based on your hosting scenario
I am trying to use ServiceStack with Ninject rather than Funq. I have the following:
public interface IContainerAdapter
{
T Resolve<T>();
T TryResolve<T>();
}
public class NinjectIocAdapter : IContainerAdapter
{
private readonly IKernel kernel;
public NinjectIocAdapter(IKernel kernel)
{
this.kernel = kernel;
}
public T Resolve<T>()
{
return this.kernel.Get<T>();
}
public T TryResolve<T>()
{
return this.kernel.TryGet<T>();
}
}
Then inside my Configure method:
public override void Configure(Funq.Container container)
{
//Set JSON web services to return idiomatic JSON camelCase properties
ServiceStack.Text.JsConfig.EmitCamelCaseNames = true;
IKernel kernel = new StandardKernel();
container.Adapter = new NinjectIocAdapter(kernel);
//Set MVC to use the same Funq IOC as ServiceStack
//ControllerBuilder.Current.SetControllerFactory(new FunqControllerFactory(container));
}
But I get the following error:
Cannot implicitly convert type 'NinjectIocAdapter' to 'ServiceStack.Configuration.IContainerAdapter'.
I'm also unsure whether I have to uncomment the line to set MVC to use Funq IoC? I have commented it out as I will be using Ninject. Is that correct?
I assume once this is all working. I can simply register any dependencies inside:
private static void RegisterServices(IKernel kernel)
{
}
The code exactly matches the documentation with one subtle difference.
You have to use ServiceStack.Configuration.IContainerAdapter instead of your own IContainerAdapter
.
Delete your implementation, add a reference to ServiceStack.Configuration and you should be fine.
Here is the set up that is not working
Using Ninject V3.0
public class LoggerModule : NinjectModule{
public override void Load()
{
Bind<ILogger>.ToProvider(MyLoggerProvider);
}
}
public class MyLoggerProvider: IProvider<ILogger>
{
public object Create(IContext context){
return new OneOfMyLoggers();
}
}
In my application wherever I inject instance of ILogger (using constructor or property injection, just does matter) I never get instance of ILogger resolved.
But If do not use module and/or povider, and bind when kernel is created, everything works like a charm. The following works
public class MyDiResolver()
{
public MyDiResolver()
{
MyKernel = new StandardKernel();
MyKernel.Bind<ILogger>().To<OneOfMyLoggers>();
}
}
The same arrangement of modules and providers works fine in Ninject2.x version. Is there something different about Ninject V3.0 that I am missing?
Thanks
Try passing the module into the StandardKernel so it knows to use it:
using (IKernel kernel = new StandardKernel(new LoggerModule()))
{
ILogger logger = kernel.Get<OneOfMyLoggers>();
}