Injecting configuration objects in a .NET application - c#

I'm reviewing a code I wrote sometime before and I noticed I did in past
public class Linq2DbSettings : ILinqToDBSettings
{
public IEnumerable<IDataProviderSettings> DataProviders
{
get { yield break; }
}
public string DefaultConfiguration =>
"SqlServer"; // lets set your configuration as default, so you can call just new DataContext() or new DataConnection()
public string DefaultDataProvider => ProviderName.SqlServer; // and set default database type
public IEnumerable<IConnectionStringSettings> ConnectionStrings
{
get
{
yield return
new ConnectionStringSettings
{
Name = "SqlServer",
ProviderName = "SqlServer",
ConnectionString =ConfigurationManager.ConnectionStrings["default"].ConnectionString
};
}
}
}
public class ConnectionStringSettings : IConnectionStringSettings
{
public string ConnectionString { get; set; }
public string Name { get; set; }
public string ProviderName { get; set; }
public bool IsGlobal => false;
}
Even if it's related to Linq2Db it appies to all classes where I need to resolve the container.
As you can see I'm using here ConfigurationManager.ConnectionStrings["default"] while it would be best to use IConfiuration from Microsoft.Extensions.Configuration
To do so I should resolve the IConfiguration item, registered in SimpleInjector's Container.
In past I used a wrapper
public static class ContainerWrapper
{
public static Container Container { get; set; }
}
and I assigned it as
ContainerWrapper.Container = container;
container.Verify();
But I think it's a wrong approach, what's the best solution?

My advise is the following:
Keep your configuration objects narrow; don't create wide configuration objects that contain a large set of propertiesand are used by many consumers.
Prevent configuration objects from reading from the configuration system. Make them immutable behaviorless data objects instead, and supply them with configuration values in their constructor. This prevents the configuration object from becoming a Volatile Dependency.
Remove the interfaces on your configuration objects. Interfaces are meant to hide behavior, but the configuration objects should only contain data.
Load configuration values during application startup, and register those configuration objects as Singleton in the container.

Related

Get Connection String through Dependency Injection

I am developing a blazor application. I want to have connection string and some other key in a class as service.
For that I have created an interface
interface IDbConnector
{
string ConnectionString { get; set; }
bool SomeKey { get; set; }
}
and in my class I want to have something like that
using Microsoft.Extensions.Configuration;
public class DbConnector : IDbConnector
{
private IConfiguration Configuration { get; set; }
public DbConnector(IConfiguration configuration)
{
Configuration = configuration;
}
public string ConnectionString = Configuration.GetConnectionString();
public bool SomeKey = Configuration.GetSection("xyz");
}
I can register it as a service with
services.AddScoped<IDbConnector, DbConnector>();
But inside DbConnector class it says
A fiels initializer can not refrence the non static field ,method or
property DbConnector.Configuration
Pardon for my coding pattern as I am new to DI concept. Please suggest if there is another and better way to do this.
You've made a syntax error, these should be expression bodied property accessors. = to =>
public string ConnectionString => Configuration.GetConnectionString();
public bool SomeKey => Configuration.GetSection("xyz");
Instead you've tried to initialise them as fields. Fields initialise pre-constructor, therefore cannot access the configuration.
services.AddConfiguration() // enable Configuration Services
var config = new WeblogConfiguration();
Configuration.Bind("Weblog", config); // <--- This
services.AddSingleton(config);

Proper way to inject custom configuration

