I do feel the recent changes have moved to prescription rather than flexibiity though.
For example, last I looked, the new configuration manager has to be injected.
Injected!
To me, who's not a particular fan of injection, that's insane. Especially given that the previous iteration was a static class that most of would just wrap in a static class called something like "Constants" or something.
These are global, static, variables. They shouldn't be changing during the application's run-time It should be super simple to use and fire-and-forget. Having to pass it into every constructor and then manage it is crazy to me.
Yeah, you can wrap the whole thing yourself, but it's just been done because DI is trendy at the moment. In my opinion, for a statically typed language, DI is just a pretty nasty language hack that's only there to facilitate test methods. You shouldn't be designing things around it.
Configuration can come from different sources, it can be reloaded, it can be lazy-loaded, there are so many different reasons why config is more like service and not a static variable.
It has to be injected.
Maybe you're using it in a way static access makes sense, but when you're designing generic Configuration solution, its whole another set of problems, requirements and constraints.
I personally like DI because you can take one glance at the constructor and know what the class depends on, as well as the testability benefits. Can you expand more on what you don’t like about it?
Fundamentally DI is magic code. Where did that thing come from? How did it get initialised? How do I fix it when it breaks?
But especially for config, it's that if I'm making a tiny app, or just bashing something out, or prototyping, I don't want to spend a bunch of time setting up and configuring something as fundamental as config.
At the moment you add the System.Configuration library as soon as you start a project and out of the box you can just do:
var key = ConfigurationManager.AppSettings["SendGridAPIKey"]; //will be a string
With the newer features of C# you can put that on a class as simply as:
public static class GlobalConfig
{
public static string SendGridAPIKey => ConfigurationManager.AppSettings["SendGridAPIKey"];
public static bool EmailsEnabled => bool.parse(ConfigurationManager.AppSettings["EmailsEnabled"]);
//etc.
}
Then access that anywhere in your code with the below. At worst you might have to add a namespace, which is literally ctrl+. then press space, if it happens to be in a different namespace:
GlobalConfig.EmailEnabled
What I wanted from a new config manager is for it to be even simpler, to handle empty strings, etc. automatically. I don't see anything wrong with accessing those sort of config setting with the below. It's config, it's static, it's supposed to be globally accessed. It's a special sort of const that you're allowed to change without re-compiling.
public static decimal MinimumOrderValue => Config.GetDecimalOrDefault("MinimumOrderValue");
(The OrDefaultValue there would mean 0 by .Net conventions for non-C#ers, if the string was null)
Instead virtually every business class you make has to accept the config object, store it on a local variable and then access it through a local variable. Or you can spend a bunch of time wrapping the god-awful thing in your own static class or singleton (ugh) or whatever.
With the new way you have to do a bunch of extra work, write extra code. And what have I gained? Sod all. I can already change the config for unit tests by changing the config file in the unit test project. The functionality already exists. You still have to do that with the new method.
I want simple, easy and usable without having to spend a load of time configuring. Instead we got a steaming turd because it feels like their ASP.Net Core developers don't seem to get what DI is for. I feel like the ASP.Net team has been getting far too opinionated and often shows a lack of understanding about how swathes of their developers use their product.
This in particular reminds me a lot of the WCF and WWF over-engineering and over-reliance on lots of config and voodoo magic and rain-dances when it went wrong.
If you don't like DI then I feel like C# in general won't be your cup of tea. As for the "only for testing" bit, I disagree.
I'm currently in a situation where I'm on the fence about two different ways to access my database (both are terrible options). Both db drivers are very different, and require very different code. However, they both return the same models. By defining an interface and injecting one service or the other in to my controllers, I can quite effectively and painlessly switch between the two. Why not a singleton? Because with one of the drivers it's better to build a new one on every request.
Back to injecting configuration... I actually use this database access service across two different ASP.NET Core projects. The configuration manager has a nice way to extract only parts of the configuration in to a model. Combine with DI and I can easily split my configuration models between these projects without a shared config model.
Sure, there's other ways to skin the cat. I'm not saying DI is the only way, but I do find it an effective method and certainly not a "nasty language hack".
There's absolutely no reason you have to do DI in C#. C# only recently had native DI introduced.
The only reason to use DI is to be able to inject mocks. It's the only reason. Ruby, for example, can do this without DI. It's literally making bad code just to be able to test it.
It's a language bug that DI is needed.
There are some who believe DI somehow magically makes your code less coupled, but go take a look at any client project in the wild and that's immediately a laughable and indefensible position. If anything, DI makes it worse. I've seen classes with 20 odd service injected, which each all have their own 10 or 15 classes injected, making that class coupled to something like 70-100 other classes.
I just gave you my reasons for using DI and you just ignored them, repeating that the only reason for DI is testing, even though my reasons are not for testing. That's utter nonsense.
If you don't like DI and you're using C#, you're gonna be swimming upstream. It's been the C# dogma for many years. You sound like you'd be much happier away from C#.
Read about Ruby and DHH's opinion on DI. That's how.
Also, remember there's a huge swathe of apps where tests are wholly inappropriate and designing everything around DI is annoying for all those cases.
Prototypes, one time apps, internal tasks, the significant proportion of programmers who think testing's a waste of time and/or money, etc. or that only small, key parts should be tested.
I don't see how injecting is crazy? You mean you find it crazy that you have to add an additional config parameter to your controller constructors? If that is what bothers you then you can create one base constructor where you inject the config and then have other classes derive from it...
It's simply not an appropriate thing to be injecting. App settings are that, settings for the app. They don't change on the fly, if they do change the app should restart, and it's definitely not something that should be injected.
I'm not quite sure what you mean. There is no ConfigurationManager used in Asp.NET Core. Are you talking about IConfiguration?
The reason why you want to have it injected as IOption<> is cause you might want to change configuration on the fly. If you do it right, there is no need for the app to restart if you change the appsettings.json at all. Also it makes things much more flexible and loosely coupled, for example third party middlewares can bring their own Settings and you can define which part of the whole appsettings.json is meant to be for that Middleware.
There is also no need to pass it to the constructor and manage it yourself via a field inside your class, at least in your controllers. You can just use
the [FromServices] attribute before an action argument to inject it into just that method (and that works for properties deeper down the "callstack" aswell).
Sigh. Of course I want the app to restart, it's configuration. Changing it on the fly and hoping the Devs coded defensively enough to handle mutating config is a recipe for disaster.
I can see a case for mutable config, I use it, but the majority of config is not app time mutable and you shouldn't be designing a system for an edge case. Have a mutable config.
Experienced devs use the db for that by the way as mutable config generally needs an in app interface.
For example, last I looked, the new configuration manager has to be injected.
Injected!
To me, who's not a particular fan of injection, that's insane. Especially given that the previous iteration was a static class that most of would just wrap in a static class called something like "Constants" or something.
These are global, static, variables. They shouldn't be changing during the application's run-time It should be super simple to use and fire-and-forget. Having to pass it into every constructor and then manage it is crazy to me.
Yeah, you can wrap the whole thing yourself, but it's just been done because DI is trendy at the moment. In my opinion, for a statically typed language, DI is just a pretty nasty language hack that's only there to facilitate test methods. You shouldn't be designing things around it.