How to set instance in attribute, using DI. ASP.NET vNext - c#

I have next code:
public class BasicAuthenticationAttribute : ActionFilterAttribute
{
public BasicAuthenticationAttribute()
{
var appEnvironment = CallContextServiceLocator.Locator.ServiceProvider.GetService(typeof(IApplicationEnvironment)) as IApplicationEnvironment;
IConfiguration configuration = new ConfigurationBuilder()
.SetBasePath(appEnvironment.ApplicationBasePath)
.AddJsonFile("config.json").Build();
UserName = configuration["UserName"];
Password = configuration["UserPassword"];
}
}
public class PagesController : Controller
{
[HttpGet("settings")]
[BasicAuthentication] // I use it there
public IActionResult Settings()
{
...
}
}
This's attribute for basicAuth.
I need to inject "IConfiguration configuration" into my constructor, to read config json.
How can I get IConfiguration into my attribute? May be there is a service like CallContextServiceLocator.Locator.ServiceProvider but for IConfiguration
PS I understand that Attribute values must be constants

Attribute values must be constants, so you will not be able to pass in an instance of a class.

To use this in your attribute class:
[FromServices]
public IConfiguration Configuration { get; set; }
it must be public and has getter and setter, it will get the service from DI automaticly.

Related

ASP.NET MVC Failing to Access appsettings.JSON

I have an issue that I cannot access the settings within my appsettings.json file.
I have set the class as this :
public class apisettings
{
public const string SectionName = "LocalConfig";
public string Url { get; set; }
}
The section in the appsettings.json is as follows :
"LocalConfig": {
"Url": "https://someurl.com/api/"
}
In my Startup.cs I have the following (please excuse all the additional parts for Azure Authentication) :
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)
{
services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(Configuration.GetSection("AzureAd"));
services.AddControllersWithViews(options =>
{
var policy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
options.Filters.Add(new AuthorizeFilter(policy));
});
services.AddRazorPages()
.AddMicrosoftIdentityUI();
services.AddOptions();
services.Configure<apisettings>(Configuration.GetSection(apisettings.SectionName));
}
In my controller I have it set as per the following :
public static string URL = "";
public IActionResult Index(IOptions<apisettings> apisettings)
{
URL = apisettings.Value.Url;
return View();
}
Now every time I access that particular section of the application I get an error that states "Could not create an instance of type 'Microsoft.Extensions.Options.IOptions`1[[ManagementApplication.Models.apisettings, ManagementApplication, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]'. Model bound complex types must not be abstract or value types and must have a parameterless constructor. Record types must have a single primary constructor. Alternatively, give the 'apisettings' parameter a non-null default value."
I am sure I am missing something very simple, but please help?
UPDATE
I have edited the class as per the advice below so it now shows as :
public class apisettings
{
public apisettings()
{
}
public const string SectionName = "LocalConfig";
public string Url { get; set; }
}
It is still giving me exactly the same error, I know I am missing something simple but can't find it :(
Looks like the apisettings class has got some other constructor configured and you are trying to call the default constructor. A very good explanation has been provided here
Add a default constructor as below
public apisettings() {
};
After much trawling through the answers and the internet I was missing a very simple piece of the puzzle. The IOptions was being created in the services and I needed to refer to that in the Controller code to make sure it knew where to look.
The below is a cut down version of all the code with only the relevant parts included so anyone can follow the solution.
I have set the class as this :
public class apisettings
{
public apisettings() { }
public apisettings(string url)
{ Url = url; }
public string Url { get; set; }
}
The section in the appsettings.json is as follows :
"LocalConfig": {
"Url": "https://someurl.com/api/"
}
In my Startup.cs I have the following :
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
services.AddOptions();
services.Configure<apisettings> Configuration.GetSection("LocalConfig"));
}
In my controller I have it set as per the following :
public static string URL = "";
public IActionResult Index([FromServices] IOptions<apisettings> apisettings)
{
URL = apisettings.Value.Url;
return View();
}
The key to the solution was adding the [FromServices] to the declaration.
Model bound complex types must not be abstract or value types and must
have a parameterless constructor
Add a constructor method without parameters
public class apisettings
{
public apisettings() {
}
public const string SectionName = "LocalConfig";
public string Url { get; set; }
}
Since you are still getting the same error, I think you should check that you are following this pattern
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/configuration/options?view=aspnetcore-5.0

