ANT.NETCore.PluginLoader 2.1.0

ANT Inversion of Control Plugin Loading Library

This package contains everything required to setup dynamic IOC on .net core 3 and .Net Framework 4.6+ apps

There is a newer version of this package available.
See the version list below for details.
Install-Package ANT.NETCore.PluginLoader -Version 2.1.0
dotnet add package ANT.NETCore.PluginLoader --version 2.1.0
<PackageReference Include="ANT.NETCore.PluginLoader" Version="2.1.0" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add ANT.NETCore.PluginLoader --version 2.1.0
The NuGet Team does not provide support for this client. Please contact its maintainers for support.
#r "nuget: ANT.NETCore.PluginLoader, 2.1.0"
#r directive can be used in F# Interactive, C# scripting and .NET Interactive. Copy this into the interactive tool or source code of the script to reference the package.
// Install ANT.NETCore.PluginLoader as a Cake Addin
#addin nuget:?package=ANT.NETCore.PluginLoader&version=2.1.0

// Install ANT.NETCore.PluginLoader as a Cake Tool
#tool nuget:?package=ANT.NETCore.PluginLoader&version=2.1.0
The NuGet Team does not provide support for this client. Please contact its maintainers for support.

ANT.NETCore.PluginLoader


The purpose of this package is to facilitate using IOC
on .NET Core 3.0+ and .NET Framework 4.6+ projects.

The .NET Core implentation uses DotNetCorePlugins by nate McMaster
The .NET Framework implementation uses Microsoft Unity

========================================

Plugin Instructions:

  1. Add a reference to System.ComponentModel.Composition or install the NuGet package depending on the framework you're targetting.
  2. Add the Export attribute to any classes that you wish to expose to the DI provider.
[Export(typeof(IMyInterface))]
public class MyInterfaceImplementation : IMyInterface { }
  1. Optionally, you might add a contractName param to the Export attribute, this will create named instances and allow you to recall different implementations of the same interface (more on that later).
[Export("Foo", typeof(IMyInterface))]
public class MyInterfaceImplementationFoo : IMyInterface { }

[Export("Bar", typeof(IMyInterface))]
public class MyInterfaceImplementationBar : IMyInterface { }
  1. Optionally add the PartCreationPolicy attribute to specify whether the class to be exported should be instantiated as a Singleton or Transient instance (Shared = Singleton, Nonshared = Transient).
[Export(typeof(IMyInterface))]
[PartCreationPolicy(CreationPolicy.Shared)]
public class MyInterfaceImplementation : IMyInterface { }
  1. If you wish to export an instance of a configuration section (only for .Net Core), use the contractName param of the Export attribute to declare it. Begin the name of the contract with the word Configuration. and then the name of the configuration section. All configuration sections can be instanced using IOptions<> or by their class type.

appSettings.json file:

{
  "MyConfigSectionName": {
    "StringEntry": "String",
    "NumericEntry": 42,
    "BooleanEntry": false
  }
}

class file:

[Export("Configuration.MyConfigSectionName")]
public class MyConfig
{
    StringEntry { get; set; }
    NumericEntry { get; set; }
    BooleanEntry { get; set; }
}

calling constructor:

public MyConstructor(IOptions<MyConfig> myConfigOpts, MyConfig myConfigInstance)
{
    string stringVal = myConfigOpts.Value.StringEntry;
    int numericVal = myConfigInstance.NumericEntry;
}
  1. In order to export ASP.Net Core controllers (.Net Core only), export your controller type. As long as it inherits from Microsoft.AspNetCore.Mvc.Controller or Microsoft.AspNetCore.Mvc.ControllerBase it'll be automatically wired into your host application.
using Microsoft.AspNetCore.Mvc;

[Export(typeof(CustomController))]
public class CustomController : Controller { }
  1. That's it for plugin development

========================================

.NET Core 3.0+ Host Instructions:

Example for ASP.Net Core:

  1. Use the LoadDinamically extension method that's added to IServiceCollection the to setup DI, optionally pass you IConfiguration instance if you want to setup configuration classes:
IConfiguration config = new ConfigurationBuilder()
        .AddJsonFile("appsettings.json")
        .Build();

Host.CreateDefaultBuilder(args)
        .ConfigureServices((hostContext, services) =>
        {
            // These shared types need to include any interfaces or types that the host and plugins share.
            var sharedTypes = new[]
            {
                typeof(IMyInterface),
                typeof(IMyOtherInterface)
            };

            services
                .LoadDinamically(config, sharedTypes);
        })
  1. If you wish to use Mvc and setup controllers developed on Plugins, use the LoadDinamically extension method that's added to IMvcBuilder the to setup DI, optionally pass you IConfiguration instance if you want to setup configuration classes:
// These shared types need to include any interfaces or types that the host and plugins share.
var sharedTypes = new[]
{
    typeof(IMyInterface),
    typeof(IMyOtherInterface)
};

services
    .AddMvc()
    .LoadDinamically(Configuration, sharedTypes);

Example for Non-host console app:

static void Main(string[] args) 
{
    IServiceCollection serviceCollection = new ServiceCollection();
    IConfiguration config = new ConfigurationBuilder()
        .AddJsonFile("appsettings.json")
        .Build();

    var sharedTypes = new[]
    {
        typeof(IMyInterface),
        typeof(IMyOtherInterface)
    };
    serviceCollection.LoadDinamically(config, sharedTypes);
    var provider = serviceCollection.BuildServiceProvider();
    
    IMyInterface instance = provider.GetService<IMyInterface>();
}

========================================

Loading Named Instances:

Sometimes you need to have different implementations of the same interface, for example when you have a Mock-Data-Access plugin vs your Database-Access plugin, in order to achieve it, add a contractName param to the Export attribute (view part 3 of Plugin Instructions above), and use the IServiceResolver interface exposed on the ANT.NETCore.PluginLoader.Abstractions NuGet package

private readonly IMyDataAccessService dataAccessService;

public MyController(IOptions<MyAppConfig> config, IServiceResolver<IMyDataAccessService> dataAccessServiceResolver)
{
    string dataAccessPluginName = config.Value.DataAcessPlugin;
    this.dataAccessService = dataAccessServiceResolver.GetService(dataAccessPluginName);
}

public List<object> GetFromDB() {
    return this.dataAccessService.ReadSomethingFromDB();
}

========================================

.NET Framework 4.6+ Host Instructions:

Example for console app:

IUnityContainer unityContainer = ServiceLoader.LoadDinamically();
IMyInterface instance = unityContainer.Resolve<IMyInterface>();

========================================

Plugins directory (.Net Core and .Net 4.6+ instructions):

