6

I have some configuration interfaces

public interface IAppConfig
{
    IFeatureConfiguration FeatureConfiguration { get; set; }
    IOtherConfiguration OtherConfiguration { get; set; }
}
public interface IFeatureConfiguration 
{
    string SettingFoo { get; set; }
}
public interface IOtherConfiguration 
{
    string SettingBar { get; set; }
}

and classes

public class AppConfig : IAppConfig
{
    public IFeatureConfiguration FeatureConfiguration { get; set; }
    public IOtherConfiguration OtherConfiguration { get; set; }
}
public class FeatureConfiguration : IFeatureConfiguration 
{
    public string SettingFoo { get; set; }
}
public class OtherConfiguration : IOtherConfiguration 
{
    public string SettingBar { get; set; }
}

I wanted to bind appsetting.json to class in Startup.cs using code:

var appConfiguration = Configuration.GetSection("Configuration").Get<AppConfig>();

And I have exception: "Cannot create instance of type '[...].IFeatureConfiguration' because it is either abstract or an interface."

I know I can get rid of interfaces and use only classes, but I really want them... Any help would be appreciated

Ramil Aliyev 007
  • 4,437
  • 2
  • 31
  • 47
Krzyserious
  • 364
  • 4
  • 12
  • The message is clear. The configuration classes can't guess what type of object to create when all they have is an interface. Why are you using interfaces instead of classes? – Panagiotis Kanavos Aug 27 '20 at 14:16
  • `Get` inspects the type you passed as a type parameter, creates a new instance and sets its properties based on the config value paths. For complex properties, `Get` will try to do the same - use the property's type to create a new instance and fill it's properties with the matching subsection's values. When a property is an interface - which class should it instantiate? There may be an infinite number of classes that implement this specific interface – Panagiotis Kanavos Aug 27 '20 at 14:20
  • Are you trying to use those classes for different roles perhaps? Are you expecting `IAppConfig` to use one set of classes in one part of the application and a different set in another? Or pull data from different sources based on configuration - which is what the *configuration subsystem* already does? Both cases would be problematic – Panagiotis Kanavos Aug 27 '20 at 14:23
  • I inject IAppConfig to class from class library project. Two projects references this class library project - asp.net core api and full framework console app. So I wanted asp.net core api to bind this from appsetting.json and console app to implement config classes with some custom logic. And also interfaces will be more friendly to mock in UT. – Krzyserious Aug 28 '20 at 07:26
  • I understand why I get this error. I asked question in hope that someone did something similar and found other way to do this. – Krzyserious Aug 28 '20 at 07:29
  • Mocking: no they won't because there's nothing to mock. Those are just data objects, they don't need mocking. Even if you only want to fill a single property, all mock frameworks can do that already. Otherwise you wouldn't be able to mock a `string` property without specifying an `IString` interface – Panagiotis Kanavos Aug 28 '20 at 07:32
  • Sharing: there's no need for interfaces either - they won't make things any easier. If you want both .NET Old and .NET Core to share the same types, put them in a .NET Standard 2.0 library. All Microsoft Extensions, including Configuration, are .NET Standard 2.0 packages for that reason. You can use Configuration in your .NET Old application directly – Panagiotis Kanavos Aug 28 '20 at 07:34
  • All Microsoft Extensions services have a `.Abstractions` package too, so client libraries don't have to include the providers, configuration etc. This way, a library that wants to use eg logging only needs to use `ILogger` by including `.Logging.Abstractions` and use the loggers provided by the parent application – Panagiotis Kanavos Aug 28 '20 at 07:37
  • In my console app I wanted to use `ConfigurationManager.AppSettings` in constructor of IFeatureConfiguration implementation. If there is no other solution I will get rid of interfaces and will set class properties from other place – Krzyserious Aug 28 '20 at 07:39
  • So you *are* trying to use the classes for two completely different things. First, you no longer need .NET Old configuration. You can easily use .NET Core's configuration in .NET Old because *all* packages are .NET Standard 2.0. I've done that in some of my own console tools, to load settings from both local json files and databases. – Panagiotis Kanavos Aug 28 '20 at 08:12
  • Second, you're using the interfaces as *loaders* in .NET Old, not as config classes. Again, that's something I've done before the new Configuration came around. The only way to use strongly typed settings in .NET Old is to either use Properties, or compile the DTOs in a separate assembly and add that to the `app.config` file (done that too). So you *have* to use extra code to convert the untyped `appSettings` data into DTOs, which I suspect is behind those interfaces. That's a bad design though - you're mixing the DTOs and their loaders – Panagiotis Kanavos Aug 28 '20 at 08:14
  • I eventually moved to just the new Configuration, and now that .NET Core 3 allows single-file publishing, I move the console tools to .NET Core. Even with .NET Framework, using .NET Core's configuration means I can use the same code and concepts *and* use multiple config sources, eg a common source for service credentials that are used by multiple projects, secrets *outside* the application folder, etc – Panagiotis Kanavos Aug 28 '20 at 08:18
  • Ok, thx! I will check .net core configuration in .net old. – Krzyserious Aug 28 '20 at 10:55
  • The good of using interface is to MOCK in the tests. Did you sort the problem? – CidaoPapito Aug 31 '20 at 10:42
  • I haven't code it yet, but my plan is to remove interfaces and mock properties of config classes via Moq. And also make config classes only as data containers without any logic. – Krzyserious Aug 31 '20 at 13:20
  • @PanagiotisKanavos you can use this way: https://stackoverflow.com/a/75333823/8810311 – Ramil Aliyev 007 Feb 03 '23 at 09:40
  • @RamilAliyev you don't have to, because it adds nothing – Panagiotis Kanavos Feb 03 '23 at 09:47

