In the DI container to create a singleton would the following be an ok way to do?
public void ConfigureServices(IServiceCollection services)
{
var botClient = new TelegramBotClient(_config["Tokens:Telegram"]);
services.AddSingleton(botClient);
}
TelegramBotClient is from the library that I'm using, so I can't start changing that.
A more orthadox way of handling a DI service with a configuration is to use the IOptions pattern. This way you aren't tightly coupling your startup object with the service. As it stands now, if your configuration changes, you have to modify your startup object.
A way to tackle this and keep your concerns separated, take a look at this:
TelegramBotClientService.cs
public interface ITelegramBotClientService
{
Task DoSomethingAsync();
}
public sealed class TelegramBotClientService : ITelegramBotClientService
{
private readonly TelegramConfigModel _config;
public TelegramBotClientService(IOptions<TelegramConfigModel> options)
{
_config = options.Value;
}
public Task DoSomethingAsync()
{
var token = _config.Token;
// ...
}
}
Startup.cs
// ...
public void ConfigureServices(IServiceCollection services)
{
services.Configure<TelegramConfigModel>(
Configuration.GetSection("TelegramConfig"));
services.AddSingleton<ITelegramBotClientService, TelegramBotClientService>();
}
// ...
appsettings.json
{
"TelegramConfig": {
"Token": "12345"
}
}
TelegramConfigModel.cs
public sealed class TelegramConfigModel
{
public string Token { get; set; }
}
This hasn't been tested, so there may be a typo somewhere, but, now your concerns are separated. The DI pipeline is now doing the instantiation and also injecting your configurations.
A side note
I noticed you may be injecting a singleton to maintain a bot. I would highly suggest you use IHostedService or BackgroundService and inject using AddHostedService to maintain something like a bot.
Related
I'm using ASP.NET Core Web API. I am having a hard time wrapping my head around instantiating a non-controller class that uses DI. There are a multitude of SO articles related to this, but none that have answered my question (as far as I can understand). These are the most popular and relevant:
Net Core Dependency Injection for Non-Controller
Dependency Injection without a Controller
ASP.NET 5 Non-Controller DI injection
My use case (a contrived example):
I have a class SpeechWriter that has a dependency on IRandomTextService:
public class SpeechWriter
{
private readonly IRandomTextService _textService;
// Constructor with Text Service DI
public SpeechWriter(IRandomTextService textService)
{
_textService = textService;
}
public string WriteSpeech()
{
var speech = _textService.GetText(new Random().Next(5,50));
return speech;
}
}
IRandomTextService interface:
public interface IRandomTextService
{
public string GetText(int wordCount);
}
and the implementation:
public class RandomTextService : IRandomTextService
{
public string GetText(int wordCount)
{
return Lorem.Words(wordCount);
}
}
IRandomTextService is registered as a service in Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddScoped<IRandomTextService, RandomTextService>();
}
In my controller action, if I want to instantiate a SpeechWriter like this:
public IActionResult Index()
{
var speech = new SpeechWriter();
return Ok(speech.WriteSpeech());
}
I can't do it because an argument (the injected service) is expected.
The only way I can seem to get DI to inject RandomTextService in SpeechWriter is if SpeechWriter itself is a service and injected in the controller:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddScoped<IRandomTextService, RandomTextService>();
services.AddScoped<SpeechWriter>();
}
public class EchoController : ControllerBase
{
private readonly SpeechWriter _speechWriter;
public EchoController(SpeechWriter speechWriter)
{
_speechWriter = speechWriter;
}
public IActionResult Index()
{
return Ok(_speechWriter.WriteSpeech());
}
}
Is there any way to get RandomTextService injected when SpeechWriter is instantiated as in my first example, like this?
var speech = new SpeechWriter();
If not, what is it about DI that I'm missing? My actual application is more complex than this and I would effectively have to create a chain of DI and services all the way back up to the controller. I could use the ServiceProvider "anti-pattern", but I prefer not to do that because I'd be passing ServiceProvider all over the place.
Please help educate me!
Thanks.
I've done this:
services.Configure<ApplicationSettings>(_configuration.GetSection("ApplicationSettings"));
I assumed that would allow me to inject ApplicationSettings, but apparently not.
I could just do GetSection(...) and register it as a singleton, but then what's the point of .Configure?
The msdn doc says "Registers a configuration instance that TOptions will bind against, and updates the options when the configuration changes."
It's unclear how to set up the pattern to use the config as DI in my application though.
This is the SO question I was trying to implement:
How do I transform appsettings.json in a .NET Core MVC project?
This exact thing configures a dependency injection for the strongly-typed settings section ApplicationSettings.
public void ConfigureServices(IServiceCollection services)
{
// ...
services.Configure<ApplicationSettings>(_configuration.GetSection("ApplicationSettings"));
// You can register your service
services.AddTransient<SomeService>();
}
Then you implement the service and you have the settings section automatically injected into it.
public class SomeService
{
private readonly IOptions<ApplicationSettings> _options;
public SomeService(IOptions<ApplicationSettings> options)
{
_options = options;
}
public string AddPrefix(string value)
{
// AddPrefix("test value")
// will return:
// PREFIX - test value
return $"{_options.Value.Prefix} - {value}";
}
}
Given that you have your ApplicationSettings defined as:
public class ApplicationSettings
{
public string Prefix { get; set; }
}
And your appsettings.json should look like:
{
"ApplicationSettings": {
"Prefix": "PREFIX"
}
}
I have asp.net core application. I want to use IOptions pattern to inject values from appsettings.json. So I have a class SecurityHeaderOptions, and also have target class SecurityHeadersBuilder whose constructor takes IOptions<SecurityHeaderOptions> as parameter.
I know that .net core can implicitly create instance of SecurityHeadersBuilder by injecting IOptions<SecurityHeaderOptions> after registering both with container.
However i want to explicitly create instance of SecurityHeadersBuilder, call one of its method and then register the instance with the container.
public sealed class SecurityHeaderOptions
{
public string FrameOption { get; set; }
public string XssProtection { get; set; }
}
public class SecurityHeadersBuilder
{
private readonly SecurityHeaderOptions _options = null;
public SecurityHeadersBuilder(IOptions<SecurityHeaderOptions> options)
{
_options = options.Value;
}
public SecurityHeadersBuilder AddDefaultPolicy()
{
AddFrameOptions();
AddConetntSecurityPolicy();
return this;
}
}
ConfigureServices method
public void ConfigureServices(IServiceCollection services)
{
services.Configure<SecurityHeaderOptions>(Configuration.GetSection("SecurityHeaderOptions"));
services.AddScoped<SecurityHeadersBuilder>(provider =>
new SecurityHeadersBuilder(?????).AddDefaultPolicy())
}
Questions
1> If i am explicitly passing options into constructor, do i need to register SecurityHeaderOptions with the container using service.Configure method?
2> Configuration.GetSection("SecurityHeaderOptions") can't return instance of IOptions<SecurityHeaderOptions> , instead it returns IConfigurationSection?
3>Either way, how do I retrieve and pass SecurityHeaderOptions into SecurityHeadersBuilder's constructor?
Using .NET Core 2 and not having a provider available (or caring to add it) in ConfigureServices I opted to go with something like this (using OP code as example):
public void ConfigureServices(IServiceCollection services)
{
// secOpts available for use in ConfigureServices
var secOpts = Configuration
.GetSection("SecurityHeaderOptions")
.Get<SecurityHeaderOptions>();
...
}
This is how I register options and inject into SecurityHeadersBuilder
public void ConfigureServices(IServiceCollection services)
{
services.Configure<SecurityHeaderOptions>(Configuration.GetSection("SecurityHeaderOptions"));
services.AddScoped<SecurityHeadersBuilder>(provider =>
{
var option = provider.GetService<IOptions<SecurityHeaderOptions>>();
return new SecurityHeadersBuilder(option)
.AddDefaultPolicy();
});
}
Docs specifically say:
Don't use IOptions<TOptions> or IOptionsMonitor<TOptions> in Startup.ConfigureServices. An inconsistent options state may exist due to the ordering of service registrations.
So you'll have to access the configuration some other way from Startup.ConfigureServices, e.g. Quinton's answer
Firstly you need to add a second constructor to SecurityHeadersBuilder, that takes a plain SecurityHeaderOptions:
public class SecurityHeadersBuilder
{
private readonly SecurityHeaderOptions _options;
public SecurityHeadersBuilder(IOptions<SecurityHeaderOptions> options)
{
_options = options.Value;
}
public SecurityHeadersBuilder(SecurityHeaderOptions options)
{
_options = options;
}
public SecurityHeadersBuilder AddDefaultPolicy()
{
AddFrameOptions();
AddContentSecurityPolicy();
return this;
}
}
Then the answer entirely depends on whether or not you need to use those options outside of your Startup class.
If not, you can simply use the Microsoft.Extensions.Configuration.ConfigurationBinder.Get<T>() extension method:
services.AddScoped<SecurityHeadersBuilder>(provider =>
{
var options = Configuration.GetSection("SecurityHeaderOptions")
.Get<SecurityHeaderOptions>();
return new SecurityHeadersBuilder(options)
.AddDefaultPolicy();
});
(you can then delete the SecurityHeadersBuilder constructor that takes IOptions<SecurityHeaderOptions>).
If you will need to use these options elsewhere, then you can combine the above approach with the Microsoft.Extensions.DependencyInjection.OptionsConfigurationServiceCollectionExtensions.Configure() extension method:
var optionsSection = Configuration.GetSection("SecurityHeaderOptions");
services.Configure<SecurityHeaderOptions>(optionsSection);
services.AddScoped<SecurityHeadersBuilder>(provider =>
{
var options = optionsSection.Get<SecurityHeaderOptions>();
return new SecurityHeadersBuilder(options)
.AddDefaultPolicy();
});
Regarding your questions:
1. Yes, you need to register the options, but I believe you are doing it the wrong way (at least by your example). You should register as this:
services.Configure<SecurityHeaderOptions>(Configuration.GetSection("SecurityHeaderOptions"));
2. I believe that the correct registration I refer above returns what you are expecting.
3. Just registering it and placing it on the SecurityHeaderBuilder constructor is enough. You do not need (neither does the default .NET Core IOC container allows) to pass constructor parameters when registering it. For that you would need to use other IOC's such as Autofac.
But you need to register SecurityHeadersBuilder in order to use it within other classes.
Just use an interface for that.
public interface ISecurityHeadersBuilder
{
SecurityHeadersBuilder AddDefaultPolicy();
}
public class SecurityHeadersBuilder : ISecurityHeadersBuilder
{
private readonly SecurityHeaderOptions _options = null;
public SecurityHeadersBuilder(IOptions<SecurityHeaderOptions> options)
{
_options = options.Value;
}
public SecurityHeadersBuilder AddDefaultPolicy()
{
AddFrameOptions();
AddContentSecurityPolicy();
return this;
}
}
Then, just register it in startup.cs as this
services.AddScoped<ISecurityHeadersBuilder, SecurityHeadersBuilder>();
You could do something like this
public static class IConfigurationExtensions
{
public static TypedConfiguration<SecurityHeaderOptions> GetSecurityHeaderOptions(this IConfiguration configuration)
{
return new TypedConfiguration<SecurityHeaderOptions>(configuration.GetSection("SecurityHeaderOptions"));
}
}
public class TypedConfiguration<T> where T : class
{
public TypedConfiguration(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public T Value => Configuration.Get<T>();
public void InitializeOptions(IServiceCollection services)
{
services.Configure<T>(Configuration);
}
}
Now from single place you've created object that has both IConfiguration, typed SecurityHeaderOptions and helper method for registering IOptions injection for that class.
Use it like this
public void ConfigureServices(IServiceCollection services)
{
var wrappedOptions = Configuration.GetSecurityHeaderOptions();
wrappedOptions.InitializeOptions(services);
var options = Options.Create(wrappedOptions.Value);
services.AddScoped<SecurityHeadersBuilder>(provider =>
new SecurityHeadersBuilder(options).AddDefaultPolicy());
}
I am using SimpleInjector for my DI in Mvc Core and I have a class that accepts ISession at the constructor.
public SessionAppAdminAuthorization(ISession session)
I need to register this at the DI configuration in StartUp.Configure method but I don't know how the get the scoped session variable.
container.Register<IAppAdminAuthorization>(() => {
return new SessionAppAdminAuthorization([I Need the ISession]); },
Lifestyle.Scoped);
ASP.NET Core's ISession can be accessed through the HttpContext.Session property. Since HttpContext is runtime data, the Session is as well. Runtime data should not be injected into your components' constructors, so your SessionAppAdminAuthorization should not depend on ISession directly.
The simplest fix is to let SessionAppAdminAuthorization depend on IHttpContextAccessor instead and call IHttpContextAccessor.HttpContext.Session later on. Example:
public class SessionAppAdminAuthorization : IAppAdminAuthorization
{
private readonly IHttpContextAccessor accessor;
public SessionAppAdminAuthorization(IHttpContextAccessor accessor) {
this.accessor = accessor;
}
public void DoSomethingUseful() {
if (this.accessor.HttpContext.Session.GetBoolean("IsAdmin")) {
// ...
} else {
// ...
}
}
}
Now you can make the registrations as follows:
public void ConfigureServices(IServiceCollection services)
{
// You need to register IHttpContextAccessor.
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
// ...
}
public void Configure(IApplicationBuilder app, IHostingEnvironment e, ILoggerFactory f)
{
container.RegisterSingleton(
app.ApplicationServices.GetRequiredService<IHttpContextAccessor>());
container.Register<IAppAdminAuthorization, SessionAppAdminAuthorization>();
// ...
}
Although this will effectively solve your problem, you might want to take it up one step. In general it's better to hide framework components and abstractions like IHttpContextAccessor, HttpContext and ISession from application components. Instead the Dependency Inversion Principle guides us towards application-specific abstractions implemented by adapters that allow translating these application-specific calls onto framework components. For instance:
// Application-specific abstraction (part of your application's core layer)
public interface IUserContext
{
bool IsAdmin { get; }
}
// Adapter implementation (placed in the Composition Root of your web app)
public class AspNetSessionUserContextAdapter : IUserContext
{
private readonly IHttpContextAccessor accessor;
public AspNetSessionUserContextAdapter(IHttpContextAccessor accessor) {
this.accessor = accessor;
}
public bool IsAdmin => this.accessor.HttpContext.Session.GetBoolean("IsAdmin");
}
// Improved version of SessionAppAdminAuthorization
public class SessionAppAdminAuthorization : IAppAdminAuthorization
{
private readonly IUserContext userContext;
// This class can now be moved to the business layer, since there's no
// more dependency on ASP.NET.
public SessionAppAdminAuthorization(IUserContext userContext) {
this.userContext = userContext;
}
public void DoSomethingUseful() {
if (this.userContext.IsAdmin) {
// ...
} else {
// ...
}
}
}
Registration:
public void Configure(IApplicationBuilder app, IHostingEnvironment e, ILoggerFactory f)
{
var accesr = app.ApplicationServices.GetRequiredService<IHttpContextAccessor>();
container.RegisterSingleton<IUserContext>(new AspNetSessionUserContextAdapter(accesr));
container.Register<IAppAdminAuthorization, SessionAppAdminAuthorization>();
// ...
}
Trying to do dependency injection into my SignalR Hub class using the SignalR-Server which is part of ASP.NET 5 (repo). I tried to figure this out from the tutorial at this link but I can't seem to identify how I can do this given that GlobalHost is no longer available. Here's what I'm trying to do:
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddSignalR();
services.AddSingleton<IState, State>();
}
public void Configure(IApplicationBuilder app)
{
app.UseSignalR();
}
MyHub.cs
public class MyHub : Hub
{
public IState State { get; set; }
// SignalR accepts this parameterless ctor
public MyHub()
{
}
// SignalR won't use this because it has a parameter
public MyHub(IState state)
{
State = state;
}
}
How can I get SignalR-Server to use the MyHub(IState state) constructor injecting the required dependencies?
the best way (for Asp.Net 5) create a custom resolver to DefaultDependencyResolver that receives IServiceProvider:
public class CustomSignalRDependencyResolver : DefaultDependencyResolver
{
private readonly IServiceProvider _serviceProvider;
public CustomSignalRDependencyResolver(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public override object GetService(Type serviceType)
{
var service = _serviceProvider.GetService(serviceType);
return service ?? base.GetService(serviceType);
}
}
Then on StartUp class
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IState, State>();
//... other services
GlobalHost.DependencyResolver = new CustomSignalRDependencyResolver(services.BuildServiceProvider());
}
I managed to resolve this by adding my State class as a Singleton for IState in Startup.ConfigureServices, and then making a ServiceProvider property publicly available on my Startup.cs class. From there, I was able to GetRequiredService within the constructor of my SignalR Hub class. It isn't the ideal solution and hopefully I'll be able to adjust this to use constructor/property injection as the platform reaches RC.
Here's my code:
Startup.cs
public static IServiceProvider __serviceProvider;
public void ConfigureServices(IServiceCollection services)
{
services.AddSignalR();
services.AddSingleton<IState, State>();
__serviceProvider = services.BuildServiceProvider();
}
public void Configure(IApplicationBuilder app)
{
app.UseSignalR();
}
MyHub.cs
public class MyHub : Hub
{
public IState State { get; set; }
public MyHub()
{
State = (IState) Startup.__serviceProvider.GetRequiredService(typeof (IState));
}
public override Task OnConnected()
{
State.Clients = Clients;
State.Groups = Groups;
return base.OnConnected();
}
}
In this way, I was able to set properties and call methods on IState implementing objects from within MyHub, allowing me to persist my app state in memory.
You're very close. You just need:
public class MyHub : Hub
{
readonly IState _state;
public MyHub(IState state)
{
_state = state;
}
}
Ok. Now, I used Autofac, which I am not sure it has ASP.NET 5 integration yet. But if(for now) only target .NET 4.6, you should be fine.
I just published this repository which contains a basic project setup with SignalR and Autofac for dependency injection.
Now, I did the dependency injection setup in order to achieve the following:
be able to inject dependencies into my hub
be able to get the context for my hubs in order to send to clients from outside the hub without using GlobalHost (which is no longer available in .NET 5, but also shouldn't be used since it's a static global object)
I hope you manage to setup your project (even though I don't think you will be able to keep DNX in your build options since Autofac doesn't have the library .NET 5 ready yet.
I hope this helps! Best of luck!
https://github.com/radu-matei/SignalRDependencyInjection
EDIT: If you want to use NInject (and build your own dependency resolver if you want to target DNX, you can follow this repository from the official guys from SignalR (actually from the guy who wrote SignalR):
https://github.com/DamianEdwards/NDCLondon2013/tree/master/DependencyInjection
In this demo they use NInject to create their own dependency resolver, so you shouldn't have any problems targeting DNX if you have NInject libraries.
UPDATE: After reading a little about Dependency Injection in ASP.NET 5, it seems that it is done in an unified manner. If you haven't had a look at this article, I recommend it, even though it doesn't specifically show SignalR DI.
I have simply made constructor with dependencies. For example, I need my IUnitOfWork instance (which was configured in startup) in hub. That is working code
[HubName("receipts")]
public class ReceiptsHub : Hub
{
public IUnitOfWork<string> UnitOfWork { get; set; }
public ReceiptsHub(IUnitOfWork<string> unitOfWork) : base()
{
UnitOfWork = unitOfWork;
}
public override Task OnConnected()
{
return base.OnConnected();
}
public override Task OnDisconnected(bool stopCalled)
{
return base.OnDisconnected(stopCalled);
}
}
In .NET 5 you can directly resolve the IServiceProvider and later you can have the required service. Please check the below codes:
public class MyHub : Hub
{
public IState State { get; set; }
private readonly IServiceProvider _serviceProvider;
public MyHub(IServiceProvider serviceProvider)
{
_serviceProvider=serviceProvider;
State = _serviceProvider.GetRequiredService<IState>();
}
public override Task OnConnected()
{
State.Clients = Clients;
State.Groups = Groups;
return base.OnConnected();
}
}