The problem to solve:
There is a list of settings lets say:
{
"Kind1":
{"attr1":"val11"},
{"attr2":"val12"},
"Kind2":
{"attr1":"val21"},
{"attr2":"val22"},
}
and a consumer class (controller) in .NET Core 2.1, needs to access the above configuration to use Kind1 or Kind2.
Supposing corresponding class is already defined in C#:
public class KindSetting
{
public string attr1{get;set;}
public string attr2{get;set;}
}
Now what is the best way of inject the configuration into the consumer object.
Is there a way to inject an IConfiguration instance into the consumer object and use it like this?:
KindSetting kindSetting =_configuration.GetValue<KindSetting>(kindSettingKey);
Is there any better approach to fulfill the above requirement?
In the startup.cs file, in the ConfigureServices method you can do a configuration. Sample code below:
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
//Need to add following lines
services.Configure<KindSetting>(Configuration.GetSection("Kind1"));
}
After adding into services, you can inject this configuration in your class, like follow:
public class HomeController : Controller
{
private readonly IOptions<KindSetting> _KindSetting;
public HomeController(IOptions<KindSetting> KindSetting)
{
_KindSetting = KindSetting
}
public void myFunction()
{
var mysetting = _KindSetting.Value.attr1
}
}
I used the following approach, however I am not sure be the best
in startup class configure method:
services.Configure<List<KindSetting>>(Configuration.GetSection("KindSettingList"));
and in consumer object side:
public ConsumerController(IOptions<List<KindSetting>> kindSettingsListAccessor,...)
After adding services.Configure<KindSettings>, you can inject configuration via DI by adding to your constructor.
IOptionsSnapshot<KindSettings> kindSettingsConfiguration
or
IOptions<KindSettings> kindSettingsConfiguration
The difference is, IOptionsSnapshot will reflect live changes in your config file, while IOptions is for singleton use.
Edit after comment:
Lets say your configuration file looks like this:
{
"Kind1":
{"attr1":"val11"},
{"attr2":"val12"},
"Kind2":
{"attr1":"val21"},
{"attr2":"val22"},
}
To successfully bind this, you would need two configuration classes
public class Kind1Configuration
{
public string Attr1 { get; set; }
public string Attr2 { get; set; }
}
public class Kind2Configuration
{
public string Attr1 { get; set; }
public string Attr2 { get; set; }
}
And as previously stated, to connect the dots just add
public void ConfigureServices(IServiceCollection services)
{
services.Configure<Kind1Configuration>(Configuration.GetSection("Kind1"));
services.Configure<Kind2Configuration>(Configuration.GetSection("Kind2"));
}
To use this in a controller, add your IOptions to the constructor
public class TestController(IOptionsSnapshot<Kind1Configuration> kindSettingsConfiguration)
{
Kind1Configuration configuration = kindSettingsConfiguration.Value;
}
Hope it helps.

Accessing strongly typed configuration settings directly into class library in ASP.NET 5 (vNext)?

I have an ASP.NET 5 MVC 6 application. It has a Data Access library which needs a connection string to make a connection to the database.
Currently I am passing a strongly typed configuration settings class with connection string as a public property all the way up from the MVC controllers (Where it is received through DI) to the Data Access Class library.
I want to know if there is a better way for a class library to access strongly typed configuration settings using dependency injection or any other mechanism ?
Thank you.
EDIT : Code Example
This is a generic DbTransaction class which is called from the business layer.
public class DbTransactions<TEntity> where TEntity : DbEntity, new()
{
private readonly Query _query;
public DbTransactions(string connectionString)
{
_query = new Query(connectionString);
}
public TEntity GetById(long id)
{
var sqlGenerator = new SqlGenerator<TEntity>();
var sql = sqlGenerator.GetSelectByIdQuery();
var mapper = new NMapper.Mapper<TEntity>();
var cmd = _query.GetNpgsqlCommand(sql, new { id });
return mapper.GetObject(cmd);
}
}
The query class creates the connection object from the connection string that is provided to it.
I agree with #Steven that using IOptions<T> is a bad idea. You can however use the ConfigurationBinder extensions to read out a specific section of configuration into a strongly-typed POCO class. Just make sure you have this somewhere in your project.json's dependencies section:
"dependencies": {
[other dependencies],
"Microsoft.Extensions.Configuration.Binder": "1.0.0-rc1-final",
[other dependencies]
}
Just build up your configuration as normal. For example, say you had a Database.json configuration file that looked like this:
{
"Database": {
"ConnectionInfo": {
"connectionString": "myConnectionString"
}
}
}
You can build your configuration from the Startup method in Startup.cs:
public IConfiguration Configuration { get; private set; }
public Startup(IHostingEnvironment env, IApplicationEnvironment appEnv) {
IConfigurationBuilder configBuilder = new ConfigurationBuilder()
.SetBasePath(appEnv.ApplicationBasePath)
.AddJsonFile("Database.json")
.AddEnvironmentVariables()
Configuration = configBuilder.Build();
}
Now we can make a POCO class to match the "Database:ConnectionInfo" section of the JSON configuraiton file. You can match it to an interface as #janhartmann suggests, but it may or may not be necessary.
public class DatabaseConnectionInfo {
public string ConnectionString { get; set; }
}
Now, how can we get that DatabaseConnectionInfo class populated with the data from the JSON config file? One way is to use the IOptions<T> framework type, but I don't like using framework types when I can avoid them. Instead, you can get an instance like so:
DatabaseConnectionInfo dbConnInfo = Configuration
.GetSection("Database:ConnectionInfo")
.Get<DatabaseConnectionInfo>();
Now you can just register the dbConnInfo type as a singleton of the type DatabaseConnectionInfo (or as a singleton of an interface type if you prefer to have an immutable configuration settings object). Once it's registered in the IoC container, you can constructor inject it where needed:
public class DbTransactions<TEntity> where TEntity : DbEntity, new()
{
private readonly Query _query;
public DbTransactions(DatabaseConnectionInfo dbConnInfo)
{
_query = new Query(dbConnInfo.ConnectionString);
}
public TEntity GetById(long id) { ... }
}
You can let your service class depend on a an interface, e.g.:
public interface IConnectionFactory {
string ConnectionString();
}
public class MyDataAccessClass {
private readonly IConnectionFactory _connectionFactory
public MyDataAccessClass(IConnectionFactory connectionFactory) {
_connectionFactory = connectionFactory;
}
public void Whatever() {
var connectionString = _connectionFactory.ConnectionString();
}
}
And then make an implementation of it (as near to your composition root as possible):
public class SqlConnectionFactory : IConnectionFactory {
public string ConnectionString() {
return "myConnectionString";
}
}
Let the interface have the methods or properties you need.
Wire like:
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IConnectionFactory, SqlConnectionFactory>();
}
I use a similar method to some of those listed earlier, but I think its sufficiently different to warrant another answer.
Firstly I define an interface with all the configuration that my class needs. In this case
public interface IDbTransactionsConfiguration {
string ConnectionString { get; }
}
Then I alter my class to take this configuration via constructor injection
public class DbTransactions<TEntity> where TEntity : DbEntity, new() {
public DbTransactions(IDbTransactionsConfiguration configuration) {
...
}
}
Then I define a class that handles all the configuration for my application.
public class MyApplicationConfiguration : IDbTransactionsConfiguration, ISomeOtherConfiguration, etc {
public string ConnectionString { get; }
... other configuration
}
Then I pass this class into all classes that need it using some kind of Depenendency Injection (normally Castle Windsor or AutoFac for me).
If it is too difficult to construct DbTransactions for legacy type reasons, I define a static version of MyApplicationConfiguration and access this directly.
More details on this blog post.

