Self-hosted SignalR2 multi-layer dependency injection - c#

I've just implemented DI using Ninject for my self-hosted SignalR2 project.
public class NinjectSignalRDependencyResolver : DefaultDependencyResolver
{
private readonly IKernel _kernel;
public NinjectSignalRDependencyResolver(IKernel kernel)
{
_kernel = kernel;
}
public override object GetService(Type serviceType)
{
return _kernel.TryGet(serviceType) ?? base.GetService(serviceType);
}
public override IEnumerable<object> GetServices(Type serviceType)
{
return _kernel.GetAll(serviceType).Concat(base.GetServices(serviceType));
}
}
And my Startup class:
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.UseCors(CorsOptions.AllowAll);
var kernel = new StandardKernel();
var resolver = new NinjectSignalRDependencyResolver(kernel);
kernel.Bind<MyDbContext>().ToSelf();
kernel.Bind<IRealTimeDataEngine>().To<RealTimeDataEngine>().InSingletonScope();
kernel.Bind<IHistoricalDataEngine>().To<HistoricalDataEngine>().InSingletonScope();
kernel.Bind(typeof(IHubConnectionContext<dynamic>)).ToMethod(context =>
resolver.Resolve<IConnectionManager>().GetHubContext<MyHub>().Clients
).WhenInjectedInto<IRealTimeDataEngine>();
var config = new HubConfiguration {Resolver = resolver};
ConfigureSignalR(app, config);
}
public static void ConfigureSignalR(IAppBuilder app, HubConfiguration config)
{
app.MapSignalR(config);
}
}
In my signalr hub constructor I expect an IRealTimeDataEngine.
public MyHub(IRealTimeDataEngine realTimeDataEngine, IHistoricalDataEngine historicalDataEngine)
In my host, which is a console application, I need the same IRealTimeDataEngine to be injected.
public DummyProvider(IRealTimeDataEngine realTimeDataEngine)
In my Main method I need to create a DummyProvider object.
If I'm not mistaken, creating a new kernel would not give me the same object in the two different projects, so how should I ask for the same IRealTimeDataEngine at my composition root?

