use dependency injection in Signalr 2.0 self-host? - c#

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();
}
}
}

Related

Aspnet core 5.0 inject dependency in startup.cs constructor

I'm trying to add a parameter to the constructor of the Startup class in Asp.net core using this method: https://mattmazzola.medium.com/asp-net-core-injecting-custom-data-classes-into-startup-classs-constructor-and-configure-method-7cc146f00afb
However, this doesn't work for me.
Here's my C# Code:
public class AppAProgram
{
#region Members
private IHostBuilder hostBuilder;
private IHost host;
#endregion
#region Constructors
public AppAProgram(string[] args)
{
this.hostBuilder = Host.CreateDefaultBuilder(args);
ILauncherContext launcherContext = new DummyLauncherContext();
this.hostBuilder.ConfigureServices((hostContext, services) => { services.AddSingleton<ILauncherContext>(launcherContext); });
this.hostBuilder.ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<AppAStartup>(); });
this.host = hostBuilder.Build();
}
#endregion
public static void Main(string[] args)
{
AppAProgram appAProgram = new AppAProgram(args);
appAProgram.Start();
}
public void Start()
{
this.host.Run();
}
}
Here's the constructor for which I add an injected parameter:
#region Constructors
public AppAStartup(IConfiguration configuration, ILauncherContext launcherContext)
{
Configuration = configuration;
this.launcherContext = launcherContext;
}
#endregion
When I do this, I get this exception:
System.InvalidOperationException: 'Unable to resolve service for type 'Spike.Run.Launcher.ILauncherContext' while attempting to activate 'Spike.Run.AppA.AppAStartup'.'
Reference The Startup class
Only the following service types can be injected into the Startup constructor when using the Generic Host (IHostBuilder):
IWebHostEnvironment
IHostEnvironment
IConfiguration
...
Most services are not available until the Configure method is called.
emphasis mine
Use the Configure method instead
public AppAStartup(IConfiguration configuration) {
Configuration = configuration;
}
public void Configure(IApplicationBuilder app, ..., ILauncherContext launcherContext) {
//access launcherContext here
//...
}

Dependency injection net core console application setup

