Adventures on the edge

Learning new development technologies
    "When living on the bleeding edge - you sometimes have to bleed" -BillKrat

ASP.NET MVC - how to get config.json settings (beta7)

This adventure involves creating an IAppSettings implementation that can survive versioning changes - we want a single entry point to get settings so that if something changes, we only have to update one class or create a new implementation.  Note in the image below (on line 100) that our implementation of IAppSettings is AppSettingsBeta7..

Finding how to retrieve the config.json settings was quite the challenge since the methods shown during research vary from version to version.
Adventures on the edge I had to pave the way on this one as I couldn’t find anything that worked under beta7

The short version is that you have to store an instance of the IConfiguration in the IOC container (line 99 in the image below).  You can then retrieve the settings from it as shown in the AppSettingsBeta7 class (source code further down in this article).

The long version is a proof of concept that has a setting in the configuration file (DefaultFoo) which will designate which implementation of IFoo that should be used.  First I started by creating two implementations of IFoo (lines 94 and 95 in the image below).  We’ll let our factory decide which implementation to use based on the value of “DefaultFoo” in our config.json file.  

Startup

Since I throw an exception on line 168 above if the wrong implementation of IFoo is used, I can operate on the premise that  “no news is good news” and everything is working if I get to my index page.

Our config.json file is configured to use the value of “Foo2” for DefaultFoo

{
"AppSettings": {
"DefaultFoo": "Foo2"
},
"Data": {
"DefaultConnection": {
"ConnectionString": "Server=(localdb)\\mssqllocaldb;Da...
}
}
}
In the Configure method in the above image we resolve our factory on line 159 which instantiates the class propagating the dependency chain via constructor injection (reference line 188 below) and then uses the factory to get the concrete implementation of IFoo.
 
This is where the IAppSettings implementation comes in - we need to use its GetSetting() method to retrieve the setting for “defaultFoo”.   Because I am coding against an interface, I can use IAppSettings throughout the application without concern about breaking changes in the future; the new implementation will handle it.

Foo

 

//:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
// AppSettings
//:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
public interface IAppSettings { string GetSetting(string key); }

/// <summary>
/// Our application will code to IAppSettings since it is possible
/// configuration code will change - only this implementation will
/// have to be updated if required.
/// </summary>
public class AppSettingsBeta7 : IAppSettings
{
public const string APP_SETTINGS = "AppSettings";
private ConfigurationSection _section;

public AppSettingsBeta7(IConfiguration config)
{
_section = config
.GetSection(APP_SETTINGS) as ConfigurationSection;
}
public string GetSetting(string key)
{
var result = _section.Sources.Select(s =>
{
string value;

// Build the expected search key
var searchKey = string.Format("{0}:{1}", APP_SETTINGS, key);

// Try to get search key from IConfigurationSource
s.TryGet(searchKey, out value);

return value; // Return result
})
.Where(s => s != null);
// Only return non-null results

// Only get first or default if there are results
var returnValue = result.Any() ? result.FirstOrDefault() : "";
return returnValue;
}
}


The GetSetting() method above demonstrates “a way” to retrieve the config.json settings for beta7.   Note that it has a dependency on IConfiguration which is not set in the container by default - we had to add it as a singleton in the Startup class method ConfigurationServices.

Also note that we get our configKey from the config.json file and then we send it as a named parameter to the provider’s Resolve() method. 

var configKey = _settings.GetSetting("defaultFoo");
var returnValue = _provider.Resolve<IFoo>(configKey);


This is not actually a beta7 syntax for the built-in dependency injection (DI) container, I am use to Unity IOC (which I am now abandoning since Microsoft did) but liked the syntax so I’ll hold on to it - at least for now.   I used the following extension methods to emulate Unity’s Resolve<> method:
 

//:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

// ServiceProvider
//:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

/// <summary>
/// Used to Unity - will emulate its syntax
/// </summary>
public static class IServiceProviderExtension
{
public static T Resolve<T>(this IServiceProvider provider)
{
var result = provider.GetService(typeof(T));
return (T)result;
}
public static T Resolve<T>(
this IServiceProvider provider, string key)
{
// Wee bit of a hack - we'll use the class name as the key
var result = provider
.GetServices<T>()
.Where(s => s.GetType().Name == key);


if (result.Any<T>())
return result.FirstOrDefault();
return default(T);
}
}


Below are all of the code bits for IFoo and its implementation as you can see I am expecting the message “I am a foo too!” because I use Foo2 to implement IFoo based on the config.json file setting of “DefaultFoo”:

//:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

// Foo stuff
//:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
public interface IFoo { string GetFooMessage(); }

public class Foo1 : IFoo { public string GetFooMessage() {
return "I am a foo!"; } }

public class Foo2 : IFoo { public string GetFooMessage() {
return "I am a foo too!"; } }

public class FooFactory
{
private IServiceProvider _provider;
private IAppSettings _settings;

public FooFactory(
IServiceProvider provider, IAppSettings settings)
{
_provider = provider;
_settings = settings;
}
public IFoo GetDefaultFoo()
{
// Get the "defaultFoo" setting from the config.json file
var configKey = _settings.GetSetting("defaultFoo");

// Use it to get a named value (where name is implementation)
var returnValue = _provider.Resolve<IFoo>(configKey);
return returnValue;
}
}


Comments are closed