Advanced scenarios with the Simple Injector
The
Simple Injector is meant as an easy IoC implementation, which supports very little out of the box. Still, many complex scenarios are possible, often by writing some custom code, taking the code from this site, or by using the
SimpleInjector.Extensions assembly (which is part of the default download package and
available as NuGet package). The
SimpleInjector.Extensions.dll is needed for most scenarios listed below.
Note: After referencing the SimpleInjector.Extensions.dll, you will have to add the SimpleInjector.Extensions namespace to your code to be able to use the extension methods from that assembly.
This page discusses the following subjects:
Batch registration / Automatic registration
Batch registration or automatic registration is a way to register all types in a given source based on some sort of convention. This saves you from adding a new line to your configuration for each new type that you register, as in the following example:
container.Register<IUserRepository, SqlUserRepository>();
container.Register<ICustomerRepository, SqlCustomerRepository>();
container.Register<IOrderRepository, SqlOrderRepository>();
container.Register<IProductRepository, SqlProductRepository>();
// and the list goes on...
To prevent having to change the container configuration too often, we can use the non-generic registration overloads extension methods from the
SimpleInjector.Extensions.dll, combined with a simple LINQ query:
var repositoryAssembly = typeof(SqlUserRepository).Assembly;
var registrations =
from type in repositoryAssembly.GetExportedTypes()
where type.Namespace == "MyComp.MyProd.BL.SqlRepositories"
where type.GetInterfaces().Length > 0
select new
{
Service = type.GetInterfaces().First(),
Implementation = type
};
foreach (var reg in registrations)
{
container.Register(reg.Service, reg.Implementation);
}
Although many DI frameworks contain an advanced API for doing convention based registration, we found that doing this with custom LINQ queries is often easier to write, easier to understand, and even more flexible than using such an API.
Another interesting scenario is that of registering implementations of some generic interface. Say for instance that your application contains the following interface:
public interface IValidate<T>
{
ValidationResults Validate(T instance);
}
Your application might contain many implementations of this interface, for instance for validating customers, employees, orders, products, etc. You would normally end up with a registration as follows:
container.Register<IValidate<Customer>, CustomerValidator>();
container.Register<IValidate<Employee>, EmployeeValidator>();
container.Register<IValidate<Order>, OrderValidator>();
container.Register<IValidate<Product>, ProductValidator>();
// and the list goes on...
Using the extension methods for batch registration of open generic types from the
SimpleInjector.Extesions.dll assembly however, you can write it down to a single line:
container.RegisterManyForOpenGeneric(typeof(IValidate<>),
AppDomain.CurrentDomain.GetAssemblies());
This call searches for all public types in all assemblies loaded in the current
AppDomain that implement the
IValidate<T> interface and registers each type by their specific interface. It even works for types that implement multiple closed versions of the given interface.
Note: There are RegisterManyForOpenGeneric overloads available that take a list of System.Type instances, instead a list of Assembly instances.
This however is just one example of what you can do using batch registration. A more advanced scenario is the registration of multiple implementations of the same closed generic type to a single interface. Because there are many possible variations of this scenario, the
SimpleInjector.Extensions.dll does not contain any methods to do this. It does contain however, an overload of the
RegisterManyForOpenGeneric that allows you to supply a callback delegate that lets you do the registration yourself. Take for instance the scenario where you have a
CustomerValidator and a
GoldCustomerValidator type, that each implement
IValidate<Customer>, and you want to register both of them. The previous registration would throw an exception telling you that you have multiple types implementing the same closed generic type. The following registration however, does enable this scenario:
container.RegisterManyForOpenGeneric(typeof(IValidate<>),
AccessibilityOption.PublicTypesOnly,
(serviceType, implTypes) => container.RegisterAll(serviceType, implTypes),
AppDomain.CurrentDomain.GetAssemblies()
);
The previous code snippet registers all types from all assembly loaded in the current
AppDomain that implement
IValidate<T> and it registers them as a collection of instances. In other words, with the given example,
IValidate<T> instances can be retrieved by calling
container.GetAllInstances<IValidate<T>>() or by using an
IEnumerable<IValidate<T>> argument in a constructor. However, using an
IEnumerable<IValidate<T>> dependency in your constructor or calling into the container directly, are generally not recommended approaches. Depending on a collection of types complicates your application design, and can often be simplified with the right configuration of your container. Your constructors should simply depend on a single
IValidator<T>. A better solution would therefore be to create and inject a
CompositeValidator<T> in that case:
public class CompositeValidator<T> : IValidate<T>
{
private readonly IEnumerable<IValidate<T>> validators;
public CompositeValidator(IEnumerable<IValidate<T>> validators)
{
this.validators = validators;
}
public ValidationResults Validate(T instance)
{
var allResults = ValidationResults.Valid;
foreach (var validator in this.validators)
{
var results = validator.Validate(instance);
allResults = ValidationResults.Join(allResults, results);
}
return allResults;
}
}
This
CompositeValidator<T> can be registered as follows:
container.RegisterOpenGeneric(typeof(IValidate<>),
typeof(CompositeValidator<>));
This maps the open generic IValidate<T>
interface to the open generic CompositeValidator<T>
implementation. Because the CompositeValidator<T>
contains an IEnumerable<IValidator<T>>
dependency, the registered types will be injected. This allows you to let the rest of the application simply depend on the IValidate<T>
, while registering a collection of IValidate<T>_ implementations under the covers.
The next section will explain mapping of open generic types, as the one we just seen.
Registration of open generic types
When working with generic interfaces, we will often see many implementations of that interface being registered:
container.Register<IValidate<Customer>, CustomerValidator>();
container.Register<IValidate<Employee>, EmployeeValidator>();
container.Register<IValidate<Order>, OrderValidator>();
container.Register<IValidate<Product>, ProductValidator>();
// and the list goes on...
As the previous section explained, using the
SimpleInjector.Extensions.dll, this can be rewritten to the following one-liner:
container.RegisterManyForOpenGeneric(typeof(IValidate<>),
typeof(IValidate<>).Assembly);
Sometimes you'll find that many implementations of the given generic interface are no-ops. The
IValidate<T> is a good example, because it is very likely that not all entities will need validation. Still we would like to prevent from having to write an empty validation class per entity or having to write special application logic to check if an entity has validation. In this situation, we will probably want to use the registration as described in the previous section, and fallback to returning the default implementation when no such registration exists for a given type. Such default implementation could look like this, for instance:
// Implementation of the Null Object pattern.
class NullValidator<T> : IValidate<T>
{
public ValidationResults Validate(T instance)
{
return ValidationResults.Valid;
}
}
We can use the
NullValidator<T> for all entities that don't need validation:
container.Register<IValidate<OrderLine>, NullValidator<OrderLine>>();
container.Register<IValidate<Address>, NullValidator<Address>>();
container.Register<IValidate<UploadImage>, NullValidator<UploadImage>>();
container.Register<IValidate<Mothership>, NullValidator<Mothership>>();
// and the list goes on...
This is of course not very practical. Falling back to such a default implementation is a typical scenario for
unregistered type resolution. The
Simple Injector contains an event that you can hook to that allows to fallback to a default implementation. On top of this event, the
RegisterOpenGeneric extension method for registration of open generic types is available in the
SimpleInjector.Extensions.dll assembly. The given situation would be implemented as follows:
container.RegisterOpenGeneric(typeof(IValidate<>), typeof(NullValidator<>));
The result of this registration is that for instance an
NullValidator<Department> instance is returned when an
IValidate<Department> is requested.
Note: Because the use of unregistered type resolution, a NullValidator<T> will only get returned for types that are not registered, therefore allowing the default implementation to be overridden for a specific implementation. The RegisterManyForOpenGeneric method, for instance, doesn’t use unregistered type resolution, but simply registers all concrete types it finds in the given assemblies. Those types will therefore always be returned, giving a very convenient and easy to grasp mix.
Unregistered type resolution
Unregistered type resolution is the ability to get notified by the container when a type is requested that is currently unregistered in the container. This gives the user the change of registering that type. The
Simple Injector supports this scenario with the
ResolveUnregisteredType event. Unregistered type resolution enables many advanced scenarios. The
SimpleInjector.Extensions.dll assembly for instance, uses this event for implementing the
registration of open generic types. Other examples of possible scenarios that can be built on top of this event are
resolving array and lists,
lifetime scoping, and
covariance and contravariance. Those scenarios are all described here in the advanced scenarios page.
For more information about how to use this event, please look at the
ResolveUnregisteredType event documentation in the
reference library.
Context based injection Cannot resolve image macro, invalid image name or id.
Context based injection is the ability to inject a dependency based on the context it lives in. This context is often supplied by the container. Some DI frameworks contain a feature that allows this, while others don’t. The
Simple Injector does
not does not contain such a feature out of the box, but this ability can easily be added by using the
context based injection extension method (this extension is .NET 4.0 only).
Note: In many cases context based injection is not the best solution, and the design should be reevaluated. In some scenarios it however does make a lot of sense.
The most common scenario is to base the type of the injected dependency on the type of the consumer. Take for instance the following
ILogger interface with a generic
Logger<T> class that needs to be injected into several consumers.
public interface ILogger
{
void Log(string message);
}
public class Logger<T> : ILogger
{
public void Log(string message) { }
}
public class Consumer1
{
public Consumer1(ILogger logger) { }
}
public class Consumer2
{
public Consumer2(ILogger logger) { }
}
In this case we want to inject a
Logger<Consumer1> into
Consumer1 and a
Logger<Consumer2> into
Consumer2. By using the previous
extension method, we can accomplish this as follows:
container.RegisterWithContext<ILogger>(dependencyContext =>
{
var type = typeof(Logger<>).MakeGenericType(
dependencyContext.ImplementationType);
return (ILogger)container.GetInstance(type);
});
In the previous code snippet we registered a
Func<DependencyContext, ILogger> delegate, that will get called each time a
ILogger dependency gets resolved. The
DependencyContext instance that gets supplied to that instance, contains the
ServiceType and
ImplementationType into which the
ILogger is getting injected.
Note that although building a generic type using MakeGenericType is relatively slow, the call to the Func<DependencyContext, TService> delegate itself is almost as cheap as calling a Func<TService> delegate. If performance of the MakeGenericType gets a problem, you can always cache the generated types, cache IInstanceProducer instances, or cache ILogger instances (note that caching the ILogger instances will make them singletons).
Note: You can find a more elaborate example of the use of context dependent injection in this article.
Generic Decorators Cannot resolve image macro, invalid image name or id.
The
SOLID principles are an important guideline when it comes to writing maintainable software. The 'O' of 'SOLID' stands for the
Open/closed Principle which states that classes should be open for extension, but closed for modification. Designing systems around the Open/closed principle means that new behavior can be plugged into the system, without the need to change any existing parts, making the change of breaking existing code much smaller.
One of the ways to add new functionality (such as cross-cutting concerns) to types is by the use of the
decorator pattern. The decorator pattern can be used to extend (decorate) the functionality of a certain object at run-time. Especially when using generic interfaces, the concept of decorators gets really powerful. Take for instance the examples given in the
Registration of open generic types section of this page or the use of an generic
ICommandHandler<TCommand> interface.
When we want to validate all commands that get executed by an
ICommandHandler<TCommand> implementation, we want to do this, without having to alter each and every implementation. We can do this using a decorator:
public class ValidationCommandHandlerDecorator<TCommand>
: ICommandHandler<TCommand>
{
private readonly IValidator validator;
private readonly ICommandHandler<TCommand> handler;
public ValidationCommandHandlerDecorator<(
IValidator validator,
ICommandHandler<TCommand> handler)
{
this.validator = validator;
this.handler = handler;
}
void ICommandHandler<TCommand>.Handle(TCommand command)
{
this.validator.ValidateObject(command);
this.handler.Handle(command);
}
}
The
ValidationCommandHandlerDecorator<TCommand> class is an implementation of the
ICommandHandler<TCommand> interface, but it also wraps / decorates an
ICommandHandler<TCommand> instance. Instead of injecting an implementation directly into any consumer, we can (let the Simple Injector) inject a validator decorator that calls the real implementation.
The
ValidationCommandHandlerDecorator<TCommand> depends on an
IValidator interface. An implementation that used Microsoft Data Annotations might look like this:
using System.ComponentModel.DataAnnotations;
public class DataAnnotationsValidator : IValidator
{
private readonly IServiceLocator container;
public DataAnnotationsValidator(Container container)
{
this.container = container;
}
void IValidator.ValidateObject(object instance)
{
var context = new ValidationContext(instance,
this.container, null);
Validator.ValidateObject(instance, context);
}
}
The implementations of the
ICommandHandler<T> interface can be registered using the
RegisterManyForOpenGeneric extension method of the
SimpleInjector.Extensions.dll:
container.RegisterManyForOpenGeneric(
typeof(ICommandHandler<>),
AppDomain.CurrentDomain.GetAssemblies());
By using the following extension method from the
SimpleInjector.Extensions.dll, you can wrap the
ValidationCommandHandlerDecorator<TCommand> around each and every
ICommandHandler<TCommand> implementation:
container.RegisterDecorator(typeof(ICommandHandler<>),
typeof(ValidationCommandHandlerDecorator<>));
Multiple decorators can be wrapped by calling the
RegisterDecorator method multiple times, as the following registration shows:
container.RegisterManyForOpenGeneric(
typeof(ICommandHandler<>),
AppDomain.CurrentDomain.GetAssemblies());
container.RegisterDecorator(typeof(ICommandHandler<>),
typeof(TransactionCommandHandlerDecorator<>));
container.RegisterDecorator(typeof(ICommandHandler<>),
typeof(DeadlockRetryCommandHandlerDecorator<>));
container.RegisterDecorator(typeof(ICommandHandler<>),
typeof(ValidationCommandHandlerDecorator<>));
The decorators are applied in the order in which they are registered, which means that the first decorator (
TransactionCommandHandlerDecorator in this case) wraps the real instance, the second decorator (
DeadlockRetryCommandHandlerDecorator in this case) wraps the first decorator, and so on.
There's an overload of the
RegisterDecorator available that allows you to supply a predicate to determine whether that decorator should be applied to a specific service type. Using a given context you can determine whether the decorator should be applied. Here is an example:
container.RegisterDecorator(typeof(ICommandHandler<>),
typeof(AccessValidationCommandHandlerDecorator<>),
c => !c.ImplementationType.Namespace.EndsWith("Admins"));
The given context contains several properties that allows you to analyze whether a decorator should be applied to a given service type, such as the current closed generic service type (using the
ServiceType property) and the concrete type that will be created (using the
ImplementationType property). The predicate will (under normal circumstances) be called only once per generic type, so there is no performance implications for using it.
Interception Cannot resolve image macro, invalid image name or id.
Interception is the ability to intercept a call from a consumer to a service, and add or change behavior. The
decorator pattern describes a form of interception, but when it comes to applying cross-cutting concerns, you will end up writing decorators for many service interfaces, but with the exact same code. If this is happening, it is time to explore the possibilities of interception.
Using the
Interception extensions code snippets, you can add the ability to do interception with the
Simple Injector. Using the given code, you can for instance define a
MonitoringInterceptor that allows logging the execution time of the called service method:
private class MonitoringInterceptor : IInterceptor
{
private readonly ILogger logger;
// Using constructor injection on the interceptor
public MonitoringInterceptor(ILogger logger)
{
this.logger = logger;
}
public void Intercept(IInvocation invocation)
{
var watch = Stopwatch.StartNew();
// Calls the decorated instance.
invocation.Proceed();
var decoratedType =
invocation.InvocationTarget.GetType();
this.logger.Log(string.Format(
"{0} executed in {1} ms.",
decoratedType.Name,
watch.ElapsedMiliseconds));
}
}
This interceptor can be registered to be wrapped around a concrete implementation. Using the given extension methods, this can be done as follows:
container.InterceptWith<MonitoringInterceptor>(type => type == typeof(IUserRepository));
This registration ensures that every time an IUserRepository interface is requested, an interception proxy is returned that wraps that instance and uses the
MonitoringInterceptor to extend the behavior.
The current example doesn't add much compared to simply using a decorator. When having many interface service types that need to be decorated with the same behavior however, it gets different:
container.InterceptWith<MonitoringInterceptor>(t => t.Name.EndsWith("Repository"));
Note: The Interception extensions code snippets use .NET's System.Runtime.Remoting.Proxies.RealProxy class to generate interception proxies. The RealProxy only allows to proxy interfaces.
Note: the interfaces in the given Interception extensions code snippets are a simplified version of the Castle Project interception facility. If you need to create lots different interceptors, you might benefit from using the interception abilities of the Castle Project. Also please note that the given snippets use dynamic proxies to do the interception, while Castle uses lightweight code generation (LCG). LCG allows much better performance than the use of dynamic proxies.
Note: Don't use interception for intercepting types that all implement the same generic interface, such as ICommandHandler<T> or IValidator<T>. Try using decorator classes instead, as shown in the Decorators section on this page.
Implicit property injection
Some DI containers (such as Castle Windsor) implicitly inject public writable properties by default for any instance you resolve. They do this by mapping those properties to configured types. When no such registration exists, the property will be skipped.
Simple Injector does not do this, and for good reason. First of all, not all DI frameworks support this feature, and
Simple Injector tries to supply you with a feature set that allows you to migrate without much pain to another DI container. Second, we think that
implicit property injection is simply too uuhh... implicit :-). We feel that this implicitness complicates the use of the container, which is bad for any container which name starts with the word 'Simple'. Especially because the container needs to skip properties that can't be mapped. We strongly feel that explicit property injection is a much better way to go.
Simple Injector has great support for
explicit property injection through the use of the
RegisterInitializer<T> method. This works for types that derive or inherit from T as well. This gives you compile time support and allows the application to fail fast when a registration is missing. So if you can, try to avoid needing this and think about designing your application in such way that you don't need this.
There are scenarios where implicit property injection can be useful. Note that those scenarios will be pretty rare. Scenarios where property injection might seem useful are integration scenarios. We still think that implicit property injection is even here not really good, but it does allow us to show how to use implicit property injection, so let's go with this example for a while.
Take for instance an ASP.NET Web Forms application where you want to inject dependencies into
Page classes and User Controls. While ASP.NET MVC was designed especially to allow dependency injection, Web Forms was not. Since constructor injection isn't a good option in that scenario, allowing property injection is useful. For this scenario, the container class contains a
InjectProperties(object) method. In your ASP.NET Web Forms application, you could for instance create a base class that looks like this:
public abstract class BasePage : Page
{
public BasePage()
{
// This assumes the container instance to be stored in a
// public static 'Container' property in the Global.asax class.
Global.Container.InjectProperties(this);
}
}
When all
Page classes inherit from this base page, all public settable properties will be injected automatically. A derived class could look like this:
public class _Default : BasePage
{
public IUserRepository UserRepository { get; set; }
public ILogger Logger { get; set; }
}
When there is a registration for either
IUserRepository or
ILogger in the container, those dependencies will be injected (otherwise they will be skipped).
Although implicit property injection seems useful in this scenario, there is a problem with this. Implicit property injection skips properties for which the container can't find a proper dependency. This can hide configuration errors, that will only show up when the Page is tested. When a dependency is only used in certain situations, such as a button click, the error will only show up when that button is clicked, making it even more likely that this error slips testing.
So rather than using implicit property injection, call the container explicitly in the constructor:
public class _Default : Page
{
private IUserRepository userRepository;
private ILogger logger;
public _Default()
{
this.userRepository = Global.Container.GetInstance<IUserRepository>();
this.logger = Global.Container.GetInstance<ILogger>();
}
}
Using this approach configuration errors will pop up as soon as the page is opened. But we can take is even one step further and test each page during application startup:
protected void Application_Start(object sender, EventArgs e)
{
// Create the container as usual.
var container = new Container();
// Register your types, for instance:
container.Register<IUserRepository, SqlUserRepository>();
container.RegisterSingle<ILogger, SqlLogger>();
// Validate the container
container.Validate();
var pagesToValidate =
from assembly in AppDomain.CurrentDomain.GetAssemblies()
from type in assembly.GetExportedTypes()
where typeof(Page).IsAssignableFrom(type)
select type;
// Test each page if it can be created.
pagesToValidate.ToList().ForEach(type => Activator.CreateInstance(type));
}
Note that you can implicitly inject properties into all types, if you really want. The way to do this is by registering a delegate using
RegisterInitializer that calls into the
InjectProperties method. When you want properties to be implicitly injected into the
SqlUserRepository for instance, this code looks like this:
container.RegisterInitializer<SqlUserRepository>(
repositoryInstance => container.InjectProperties(repositoryInstance));
And if you really want, you can configure the container to implicitly inject properties in all types created by the container, by simply registering a delegate for the
object base class:
container.RegisterInitializer<object>(service => container.InjectProperties(service));
This basically tells the container to inject properties in every object it creates.
Instead of calling
RegisterInitializer directly, you can also use this
extension methods. It uses the
RegisterInitializer under the covers.
Covariance and Contravariance
Since version 4.0 of the .NET framework, the type system allows
Covariance and Contravariance in Generics (especially interfaces and delegates). This allows for instance, to use a
IEnumerable<string> as an
IEnumerable<object> (covariance), or to use an
Action<object> as an
Action<string> (contravariance).
In some circumstances, the application design can benefit from the use of covariance and contravariance (or variance for short) and it would be beneficial when the IoC container returns services that are 'compatible' to the requested service, even although the requested service is not registered. To stick with the previous example, the container could return an
IEnumerable<string> even when an
IEnumerable<object> is requested.
By default, the Simple Injector does not return variant implementations of given services, but the Simple Injector can be extended to behave this way. The actual way to write this extension depends on the requirements of the application.
Take a look at the following application design around the
IEventHandler<in TEvent> interface:
public interface IEventHandler<in TEvent>
{
void Handle(TEvent e);
}
public class CustomerMovedEvent
{
public int CustomerId { get; set; }
public Address NewAddress { get; set; }
}
public class CustomerMovedAbroadEvent : CustomerMovedEvent
{
public Country Country { get; set; }
}
public class CustomerMovedEventHandler : IEventHandler<CustomerMovedEvent>
{
public void Handle(CustomerMovedEvent e) { ... }
}
The design contains two event classes
CustomerMovedEvent and
CustomerMovedAbroadEvent (where
CustomerMovedAbroadEvent inherits from
CustomerMovedEvent) one concrete event handler
CustomerMovedEventHandler and a generic interface for event handlers.
We can configure the container in such way that not only a request for
IEventHandler<CustomerMovedEvent> results in a
CustomerMovedEventHandler, but also a request for
IEventHandler<CustomerMovedAbroadEvent> results in that same
CustomerMovedEventHandler (because
CustomerMovedEventHandler also accepts
CustomerMovedAbroadEvents).
There are several ways to achieve this, but one of the ways is the following:
container.Register<CustomerMovedEventHandler>();
container.RegisterSingleOpenGeneric(typeof(IEventHandler<>),
typeof(ContravarianceEventHandler<>));
This registration depends on the custom
ContravarianceEventHandler<TEvent> that should be placed close to the registration itself:
public sealed class ContravarianceEventHandler<TEvent>
: IEventHandler<TEvent>
{
private Func<IEventHandler<TEvent>> handlerFactory;
public ContravarianceEventHandler(Container container)
{
var handlerType = typeof(IEventHandler<TEvent>);
var registration = (
from r in container.GetCurrentRegistrations()
let assignableInterfaces = (
from iface in r.ServiceType.GetInterfaces()
where handlerType.IsAssignableFrom(iface)
select iface)
where assignableInterfaces.Any()
select r)
.Single();
this.handlerFactory =
() => (IEventHandler<TEvent>)r.GetInstance();
}
void IEventHandler<TEvent>.Handle(TEvent e)
{
this.handlerFactory().Handle(e);
}
}
The registration ensures that every time an
IEventHandler<TEvent> is requested, a
ContravarianceEventHandler<TEvent> is returned. The
ContravarianceEventHandler<TEvent> will on creation query the container for a single service type that implements
IEventHandler<TEvent>. Because the
CustomerMovedEventHandler is not registered by its interface, but by itself, the
ContravarianceEventHandler<TEvent> will find that type.
This is just one example and one way of adding variance support. For a more elaborate discussion on this subject, please read the following article:
Adding Covariance and Contravariance to the Simple Injector.
Registering plugins dynamically
Applications with a plugin architecture often allow special plugin assemblies to be dropped in a special folder and to be picked up by the application, without the need of a recompile. Although Simple Injector does not support this out of the box, registering plugins from dynamically loaded assemblies can be implemented in a few lines of code. Here is an example:
string pluginDirectory =
Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Plugins");
var pluginAssemblies =
from file in new DirectoryInfo(pluginDirectory).GetFiles()
where file.Extension == ".dll"
select Assembly.LoadFile(file.FullName);
var pluginTypes =
from dll in pluginAssemblies
from type in dll.GetExportedTypes()
where typeof(IPlugin).IsAssignableFrom(type)
where !type.IsAbstract
where !type.IsGenericTypeDefinition
select type;
container.RegisterAll<IPlugin>(pluginTypes);
The given example makes use of an
IPlugin interface that is known to the application, and probably located in a shared assembly. The dynamically loaded plugin .dll files can contain multiple classes that implement
IPlugin, and all publicly exposed concrete types that implements
IPlugin will be registered using the
RegisterAll method and can get resolved using the default auto-wiring behavior of the container, meaning that the plugin must have a single public constructor and all constructor arguments must be resolvable by the container. The plugins can get resolved using
container.GetAllInstances<IPlugin>() or by adding an
IEnumerable<IPlugin> argument to a constructor.
Resolving instances by key
The information you are looking for has been moved. Please see the
Resolve Instances By Key section of the
How to documentation page, for the information you are looking for.
Resolving array and lists
The information you are looking for has been moved. Please see the
Resolve Arrays and Lists section of the
How to page, for the information you are looking for.
Lifetime scoping
The information you are looking for has been moved. Please see the
Per Lifetime Scope section of the
Object Lifestyle Management documentation page for more information.