I am trying to use dependency injection for a .Net Core Console application using the built in DI.
I have 2 following Methods :
private static void RegisterServices()
{
var collection = new ServiceCollection();
//repositories
collection.AddScoped<IAccountDataRepository, AccountDataRepository>();
collection.AddScoped<IClientDataRepository, ClientDataRepository>();
collection.AddScoped<IAddressDataRepository, AddressDataRepository>();
collection.AddScoped<IClientAccountDataRepository, ClientAccountDataRepository>();
//services
collection.AddScoped<IAccountDataService, AccountDataService>();
collection.AddScoped<IClientDataService, ClientDataService>();
collection.AddScoped<IAddressDataService, AddressDataService>();
collection.AddScoped<IClientAccountDataService, ClientAccountDataService>();
_serviceProvider = collection.BuildServiceProvider();
}
private static void DisposeServices()
{
if (_serviceProvider == null)
{
return;
}
if (_serviceProvider is IDisposable)
{
((IDisposable)_serviceProvider).Dispose();
}
}
I can get this to work in the main method by using this:
private static IServiceProvider _serviceProvider;
private static IClientDataRepository _clientDataRepository;
static void Main(string[] args)
{
RegisterServices();
_clientDataRepository = _serviceProvider.GetService<IClientDataRepository>();
However I need to inject the repository to the service and the service to main but I can t use the following in the service class :
_clientDataRepository = _serviceProvider.GetService<IClientDataRepository>();
Here is service:
public class ClientDataService : IClientDataService
{
private readonly ILogger _logger;
private readonly IClientDataRepository _clientDataRepository;
public ClientDataService(ILogger logger, IClientDataRepository clientDataRepository)
{
_logger = logger;
_clientDataRepository = clientDataRepository;
}
If I use
_clientDataRepository = _serviceProvider.GetService<IClientDataRepository>();
will give an error
Just resolve the service and the service provider will inject the repository into the service when building the object graph of the requested object
Based on the provided ClientDataService you would also need to make sure that all dependencies are registered with the service collection.
As it is current shown, ClientDataService also depends on ILogger which does not appear to be registered with the service collection.
services.AddLogging();
The following example uses the originally provided code and refactors to run the main using dependency injection.
public class Program
private readoonly IClientDataService service;
public Program(IClientDataService service) {
this.service = service;
}
public void SomeMethod() {
//...
}
//entry
public static void Main(string[] args) {
IServiceProvider serviceProvider = RegisterServices();
Program program = serviceProvider.GetService<Program>();
program.SomeMethod();
DisposeServices(serviceProvider);
}
//Support
private static IServiceProvider RegisterServices() {
var services = new ServiceCollection();
//repositories
services.AddScoped<IAccountDataRepository, AccountDataRepository>();
services.AddScoped<IClientDataRepository, ClientDataRepository>();
services.AddScoped<IAddressDataRepository, AddressDataRepository>();
services.AddScoped<IClientAccountDataRepository, ClientAccountDataRepository>();
//services
services.AddScoped<IAccountDataService, AccountDataService>();
services.AddScoped<IClientDataService, ClientDataService>();
services.AddScoped<IAddressDataService, AddressDataService>();
services.AddScoped<IClientAccountDataService, ClientAccountDataService>();
services.AddLogging(); //<-- LOGGING
//main
services.AddScoped<Program>(); //<-- NOTE THIS
return services.BuildServiceProvider();
}
private static void DisposeServices(IServiceProvider serviceProvider) {
if (serviceProvider == null) {
return;
}
if (serviceProvider is IDisposable sp) {
sp.Dispose();
}
}
}

How can I inject dependency to App.xaml.cs?

I did not managed to find any similar example, so I decided to ask the question.
I am using Autofac to register my service layer interfaces and I am wondering, can I inject one into App.xaml.cs?
I am having my own log service, that I want to run in case of fatal error in the application.
As far as I know similar way you can inject dependency to the window, can I do it the same in App.xaml.cs?
public partial class App : Application
{
private readonly ILogService _log;
public App()
{
}
public App(ILogService log) : this()
{
_log = log;
}
async Task App_DispatcherUnhandledExceptionAsync(object sender, DispatcherUnhandledExceptionEventArgs e)
{
_log.Error("App.xaml.cs exception: " + e.Exception.Message);
await _log.SaveAllLinesAsync();
e.Handled = true;
}
}
Autofac IoC:
public class BootStrapper
{
/// <summary>
/// IoC container
/// </summary>
/// <returns></returns>
public static IContainer BootStrap()
{
var builder = new ContainerBuilder();
builder.RegisterType<EventAggregator>()
.As<IEventAggregator>().SingleInstance();
builder.RegisterType<LogService>()
.As<ILogService>().SingleInstance();
builder.RegisterType<DeleteView>().AsSelf();
builder.RegisterType<DeleteViewModel>().AsSelf().SingleInstance();
builder.RegisterType<PhraseView>().AsSelf();
builder.RegisterType<PhraseViewModel>().AsSelf().SingleInstance().WithParameter(new NamedParameter("searchPhrase", ""));
builder.RegisterType<PopulateDictionaries>().AsSelf().SingleInstance();
return builder.Build();
}
}
IoC initializing in ViewModelLocator:
public class ViewModelLocator
{
IContainer _container;
public ViewModelLocator()
{
_container = BootStrapper.BootStrap();
}
//view models below
}
If you want to inject the App class with a dependency, you should define a custom Main method where you instantiate the App class:
public class Program
{
[STAThread]
public static void Main(string[] args)
{
ILogService logService = ...;
App app = new App(logService);
app.InitializeComponent();
app.Run();
}
}
If you do this, remember to change the Build Action of App.xaml from ApplicationDefinition to Page.

Self-hosted SignalR2 multi-layer dependency injection

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};
...
});
...

Inject settings in Setup class

On Application_Start of a MVC project, using Autofac, I have the following:
public class MvcApplication : HttpApplication {
protected void Application_Start() {
RouteSetup.Run();
} // Application_Start
}
RouteSetup is as follows:
public class RouteSetup {
public static void Run() {
ISettings settings = new Settings();
RouteTable.Routes.Localization(x => {
x.AcceptedCultures = settings.AcceptedLanguages;
x.DefaultCulture = settings.DefaultLanguage;
});
CultureSensitiveHttpModule.GetCultureFromHttpContextDelegate = context => { return new CultureResolver().GetCulture(context); };
} // Run
}
ISettings is a class I inject in various parts of my application.
How should I request this class in RouteSetup?
You can change the Run method to accept a ILifetimeScope (IContainer inherits from ILifetimeScope) or you can use the DependencyResolver provided by ASP.net MVC, in the second case the ASP.net DependencyResolver has to be configured using DependencyResolver.SetResolver(...)
public class RouteSetup {
public static void Run(ILifetimeScope scope) {
ISettings settings = scope.Resolve<ISettings>();
// or
ISettings settings = DependencyResolver.Current.GetService<ISettings>();
RouteTable.Routes.Localization(x => {
x.AcceptedCultures = settings.AcceptedLanguages;
x.DefaultCulture = settings.DefaultLanguage;
});
CultureSensitiveHttpModule.GetCultureFromHttpContextDelegate = context => {
return new CultureResolver().GetCulture(context);
};
} // Run
}
By the way, I recommend you trying to always inject dependency using constructor parameter.

Categories