ASP.NET Core Configuration Section in Startup - c#

I am migrating a ASP.NET 5 RC1 project to ASP.NET Core, and have come across an interesting issue I've not yet seen, or found a solution for.
In order to use configuration settings within Startup I have previously retrived the configuration the following way
// Works fine for DI both in ASP.NET 5 RC1 and ASP.NET Core
services.Configure<SomeConfigurationClass>(Configuration.GetSection("SomeConfigurationSection"));
// How I previous retrieved the configuration for use in startup.
// No longer available in ASP.NET Core
var someConfigurationToUseLater = Configuration.Get<SomeConfigurationClass>("SomeConfigurationSection");
After updating to ASP.NET Core 1.0 it seems Configuration.Get<T>() is no longer available.
I have tried updating the code to use Configuration.GetValue<T>() however this does not seem to work with objects and will only work when providing a path to a value. This has left me with a workaround for most of my configuration classes like so
var someConfigurationName = "someConfiguration";
var someConfigurationClass = new SomeConfigurationClass()
{
Value1 = Configuration.GetValue<string>($"{someConfigurationName}:value1"),
Foo = Configuration.GetValue<string>($"{someConfigurationName}:foo"),
Bar = Configuration.GetValue<string>($"{someConfigurationName}:bar")
};
However this is an issue when the configuration class contains an array of objects. In my case an array of Client objects
public class ClientConfiguration
{
public Client[] Clients { get; set; }
}
With the following configuration
"configuredClients": {
"clients": [
{
"clientName": "Client1",
"clientId": "Client1"
},
{
"clientName": "Client2",
"clientId": "Client2"
}
]
}
Where this would previously bind to the Clients property of my configuration class no problem, I can no longer find a way of doing so in ASP.NET Core 1.0

Updated Answer
For ASP Core 1.1.0 generic model binding is now done using Get:
var config = Configuration.GetSection("configuredClients").Get<ClientConfiguration>();
Original Answer
How about this:
var config = Configuration.GetSection("configuredClients").Bind<ClientConfiguration>();

With ASP.NET Core 2.0 (basically Core 1.1+), the IConfiguration is injected to Startup, and that can be used within ConfigureServices() and Configure() methods.
As shown in the accepted answer, the configuration can be bound to an object. But if just one value is required, the key based approach works well.
The IConfiguration still works with colon : separated string keys. And for array, use 0-based index. Or use the the generic getValue<T>() method with same keys. See example below:
var clientId2 = Configuration["configuredClients:clients:1:clientId"]?.ToString();
var clientName1 = Configuration.GetValue<string>("configuredClients:clients:0:clientName");
To use the same configuration values in other classes (e.g. Controllers)
Either inject the IConfiguration and use the same key-based approach like above. Or
Register an instance of the strongly-typed configuration object with the DI container, and inject that object directly into client classes.
Sample code below:
//In Startup.ConfigureServices()
var clientConfig = Configuration.GetSection("configuredClients")
.Get<ClientConfiguration>();
services.AddSingleton(clientConfig);
//Controller
public class TestController : Controller
{
IConfiguration _configStore;
ClientConfiguration _clientConfiguration;
public TestController(IConfiguration configuration,
ClientConfiguration clientConfiguration)
{
_configStore = configuration;
_clientConfiguration = clientConfiguration;
}
public IActionResult Get()
{
//with IConfiguration
var clientId1 = _configStore
.GetValue<string>("configuredClients:clients:0:clientId");
//with strongly typed ClientConfiguration
var clientName1 = _clientConfiguration.Clients[0]?.ClientName;
return new OkObjectResult("Configuration test");
}
}
More examples here.

You don't read the configuration manually generally in ASP.NET Core yourself, instead you create an object that matches your definition. You can read more on that in the official documentation here.
E.g.
public class MyOptions
{
public string Option1 { get; set; }
public int Option2 { get; set; }
}
public void ConfigureServices(IServiceCollection services)
{
// Setup options with DI
services.AddOptions();
services.Configure<MyOptions>(Configuration);
}
Then you just inject the options IOptions<MyOptions> where you need them.

If you want to get first "clientName"(expected "Client1"), just write:
Configuration.GetSection("configuredClients")["clients:0:clientName"];
Update for comment
Install .NET Core 1.0.1 and go with #TomMakin's way.

Related

Can't get config section after update to ASP.NET Core 2