You're right you must use only one kernel per application. That means you should create your kernel outside of Startup class in your case. This can be achieved by using overloaded call of WebApp.Start method like:
class Program
{
static void Main(string[] args)
{
var kernel = new StandardKernel();
var server = WebApp.Start("http://localhost:8080/", (appBuilder) =>
{
var resolver = new NinjectSignalRDependencyResolver(kernel);
var config = new HubConfiguration {Resolver = resolver};
...
});
...

Related

How to Inject dependencies in a Unity IoC container for SignalR hub?

I have an application that is written with c# on the top of the ASP.NET MVC 5 Framework. I implemented Unity.Mvc into my project. Now, I am trying to inject dependencies objects into my SignalR Hub.
I created a class called UnityHubActivator
My class looks like this
public class UnityHubActivator : IHubActivator
{
private readonly IUnityContainer _container;
public UnityHubActivator(IUnityContainer container)
{
_container = container;
}
public IHub Create(HubDescriptor descriptor)
{
return (IHub)_container.Resolve(descriptor.HubType);
}
}
Then in my UnityConfig class I added the following to my RegisterTypes method
var unityHubActivator = new UnityHubActivator(container);
container.RegisterInstance<IHubActivator>(unityHubActivator);
My hub looks like this
[Authorize]
public class ChatHub : Hub
{
protected IUnitOfWork UnitOfWork { get; set; }
public ChatHub(IUnitOfWork unitOfWork)
: base()
{
UnitOfWork = unitOfWork;
}
}
But when I run the hub, the constructor never get called and the connection never takes place.
How can I correctly use Unity framework to inject dependencies into my hub?
UPDATED
I tried to created a custom container like so
public class UnitySignalRDependencyResolver: DefaultDependencyResolver
{
protected IUnityContainer Container;
private bool IsDisposed = false;
public UnitySignalRDependencyResolver(IUnityContainer container)
{
if (container == null)
{
throw new ArgumentNullException("container");
}
Container = container.CreateChildContainer();
}
public override object GetService(Type serviceType)
{
if (Container.IsRegistered(serviceType))
{
return Container.Resolve(serviceType);
}
return base.GetService(serviceType);
}
public override IEnumerable<object> GetServices(Type serviceType)
{
if (Container.IsRegistered(serviceType))
{
return Container.ResolveAll(serviceType);
}
return base.GetServices(serviceType);
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if(IsDisposed)
{
return;
}
if(disposing)
{
Container.Dispose();
}
IsDisposed = true;
}
}
Then here is how I configured the hub in the Startup class
public class Startup
{
public IUnityContainer Container { get; set; }
public Startup(IUnityContainer container)
{
Container = container;
}
public void Configuration(IAppBuilder app)
{
app.Map("/signalr", map =>
{
var resolver = new UnitySignalRDependencyResolver(Container);
var hubConfiguration = new HubConfiguration
{
Resolver = resolver
};
map.RunSignalR(hubConfiguration);
});
}
}
But still now working... the hub constructor never get called.
Here is how I am calling my hub from the client
<script src="~/Scripts/jquery.signalR-2.2.2.min.js"></script>
<script src="~/signalr/hubs"></script>
<script>
$(function () {
// Reference the auto-generated proxy for the hub.
var app = $.connection.chatHub;
console.log('Getting things ready....');
app.client.outOfTasks = function () {
console.log('Nothing to do here')
};
app.client.logError = function (message) {
console.log(message)
};
app.client.logNote = function (message) {
console.log(message)
};
app.client.assignTask = function (taskId) {
app.server.taskReceived();
console.log('task received!!!' + taskId);
};
// Start the connection.
$.connection.hub.start().done(function () {
console.log('Connection Started....');
});
});
</script>
The UnitySignalRDependencyResolver appears accurate for that container.
Taken from the official documentation,
Dependency Injection in SignalR
try the following example for configuring Startup
public class Startup{
public void Configuration(IAppBuilder app) {
IUnityContainer container = GetContainer();
var resolver = new UnitySignalRDependencyResolver(container);
var config = new HubConfiguration {
Resolver = resolver
};
app.MapSignalR("/signalr", config);
}
IUnityContainer GetContainer() {
//...call the unity config related code.
var container = UnityConfig.Container;
//...any other code to populate container.
return container;
}
}
Make sure to register the necessary objects, including the hub (ChatHub) with the container as the container needs to know the object graph in order to resolve the necessary dependencies.

Creating actorRefs from ACtorSystem using DI() Extension, throws object reference exception

I am trying to setup a base project that will make use of Autofac for dependency injection.
I have the following problem, when trying to get an instance of my Actor doing the following:
_system = Akka.Actor.ActorSystem.Create(_settings.SystemName);
PingActor = _system.ActorOf(_system.DI().Props<PingActor>(), ActorNames.PingActor);
This throws an 'Object reference' exception. The PingActor is null.
Below is how I am loading my modules in my setup, and registering the Actor Instance.
Static class that contains a helper to help setup my Autofac setup.
public static class IoC
{
public static IContainer Container { get; set; }
public static void Bootstrapping(Action<ContainerBuilder> builderConfig)
{
//new builder instance
var builder = new ContainerBuilder();
builderConfig.Invoke(builder);
Container = builder.Build();
builder = new ContainerBuilder();
builder.ScanAssembly();
builder.Update(Container);
}
}
Here is the ScanAssembly extension method:
public static class ContainerBuilderExtensions
{
public static void ScanAssembly(this ContainerBuilder builder, string searchPattern = "Template.*.dll")
{
var path = AppDomain.CurrentDomain.BaseDirectory;
foreach (var assembly in Directory.GetFiles(path, searchPattern).Select(Assembly.LoadFrom))
{
builder.RegisterAssemblyModules(assembly);
}
}
}
Where I try to register my Actor:
public class IoCModule : Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterType<ActorSystemShell>().As<IActorSystemShell>();
builder.RegisterType<PingActor>();
}
}
Why am I getting my Actor as Null?
I was missing the AutoFacDependencyResolver.
I modified my code to look as follow:
public void Start()
{
_system = Akka.Actor.ActorSystem.Create(_settings.SystemName);
// Create the dependency resolver
IDependencyResolver resolver = new AutoFacDependencyResolver(IoC.Container, _system);
PingActor = _system.ActorOf(_system.DI().Props<PingActor>(), ActorNames.PingActor);
Console.WriteLine($"Starting actor system: {_settings.SystemName}");
}

