What is best practice to inject IOptions in to my ICollectionService Extension? - c#

I have a library class that accepts an IOptions parameter in its constructor as below:
public class MyClass{
MySetting _setting;
public MyClass(MySetting setting){
_setting = setting;
}
public void DoSomeThings(){
//...
}
}
now I want to create an IServiceCollection Extension to facilitate the setup of my library in user code but I am not certain about the best way to inject MySetting into the Constructor.
I can Rely on users to use this code in their startup.cs:
services.Configure<MySettings>(Configuration.GetSection("MySetting"));
or I can inject MySetting into my Extention method as a parameter like this:
public static IServiceCollection AddMtService<T>(this IServiceCollection services, MySetting setting)
Or I can read the appsetting.json file in my extension method and create the setting inside it.
Which one is the best approach and is there any other option that can be better of these?

You can check in more detail from: link
Basic usage:
Inject IOptions with your config class in your service:
public class TestService : ITestService
{
private readonly MySettings _config;
public TestService(IOptions<MySettings> config)
{
_config = config.Value; // You have the configuration class hire...
}
public void TestFunction()
{
string name = _config.Name;
bool isTrue = _config.IsTrue;
bool isFalse = _config.IsFalse;
}
}
When your config class looks something like this:
public class MySettings
{
public const string ConfigSectionKey = "MySettings";
public string Name { get; set; }
public bool IsTrue { get; set; }
public bool IsFalse { get; set; }
}
Registration of your config class to IOptions:
builder.Services.Configure<MySettings>(builder.Config.GetSection(MySettings.ConfigSectionKey));
And the properties are present in appsettings.json (or where you hold your configuration)
{...
"MySettings": {
"Name": "Tralalaa",
"IsTrue": true,
"IsFalse": false
}
}

Related

How do i authenticate my app to a external service at startup [duplicate]

