The beauty of doing this in combination with an IoC Container such as Simple Injector is that it is so easy to add common logic to all of the plug-ins. I recently wrote a bulk image converter utility that allowed plugging in new image converters.
This is the interface
public interface IImageConverter : IDisposable
{
string Name { get; }
string DefaultSourceFileExtension { get; }
string DefaultTargetFileExtension { get; }
string[] SourceFileExtensions { get; }
string[] TargetFileExtensions { get; }
void Convert(ImageDetail image);
}
Registration is done like this (note the error checking for plug-in dependencies that are not .NET assemblies)
private void RegisterImageConverters(Container container)
{
var pluginAssemblies = this.LoadAssemblies(this.settings.PluginDirectory);
var pluginTypes =
from dll in pluginAssemblies
from type in dll.GetExportedTypes()
where typeof(IImageConverter).IsAssignableFrom(type)
where !type.IsAbstract
where !type.IsGenericTypeDefinition
select type;
container.RegisterAll<IImageConverter>(pluginTypes);
}
private IEnumerable<Assembly> LoadAssemblies(string folder)
{
IEnumerable<string> dlls =
from file in new DirectoryInfo(folder).GetFiles()
where file.Extension == ".dll"
select file.FullName;
IList<Assembly> assemblies = new List<Assembly>();
foreach (string dll in dlls) {
try {
assemblies.Add(Assembly.LoadFile(dll));
}
catch { }
}
return assemblies;
}
Common logic required for all plug-in operations is handled by decorators
container.RegisterDecorator(
typeof(IImageConverter),
typeof(ImageConverterChecksumDecorator));
container.RegisterDecorator(
typeof(IImageConverter),
typeof(ImageConverterDeleteAndRecycleDecorator));
container.RegisterDecorator(
typeof(IImageConverter),
typeof(ImageConverterUpdateDatabaseDecorator));
container.RegisterDecorator(
typeof(IImageConverter),
typeof(ImageConverterLoggingDecorator));
This final class is more specific but I have added it as an example of how you can find the required plug-in without taking a direct dependency on the container
public sealed class PluginManager : IPluginManager
{
private readonly IEnumerable<IImageConverter> converters;
public PluginManager(IEnumerable<IImageConverter> converters) {
this.converters = converters;
}
public IList<IImageConverter> List() {
return this.converters.ToList();
}
public IList<IImageConverter> FindBySource(string extension) {
IEnumerable<IImageConverter> converters = this.converters
.Where(x =>
x.SourceFileExtensions.Contains(extension));
return converters.ToList();
}
public IList<IImageConverter> FindByTarget(string extension) {
IEnumerable<IImageConverter> converters = this.converters
.Where(x =>
x.TargetFileExtensions.Contains(extension));
return converters.ToList();
}
public IList<IImageConverter> Find(
string sourceFileExtension,
string targetFileExtension)
{
IEnumerable<IImageConverter> converter = this.converters
.Where(x =>
x.SourceFileExtensions.Contains(sourceFileExtension) &&
x.TargetFileExtensions.Contains(targetFileExtension));
return converter.ToList();
}
public IImageConverter Find(string name) {
IEnumerable<IImageConverter> converter = this.converters
.Where(x => x.Name == name);
return converter.SingleOrDefault();
}
}
You register IPluginManager
in the container and Simple Injector does the rest:
container.Register<IPluginManager, PluginManager>();
I hope this helps.