1 Answers1

0

I have a same scenario and I did it this way:

For example the appsettings.json is like as below:

{
  "Configuration": {
    "FeatureConfiguration ": {
      "SettingFoo": "Test setting foo"
    },
    "OtherConfiguration": {
      "SettingBar": "Test setting bar"
    }
  }
}

And you have some interfaces like as below:

public interface IAppConfig
{
    IFeatureConfiguration FeatureConfiguration { get; set; }
    IOtherConfiguration OtherConfiguration { get; set; }
}

public interface IFeatureConfiguration 
{
    string SettingFoo { get; set; }
}

public interface IOtherConfiguration 
{
    string SettingBar { get; set; }
}

And you implemented these interfaces like as below:

public class AppConfig : IAppConfig
{
    public IFeatureConfiguration FeatureConfiguration { get; set; }
    public IOtherConfiguration OtherConfiguration { get; set; }
}

public class FeatureConfiguration : IFeatureConfiguration 
{
    public string SettingFoo { get; set; }
}

public class OtherConfiguration : IOtherConfiguration 
{
    public string SettingBar { get; set; }
}

Firs, you must initalize the appConfiguration with concrete class objects

IAppConfig appConfiguration = new AppConfig {
   FeatureConfiguration = new FeatureConfiguration(),
   OtherConfiguration = new OtherConfiguration()
}

And you can bind nested configuration to this object. You can use Bind method for this purpose like as below:

Configuration.GetSection("Configuration").Bind(appConfiguration);

In conclusion appsettings.json bind to appConfiguration object.

You can see also this answer: https://stackoverflow.com/a/63170497/8810311

Ramil Aliyev 007
  • 4,437
  • 2
  • 31
  • 47
  • That's not what that answer talks about. What's the point of adding interfaces to data-only settings classes? There's nothing to abstract. `Bind` works fine *without* the interfaces. – Panagiotis Kanavos Feb 03 '23 at 09:46
  • @PanagiotisKanavos this is for loose coupling. – Ramil Aliyev 007 Feb 03 '23 at 09:50
  • You already have that through DI and Invertion of Control, not interfaces. You can already create your classes without having to bring up the entire DI and configuration middleware, just by passing an instance of the classes – Panagiotis Kanavos Feb 03 '23 at 09:51
  • @PanagiotisKanavos "Bind works fine without the interfaces." are you sure ? If you passed non initialize object to Bind, then your result is always null – Ramil Aliyev 007 Feb 03 '23 at 09:53
  • Yes, and so is the answer you linked to, [the official documentation and all .NET Core code](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?view=aspnetcore-7.0#bind-hierarchical-configuration-data-using-the-options-pattern) is sure. People don't use interfaces for settings DTOs. There's no such thing as `non-initialized object`. Once constructed, the object *is* initialized. Changing the original values isn't initialization. Interfaces have nothing to do with how `Bind<>`, `Get<>` or `Configure<>` work – Panagiotis Kanavos Feb 03 '23 at 10:00
  • @PanagiotisKanavos I don't see any interface, on your link. I tested Bind method with non initialize IAppConfig appConfiguration and result is always null. In my project some methods can work different appsettings, therefore I use interface for this purpose – Ramil Aliyev 007 Feb 03 '23 at 10:07
  • Then you misuse the configuration middleware. Different settings are different settings and therefore different DTOs. The entire Configuration middleware is built on this. Different features use different DTOs which only need to be loaded from their section, not a global root object – Panagiotis Kanavos Feb 03 '23 at 10:09
  • How will this rigid, tightly coupled global `AppConfig` going to handle config sections that come from different changing sources? That's possible right now, in fact the Configuration middleware already handles reloading. The `IOptions<>` interface variants already handle static or changing settings. Why would feature 3 have to be tied to `AppConfig` and features 1-100? – Panagiotis Kanavos Feb 03 '23 at 10:12
  • @PanagiotisKanavos because some settings' source is different location, on remote machine, in encrypted file and etc. I have a some custom logic in concrete classes which implemented interface – Ramil Aliyev 007 Feb 03 '23 at 10:16
  • Which should go into the configuration sources, or before actually registering the settings DTOs. The whole point of using the Configuration middleware is to decouple the configuration sources and their logic from the settings themselves. If you have custom logic, they're services, not configuration settings – Panagiotis Kanavos Feb 03 '23 at 10:33
  • Perhaps you should check [Configuration](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?view=aspnetcore-7.0), [Custom Configuration Providers](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?view=aspnetcore-7.0#custom-configuration-provider) and the [Options pattern](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/configuration/options?view=aspnetcore-7.0). This question isn't asking about a configuration *service* but why the Configuration middleware hates interfaces. Because it's not meant to produce interfaces – Panagiotis Kanavos Feb 03 '23 at 10:36
  • I don't want use configuration middleware for this purpose. Yes I can use it, but I only bind different appsettings to one interface type in one point via some condition – Ramil Aliyev 007 Feb 03 '23 at 10:39
  • @PanagiotisKanavos I agree with you, but in my project situation is different and I don't want use configuation middleware. Bind method is simple for me than create custom configuration provider and etc. And I don't write this code in startup.cs – Ramil Aliyev 007 Feb 03 '23 at 11:04