what is diffrence between Configuration binding and IOption Pattern in dotnet core?

I know there are two ways to inject configuration into my other layers class:
first one Configuration binding:
services.AddConfiguration() // enable Configuration Services
var config = new WeblogConfiguration();
Configuration.Bind("Weblog", config); // <--- This
services.AddSingleton(config);
usage:
public class PostsController : Controller
{
PostRepository PostRepo { get; }
WeblogConfiguration Config { get; }
public PostsController(PostRepository postRepo,
WeblogConfiguration config)
{
PostRepo = postRepo;
Config = config;
}
...
}
and IOption Pattern:
services.Configure<WeblogConfiguration>(option => Configuration.GetSection("WeblogConfiguration").Bind(option));
services.AddOptions();
usage:
public class PostsController : Controller
{
PostRepository PostRepo { get; }
WeblogConfiguration Config { get; }
public PostsController(PostRepository postRepo,
IOptions<WeblogConfiguration> config)
{
PostRepo = postRepo;
Config = config.Value;
}
...
}
I want to know the difference between these methods and the cons and pros for each of them.
So far only one difference come into my mind:
The possibility to reload the configuration.
When you bind your WeblogConfiguration and add it as singleton it will be loaded once and never changes.
Once you use the IOptions pattern you could use the reloadOnChange argument when adding the json file and will get updated values via the IOptions.Value method.

Configure IOptions<T> with ServiceCollection when T is Type

I need to register an IOptions<T> with a .NET Core ServiceCollection.
Normally the method would be something like this:
var configSection = configuration.GetSection("sectionName");
serviceCollection.Configure<KnownClassName>(configSection);
This registers a strongly typed IOptions<KnownClassName> with the container.
I need to register an IOptions<UnknownName>. I have the Type of the class. I can't seem to find a method that will allow me to register the Type and the configuration section.
This is what am attempting to do:
interface ILoggingProvider
{
Type GetSettingType();
string ProviderName {get;}
}
IList<ILoggingProvider> loggingProviders = GetLoggingProviders();
foreach(var provider in loggingProvider)
{
var providerSection = configuration.GetSection(providerSection);
var providerSettingType = provider.GetSettingType();
// can't find an overload or other method to do the same thing as
// serviceCollection.Configure<LoggerSettings>(providerSection);
serviceCollection.Configure(providerSection, providerSettingType);
}
I don't think that this is possible; checking the source of IServiceCollection.Configure<TOptions> shows that the entire options/configuration functionality is built around generic types.
Also, as far as I know, there is no non-generic IOptions interface, so there is not really a justification for providing a non-generic configuration registration method.
However, this implies that your ILoggingProvider instances have to make use of IOptions<UnknownName> themselves, else they would not be able to retrieve the configuration from the DI container. In this case, you may either expose the options class of the particular providers, or add an (extension-)method ILoggingProvider.RegisterOptions(IServiceCollection, IConfiguration) which does this registration itself.
In Startup.cs, you would then simply call
provider.RegisterOptions(serviceCollection, providerSection);
for each provider.
This of course requires write access to the source code of the ILoggingProvider implementations.
You could use services.ConfigureOptions, if it is possible to return type that implements IConfigureOptions<T> from your ILoggingProvider.GetSettingType()
public class CustomConfiguration
{
public string Data { get; set; }
}
public class CustomConfigurationOptions : IConfigureOptions<CustomConfiguration>
{
private readonly IConfiguration configuration;
public CustomConfigurationOptions(IConfiguration configuration)
{
this.configuration = configuration;
}
public void Configure(CustomConfiguration options)
{
var optionsFromConfig = configuration
.GetSection(nameof(CustomConfiguration))
.Get<CustomConfiguration>();
options.Data = optionsFromConfig.Data;
}
}
public class LoggingProvider
{
public Type Type { get; set; }
}
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)
{
services.AddControllers();
var loggingProviders = new List<LoggingProvider>()
{
new LoggingProvider()
{
Type = typeof(CustomConfigurationOptions)
}
};
foreach (var loggingProvider in loggingProviders)
{
services.ConfigureOptions(loggingProvider.Type);
}
}
...
}

