Instead of building verbose nested XML configSection classes our preference is to instead store structured configuration in Web.config's <appSetting/>
which can still express rich config graphs but in a much more human-friendly and manageable way.
ServiceStack's pluggable IAppSettings
API is a cleaner alternative for storing your Application structured configuration, providing a high-level API to read your Web.config's <appSetting/>
values into a List
, Dictionary
or your own clean Custom POCO Types using the human friendly JSV format.
public interface IAppSettings
{
Dictionary<string, string> GetAll();
List<string> GetAllKeys();
bool Exists(string key);
void Set<T>(string key, T value);
string GetString(string name);
IList<string> GetList(string key);
IDictionary<string, string> GetDictionary(string key);
T Get<T>(string name);
T Get<T>(string name, T defaultValue);
}
Benefits over existing Configuration API include the ability to store rich data structures in appSettings values, more succinct access to typed data and since its an interface it's decoupled from .NET Configuration classes and can easily be swapped to source your configuration from an different sources without a rewrite, e.g. from a text file or central DB.
Example Usage​
<appSettings>
<add key="LastUpdated" value="01/01/2012 12:00:00" />
<add key="AllowedUsers" value="Tom,Mick,Harry" />
<add key="RedisConfig"
value="{Host:localhost,Port:6379,Database:1,Timeout:10000}" />
</appSettings>
Reading the above configuration in code:
IAppSettings appSettings = new AppSettings();
DateTime lastUpdate = appSettings.Get<DateTime>("LastUpdated");
IList<string> allowedUsers = appSettings.GetList("AllowedUsers");
RedisConfig redisConf = appSettings.Get<RedisConfig>("RedisConf");
//use default value if no config exists
var searchUrl = appSettings.Get("SearchUrl", "http://www.google.com");
The last default value provides a convenient way to maintain workable default options in code (allowing re-use in Unit/Integration tests) whilst still being overridable in the Web.config when you need to.
Multi AppSettings​
The MultiAppSettings
AppSettings provider enables reading configuration from multiple configuration sources.
Example Usage​
The example below creates a cascading configuration that first checks Environment variables, then looks in a local ~/appsettings.txt
plain-text file before falling back to Web.config
:
AppSettings = new MultiAppSettings(
new EnvironmentVariableSettings(),
new TextFileSettings("~/appsettings.txt".MapHostAbsolutePath()),
new AppSettings());
Multi AppSettings Builder​
An alternative is to use MultiAppSettingsBuilder
if you prefer to use a fluent discoverable API:
AppSettings = new MultiAppSettingsBuilder()
.AddAppSettings()
.AddDictionarySettings(new Dictionary<string,string> { "override" : "setting" })
.AddEnvironmentalVariables()
.AddTextFile("~/path/to/settings.txt".MapProjectPath())
.Build();
OrmLite AppSettings​
OrmLiteAppSettings
provides an alternative read/write API that lets you maintain your applications configuration in any RDBMS back-end OrmLite supports. It works like a mini Key/Value database in which can store any serializable value against any key which is maintained into the simple Id/Value ConfigSettings
table.
Usage​
Registration just uses an OrmLite DB Factory, e.g:
container.Register(c =>
new OrmLiteAppSettings(c.Resolve<IDbConnectionFactory>()));
//Create the ConfigSettings table if it doesn't exist
container.Resolve<OrmLiteAppSettings>().InitSchema();
It then can be accessed like any AppSetting APIs. The example below reads the MyConfig
POCO stored at config
otherwise use default value if it doesn't exist:
var config = appSettings.Get("config",
new MyConfig { Key = "DefaultValue" });
In addition to the AppSettings read-only API's, it also supports writing config values , e.g:
var latestStats = appSettings.GetOrCreate("stats",
() => statsProvider.GetLatest());
EnvironmentVariableSettings​
The new EnvironmentVariableSettings
AppSettings provider to source configuration from Environment variables:
var appSettings = new EnvironmentVariableSettings();
TextFileSettings​
The TextFileSettings
lets you read your Applications configuration in a plain-text file, which can easily be overridden with custom environment settings as part of the CI deployment process, providing a nice alternative to custom Web.config configurations.
Example Usage​
To use just provide the path to the plain-text file that contains the app-settings:
var appSettings = new TextFileSettings("~/app.settings".MapHostAbsolutePath());
TextFile Format​
Each appSetting is on a new line with the Key and Value separated by a space:
{Key} {Value}\n
The delimiter can be changed in the constructor e.g.
new TextFileSettings(path,delimiter:": ");
Extract key / value settings from text file​
Under the hood TextFileSettings uses the ParseKeyValueText extension method to extract key / value data from a string, e.g:
var configText = @"
# comments starting with '#' and blank lines are ignored
StringKey string value
IntKey 42
ListKey A,B,C,D,E
DictionaryKey A:1,B:2,C:3,D:4,E:5
PocoKey {Foo:Bar,Key:Value}";
Dictionary<string, string> configMap =
configText.ParseKeyValueText(delimiter:" ");
DictionarySettings​
When combined with the existing DictionarySettings
, enables a rich, simple and clean alternative to .NET's App.config config section for reading structured configuration into clean data structures, e.g:
IAppSettings settings = new DictionarySettings(configMap);
string value = settings.Get("StringKey");
int value = settings.Get("IntKey", defaultValue:1);
List<string> values = settings.GetList("ListKey");
Dictionary<string,string> valuesMap = settings.GetDictionary("key");
MyConfig config = settings.Get("key", new MyConfig { Key = "default"});
SimpleAppSettings​
SimpleAppSettings
is an alternative Dictionary-based provider that only requires a dependency to ServiceStack.Common
, e.g:
AppSettings = new SimpleAppSettings(new Dictionary<string, string> {
["string"] = "value",
["EnableFeature.1"] = "true",
["AllowedUsers"] = "Tom,Mick,Harry",
}));
string value = AppSettings.GetString("string");
bool enableFeature1 = AppSettings.Get("EnableFeature.1", defaultValue:false);
bool enableFeature2 = AppSettings.Get("EnableFeature.2", defaultValue:false);
IList<string> allowedUsers = AppSettings.GetList("AllowedUsers");
DynamoDbAppSettings​
Storing production config in DynamoDB reduces the effort for maintaining production settings decoupled from source code.
Here DynamoDbAppSettings
is registered first in a MultiAppSettings
collection it checks entries in the DynamoDB ConfigSetting
Table first before falling back to local Web.config appSettings:
#if !DEBUG
AppSettings = new MultiAppSettings(
new DynamoDbAppSettings(new PocoDynamo(awsDb), initSchema:true),
new AppSettings()); // fallback to Web.confg
#endif
First class AppSettings​
After proving its value over the years we've decided to make it a first-class property on IAppHost.AppSettings
which defaults to looking at .NET's App/Web.config's.
The new Chat.zip App explores different ways AppSettings can be used:
If there's an existing appsettings.txt
file where the .exe is run it will use that, otherwise it falls back to Web.config appSettings:
public AppHost() : base("Chat", typeof (ServerEventsServices).Assembly)
{
var customSettings = new FileInfo("appsettings.txt");
AppSettings = customSettings.Exists
? (IAppSettings)new TextFileSettings(customSettings.FullName)
: new AppSettings();
}
As a normal property in your AppHost, AppSettings can be accessed directly in AppHost.Configure()
:
public void Configure(Container container)
{
...
var redisHost = AppSettings.GetString("RedisHost");
if (redisHost != null)
{
container.Register<IServerEvents>(c =>
new RedisServerEvents(new PooledRedisClientManager(redisHost)));
container.Resolve<IServerEvents>().Start();
}
}
Inside your services or IOC dependencies, like any other auto-wired dependency:
public class ServerEventsServices : Service
{
public IAppSettings AppSettings { get; set; }
public void Any(PostRawToChannel request)
{
if (!IsAuthenticated && AppSettings.Get("LimitRemoteControlToAuthenticatedUsers", false))
throw new HttpError(HttpStatusCode.Forbidden, "You must be authenticated to use remote control.");
...
}
}
Directly within Razor views:
<style>
body {
background-image: url(@AppSettings.Get("background","/img/bg.jpg"))
}
</style>
As well as outside ServiceStack, via the HostContext
static class:
var redisHost = HostContext.AppSettings.GetString("redis");
AppSettings are Writable​
A new Set()
API was added to IAppSettings letting you save any serializable property that works for all providers:
public interface IAppSettings
{
void Set<T>(string key, T value);
...
}
AppSettings.Set("Poco", new MyConfig { Foo = "Baz" });
In providers that support writable configuration natively like OrmLiteAppSettings
and DictionarySettings
, the settings get written through to the underlying provider. For read-only providers like Web.config's AppSettings
or TextFileSettings
a shadowed cache is kept that works similar to prototypal shadowing in JavaScript where if a property doesn't exist, setting a property will be stored on the top-level object instance which also takes precedence on subsequent property access.
IConfiguration​
To create AppSettings from IConfiguration object
AppSettings = new NetCoreAppSettings(configuration);
Community AppSettings​
ServiceStack.Configuration.Consul​
An implementation of IAppSettings that uses Consul.io key/value store as backing storage