I updated my project from 1.0.0-rc1-final to 1.0.0-rc2-final which is called ASP.NET Core 2 now. This is how I initialize the configuration builder:
var builder = new ConfigurationBuilder().SetBasePath(Environment.GetEnvironmentVariable("ASPNETCORE_CONTENTROOT")).AddJsonFile(file).AddEnvironmentVariables();
IConfiguration configuration = builder.Build();
I know for sure that the initialization is ok because I can do
configuration.AsEnumerable()
in the debugger and see all the values in the configuration files in there.
However, if I try to get a whole configuration section like this
configuration.GetSection(section.Name);
it doesn't work. It returns an object no matter what I pass to GetSection. However, the Value field of this object is always null, regardless whether the section exists or not.
Note that this used to work perfectly fine before.
Any clues?
It turns out that one can no longer do something like:
var allSettingsInSection = configuration.Get(typeof(StronglyTypedConfigSection), sectionName);
Instead, it has to be done like this now:
IConfigurationSection sectionData = configuration.GetSection(sectionName);
var section = new StronglyTypedConfigSection();
sectionData.Bind(section);
Note that it's necessary to include Microsoft.Extensions.Configuration.Binder in project.json.
Just a cleaner version of the accepted answer:
public void ConfigureServices(IServiceCollection services)
{
services.Configure<MySettings>(options => Configuration.GetSection("MySettings").Bind(options));
}
Source
In dot net core 2.1 you can do this:
I used nameof here to get the name of the class as a string, rather than use an actual string. This is based on Uwe Kleins reply, it's cleaner.
var myConfigClass = Configuration.GetSection(nameof(MyConfigClass)).Get<MyConfigClass>();
Easily inject your strongly typed configuration as follows:
services.Configure<MyConfigClass>(myConfigClass);
I am using the GetSection allot and thus I have created an extension method to help me get sections using generics
public static class ConfigurationExtensions
{
public static T GetConfig<T>(this IConfiguration config) where T : new()
{
var settings = new T();
config.Bind(settings);
return settings;
}
public static T GetConfig<T>(this IConfiguration config, string section) where T : new()
{
var settings = new T();
config.GetSection(section).Bind(settings);
return settings;
}
}

Using and injecting from appsettings.json in unit tests on .net core

I have created an asp.net core web application and a unit test application.
I created the asp.net application using the "ASP.NET Core Web Application (.NET Core)" template and created the unit test project using the "Class Library (.NET Core)" template.
I configured MSTest using the instructions on the following article:
Announcing MSTest Framework support for .NET Core RC2 / ASP.NET Core RC2
I have organised the application into Controllers and Services, the controllers read values from appsettings.json and pass these into the service methods as arguments.
I have an AppSettings class as follows
public class AppSettings
{
public string Setting1 { get; set; }
public string Setting2 { get; set; }
public string Setting3etc { get; set; }
}
public static class App
{
public static AppSettings Settings { get; set; }
public static void ConfigureSettings(IOptions<AppSettings> settings)
{
Settings = settings.Value;
}
}
The controller constructor is as follows
public ValuesController(IOptions<AppSettings> settings)
{
App.ConfigureSettings(settings);
}
In Startup.cs I have the following line in the ConfigureServices method
services.Configure<AppSettings>(Configuration.GetSection("AppSettings"));
I learned about this technique from the following article
Strongly Typed Configuration Settings in ASP.NET Core
This works at run time, but I'm having difficulties accessing these settings in my unit tests.
I'm trying to find a way to get the values from appsettings.json into my test project, so that I can pass them to the controllers and services.
My test methods look something like this
[TestMethod]
[TestCategory("Service - Method1")]
public void ServiceMethod1Test()
{
// this compiles but gets null reference exception
var setting1 = App.Settings.Setting1;
var setting2 = App.Settings.Setting2;
var service = new Service(setting1, setting2);
var argument1 = "argument";
var actual = service.Method1(argument1);
var expected = "expected result";
CollectionAssert.AreEqual(expected, actual);
}
[TestMethod]
[TestCategory("Controller - Method1")]
public void ControllerMethod1Test()
{
// how do i create this settings instance?
var settings = ???
var controler = new ValuesController(settings);
var argument1 = "argument";
var actual = controller.Method1(argument1);
var expected = "expected result";
CollectionAssert.AreEqual(expected, actual);
}
How could I go about passing an instance of a class that implements
IOptions<MySettings> to the controller constructor for controller tests, and how could I go about getting those values to pass to service methods in the service tests.
Just reference the Options package and use
IOptions<MyOptions> options = Options.Create(new MyOptions()
{
...
});
See here for source code reference.

Application Settings in custom class ASP.Net 5 MVC 6