public class Bar
{
public static readonly string Foo = ConfigurationManager.AppSettings["Foo"];
}
In the .NET Framework 4.x, I can use the ConfigurationManager.AppSettings ["Foo"] to get Foo in Webconfig,and then I can easily get the value of Foo through Bar.Foo
But in .Net core, I mustto inject options, And can not get the value of Foothrough Bar.Foo
Is there a method, which can be directly through the Bar.Foo to get the value of Foo?
So there are really two ways to go about this.
Option 1 : Options Class
You have an appsettings.json file :
{
"myConfiguration": {
"myProperty": true
}
}
You create a Configuration POCO like so :
public class MyConfiguration
{
public bool MyProperty { get; set; }
}
In your startup.cs you have something in your ConfigureServices that registers the configuration :
public void ConfigureServices(IServiceCollection services)
{
services.Configure<MyConfiguration>(Configuration.GetSection("myConfiguration"));
}
Then in your controller/service you inject in the IOptions and it's useable.
public class ValuesController : Controller
{
private readonly MyConfiguration _myConfiguration;
public ValuesController(IOptions<MyConfiguration> myConfiguration)
{
_myConfiguration = myConfiguration.Value;
}
}
Personally I don't like using IOptions because I think it drags along some extra junk that I don't really want, but you can do cool things like hot swapping and stuff with it.
Option 2 : Configuration POCO
It's mostly the same but in your Configure Services method you instead bind to a singleton of your POCO.
public void ConfigureServices(IServiceCollection services)
{
//services.Configure<MyConfiguration>(Configuration.GetSection("myConfiguration"));
services.AddSingleton(Configuration.GetSection("myConfiguration").Get<MyConfiguration>());
}
And then you can just inject the POCO directly :
public class ValuesController : Controller
{
private readonly MyConfiguration _myConfiguration;
public ValuesController(MyConfiguration myConfiguration)
{
_myConfiguration = myConfiguration;
}
}
A little simplistic because you should probably use an interface to make unit testing a bit easier but you get the idea.
Mostly taken from here : http://dotnetcoretutorials.com/2016/12/26/custom-configuration-sections-asp-net-core/
The solutions on top are time consuming and not straightforward, this is the best effective way to do it, no setup needed on startup or anything. It's like using the good ol Configuration.Manager.AppSettings["setting"]
First create a class like "Credential":
public class Credential
{
public string Username {get;set}
public string Password {get;set;}
}
Now that's setup let put the IConfiguration on your constructor like so:
private IConfiguration _configuration;
public ValuesController(IConfiguration iconfig)
{
_configuration = iconfig;
}
Then you're ready to call it!
Credential c = new Credential();
c.UserName = _configuration.GetValue<string>("Credential:username");
c.Password = _configuration.GetValue<string>("Credential:password");
Assuming your appsettings.json looks like this:
"Credential": {
"username": "myuser",
"password": "mypassword"
}
Hope this helps somebody.
You can also use the configuration directly. Your settings are injected so you can get to them with DI...
private readonly IConfiguration _configuration;
public MyClass(IConfiguration configuration)
{
_configuration = configuration;
}
and then you can read your settings...
In this case I'm getting a collection back...
var myList = _configuration.GetSection("MyList").Get<List<string>>();
define your class as
public class MyClass{
private readonly IConfiguration _configuration;
public MyClass(IConfiguration configuration)
{
_configuration = configuration;
}
public void myFunction(){
var value= _configuration.GetValue("xxx");
}
}
when you call it from anywhere else
IConfiguration config = new ConfigurationBuilder()
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: false).Build();
MyClass myclass = new MyClass(config)
Addition to answer from #MindingData. I like to map my settings recursively using Configuration.Bind(settings); so that I don't have to add every new section in ConfigureServices.
Example:
appsettings.json:
{
"MyConfiguration": {
"MyProperty": true
}
}
Settings class, properties must match appsettings.json names:
public class Settings
{
public MyConfigurationSettings MyConfiguration { get; set; }
public class MyConfigurationSettings
{
public bool MyProperty { get; set; }
}
}
Startup.cs
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
var settings = new Settings();
Configuration.Bind(settings);
services.AddSingleton(settings);
...
Can be used in controllers like this:
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
private readonly Settings _settings;
public ValuesController(Settings settings)
{
_settings = settings;
}
[HttpGet("GetValue")]
public ActionResult<string> Get()
{
return Ok(_settings.MyConfiguration.MyProperty);
}
}
If you already passed to controller your configuration and don't want to map another service in startup configuration, you can easy get your value from configuration in this way:
public Startup(IConfiguration configuration)
{
Configuration = configuration;
var myProperty = _configuration.GetSection("MyConfiguration")["MyProperty"];
}
using dictionary property.
Enjoy
Add this in AppSettings.json file
"Swagger": { "Title": "API DEVELOPMENT" }
Then configure that in Startup.cs file
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
Next get the value from appsettings.json
var title = Configuration.GetSection("Swagger:Title").Value;
Finally put here
services.AddSwaggerGen(c => { c.SwaggerDoc("v1", new OpenApiInfo { Title = title, Version = "v1" }); }
The top answer of MindingData is great. I just want to add to it, that using IOptions is recommended, because then you can handle the situation when configuration changes and must be reloaded.
For such scenario I suggest using IOptionsMonitor which creates a singleton to store the configuration object and when the appsettings.json is changed, your object will be automatically updated.
You can even register a listener to OnChange() method of IOptionsMonitor and react to the change further.
Using this approach gives you advantage when you have a service in production, that you don't have to restart it to load the new configuration.

Accessing AppConfig settings via Injection

I have values in my AppConfig like this...
<appSettings>
<add key="id" value="1234" />
</appSettings>
In the IAppConfig class I have...
public interface IAppConfig
{
string id{ get; }
}
[ConfigurationProperty("id")]
public string id
{
get
{
return (string)this["id"];
}
set
{
this["id"] = value;
}
}
In a startup class I have
using SimpleInjector;
public static Container Container;
public static void Start()
{
Container = new Container();
IAppConfig appConfig = (IAppConfig)ConfigurationManager.GetSection("appSettings");
Container.Register<IAppConfig, AppConfig>(Lifestyle.Singleton);
}
And I inject it into another class...
private readonly IAppConfig config;
public ClassName(IAppConfig config)
{
this.config = config;
}
However the id (and other values) come up as empty strings. This happens in the Getter. I am calling the Start method before trying to access them. What am I doing wrong?
You register type AppConfig as singleton implementation of type IAppConfig, so when you request IAppConfig from container, you'll get an instance created by container, and not 'appConfig' you created in Start.
You need to register instance like:
Container.Instance<IAppConfig>(appConfig);
You have created an appconfig object but have not registered that for injection.
Try the below:
Simplify you AppConfig class
public class AppConfig
{
public string id{ get; set; }
}
var appconfig = new AppConfig{ id = ConfigurationManager.AppSettings["id"] };
container.Register<AppConfig>(() => appconfig, Lifestyle.Singleton);
You can keep the Interface pattern as well

Model validation for configuration objects in ASP.NET Core

In .NET Core, if my appsettings file looks like
{
"Section": {
"Field": "value"
}
}
I can create a class like
public class Section
{
public string Field { get; set; }
}
and retrieve the value in Startup like
public void ConfigureServices(IServiceCollection services) {
services.Configure<Section>(this.Configuration.GetSection("Section"));
}
The problem is that if for some reason (say misspelling) the binding fails, it is not going to throw, and instead it will create a Section object with null (default) value for the Field property.
Is there a way to make services.Configure<Section>(this.Configuration.GetSection("Section")); to throw if the binding fails?
I am just summing up #Nkosi's answer here which makes this validation possible using data annotation.
1- Annotate the properties of your class:
public class Section
{
[Required]
public string Field { get; set; }
}
2- Create an extension method to enable validation to take effect:
public static class ConfigurationModelValidation
{
public static T GetValid<T>(this IConfiguration configuration)
{
var obj = configuration.Get<T>();
Validator.ValidateObject(obj, new ValidationContext(obj), true);
return obj;
}
}
3- In the Startup class, register you configuration models as below using GetValid method (instead of using 'IOptions'):
public void ConfigureServices(IServiceCollection services) {
services.AddSingleton(this.Configuration.GetSection("Section").GetValid<Section>());
}
4- Now in the user's class directly inject your configuration model:
public class MyClass
{
private readonly string field;
public MyClass(Section section)
{
this.field = section.field;
}
}
Now if binding fails for any reason, the validation will kick in and it will throw, enjoy!
You can just get the section first, then verify it exists (because it will not be null).
public void ConfigureServices(IServiceCollection services) {
var section = this.Configuration.GetSection(nameof(Section));
if (!section.Exists()) throw new Exception();
services.Configure<Section>(section);
}

accessing configuration in routeattribute

I have my API route attribute class like this
public class MyRouteAttribute : RouteAttribute
{
private const string BaseRoute = "api/default";
private const string PrefixRouteBase = BaseRoute + "/";
public MyRouteAttribute() : base(BaseRoute)
{
}
public MyRouteAttribute(string route):
base(string.IsNullOrEmpty(route) ?
BaseRoute : PrefixRouteBase + route)
{
}
}
And it is used in controller like this
[MyRoute]
public class MyController : Controller
{
.....
}
How do I pass IOptions to MyRoute if I have to make the route configurable?
For example, if I do this:
public class MyRouteAttribute : RouteAttribute
{
private const string BaseRoute = "api/default";
public MyRouteAttribute(IOptions<ApiRouteBaseConfiguration> routeOptions) :
base(routeOptions.Value.Url)
{
}
public MyRouteAttribute(IOptions<ApiRouteBaseConfiguration> routeOptions, string route):
base(string.IsNullOrEmpty(route) ? (routeOptions.Value.Url: $"{routeOptions.Value.Url}/" + route)
{
}
}
Then I get error here [MyRoute] asking me to pass IOptions.
How do I access configuration in MyRoute attribute
Attribute instances are created by CLR when attributes are requested from Reflection routines. You have no way to force instantiation of attributes via any DI container.
I see two possible approaches to workaround your challenge. Both of them allow you to have configurable attribute, however configuration is set not via attribute constructor.
The simpler way is to set configuration through static property loaded on application startup:
public class MyRouteAttribute : RouteAttribute
{
public static ApiRouteBaseConfiguration RouteConfiguration { get; } = new ApiRouteBaseConfiguration();
public MyRouteAttribute() :
base(RouteConfiguration.Url)
{
}
public MyRouteAttribute(string route) :
base(string.IsNullOrEmpty(route) ? RouteConfiguration.Url : $"{RouteConfiguration.Url}/" + route)
{
}
}
Configuration (Configuration section is named "Routing" here):
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
Configuration.Bind("Routing", MyRouteAttribute.RouteConfiguration);
}
Well, this solution is not perfect because of static property usage. However it's quite simple and should do the trick.
The second possible approach - use Property Injection pattern for attribute configuration and set it in custom implementation of IApplicationModelProvider. Such approach is described in this answer, I will not duplicated the code here.

Use Dependency Injection outside Controller asp.net-core v1.1.0 for application settings

I am using asp.net-core v1.1.0 :)
I want to access the application settings values from a service class and not from a controller, my code is:
appsettings.json
// appsettings.json
{
"ImagesDBSettings": {
"Endpoint": "",
"Key": "",
}
}
Startup.cs
// Startup.cs
...
public void ConfigureServices(IServiceCollection services)
{
...
services.AddOptions();
services.Configure<ImagesDBSettings>(Configuration.GetSection("ImagesDBSettings"));
...
}
...
ImagesDBSettings.cs
// ImagesDBSettings.cs
public class ImagesDBSettings
{
public string Endpoint { get; set; }
public string Key { get; set; }
}
ImagesDBService.cs
// ImagesDBService.cs
public class ImagesDBService
{
private readonly ImagesDBSettings _settings;
public ImagesDBService(IOptions<ImagesDBSettings> settings)
{
_settings = settings.Value;
}
}
On compiling I get the error:
There is no argument given that corresponds to the required formal parameter 'settings' of 'ImagesDBService.ImagesDBService(IOptions)'
Any ideas on how to make the Dependency Injection Work?
IOptions dependancy will not be injected into the ImagesDBService with the code shown. You need to use AddTransient in startup.cs for that. For the DI outside controller see this question. Or you can pass IOptions to your service class from the controller (not the best option).
public IActionResult Index(IOptions<ImagesDBSettings> settings)
{
ImagesDBService ss = new ImagesDBService(settings);
return View();
}
Here is how I do it in my app without the DI. I have a static AppSettings class that I configure in ConfigureServices (startup.cs). I then simply have access to my AppSettings anywhere in the app.
public static class AppSettings
{
public static string ConnectionString { get; set; }
}
Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
AppSettings.ConnectionString = Configuration.GetConnectionString("DefaultConnection");
}
You said it's a compiling error so probably not something to do with DI, it's more likely you are missing the IOptions<> namespace.
If you haven't done it already, install package: Microsoft.Extensions.Options.ConfigurationExtensions from Nuget.
Then reference the namespace Microsoft​.Extensions​.Options to your class ImagesDBService

Categories

Resources