By default, the Plugin Loader searches for plugins on the Plugins directory on the root of your application. Drop any plugin libraries there (the dll files should be under a folder named exactly as the main plugin's library). Example:

MyHostDirectory
----Plugins
--------my-first-plugin
------------my-first-plugin.dll
------------my_foo_dependency.dll
------------my_bar_dependency.dll
--------my-foo-plugin
------------my-foo-plugin.dll

If you wish to load plugins from a different directory, specify multiple directories or specify a dll file to be loaded, you might do so while calling the LoadDinamically method:

<<services or ServiceLoader>>.LoadDinamically(pluginPaths: new List<string>() { "Plugins", "ExternalPlugins", "C:\load-this-library.dll", "Plugins\my-foo-plugin", "Plugins\my-foo-plugin\my-foo-plugin.dll" });

appsettings.json file (.Net Core)

{
    "DependencyInjection": {
        "DependenciesPaths": [ "Plugins", "ExternalPlugins", "C:\load-this-library.dll", "Plugins\my-foo-plugin", "Plugins\my-foo-plugin\my-foo-plugin.dll" ]
    }
}

app.Config file (.Net 4.6+)

<configuration>
  <configSections>
    <section name="pluginConfiguration" type="ANT.PluginLoader.Config.PluginConfigurationSection, ANT.PluginLoader" requirePermission="false" />
  </configSections>
  <pluginConfiguration>
    <paths>
      <add path="Plugins" />
      <add path="C:\load-this-library.dll" />
    </paths>
  </pluginConfiguration>
</configuration>

ANT.NETCore.PluginLoader


The purpose of this package is to facilitate using IOC
on .NET Core 3.0+ and .NET Framework 4.6+ projects.

The .NET Core implentation uses DotNetCorePlugins by nate McMaster
The .NET Framework implementation uses Microsoft Unity

========================================

Plugin Instructions:

  1. Add a reference to System.ComponentModel.Composition or install the NuGet package depending on the framework you're targetting.
  2. Add the Export attribute to any classes that you wish to expose to the DI provider.
[Export(typeof(IMyInterface))]
public class MyInterfaceImplementation : IMyInterface { }
  1. Optionally, you might add a contractName param to the Export attribute, this will create named instances and allow you to recall different implementations of the same interface (more on that later).
[Export("Foo", typeof(IMyInterface))]
public class MyInterfaceImplementationFoo : IMyInterface { }

[Export("Bar", typeof(IMyInterface))]
public class MyInterfaceImplementationBar : IMyInterface { }
  1. Optionally add the PartCreationPolicy attribute to specify whether the class to be exported should be instantiated as a Singleton or Transient instance (Shared = Singleton, Nonshared = Transient).
[Export(typeof(IMyInterface))]
[PartCreationPolicy(CreationPolicy.Shared)]
public class MyInterfaceImplementation : IMyInterface { }
  1. If you wish to export an instance of a configuration section (only for .Net Core), use the contractName param of the Export attribute to declare it. Begin the name of the contract with the word Configuration. and then the name of the configuration section. All configuration sections can be instanced using IOptions<> or by their class type.

appSettings.json file:

{
  "MyConfigSectionName": {
    "StringEntry": "String",
    "NumericEntry": 42,
    "BooleanEntry": false
  }
}

class file:

[Export("Configuration.MyConfigSectionName")]
public class MyConfig
{
    StringEntry { get; set; }
    NumericEntry { get; set; }
    BooleanEntry { get; set; }
}

calling constructor:

public MyConstructor(IOptions<MyConfig> myConfigOpts, MyConfig myConfigInstance)
{
    string stringVal = myConfigOpts.Value.StringEntry;
    int numericVal = myConfigInstance.NumericEntry;
}
  1. In order to export ASP.Net Core controllers (.Net Core only), export your controller type. As long as it inherits from Microsoft.AspNetCore.Mvc.Controller or Microsoft.AspNetCore.Mvc.ControllerBase it'll be automatically wired into your host application.
using Microsoft.AspNetCore.Mvc;

[Export(typeof(CustomController))]
public class CustomController : Controller { }
  1. That's it for plugin development

========================================

.NET Core 3.0+ Host Instructions:

Example for ASP.Net Core:

  1. Use the LoadDinamically extension method that's added to IServiceCollection the to setup DI, optionally pass you IConfiguration instance if you want to setup configuration classes:
IConfiguration config = new ConfigurationBuilder()
        .AddJsonFile("appsettings.json")
        .Build();

Host.CreateDefaultBuilder(args)
        .ConfigureServices((hostContext, services) =>
        {
            // These shared types need to include any interfaces or types that the host and plugins share.
            var sharedTypes = new[]
            {
                typeof(IMyInterface),
                typeof(IMyOtherInterface)
            };

            services
                .LoadDinamically(config, sharedTypes);
        })
  1. If you wish to use Mvc and setup controllers developed on Plugins, use the LoadDinamically extension method that's added to IMvcBuilder the to setup DI, optionally pass you IConfiguration instance if you want to setup configuration classes:
// These shared types need to include any interfaces or types that the host and plugins share.
var sharedTypes = new[]
{
    typeof(IMyInterface),
    typeof(IMyOtherInterface)
};

services
    .AddMvc()
    .LoadDinamically(Configuration, sharedTypes);

Example for Non-host console app:

static void Main(string[] args) 
{
    IServiceCollection serviceCollection = new ServiceCollection();
    IConfiguration config = new ConfigurationBuilder()
        .AddJsonFile("appsettings.json")
        .Build();

    var sharedTypes = new[]
    {
        typeof(IMyInterface),
        typeof(IMyOtherInterface)
    };
    serviceCollection.LoadDinamically(config, sharedTypes);
    var provider = serviceCollection.BuildServiceProvider();
    
    IMyInterface instance = provider.GetService<IMyInterface>();
}

========================================

Loading Named Instances:

Sometimes you need to have different implementations of the same interface, for example when you have a Mock-Data-Access plugin vs your Database-Access plugin, in order to achieve it, add a contractName param to the Export attribute (view part 3 of Plugin Instructions above), and use the IServiceResolver interface exposed on the ANT.NETCore.PluginLoader.Abstractions NuGet package

private readonly IMyDataAccessService dataAccessService;

public MyController(IOptions<MyAppConfig> config, IServiceResolver<IMyDataAccessService> dataAccessServiceResolver)
{
    string dataAccessPluginName = config.Value.DataAcessPlugin;
    this.dataAccessService = dataAccessServiceResolver.GetService(dataAccessPluginName);
}

public List<object> GetFromDB() {
    return this.dataAccessService.ReadSomethingFromDB();
}

========================================

.NET Framework 4.6+ Host Instructions:

Example for console app:

IUnityContainer unityContainer = ServiceLoader.LoadDinamically();
IMyInterface instance = unityContainer.Resolve<IMyInterface>();

========================================

Plugins directory (.Net Core and .Net 4.6+ instructions):

By default, the Plugin Loader searches for plugins on the Plugins directory on the root of your application. Drop any plugin libraries there (the dll files should be under a folder named exactly as the main plugin's library). Example:

MyHostDirectory
----Plugins
--------my-first-plugin
------------my-first-plugin.dll
------------my_foo_dependency.dll
------------my_bar_dependency.dll
--------my-foo-plugin
------------my-foo-plugin.dll

If you wish to load plugins from a different directory, specify multiple directories or specify a dll file to be loaded, you might do so while calling the LoadDinamically method:

<<services or ServiceLoader>>.LoadDinamically(pluginPaths: new List<string>() { "Plugins", "ExternalPlugins", "C:\load-this-library.dll", "Plugins\my-foo-plugin", "Plugins\my-foo-plugin\my-foo-plugin.dll" });

appsettings.json file (.Net Core)

{
    "DependencyInjection": {
        "DependenciesPaths": [ "Plugins", "ExternalPlugins", "C:\load-this-library.dll", "Plugins\my-foo-plugin", "Plugins\my-foo-plugin\my-foo-plugin.dll" ]
    }
}

app.Config file (.Net 4.6+)

<configuration>
  <configSections>
    <section name="pluginConfiguration" type="ANT.PluginLoader.Config.PluginConfigurationSection, ANT.PluginLoader" requirePermission="false" />
  </configSections>
  <pluginConfiguration>
    <paths>
      <add path="Plugins" />
      <add path="C:\load-this-library.dll" />
    </paths>
  </pluginConfiguration>
</configuration>

Release Notes

1.0.0: first release
1.1.0: allow loading MVC controllers
1.2.0: LoadDinamically methods now return the caller instance
2.0.0: Added Readme and IOC support for .Net Framework 4.6+ using Unity
2.1.0: Improved support for multiple plugin path styles

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version History

Version Downloads Last updated
2.1.3 209 7/14/2020
2.1.2 185 7/14/2020
2.1.1 172 7/2/2020
2.1.0 183 6/29/2020
2.0.0 227 6/22/2020
1.2.1 203 6/19/2020