Playing around with ASP.Net 5 MVC. Seen this question jumping around but not an full answer. What I want to do is have a helper class that is able to access the AppSettings. I can access it in the controller and the view but haven't figured out how to access it on my own custom class. Have startup configured like so.
public Startup(IHostingEnvironment env)
{
// Set up configuration sources.
var builder = new ConfigurationBuilder()
.AddJsonFile("config.json")
.AddJsonFile($"config.{env.EnvironmentName}.json", optional: true);
builder.AddEnvironmentVariables();
Configuration = builder.Build();
}
public IConfigurationRoot Configuration { get; set; }
// This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit http://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddOptions();
services.Configure<AppSettings>(Configuration.GetSection("AppSettings"));
}
.................
.................
So in your config.json file, suppose you have following settings
{
"smtp": {
"SenderEmail": "a#b.com",
"SenderFrom": "Test User"
}
}
Then in your ConfigureServices method you need to do something like that
services.Configure<SmtpEmailSetting>(Configuration.GetSection("smtp"));
This is your SmtpEmailSetting looks like
public class SmtpEmailSetting
{
public string SenderEmail { get; set; }
public string SenderFrom { get; set; }
}
and this is how you access your settings in any service or controller
public class SendEmailService
{
private readonly SmtpEmailSetting _smtpEmailSetting;
public SendEmailService(IOptions<SmtpEmailSetting> smtpOptions )
{
_smtpEmailSetting = smtpOptions.Value;
}
public void SendEmail()
{
var fromEmail = _smtpEmailSetting.SenderEmail;
var displayName = _smtpEmailSetting.SenderFrom;
}
}
So basically you use your settings or options (whatever you prefer to call) should be used in constructor as a generic type parameter of IOptions<> class. Hope it helps
In order to access your AppSettings properties in your custom class, make configuration as a static instance such as:
public static IConfigurationRoot Configuration { get; set; }
and make use of your AppSettings any where in your application (for connectionstring example) as:
var connectionString = Startup.Configuration["Data:DefaultConnection:ConnectionString"];
Just to add to adeel41's answer, this is correct and works great, but for myself, I didn't want to drag around an IOption object when using dependency injection.
So I prefer to do something like
services.AddSingleton<ISmtpEmailSettings>(Configuration.GetSection("SmtpSettings").Get<SmtpEmailSettings>());
Most importantly is the Get syntax added to GetSection to deserialize your JSON to an object.
At the time of RC1, I took inspiration from this post by Rick Strahl, which worked great. That is very similar to other approaches already proposed.
This answer is just to update with my findings as of RTM release. It seems like Configuration.GetSection(string topLevelKey) does not work anymore, at least for me it always returns null (even if configuration sources are set correctly).
After some search, this other SO thread pointed me in the right direction, by using:
// create empty config object
var smtpEmailSetting = new SmtpEmailSetting();
// fill it from configuration section
Configuration.GetSection("smtp").Bind(smtpEmailSetting);
// use it, e.g. by registering it into DI
services.Configure<SmtpEmailSetting>(smtpEmailSetting);
HTH
If you need it in your own class, it's probably right to pass it into the constructor of that class, or as a parameter. Eg;
public class Notifications
{
public Notifications(AppSettings settings) {
this.settings = settings;
}
public void SendEmail(string subject, string body) {
SmptClient.Send(subject, body, settings["email address"]);
}
}
So typically, you'd pass it through from your controller.
This avoids a global variable, which is always a good thing, I think.

HttpRuntime.Cache Equivalent for asp.net 5, MVC 6

