Ways of keeping configuration code out of logic code using Dependency Injection - c#

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.

Related

How to resolve Autofac dependency in runtime using parameter from request?

I have a .NET project with 50+ WebAPI Controllers. Dependencies injected in constructor:
// One of this APIs
public class ProductAController : ApiController
{
private readonly IProductDataProvider _productDataProvider;
// Constructor usually requires a lot of dependencies (for user sesisons, calculations, crud operations, some external integrations etc)
public ProductAController(IProductDataProvider productDataProvider)
{
this._productDataProvider = productDataProvider;
}
}
public interface IProductDataProvider
{
public bool ProductExists(string productName);
}
public class ProductDataProvider
{
private readonly IDbConnectionProvider _dbConnectionProvider;
public ProductDataProvider(IDbConnectionProvider dbConnectionProvider)
{
this._dbConnectionProvider = dbConnectionProvider;
}
public bool ProductExists(string productName)
{
//...
}
}
public interface IDbConnectionProvider
{
public string GetConnectionString();
}
public class DbConnectionProvider
{
// This is the config that I need to set up for several APIs
private readonly ModuleConfig _moduleConfig;
public DbConnectionProvider(ModuleConfig moduleConfig)
{
this._moduleConfig = moduleConfig;
}
public string GetConnectionString();
{
//...
}
}
IDbConnectionProvider, ModuleConfig, and IProductDataProvider registered in Autofac (all 50 API use single ModuleConfig):
builder.RegisterInstance(pdkProvidersSettings).AsSelf().SingleInstance();
builder.RegisterType<DbConnectionProvider>().As<IDbConnectionProvider>().InstancePerDependency();
builder.RegisterType<ProductDataProvider>().As<IProductDataProvider>().InstancePerDependency();
Now I need to use different instances of ModuleConfig for some APIs.
I'll have to make IConfigProvider.GetModuleConfig(string key, string ownerKey) (and call it with "ModuleConfig" and "productA123" params)
I was thinking about Named and Keyed Services feature or IIndex in Autofac, I also tried to pass moduleConfig across all that chain (but it required too much changes).
It seems like I have a design problem here but I can't figure it out.
The expected result is something like that:
API gets a request containing the key ("productA1" or "productA2". Thats why I can't hardcode "productA" in metadata attribute of ProductAController)
When it comes to IDbConnectionProvider it gets specific ModuleConfig and is able to get specific connection string
The result is that different API modules can use different DBs (that is the main requirement)

Accessing top-level fields in AppSettings.json from injected configuration class

Say we have such AppSettings.json
{
"Region": Europe,
"WeirdService": {
"JustField": "value"
}
}
Registering WeirdService settings in separate, singleton class (or using options pattern) is fine, just:
service.AddSingleton(configuration.GetSection("WeirdService").Get<WeirdService>();
And at this point it's fine. I don't know however how to deal cleanly with this top-level properties like Region in my example.
I know I can just inject IConfiguration and use config.GetValue<string>("Region") or just access configuration directly, but I wonder if there is some clean, better way without hardcoding this stuff in services.
Edit
I forgot to mention. Team I'm currently working with uses .NET Core 3.1 as it's current LTS release.
I think the easiest way would be to just create a class for the toplevel keys. In your case you could create something like AppConfig with the single property Region. Then you just register it without getting a config section using the Configuration object, the Configure methods asks for a Configuration interface anyway and not a ConfigurationSection.
AppConfig:
public class AppConfig
{
public string? Region { get; set; }
}
Registration:
public static IServiceCollection AddOptions(this IServiceCollection services, IConfiguration configuration)
{
return services.Configure<AppConfig>(configuration);
}
Usage:
public class ExampleConsumer
{
public ExampleConsumer(IOptions<AppConfig> appConfig) {}
}
You got two options
Don't have any top level fields
All top level fields would go one level in. Your configuration would look something like:
{
"App": {
"Region": "east-us-2",
"ShowMaintenancePrompt": false
},
// other options follow
}
The advantage of this approach is you can keep adding to "App" as your application grows, and continue to use the options pattern.
Gather top-level fields into a class, and register that with DI
For a configuration like:
{
"Region": "east-us-2"
}
Create a AppConfig class like:
internal class AppConfig
{
public string? Region { get; set; }
}
And register this class with the DI:
var toplevelConfig = new AppConfig {
Region = configuration.GetValue<string>("Region")
};
services.AddSingleton<AppConfig>(toplevelConfig);
You can now inject AppConfig anywhere you'd like.
The only minor downside to this is that you cannot use the options pattern anymore.
Avoid injecting IConfiguration directly.
Updated
Method 1 Preferred way
The preferred way to read related configuration values is using the options pattern.
For more detail about the options pattern check the link below.
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/configuration/options?view=aspnetcore-5.0
"WeirdService": {
"JustField": "value"
}
Create the WeiredServiceOptions class.
public class WeiredServiceOptions
{
public const string WeiredService = "WeiredService";
public string JustField { get; set; }
}
An options class:
Must be non-abstract with a public parameterless constructor.
All public read-write properties of the type are bound.
Fields are not bound. In the preceding code, WeiredService is not bound. The Position property is used so the string WeiredService doesn't need to be hardcoded in the app when binding the class to a configuration provider.
Calls ConfigurationBinder.Bind to bind the WeiredServiceOptions class to the WeiredService section.
var weiredServiceOptions = new PositionOptions();
configuration.GetSection(PositionOptions.Position).Bind(positionOptions);
An alternative approach when using the options pattern is to bind the WeiredService section and add it to the dependency injection service container. In the following code, WeiredServiceOptions is added to the service container with Configure and bound to the configuration
services.Configure<WeiredServiceOptions>(Configuration.GetSection(
WeiredServiceOptions.Position));
and then read the WeiredService Options.
private readonly WeiredServiceOptions _options;
public YourClassContructor(IOptions<WeiredServiceOptions> options)
{
_options = options
}
Console.WriteLine($"JustField: {_options.JustField}");
Method 2
service.AddSingleton(configuration.GetSection("WeirdService:JustField").value);
Method 3
service.AddSingleton(configuration["WeirdService:JustField"]);
Doing: services.AddSingleton(Configuration.GetSection("WeirdService").Get<WeirdService>());
will register WeirdService to the Ioc container without supporting Options pattern. Assuming this is what you are looking for. Here is what you could do:
Create a class with properties mathcing the top level configuration similar to the AppConfig class a couple of people have suggested in the answers
Register the AppConfig class with the Ioc as below:
services.AddSingleton(Configuration.Get<AppConfig>());
Note:
Doing Configuration.Get<AppConfig>() will bind matching properties on the AppConfig class with the corresponding values from appsettings.json
Feel free to skip properties for keys that you do not want to bind
The IConfiguration.Get<T> is an extension method defined in Microsoft.Extensions.Configuration.ConfigurationBinder just in case
You can bind section to a class as well, it allows clean usage without using much of the magic strings.
public class WeirdService{
public string JustField{ get; set;}
public string AnotherField{ get; set;}
}
In controller you can then define a field
private readonly WeirdService _weirdService = new WeirdService();
public UserController(IConfiguration configuration)
{
configuration.GetSection("WeirdService").Bind(_weirdService);
//_weirdService.JustField
//_weirdService.AnotherField
}
You can access Region field using
configuration.GetValue<string>("Region")
or other way is
public Startup(IConfiguration configuration, IWebHostEnvironment
webHostEnvironment)
{
Configuration = configuration;
environment = webHostEnvironment;
}
then you can just use
Configuration["Region"]

Injecting configuration objects in a .NET application

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.

Delay creation of instance in Simple Injector

I am using Simple Injector in my project to hook up all the required dependencies, but I cannot call container.Verify because it creates a Singleton instance for an http configuration before the actual first request
public interface IConfiguration { }
public class Configuration : IConfiguration
{
public Configuration()
{
var httpContext = HttpContext.Current;
var httpRequest = currentHttpContext.Request;
var httpRequestUrl = currentHttpRequest.Url;
this.UriScheme = currentHttpRequestUrl.Scheme;
this.UriHost = currentHttpRequestUrl.Host;
this.UriPort = currentHttpRequestUrl.Port;
}
public string UriScheme { get; private set; }
public string UriHost { get; private set; }
public int UriPort { get; private set; }
}
public class ServiceA
{
private readonly _configuration;
public ServiceA(IConfiguration configuration)
{
_configuration = configuration
}
}
public class ServiceB
{
private readonly _configuration;
public ServiceB(IConfiguration configuration)
{
_configuration = configuration
}
}
This is a basic example of the scenario. I currently have around 60 services all depending on IConfiguration
All the configuration needs to happen when the configuration class is created
This is what I do to register the instances in the container
var container = new Container();
//container.RegisterSingleton<IConfiguration, Configuration>();
var lazy = new Lazy<InstanceProducer>(() =>
Lifestyle.Singleton.CreateProducer(typeof(IConfiguration), typeof(Configuration), container));
container.Register<ServiceA>();
container.Register<ServiceB>();
container.Verify(); // Creates configuration class --> not desired
As per How can I skip verification of an object in the container
So the trick here is to trigger the creation of new InstanceProducer instances after the verification process
I know the workaround is using Lazy<T> and InstanceCreator but I cannot finish hooking my code correctly
EDIT
The Configuration class has no dependencies. The problem with Configuration is that it gets created as a Singleton on the container.Verify method call and at that time the currentHttpRequest.Url is not the actual url
I suppose I can move the configuration from the constructor to a method (e.g GetConfiguration) and do some refactoring but I am curious if delaying the instance creation can be achieved under the scenario of the question
As explained by Mark Seemann in this article, injection constructors should be simple and reliable. They shouldn't do anything that might cause it to fail. Calling HttpContext.Current inside the constructor makes it unreliable, since this is something that might fail.
Besides this, your Configuration component now depends upon runtime data (the HttpContext.Current is runtime data), which is a sin, as explained in this article.
The solution to your problem however is really simple and straightforward. Just change your Configuration class to the following:
public sealed class Configuration : IConfiguration
{
public string UriScheme => this.Url.Scheme;
public string UriHost => this.Url.Host;
public int UriPort => this.Url.Port;
private Uri Url => HttpContext.Current.Request.Url;
}
Not only does this simplify things, it also removes the anti-patterns that you were applying which caused you trouble. Your constructor is now so simple, that it isn't even there anymore (can't get any simpler than this). And the runtime data is now only requested (from HttpContext.Current) after the object graph is constructed. This allows the container to verify its configuration reliably.

Dependency Injection in Model classes (entities)

I am building an ASP.NET Core MVC application with Entity Framework Code-First.
I implemented a simple repository pattern, providing basic CRUD operations for all the model classes I have created.
I chose to follow all the recommendations provided in docs and DI is one of these.
In ~~.NET 5~~ (6 years later update: .net 5 was the alpha name of .net core 1.0) dependency injection works very well for any class that we do not directly instantiate (e.g.: controllers, data repositories, ...).
We simply inject them via the constructor, and register the mappings in the Startup class of the application :
// Some repository class
public class MyRepository : IMyRepository
{
private readonly IMyDependency _myDependency;
public MyRepository(IMyDependency myDependency)
{
_myDependency = myDependency;
}
}
// In startup.cs :
services.AddScoped<IMyDependency, MyDependency>();
services.AddScoped<IMyRepository, MyRepository>();
The problem is that in some of my model classes, I would like to inject some of the dependencies I have declared.
But I think that I cannot use the constructor injection pattern because model classes are often explicitly instantiated. Therefore, I would need to provide myself with the dependencies, which I can't.
So my question is: is there another way than constructor injection to inject dependencies, and how? I was for example thinking of an attribute pattern or something like that.
As I already explained in a comment, when creating an object using new, there is nothing from the dependency injection framework that is involved in the process. As such, it’s impossible for the DI framework to magically inject things into that object, it simply doesn’t know about it.
Since it does not make any sense to let the DI framework create your model instances (models are not a dependency), you will have to pass in your dependencies explicitly if you want the model to have them. How you do that depends a bit on what your models are used for, and what those dependencies are.
The simple and clear case would be to just have your model expect the dependencies on the constructor. That way, it is a compile time error if you do not provide them, and the model has access to them right away. As such, whatever is above, creating the models, is required to have the dependencies the model type needs. But at that level, it’s likely that this is a service or a controller which has access to DI and can request the dependency itself.
Of course, depending on the number of dependencies, this might become a bit complicated as you need to pass them all to the constructor. So one alternative would be to have some “model factory” that takes care of creating the model object. Another alternative would also be to use the service locator pattern, passing the IServiceCollection to the model which can then request whatever dependencies it needs. Note that is generally a bad practice and not really inversion of control anymore.
Both these ideas have the issue that they modify the way the object is created. And some models, especially those handled by Entity Framework, need an empty constructor in order for EF to be able to create the object. So at that point you will probably end up with some cases where the dependencies of your model are not resolved (and you have no easy way of telling).
A generally better way, which is also a lot more explicit, would be to pass in the dependency where you need it, e.g. if you have some method on the model that calculates some stuff but requires some configuration, let the method require that configuration. This also makes the methods easier to test.
Another solution would be to move the logic out of the model. For example the ASP.NET Identity models are really dumb. They don’t do anything. All the logic is done in the UserStore which is a service and as such can have service dependencies.
The pattern often used in domain driven design (rich domain model to be specific) is to pass the required services into the method you are calling.
For example if you want to calculate the vat, you'd pass the vat service into the CalculateVat method.
In your model
public void CalculateVat(IVatCalculator vatCalc)
{
if(vatCalc == null)
throw new ArgumentNullException(nameof(vatCalc));
decimal vatAmount = vatcalc.Calculate(this.TotalNetPrice, this.Country);
this.VatAmount = new Currency(vatAmount, this.CurrencySymbol);
}
Your service class
// where vatCalculator is an implementation IVatCalculator
order.CalculateVat(vatCalculator);
Finally your service can inject another services, like a repository which will fetch the tax rate for a certain country
public class VatCalculator : IVatCalculator
{
private readonly IVatRepository vatRepository;
public VatCalculator(IVatRepository vatRepository)
{
if(vatRepository == null)
throw new ArgumentNullException(nameof(vatRepository));
this.vatRepository = vatRepository;
}
public decimal Calculate(decimal value, Country country)
{
decimal vatRate = vatRepository.GetVatRateForCountry(country);
return vatAmount = value * vatRate;
}
}
I know my answer is late and may not exactly what you're asking for, but I wanted to share how I do it.
First of all: If you want to have a static class that resolves your dependencies this is a ServiceLocator and it's Antipattern so try not to use it as you can.
In my case I needed it to call MediatR inside of my DomainModel to implement the DomainEvents logic.
Anyway, I had to find a way to call a static class in my DomainModel to get an instance of some registered service from DI.
So I've decided to use the HttpContext to access the IServiceProvider but I needed to access it from a static method without mention it in my domain model.
Let's do it:
1- I've created an interface to wrap the IServiceProvider
public interface IServiceProviderProxy
{
T GetService<T>();
IEnumerable<T> GetServices<T>();
object GetService(Type type);
IEnumerable<object> GetServices(Type type);
}
2- Then I've created a static class to be my ServiceLocator access point
public static class ServiceLocator
{
private static IServiceProviderProxy diProxy;
public static IServiceProviderProxy ServiceProvider => diProxy ?? throw new Exception("You should Initialize the ServiceProvider before using it.");
public static void Initialize(IServiceProviderProxy proxy)
{
diProxy = proxy;
}
}
3- I've created an implementation for the IServiceProviderProxy which use internally the IHttpContextAccessor
public class HttpContextServiceProviderProxy : IServiceProviderProxy
{
private readonly IHttpContextAccessor contextAccessor;
public HttpContextServiceProviderProxy(IHttpContextAccessor contextAccessor)
{
this.contextAccessor = contextAccessor;
}
public T GetService<T>()
{
return contextAccessor.HttpContext.RequestServices.GetService<T>();
}
public IEnumerable<T> GetServices<T>()
{
return contextAccessor.HttpContext.RequestServices.GetServices<T>();
}
public object GetService(Type type)
{
return contextAccessor.HttpContext.RequestServices.GetService(type);
}
public IEnumerable<object> GetServices(Type type)
{
return contextAccessor.HttpContext.RequestServices.GetServices(type);
}
}
4- I should register the IServiceProviderProxy in the DI like this
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpContextAccessor();
services.AddSingleton<IServiceProviderProxy, HttpContextServiceProviderProxy>();
.......
}
5- Final step is to initialize the ServiceLocator with an instance of IServiceProviderProxy at the Application startup
public void Configure(IApplicationBuilder app, IHostingEnvironment env,IServiceProvider sp)
{
ServiceLocator.Initialize(sp.GetService<IServiceProviderProxy>());
}
As a result now you can call the ServiceLocator in your DomainModel classes "Or and needed place" and resolve the dependencies that you need.
public class FakeModel
{
public FakeModel(Guid id, string value)
{
Id = id;
Value = value;
}
public Guid Id { get; }
public string Value { get; private set; }
public async Task UpdateAsync(string value)
{
Value = value;
var mediator = ServiceLocator.ServiceProvider.GetService<IMediator>();
await mediator.Send(new FakeModelUpdated(this));
}
}
The built-in model binders complain that they cannot find a default ctor. Therefore you need a custom one.
You may find a solution to a similar problem here, which inspects the registered services in order to create the model.
It is important to note that the snippets below provide slightly different functionality which, hopefully, satisfies your particular needs. The code below expects models with ctor injections. Of course, these models have the usual properties you might have defined. These properties are filled in exactly as expected, so the bonus is the correct behavior when binding models with ctor injections.
public class DiModelBinder : ComplexTypeModelBinder
{
public DiModelBinder(IDictionary<ModelMetadata, IModelBinder> propertyBinders) : base(propertyBinders)
{
}
/// <summary>
/// Creates the model with one (or more) injected service(s).
/// </summary>
/// <param name="bindingContext"></param>
/// <returns></returns>
protected override object CreateModel(ModelBindingContext bindingContext)
{
var services = bindingContext.HttpContext.RequestServices;
var modelType = bindingContext.ModelType;
var ctors = modelType.GetConstructors();
foreach (var ctor in ctors)
{
var paramTypes = ctor.GetParameters().Select(p => p.ParameterType).ToList();
var parameters = paramTypes.Select(p => services.GetService(p)).ToArray();
if (parameters.All(p => p != null))
{
var model = ctor.Invoke(parameters);
return model;
}
}
return null;
}
}
This binder will be provided by:
public class DiModelBinderProvider : IModelBinderProvider
{
public IModelBinder GetBinder(ModelBinderProviderContext context)
{
if (context == null) { throw new ArgumentNullException(nameof(context)); }
if (context.Metadata.IsComplexType && !context.Metadata.IsCollectionType)
{
var propertyBinders = context.Metadata.Properties.ToDictionary(property => property, context.CreateBinder);
return new DiModelBinder(propertyBinders);
}
return null;
}
}
Here's how the binder would be registered:
services.AddMvc().AddMvcOptions(options =>
{
// replace ComplexTypeModelBinderProvider with its descendent - IoCModelBinderProvider
var provider = options.ModelBinderProviders.FirstOrDefault(x => x.GetType() == typeof(ComplexTypeModelBinderProvider));
var binderIndex = options.ModelBinderProviders.IndexOf(provider);
options.ModelBinderProviders.Remove(provider);
options.ModelBinderProviders.Insert(binderIndex, new DiModelBinderProvider());
});
I'm not quite sure if the new binder must be registered exactly at the same index, you can experiment with this.
And, at the end, this is how you can use it:
public class MyModel
{
private readonly IMyRepository repo;
public MyModel(IMyRepository repo)
{
this.repo = repo;
}
... do whatever you want with your repo
public string AProperty { get; set; }
... other properties here
}
Model class is created by the binder which supplies the (already registered) service, and the rest of the model binders provide the property values from their usual sources.
HTH
Is there another way than constructor injection to inject dependencies, and how?
The answer is "no", this cannot be done with "dependency injection". But, "yes" you can use the "service locator pattern" to achieve your end-goal.
You can use the code below to resolve a dependency without the use of constructor injection or the FromServices attribute. Additionally you can new up an instance of the class as you see fit and it will still work -- assuming that you have added the dependency in the Startup.cs.
public class MyRepository : IMyRepository
{
public IMyDependency { get; } =
CallContextServiceLocator.Locator
.ServiceProvider
.GetRequiredService<IMyDependency>();
}
The CallContextServiceLocator.Locator.ServiceProvider is the global service provider, where everything lives. It is not really advised to use this. But if you have no other choice you can. It would be recommended to instead use DI all the way and never manually instantiate an object, i.e.; avoid new.
I'm simply adding some supplemental information here to the answers provided that can help.
IServiceProvider was provided in the accepted answer, but not the important IServiceProvider.CreateScope() method. You can use it to create scopes as necessary that you added through ConfigureServices.
I'm not sure if IServiceProvider is actually a Service Locator pattern behind the scenes or not, but it's how you create scopes as far as I know. At least in the case if it is a Service Locator pattern, it's the official one for today in .NET, and so it's not compounded by the problems of writing your own Service Locator, which I also agree is anti-pattern.
Example, Startup.cs/ConfigureServices and Configure:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<SomeDbContext>(options =>
{
options.UseSqlServer(Configuration.GetSection("Databases").GetSection("SomeDb")["ConnectionString"]);
options.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking);
}, ServiceLifetime.Scoped);
services.AddMvcCore().AddNewtonsoftJson();
services.AddControllersWithViews();
}
public async void Configure(IApplicationBuilder app, IWebHostEnvironment env, IServiceProvider provider)
{
...
IServiceScope scope = provider.CreateScope();
SomeDbContext context = scope.ServiceProvider.GetRequiredService<SomeDbContext>();
SomeModelProxyClass example = new SomeModelProxyClass(context);
await example.BuildDefaults(
Configuration.GetSection("ProfileDefaults").GetSection("Something"),
Configuration.GetSection("ProfileDefaults").GetSection("SomethingSomething"));
scope.Dispose();
}
The above is for doing some default interactions on Startup, maybe if you need to build some default records in your database on a first usage, just as an example.
Ok so let's get to your repository and dependency though, will they work?
Yep!
Here's a test in my own CRUD project, I made a simple minimalist implementation of your IMyDependency and IMyRepository like so, then added them scoped as you did to Startup/ConfigureServices:
public interface IMyRepository
{
string WriteMessage(string input);
}
public interface IMyDependency
{
string GetTimeStamp();
}
public class MyDependency : IMyDependency
{
public MyDependency()
{
}
public string GetTimeStamp()
{
return DateTime.Now.ToLongDateString() + " " + DateTime.Now.ToLongTimeString();
}
}
public class MyRepository : IMyRepository
{
private readonly IMyDependency _myDependency;
public MyRepository(IMyDependency myDependency)
{
_myDependency = myDependency;
}
public string WriteMessage(string input)
{
return input + " - " + _myDependency.GetTimeStamp();
}
}
Here ContextCRUD is a Model class from my own project not derived from Scaffold-DbContext tooling like my other database classes, it's a container of logic from those scaffold Model classes, and so I put it in the namespace Models.ProxyModels to hold its own business logic for doing CRUD operations so that the Controllers are not gummed up with logic that should be in the Model:
public ContextCRUD(DbContext context, IServiceProvider provider)
{
Context = context;
Provider = provider;
var scope = provider.CreateScope();
var dep1 = scope.ServiceProvider.GetService<IMyRepository>();
string msg = dep1.WriteMessage("Current Time:");
scope.Dispose();
}
Debugging I get back the expected results in msg, so it all checks out.
The calling code from the Controller for reference, just so you can see how IServiceProvider is passed from upstream by constructor injection in the Controller:
[Route("api/[controller]")]
public class GenericController<T> : Controller where T: DbContext
{
T Context { get; set; }
ContextCRUD CRUD { get; set; }
IConfiguration Configuration { get; set; }
public GenericController(T context, IConfiguration configuration, IServiceProvider provider)
{
Context = context;
CRUD = new ContextCRUD(context, provider);
Configuration = configuration;
}
...
You can do it, check out [InjectionMethod] and container.BuildUp(instance);
Example:
Typical DI constructor (NOT NEEDED IF YOU USE InjectionMethod) public
ClassConstructor(DeviceHead pDeviceHead) {
this.DeviceHead = pDeviceHead; }
This attribute causes this method to be called to setup DI.
[InjectionMethod] public void Initialize(DeviceHead pDeviceHead) {
this.DeviceHead = pDeviceHead; }

Categories