How to set default value from AppSettings on a view model in C#?

I am exposing a view on a new API, and want to know how I can set a default value to a property, reading it from my appsettings.json file. How can I do it?
I've tried using dependency injection on the view, but it seems impossible since it's the framework that instantiate the class.
public class FilteringRequest
{
private readonly IAppContext appContext;
public FilteringRequest(IAppContext appContext)
{
this.appContext = appContext;
}
// TODO: appsettings?
// Perhaps something like appContext.DefaultType
public string Type { get; set; } = "top_rated";
// ...
}
This approach doesn't work since I believe the framework is expecting a parameterless default constructor.
An unhandled exception occurred while processing the request.
InvalidOperationException: Could not create an instance of type 'Wanderlust.API.Models.Requests.FilteringRequest'. Model bound complex types must not be abstract or value types and must have a parameterless constructor. Alternatively, give the 'request' parameter a non-null default value.
Microsoft.AspNetCore.Mvc.ModelBinding.Binders.ComplexTypeModelBinder.CreateModel(ModelBindingContext bindingContext)
Bear in mind that IAppContext already has all the properties defined in appsettings.json file.
Dependency injection should work. In Startup.cs
public void ConfigureServices(IServiceCollection services)
{
// ...
services.Configure<MyAppContext>(Configuration.GetSection(nameof(MyAppContext)));
// ...
}
Define your class MyAppContext:
public class MyAppContext
{
public MyAppContext()
{
}
public string DefaultType { get; set; }
// other public properties here...
}
In your FilteringRequest class:
public class FilteringRequest
{
private readonly MyAppContext _appContext;
public FilteringRequest(Microsoft.Extensions.Options.IOptions<MyAppContext> appContext)
{
_appContext = appContext.Value;
}
string _type;
public string Type
{
get => _type ?? (_type = _appContext.DefaultType);
set => _type = value;
}
// ...
}
To read data from appsettings.json in any class, you just need to pass Configuration with dependency injection by creating IConfiguration field:
public class FilteringRequest
{
private readonly IConfiguration _configuration;
public FilteringRequest(IConfiguration configuration)
{
_configuration = configuration;
Type = _configuration["Model:FilteringRequest:Type"];//set value from appsettings.json file
}
public string Type { get; set; }
// ...
}
appsettings.json:
{
"Model": {
"FilteringRequest": {
"Type": "testdata"
}
}
}
Controller:
public class HomeController : Controller
{
private readonly IConfiguration _configuration;
public HomeController (IConfiguration configuration)
{
_configuration = configuration;
}
public IActionResult Index
{
var modelinstance = new FilteringRequest(_configuration);//create the viewmodel with default 'Type"
return View();
}
}
Refer to :
Getting value from appsettings.json in .net core
Read appsettings.json from a class in .NET Core 2

ASP.NET 5 (vNext) - Getting a Configuration Setting