How can a component provide other components?

I want to have a component register other components in the registry as / after it's constructed. Let's say I have the following components:
interface IConfiguration
{
string SourceDirectory { get; }
string TargetDirectory { get; }
// other primitive-typed configuration parameters
}
class FileConfiguration : IConfiguration
{
// read parameters from some config file
}
class SourceDirectoryWrapper
{
public byte[] ReadFile(string filename)
{
// read a file from the source directory
}
public string Directory { get; set; }
}
class TargetDirectoryWrapper
{
public byte[] WriteFile(string filename)
{
// write a file into the source directory
}
public string Directory { get; set; }
}
class DirectoryWrapperFactory
{
public DirectoryWrapperFactory(IConfiguration config)
{
var source = new SourceDirectoryWrapper {
Directory = config.SourceDirectory
};
var target = new TargetDirectoryWrapper {
Directory = config.SourceDirectory
};
}
}
The components FileConfiguration and DirectoryWrapperFactory can be registered as is usual.
However, what I'd like to accomplish is to somehow "outject" the source and target objects created in DirectoryWrapperFactory. The basic idea is that different environments might require different configuration providers. (And even if not, I think it's a good idea to put reading configuration parameters into a separate component.)
I'd also like to have SourceDirectoryWrapper and TargetDirectoryWrapper managed in the IoC container. In my case, mainly for convenience – I have an EventSource implementation that I need everywhere, so I inject it using property autowiring. Every object not in the IoC container needs to have it passed explicitly, which kind of bugs me.
So: is this possible with AutoFac? If so, how? I poked at the lifecycle events but most don't allow access to the registry after an object is built.
I don't quite understand why DirectoryWrapperFactory needs to exist. You could just register SourceDirectoryWrapper and TargetDirectoryWrapper directly as part of normal wireup:
builder.Register(c => new SourceDirectoryWrapper {
Directory = c.Resolve<IConfiguration>().SourceDirectory
});
builder.Register(c => new TargetDirectoryWrapper {
Directory = c.Resolve<IConfiguration>().SourceDirectory
});

Ways of keeping configuration code out of logic code using Dependency Injection

