granadaCoder
granadaCoder

Reputation: 27894

IoC/DI in Dot Net Core with config file

So I've been working with Dot Net Core. And I have IoC/DI working with "code er up".

And I've found the "By Environment" abilities to tweak.

The "By Environment" works great when you need tweaks...by....well...environment(s).

What is not working is different DI needs "by customer".

Let's say I have a product. And its goal is to talk to a customer's db, and each customer's db is complete different.

And I want to translate that data into a "standard".

(This is an illustration example, BTW).

I would write an interface : ICustomersDataAdapter

and I would write a

AcmeAdapter : ICustomersDataAdapter

MomAndPopsShopAdapter : ICustomersDataAdapter

(etc, etc)

With Microsoft Unity, this was trivial to swap out the different client adapters when I deployed the code (at a different location for each customer) using their configuration by xml.
This is not "by environment" because all my customer's have Development, Staging and Production environments.

Has anyone solved this type of IoC/DI with DI, and does not involve hacky stuff like

enc.IsEnvironment("AcmeProduction") where I mix/combine the the concerns of a different customer and environment. (<< BOO)

DotNetCore is so well thought out in so many different ways.

But this one, not sure how it was overlooked.


https://asp.net-hacker.rocks/2018/09/27/customizing-aspnetcore-03-dependency-injection.html

Maybe you want to configure the services in a configuration file outside the application, in an XML or JSON file instead in C# only

This is a common feature in various DI containers, but not yet supported in ASP.NET Core.

Upvotes: 1

Views: 718

Answers (3)

granadaCoder
granadaCoder

Reputation: 27894

Ok.

I did find an answer....

https://autofac.readthedocs.io/en/latest/configuration/xml.html#id4

and a code snipplet (copied directly from the URL above) to help with searching (if the URL above ever dies)

using Autofac;
using Autofac.Configuration;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;


// Add the configuration to the ConfigurationBuilder.
var config = new ConfigurationBuilder();
// config.AddJsonFile comes from Microsoft.Extensions.Configuration.Json
// config.AddXmlFile comes from Microsoft.Extensions.Configuration.Xml
config.AddJsonFile("autofac.json");

// Register the ConfigurationModule with Autofac.
var module = new ConfigurationModule(config.Build());
var builder = new ContainerBuilder();
builder.RegisterModule(module);

In my specific case, I had to write some mini logic to find the specific "autofac.json" file(s) that are specifically for "MyCustomer". It wasn't hard, but in my solution, I had multiple .json files.

Below is MY code where I loop over the multiple ".json" files.

        // Register your own things directly with Autofac here. Don't
        // call builder.Populate(), that happens in AutofacServiceProviderFactory
        // for you.


    IEnumerable<string> dependencyInjectionFileNames = GetAutofacDiFiles(/* not shown, but use an environment variable or other to drive which files you need for "my-customer" */);
    

    /* now "register" each different json file */
    foreach (string diFileName in dependencyInjectionFileNames)
    {
        ConfigurationBuilder currentLoopConfigurationBuilder = new ConfigurationBuilder();
        currentLoopConfigurationBuilder.AddJsonFile(diFileName);
        ConfigurationModule currentLoopConfigurationModule =
            new(currentLoopConfigurationBuilder.Build());
        builder.RegisterModule(currentLoopConfigurationModule);
    }

Upvotes: 0

Vitaliy Fedorchenko
Vitaliy Fedorchenko

Reputation: 9235

Rather old question, however as for 2023 it seems there is no config-file based way to configure services for standard DI-container for cases like described in this question.

To address this, I've developed new library NReco.DependencyInjection (https://github.com/nreco/dependencyinjection/) that implements JSON-based IoC configuration as an extension. It can be used in addition to existing code-based definitions, only for configuration of components that may be changed without app recompilation, for example:

[
 {"Type": "AcmeAdapter", "ServiceType": "ICustomersDataAdapter"}
]

(it is possible to have explicit injections by specifying "Constructor", "Properties")

Also it makes sense to use JSON-config to describe complex app configurations, instead of manual handling of IConfigurationSection loaded from appsettings.json.

Upvotes: 0

shiming
shiming

Reputation: 89

When I have multiple clients, for example, A, B and C, I will create different JSON files based on different agents and envs:

  • A.Development.json, A.Staging.json, A.Production.json
  • B.Development.json, B.Staging.json, B.Production.json
  • C.Development.json, C.Staging.json, C.Production.json

Inside each JSON file, it will looks like

{
  "adapter" : "acme" // it can be "acme" or "momAndPopsShop", based on requirement
}

And I will create a parameter called 'client_id' in the Environment Variables.

With the above configurations, inside the Program.cs, I can know both 'client' and 'environment':

var clientId = Environment.GetEnvironmentVariable("client_id");  // clientId will be A,B or C
var env = hostingContext.HostingEnvironment;
var settingFile = $"{clientId}.{env}.json";  // the exact client & env

// config is IConfigurationBuilder
config.AddJsonFile("appsettings.json", true, true) // add default app settings
      .AddJsonFile(settingFile, true, true); // add customized client settings   

Till now, I have added the customized settings for the client & environment. Next, inside the DI part, I will read the IConfiguration and inject the corresponding service based on the settings.

public class Startup
{
    public IConfiguration Configuration { get; }

    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public void ConfigureServices(IServiceCollection services)
    {
        ....

        services.ConfigureDatabase(Configuration);
    }

    ...
}

public static void ConfigureAdapter(this IServiceCollection services, IConfiguration configuration)
{
    var adapterType = configuration.GetSection("adapter").Value; // this value is read from A/B/C.env.json
    switch (adapterType)
    {
        case "acme":
            services.AddSingleton<ICustomersDataAdapter, AcmeAdapter>();
            break;
        case "momAndPopsShop":
            services.AddSingleton<ICustomersDataAdapter, MomAndPopsShopAdapter>();
            break;
        default:
            //log or throw error
            break;
     }
}

I am not very sure whether this is a good practice, but this is so far how I configure my app for different client and environment. Hope this method can help you find a better solution. (I think there will be a better way to do this)

Upvotes: 1

Related Questions