So I've just moved from ASP.Net 4 to ASP.Net 5. Im at the moment trying to change a project so that it works in the new ASP.Net but of course there is going to be a load of errors.
Does anyone know what the equivalent extension is for HttpRuntime as I cant seem to find it anywhere. I'm using to cache an object client side.
HttpRuntime.Cache[Findqs.QuestionSetName]
'Findqs' is just a general object
You can an IMemoryCache implementation for caching data. There are different implementations of this including an in-memory cache, redis,sql server caching etc..
Quick and simple implemenation goes like this
Update your project.json file and add the below 2 items under dependencies section.
"Microsoft.Extensions.Caching.Abstractions": "1.0.0-rc1-final",
"Microsoft.Extensions.Caching.Memory": "1.0.0-rc1-final"
Saving this file will do a dnu restore and the needed assemblies will be added to your project.
Go to Startup.cs class, enable caching by calling the services.AddCaching() extension method in ConfigureServices method.
public void ConfigureServices(IServiceCollection services)
{
services.AddCaching();
services.AddMvc();
}
Now you can inject IMemoryCache to your class via constructor injection. The framework will resolve a concrete implementation for you and inject it to your class constructor.
public class HomeController : Controller
{
IMemoryCache memoryCache;
public HomeController(IMemoryCache memoryCache)
{
this.memoryCache = memoryCache;
}
public IActionResult Index()
{
var existingBadUsers = new List<int>();
var cacheKey = "BadUsers";
List<int> badUserIds = new List<int> { 5, 7, 8, 34 };
if(memoryCache.TryGetValue(cacheKey, out existingBadUsers))
{
var cachedUserIds = existingBadUsers;
}
else
{
memoryCache.Set(cacheKey, badUserIds);
}
return View();
}
}
Ideally you do not want to mix your caching within your controller. You may move it to another class/layer to keep everything readable and maintainable. You can still do the constructor injection there.
The official asp.net mvc repo has more samples for your reference.
My answer is focused on the "Does anyone know what the equivalent extension is for HttpRuntime as I cant seem to find it anywhere"
You tagged two different frameworks (.net and .net core), and for them there are two completely different caching implementations/solutions. The first one below is the one you were looking for:
1 - System.Runtime.Caching/MemoryCache
2 - Microsoft.Extensions.Caching.Memory/IMemoryCache
System.Runtime.Caching/MemoryCache:
This is pretty much the same as the old day's ASP.Net MVC's HttpRuntime.Cache. You can use it on ASP.Net CORE without any dependency injection. This is how to use it:
// First install 'System.Runtime.Caching' (NuGet package)
// Add a using
using System.Runtime.Caching;
// To get a value
var myString = MemoryCache.Default["itemCacheKey"];
// To store a value
MemoryCache.Default["itemCacheKey"] = myString;
Microsoft.Extensions.Caching.Memory
This one is tightly coupled with Dependency Injection, and is the recommended way to do it on ASP.Net CORE. This is one way to implement it:
// In asp.net core's Startup add this:
public void ConfigureServices(IServiceCollection services)
{
services.AddMemoryCache();
}
Using it on a controller:
// Add a using
using Microsoft.Extensions.Caching.Memory;
// In your controller's constructor, you add the dependency on the 'IMemoryCache'
public class HomeController : Controller
{
private IMemoryCache _cache;
public HomeController(IMemoryCache memoryCache)
{
_cache = memoryCache;
}
public void Test()
{
// To get a value
string myString = null;
if (_cache.TryGetValue("itemCacheKey", out myString))
{ /* key/value found - myString has the key cache's value*/ }
// To store a value
_cache.Set("itemCacheKey", myString);
}
}

C# ASP.Net 5 configuration and backwards compatibility with Class Libraries

I've got multiple legacy libraries which configure themselves via the ConfigurationManager. Example:
var port = ConfigurationManager.AppSettings["service.port"];
The new ASP.Net system prefers strongly typed models based on an additional "config.json" file. Example (taken from Rick Strahl's Web Log):
//from AppSettings.cs
public class AppSettings
{
public string SiteTitle { get; set; }
}
//from config.json
{
"AppSettings": {
"SiteTitle": "WebApplication2",
},
"Data": {
"DefaultConnection": {
"ConnectionString": "Server=(localdb)\\mssqllocaldb;Database=blahfoo;Trusted_Connection=True;MultipleActiveResultSets=true"
}
}
}
// from Startup.cs
public class Startup
{
public IConfiguration Configuration { get; set; }
public Startup(IHostingEnvironment env)
{
// Setup configuration sources.
var configuration = new Configuration()
.AddJsonFile("config.json")
.AddJsonFile($"config.{env.EnvironmentName}.json", optional: true);
configuration.AddEnvironmentVariables();
Configuration = configuration;
}
public void ConfigureServices(IServiceCollection services)
{
// Add Application settings to the services container.
services.Configure<AppSettings>(Configuration.GetSubKey("AppSettings"));
…
}
}
My question: Is there a way to embrace the new ASP.Net 5 and it's strongly typed configuration methodology while maintaining backwards compatibility with my other application libraries?
Or rather, can I utilize common libraries from our portfolio without having to rewrite them?
Your problem is that you relied on a concrete implementation of configuration and used the ConfigurationManager's static members from your classes instead of writing a SOLID implementation with proper dependency injection.
You could find some hacky tricks where you don't have to change your code to make use of the new configuration model, but I reckon you should do yourself a favour and use this as an opportunity to actually re-factor your code and abstract your current configurations behind one simple interface like e.g.:
public interface IMyAppConfiguration
{
string Setting1 { get; }
string Setting2 { get; }
SomeOtherMoreComplexSetting Setting3 { get; }
}
Then inject this dependency in every class where you require one of the settings and provide one implementation which wraps the current ConfigurationManager class and another implementation which wraps the new configuration model.
This is a perfect example why SOLID design is important and makes code maintenance and innovation easier when done right.

Categories

Resources