How can keep all the configuration file code out of my logic code using Settings (ApplicationSettingsBase) and Dependency Injection?
With configuration I mean a customer specific configuration file.
Do I really have to inject a configuration class everytime I need it or is there another pattern?
It would be great to get some sample code!
Samples:
Static Configuration:
public static class StaticConfiguration
{
public static bool ShouldApplySpecialLogic { get; set; }
public static string SupportedFileMask { get; set; }
}
public class ConsumerOfStaticConfiguration
{
public void Process()
{
if (StaticConfiguration.ShouldApplySpecialLogic)
{
var strings = StaticConfiguration.SupportedFileMask.Split(',');
foreach (var #string in strings)
{
}
}
}
}
Non static Configuration:
public interface IConfiguration
{
bool ShouldApplySpecialLogic { get; set; }
string SupportedFileMask { get; set; }
}
public class Configuration : IConfiguration
{
public bool ShouldApplySpecialLogic { get; set; }
public string SupportedFileMask { get; set; }
}
public class Consumer
{
private readonly IConfiguration _configuration;
public Consumer(IConfiguration configuration)
{
_configuration = configuration;
}
public void Process()
{
if (_configuration.ShouldApplySpecialLogic)
{
var strings = _configuration.SupportedFileMask.Split(',');
foreach (var #string in strings)
{
}
}
}
}
Static Context with non static configuration:
public static class Context
{
public static IConfiguration Configuration { get; set; }
}
public class ConsumerOfStaticContext
{
public void Process()
{
if (Context.Configuration.ShouldApplySpecialLogic)
{
var strings = Context.Configuration.SupportedFileMask.Split(',');
foreach (var #string in strings)
{
}
}
}
}
Configuration classes reduce cohension and increase coupling in the consumers. This is because there may be many settings that don't relate to the one or two needed by your class, yet in order to fulfill the dependency, your implementation of IConfiguration must supply values for all of the accessors, even the irrelevant ones.
It also couples your class to infrastructure knowledge: details like "these values are configured together" bleed out of the application configuration and into your classes, increasing the surface area affected by changes to unrelated systems.
The least complex, most flexible way to share configuration values is to use constructor injection of the values themselves, externalizing infrastructure concerns. However, in a comment on another answer, you indicate that you are scared of having a lot of constructor parameters, which is a valid concern.
The key point to recognize is that there is no difference between primitive and complex dependencies. Whether you depend on an integer or an interface, they are both things you don't know and must be told. From this perspective, IConfiguration makes as much sense as IDependencies. Large constructors indicate a class has too much responsibility regardless of whether the parameters are primitive or complex.
Consider treating int, string and bool like you would any other dependency. It will make your classes cleaner, more focused, more resistant to change, and easier to unit test.
The important part to realize is that configuration is only one among several sources of values that drive your application's behavior.
The second option (non-static configuration) is best because it enables you to completely decouple the consumer from the source of the configuration values. However, the interface isn't required, as configuration settings are normally best modeled as Value Objects.
If you still want to read the values from a configuration file, you can do that from the application's Composition Root. With StructureMap, it might looks something like this:
var config = (MyConfigurationSection)ConfigurationManager.GetSection("myConfig");
container.Configure(r => r
.For<Consumer>()
.Ctor<MyConfigurationSection>()
.Is(config));
One way is to inject a configuration interface like you post. Here are a couple other ways.
Exposing a Setter
class Consumer
{
public bool ShouldApplySpecialLogic { get; set; }
...
}
In the composition root, you can read a config file or hardcode it. Autofac example:
builder.RegisterType<Consumer>().AsSelf()
.OnActivated(e => e.Instance.ShouldApplySpecialLogic = true);
This is probably only advisable when you have a good default
Constructor Injection
public class Server
{
public Server(int portToListenOn) { ... }
}
In the composition root:
builder.Register(c => new Server(12345)).AsSelf();
In my applications I do what you have done above with IoC. That is to say, having my IoC container (StructureMap also) inject an IApplicationSettings into my classes.
For example, in an ASP.NET MVC3 project it may look like:
Public Class MyController
Inherits Controller
...
Private ReadOnly mApplicationSettings As IApplicationSettings
Public Sub New(..., applicationSettings As IApplicationSettings)
...
Me.mApplicationSettings = applicationSettings
End Sub
Public Function SomeAction(custId As Guid) As ActionResult
...
' Look up setting for custId
' If not found fall back on default like
viewModel.SomeProperty = Me.mApplicationSettings.SomeDefaultValue
Return View("...", viewModel)
End Function
End Class
My implementation of IApplicationSettings pulls most things from the app's .config file and has a few hard-coded values in there as well.
My example wasn't logic flow-control (like your example), but it would have worked just the same if it was.
The other way to do this would be to do a service-locator type pattern, where you ask your Dependency Injection container to get you an instance of the configuration class on-the-fly. Service-Location is considered an anti-pattern generally, but might still be of use to you.

Categories