DependencyInjection.ContainerConfiguration 1.0.0

dotnet add package DependencyInjection.ContainerConfiguration --version 1.0.0
NuGet\Install-Package DependencyInjection.ContainerConfiguration -Version 1.0.0
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="DependencyInjection.ContainerConfiguration" Version="1.0.0" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add DependencyInjection.ContainerConfiguration --version 1.0.0
#r "nuget: DependencyInjection.ContainerConfiguration, 1.0.0"
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install DependencyInjection.ContainerConfiguration as a Cake Addin
#addin nuget:?package=DependencyInjection.ContainerConfiguration&version=1.0.0

// Install DependencyInjection.ContainerConfiguration as a Cake Tool
#tool nuget:?package=DependencyInjection.ContainerConfiguration&version=1.0.0

DependencyInjection.ContainerConfiguration

This package adds extensions to IServiceCollection to automatically register all services defined in an external configuration json file, with the possibility of injecting interception behaviors.

I was a huge fan of Enterprise Library and Unity back in the day, and as an exercise I created this package that allows you to define mappings of services/interfaces to be added to your ServiceCollection based on configuration.

As a bonus, this package also lets you use interception behaviors (based on DispatchProxy) so you can decorate and extend functionality without having to alter the original code.

Icon made by Freepik (https://www.freepik.com) from Flaticon (https://www.flaticon.com/)

Installation

Simply add the package using your client of choice, such as the command line:

dotnet add package DependencyInjection.ContainerConfiguration

Usage

First you need to create a container.json configuration file with the mappings. Basically it's a simple file with the Services element, which contains Transient, Singleton and Scoped mappings.

Each mapping needs an Interface and the actual Implementation to be mapped to:

{
  "Services": {
    "Transient": [
      {
        "Interface": "DependencyInjection.ContainerConfiguration.ConsoleApp.Interfaces.IDemoInterface, DependencyInjection.ContainerConfiguration.ConsoleApp",
        "Implementation": "DependencyInjection.ContainerConfiguration.ConsoleApp.Managers.DemoManager, DependencyInjection.ContainerConfiguration.ConsoleApp"
      }
    ],
    "Singleton": [],
    "Scoped": []
  }
}

Next make sure you register the services by calling the AddContainerConfiguration extension:

var serviceCollection = new ServiceCollection()
    // Do other common things here...
    .AddMemoryCache()
    // Register services based on configuration file
    .AddContainerConfiguration();

And that's pretty much it, just get the service you want and invoke it!

var mgr = _serviceProvider.GetService<IDemoInterface>();
Console.WriteLine($"Value returned: {mgr?.Run(2, new List<int> { 1, 2, 3 })}");

Note: Just resolve how you would typically would do, in the sample source I have added a helper class

You can also call the AddContainerConfiguration extension method with the path to a different container file (instead of container.json in the running folder) or even with an existing IConfiguration object that has to adhere to the schema above.

Extending with interception behaviors

If you want to add cross cutting concerns to your services on a configuration based form, you can also use this package.

The approach used is based on the Decorator pattern using DispatchProxy. Create your class and make it implement the abstract DispatchProxyBase class.

Next ensure the Invoke method does what you want (you may choose not to call the next method in the pipeline, etc):

public class MyCustomBehavior<T> : DispatchProxyBase<T, MyCustomBehavior<T>>
{
    protected override object Invoke(MethodInfo targetMethod, object[] args)
    {
        // ######################
        // Do something before...
        // ######################

        // Actual method invocation (or delegate to next interceptor)
        var result = targetMethod.Invoke(_original, args);

        // #####################
        // Do something after...
        // #####################

        return result; // Return original result, or something else, etc...
    }
}

Finally going back to the configuration file, after each registered service simply add an array of InterceptionBehaviors. The order matters as they will be invoked the way they are found in the collection.

{
  "Services": {
    "Transient": [
      {
        "Interface": "DependencyInjection.ContainerConfiguration.ConsoleApp.Interfaces.IDemoInterface, DependencyInjection.ContainerConfiguration.ConsoleApp",
        "Implementation": "DependencyInjection.ContainerConfiguration.ConsoleApp.Managers.DemoManager, DependencyInjection.ContainerConfiguration.ConsoleApp",
        "InterceptionBehaviors": [
          "DependencyInjection.ContainerConfiguration.Interceptors.Behaviors.ConsoleLoggingInterceptionBehavior`1, DependencyInjection.ContainerConfiguration.Interceptors",
          "DependencyInjection.ContainerConfiguration.Interceptors.Behaviors.CachingInterceptionBehavior`1, DependencyInjection.ContainerConfiguration.Interceptors"
        ]
      }
    ],
    "Singleton": [],
    "Scoped": []
  }
}

Please note the class name with the Generics notation: (DependencyInjection.ContainerConfiguration.Interceptors.Behaviors.ConsoleLoggingInterceptionBehavior`1)

There are a couple of behaviors included in the source (caching and logging) to give you an example of what you can do.

Notes

This may not be the most efficient way but it was a lot of fun developing and serves the purposes for most of the scenarios. Also, full source code is included so feel free to modify/extend at your heart's desire. 😉

Due to the nature of DispatchProxy, if you wish to inject services from the DI container into an interception behavior you have to create an empty constructor (used by DispatchProxy) and AFTERWARDS a constructor with the parameters to be injected, like in the example for the caching behavior:

public class CachingInterceptionBehavior<T> : DispatchProxyBase<T, CachingInterceptionBehavior<T>>
{
    // Variable has to be protected (not private)
    protected readonly IMemoryCache _cache;

    public CachingInterceptionBehavior()
    {
        // We need at least one parameterless constructor because DispatchProxy uses it to create the proxy
    }

    public CachingInterceptionBehavior(IMemoryCache _memoryCache)
    {
        // If we want to inject other services from the container, we need this other constructor next
        _cache = _memoryCache;
    }

    protected override object Invoke(MethodInfo targetMethod, object[] args)
    {
        ...

I hope you enjoy using this!

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
.NET Core netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.1 is compatible. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
1.0.0 310 7/23/2021