Passing configuration to Ninject

I need to pass an IConfiguration to my DbClient class and my repositories depend on this DbClient. I couldn't get this to work.
My DbClient:
public class DbClient
{
public DbClient(IConfiguration config)
{
// Perform some initialization
}
}
My repository depends on the DbClient:
public class MyRepository : IMyRepository
{
private DbClient _client;
public MyRepository(DbClient client)
{
_client = client;
}
}
My Bindings class
public class NinjectBindings : NinjectModule
{
public override void Load()
{
Bind<DbClient>().To<DbClient>(); // ?? Not sure about this
Bind<IMyRepository>().To<MyRepository>();
}
}
And the Main in my console app:
static void Main()
{
var config = new Configuration();
config.AddJsonFile("settings.json");
var kernel = new StandardKernel();
kernel.Load(Assembly.GetExecutingAssembly());
var myRepository = kernel.Get<IMyRepository>();
}
What am I missing? What do I need to do to pass IConfiguration into the DbClient and make sure the repository initializes properly?
You need to bind IConfiguration.
You can do it in Load method:
Bind<IConfiguration>().ToMethod(ctx =>
{
var config = new Configuration();
config.AddJsonFile("settings.json");
return config;
});
Or in Main method:
kernel.Bind<IConfiguration>().ToMethod(...);
Binding type depends on your need. Maybe you should bind config as constant without context dependency.
Also line Bind<DbClient>().To<DbClient>(); isn't necessary because DbClient will be automatically bind to self.

Self-Registering Libraries with Autofac 4 and vNext