I'm writing a basic app to learn ASP.NET 5. One area I find very confusing is configuration. Prior to ASP.NET 5, I could do the following:
var settingValue = ConfigurationManager.AppSettings["SomeKey"];
I would have lines of code like that sprinkled throughout my code. Now, in the vNext world, I have a config.json file that looks like this:
config.json
{
"AppSettings": {
"SomeKey":"SomeValue"
}
}
Then in Startup.cs, I have the following:
Startup.cs
public IConfiguration Configuration { get; set; }
public Startup(IHostingEnvironment environment)
{
Configuration = new Configuration()
.AddJsonFile("config.json");
}
From there, I'm totally stumped. I have MyClass.cs in /src/Website/Code/Models/MyClass.cs.
MyClass.cs
public class MyClass
{
public string DoSomething()
{
var result = string.Empty;
var keyValue = string.Empty; // TODO: What do I do here? How do I get the value of "AppSettings:SomeKey"?
return result;
}
}
How do I get the value of "AppSettings:SomeKey"?
ASP.NET 5 makes heavy use of Dependency Injection, so if you are also using Dependency Injection then this is very simple. If you examine the sample MVC6 project, you can see how this works:
First, there's a class AppSettings defined in Properties, which is a strongly-typed version of the options your class supports. In the sample project, this just contains SiteTitle.
public class AppSettings
{
public string SiteTitle { get; set; }
}
Then, this class is initialised through dependency injection in ConfigureServices. Configuration here is the one you created in the constructor of the Startup class.
public void ConfigureServices(IServiceCollection services)
{
services.Configure<AppSettings>(Configuration.GetSubKey("AppSettings"));
// ...
}
Then, assuming your class is instantiated by the dependency injection container, you can simply ask for an IOptions and you'll get one. For example, in a controller you could have the following:
public class HomeController
{
private string _title;
public HomeController(IOptions<AppSettings> settings)
{
_title = settings.Options.SiteTitle;
}
}
I use ASP.NET 5 dependency injection, like so.
config.json:
{
"random": "Hello World!"
}
startup.cs:
public class Startup
{
public Startup(IHostingEnvironment env, IApplicationEnvironment appEnv)
{
var builder = new ConfigurationBuilder(appEnv.ApplicationBasePath)
.AddJsonFile("config.json");
Configuration = builder.Build();
}
public IConfiguration Configuration { get; set; }
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddSingleton<IConfiguration>(sp => { return Configuration; });
}
public void Configure(IApplicationBuilder app)
{
app.UseMvc(routes =>
{
routes.MapRoute(name: "default", template: "{controller=Home}/{action=Index}/{id?}");
});
}
}
Controller:
public class HomeController : Controller
{
IConfiguration config;
public HomeController(IConfiguration config)
{
this.config = config;
}
public IActionResult Index()
{
var template = "<marquee>{0}</marquee>";
var content = string.Format(template, config.Get("random"));
return Content(content, "text/html");
}
}
I highly recommend using the OptionsModel instead of reading the configuration directly. It allows strong typed model binding to configuration.
Here is an example: GitHub.com/aspnet/Options/test/Microsoft.Extensions.Options.Test/OptionsTest.cs
For your particular case create a model:
class AppSettings {
public string SomeSetting {get;set;}
}
and then bind it to your configuration:
var config = // The configuration object
var options = ConfigurationBinder.Bind<AppSettings>(config);
Console.WriteLine(options.SomeSetting);
That way you don't have to worry from where the setting comes from, how it is stored or what is the structure. You simply predefine your options model and magic happens.
Use this:
var value = Configuration.Get("AppSettings:SomeKey");
Based on this blog post. The colon is similar to dot notation and is used for navigation down the hierarchy.
If you need the value in other classes, you should inject it in. ASP.NET has built in dependency injection, but if you just need one instance of MyClass you can new it up instead of setting up a DI container.
public IConfiguration Configuration { get; set; }
public Startup(IHostingEnvironment environment)
{
Configuration = new Configuration()
.AddJsonFile("config.json");
//generally here you'd set up your DI container. But for now we'll just new it up
MyClass c = new MyClass(Configuration.Get("AppSettings:SomeKey"));
}
public class MyClass
{
private readonly string Setting; //if you need to pass multiple objects, use a custom POCO (and interface) instead of a string.
public MyClass(string setting) //This is called constructor injection
{
Setting = setting;
}
public string DoSomething()
{
var result = string.Empty;
//Use setting here
return result;
}
}

Categories