i'd like to create a Plugin Enviroment for my ASP.Net 5.0 / MVC 6 Application. I'm using Autofac as IOC Container and i like to load the Plugins (Class Libraries) from the build in DNX LibraryManager. The goal of using the Library Manager is, that i don't have to care about NuGet Packages and Frameworks.
The Problem i have is the LifeCycle, i have to build the IOC Container before the instance of the LibraryManager is available. Because the Autofac Container provides his own IServiceProvider Instance which i have to inject within the ConfigureService() Method call (AddAutofac).
Does anyone know how to get this working?
Update: I have fixed my problem with Davids help and updated the code to get it working with the release candidates. Also i have added support for configuration.
In my DNX Class Library i implemented a Class for Self-Registration:
public class AutofacModule : Module
{
protected override void Load(ContainerBuilder builder)
{
builder.Register(c => new SimpleService())
.As<IService>()
.InstancePerLifetimeScope();
}
}
In my MVC WebApplication i have added the Class Library as Dependency.
Startup.cs
public IConfiguration Configuration { get; set; }
public class Startup
{
public Startup( IApplicationEnvironment applicationEnvironment )
{
IConfigurationBuilder configurationBuilder = new ConfigurationBuilder();
configurationBuilder.SetBasePath( applicationEnvironment.ApplicationBasePath );
configurationBuilder.AddJsonFile( "appsettings.json" );
configurationBuilder.AddJsonFile( "autofac.json" );
configurationBuilder.AddEnvironmentVariables();
this.Configuration = configurationBuilder.Build();
}
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddDependencies();
}
public void Configure(IApplicationBuilder applicationBuilder, IHostingEnvironment hostingEnvironment)
{
applicationBuilder.UseDependencies( this.Configuration );
applicationBuilder.UseStaticFiles();
applicationBuilder.UseMvc();
}
}
I have created an DependencyResolver to keep the ContainerBuilder instance.
DependencyResolver.cs
public class DependencyResolver : IDependencyResolver
{
private IContainer container;
private readonly ContainerBuilder builder;
public DependencyResolver()
{
this.builder = new ContainerBuilder();
}
public void RegisterModule( IModule module )
{
this.builder.RegisterModule( module );
}
public void RegisterModules( IEnumerable<Assembly> assemblies )
{
this.builder.RegisterAssemblyModules(assemblies.ToArray());
}
public void Populate( IServiceCollection services)
{
this.builder.Populate( services );
}
public void Build()
{
this.container = this.builder.Build();
}
public T Resolve<T>() where T : class
{
return this.container?.Resolve<T>();
}
}
IDependencyResolver.cs
public interface IDependencyResolver
{
void RegisterModule( IModule module );
void RegisterModules( IEnumerable<Assembly> assemblies );
void Populate(IServiceCollection services);
void Build();
T Resolve<T>() where T : class;
}
Last but not least i have created an Extension Class
DependencyResolverExtensions.cs
public static class DependencyResolverExtensions
{
public static IServiceCollection AddDependencies( this IServiceCollection services )
{
DependencyResolver dependencyResolver = new DependencyResolver();
dependencyResolver.Populate(services);
ServiceDescriptor serviceDescriptor = new ServiceDescriptor(typeof ( IDependencyResolver ), dependencyResolver );
services.TryAdd(serviceDescriptor);
return services;
}
public static IApplicationBuilder UseDependencies(this IApplicationBuilder applicationBuilder, IConfiguration configuration)
{
IDependencyResolver dependencyResolver = applicationBuilder.GetService<IDependencyResolver>();
if (dependencyResolver == null) return applicationBuilder;
ILibraryManager libraryManager = applicationBuilder.GetService<ILibraryManager>();
if (libraryManager == null) return applicationBuilder;
IEnumerable<Assembly> assemblies = libraryManager.GetLoadableAssemblies();
dependencyResolver.RegisterModules(assemblies);
ConfigurationModule configurationModule = new ConfigurationModule( configuration );
dependencyResolver.RegisterModule( configurationModule );
dependencyResolver.Build();
IServiceProvider serviceProvider = dependencyResolver.Resolve<IServiceProvider>();
applicationBuilder.ApplicationServices = serviceProvider;
return applicationBuilder;
}
public static IEnumerable<Assembly> GetLoadableAssemblies(this ILibraryManager libraryManager)
{
List<Assembly> result = new List<Assembly>();
IEnumerable<Library> libraries = libraryManager.GetLibraries();
IEnumerable<AssemblyName> assemblyNames = libraries.SelectMany(e => e.Assemblies).Distinct();
assemblyNames = Enumerable.Where(assemblyNames, e => e.Name.StartsWith("MyLib."));
foreach (AssemblyName assemblyName in assemblyNames)
{
Assembly assembly = Assembly.Load(assemblyName);
result.Add(assembly);
}
return result;
}
public static T GetService<T>(this IApplicationBuilder applicationBuilder) where T : class
{
return applicationBuilder.ApplicationServices.GetService(typeof (T)) as T;
}
}
If you need to switch between different implementations, like mock and real data you can use the Autofac Configuration.
autofac.json
{
"components": [
{
"type": "MyLib.Data.EF.EntitiesData, MyLib.Data.EF",
"services": [
{
"type": "MyLib.Abstractions.IDataRepository, MyLib.Abstractions"
}
]
}
]
}
It's a shame that ConfigureServices is not injectable, that would make this a lot easier.
Looking at the code you should be safe to replace the IServiceProvider inside Configure(...) instead of inside ConfigureServices(...) and get the intended behavior. ApplicationServices is setable.
In your UseAutofac method you should be able to do something like:
public static IApplicationBuilder UseAutofac( [NotNull] this IApplicationBuilder applicationBuilder )
{
IAutofacResolver autofacResolver = applicationBuilder.GetService<IAutofacResolver>();
ILibraryManager libraryManager = applicationBuilder.GetService<ILibraryManager>();
autofacResolver.RegisterLibraryModules( libraryManager);
applicationBuilder.ApplicationServices = autofacResolver.Resolve();
return applicationBuilder;
}
I've come up with a solution that uses part of this, but also uses a ComponentContainer that addresses the potential memory leaks in the DependencyResolver. This also works with RC1. Not sure yet about RC2 as it's not complete enough for me to test.
The ComponentContainer looks like this:
public static class ComponentContainer {
static IContainer _container;
static ContainerBuilder _containerBuilder;
public static ContainerBuilder Builder {
get {
if (_containerBuilder == null)
_containerBuilder = new ContainerBuilder();
return _containerBuilder;
}
}
public static IServiceProvider ServiceProvider {
get {
if (_container == null)
_container = _containerBuilder.Build();
return _container.Resolve<IServiceProvider>();
}
}
public static ComponentFactory<TObject> Component<TObject>() => new ComponentFactory<TObject>(_container);
public static void RegisterAssembly(Assembly assembly) {
if (assembly == null) return;
foreach (var obj in assembly.GetTypes().Where(t => t.GetCustomAttribute<ExportAttribute>() != null)) {
ExportAttribute att = obj.GetCustomAttribute<ExportAttribute>();
if (att.ContractType != null) {
_containerBuilder.RegisterType(obj).As(att.ContractType);
} else {
foreach (var intf in obj.GetInterfaces())
_containerBuilder.RegisterType(obj).As(intf);
}
}
}
}
public class ComponentFactory<TObject> : IDisposable {
protected TObject CurrentObject;
protected ILifetimeScope CurrentScope;
public TObject Current => (TObject)CurrentObject;
public ComponentFactory(IContainer container) {
CurrentScope = container.BeginLifetimeScope();
CurrentObject = CurrentScope.Resolve<TObject>();
}
public TObject Component => CurrentObject;
public void Dispose() {
(CurrentObject as IDisposable)?.Dispose();
CurrentScope.Dispose();
}
}
Then in Startup.cs I do the following:
public virtual IServiceProvider ConfigureServices(IServiceCollection services) {
services.AddMvc();
services.AddOptions();
services.AddSession();
services.AddCaching();
var assemblyLoadContextAccessor = services.FirstOrDefault(s => s.ServiceType == typeof(IAssemblyLoadContextAccessor)).ImplementationInstance as IAssemblyLoadContextAccessor;
var libraryManager = services.FirstOrDefault(s => s.ServiceType == typeof(ILibraryManager)).ImplementationInstance as ILibraryManager;
var loadContext = assemblyLoadContextAccessor.Default;
foreach(var library in libraryManager.GetLibraries()) {
var assembly = loadContext.Load(library.Name);
if(assembly != null) {
var module = assembly.GetTypes().FirstOrDefault(t => t == typeof(IModule));
if(module != null)
ComponentContainer.Builder.RegisterAssemblyModules(assembly);
else
ComponentContainer.RegisterAssembly(assembly);
}
}
ComponentContainer.Builder.Populate(services);
return ComponentContainer.ServiceProvider;
}
To export modules within an assembly, I either mark them with an ExportAttribute or add a class to the assembly that implements Autofac's IModule. The code in ConfigureServices will enumerate through the application's modules and feed them to the static Builder in ComponentContainer. Once the container has been built, you can either resolve modules through injection in a constructor or you can request a specific type by:
(using var myComponentFactory = ComponentContainer.Component<IMyModule>()) {
//You can now access your component through myComponentFactory.Component
//Once it passes out of scope of using, it will be properly disposed of
//along with the scope from which it was created.
}
Edit: With the release of RC2, this code is no longer valid as the enumeration of assemblies and classes will fail. I haven't come up with a good solution yet. If anyone else has any suggestions for enumerating assemblies in RC2, please let me know.

use dependency injection in Signalr 2.0 self-host?

With SignalR 2.0 in a self-hosted application, from these instructions you have something like this:
class Startup
{
public void Configuration(IAppBuilder app)
{
app.MapSignalR(new HubConfiguration { Resolver = ... });
}
}
class Program
{
static void Main(string[] args)
{
using (WebApp.Start("http://localhost:8080")) // constructs Startup instance internally
{
Console.WriteLine("Server running on {0}", url);
Console.ReadLine();
}
}
}
You will notice that the Startup class instance is created with some behind-the-scenes magic. I can't figure out how to fill in dependencies on it. Is there some way to override the construction of the Startup class so that I can inject dependencies into it?
Instead of replacing the IAppActivator, you can simply register Startup's constructor arguments with Katana's ServiceProvider.
The default IAppActivator will resolve any services matching the Startup constructor's argument types for you. The only downside is you can't use WebApp.Start, since that doesn't expose the ServiceProvider:
public class MyService : IMyService
{
private readonly IMyOtherService _myOtherService;
// Services will be recursively resolved by Katana's ServiceProvider
public MyService(IMyOtherService myOtherService)
{
_myOtherService = myOtherService;
}
// Implementation
}
public class Startup
{
private readonly IMyService _myService;
// Startup must have exactly one constructor.
public Startup(IMyService myService)
{
_myService = myService
}
public void Configuration(IAppBuilder app)
{
app.MapSignalR(new HubConfiguration { Resolver = ... });
}
}
using System;
using Microsoft.Owin.Hosting;
using Microsoft.Owin.Hosting.Services;
using Microsoft.Owin.Hosting.Starter;
public class Program
{
static void Main(string[] args)
{
var url = "http://localhost:8080";
var services = (ServiceProvider)ServicesFactory.Create();
var options = new StartOptions(url);
services.Add<IMyOtherService, MyOtherService>();
services.Add<IMyService, MyService>();
var starter = services.GetService<IHostingStarter>();
using (starter.Start(options)) // constructs Startup instance internally
{
Console.WriteLine("Server running on {0}", url);
Console.ReadLine();
}
}
}
I copied the default implementation of WebApp.Start into Program.Main, but instead of calling IHostingStarter.Start immediately, I add custom services first: http://katanaproject.codeplex.com/SourceControl/changeset/view/c726b87e90c05677a256ca1821bac481f402d6bd#src/Microsoft.Owin.Hosting/WebApp.cs
There are a bunch of other overloads for ServiceProvider.Add if you need them: http://msdn.microsoft.com/en-us/library/microsoft.owin.hosting.services.serviceprovider(v=vs.111).aspx
This should be much simpler than replacing Katana's IAppActivator using StartOptions.Settings like I suggest in my previous answer.
I am leaving my previous answer up, however, as it does explain in more detail how the Startup class is constructed and how to replace default service implementations using the Settings dictionary.
Checkout the dependency injection information here: http://www.asp.net/signalr/overview/signalr-20/extensibility/dependency-injection
Should have everything you need to know :)
Hope this helps!
class Startup
{
private readonly IDependencyResolver _resolver;
public Startup(IDependencyResolver resolver)
{
_resolver = resolver;
}
public void Configuration(IAppBuilder app)
{
app.MapSignalR(new HubConfiguration { Resolver = _resolver; });
}
}
class Program
{
static void Main(string[] args)
{
Startup startup = new Statrtup(new MyResolver());
using (WebApp.Start("http://localhost:8080", startup.Configuration))
{
Console.WriteLine("Server running on {0}", url);
Console.ReadLine();
}
}
}

Categories