Note: this documentation is specific for Simple Injector version 2.0 and up. Look here for 1.x specific documentation.
Advanced scenarios with the Simple Injector
Although its name might not indicate it,
Simple Injector is capable of handling many complex scenarios. Sometimes by writing some custom code, taking code from this wiki, or by using the extension points from the
SimpleInjector.Extensions namespace of the core library.
Note: After including the SimpleInjector.dll to your project, you will have to add the SimpleInjector.Extensions namespace to your code to be able to use most features that are presented in this wiki page.
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, 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().Any()
select new
{
Service = type.GetInterfaces().Single(),
Implementation = type
};
foreach (var reg in registrations)
{
container.Register(reg.Service, reg.Implementation, Lifestyle.Transient);
}
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 namespace however, you can write it down to a single line:
container.RegisterManyForOpenGeneric(typeof(IValidate<>),
typeof(IValidate<>).Assembly);
This call searches in the supplied assembly for all public types that implement the
IValidate<T> interface and it registers each type by their specific interface (closed generic) 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,
Simple Injector does not contain any methods to do this. It does contain however, multiple overloads 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),
typeof(IValidate<>).Assembly
);
The previous code snippet registers all types from the given assembly 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.RegisterSingleOpenGeneric(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 into its constructor. 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've 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, 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 this
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 available. The given situation would be implemented as follows:
container.RegisterSingleOpenGeneric(
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 framework itself 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 and
covariance and contravariance. Those scenarios are 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
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 code snippet.
Note: In many cases context based injection is not the best solution, and the design should be reevaluated. In some narrow cases however it can make 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: Although building a generic type using MakeGenericType is relatively slow, the call to the Func<DependencyContext, TService> delegate itself is about as cheap as calling a Func<TService> delegate. If performance of the MakeGenericType gets a problem, you can always cache the generated types, cache InstanceProducer instances, or cache ILogger instances (note that caching the ILogger instances will make them singletons).
Note: Even though the use of a generic Logger<T> is a common design (with log4net as the grand godfather of this design), doesn't always make it a good design. The need for having the logger contain information about its parent type, might indicate design problems. If you're doing this, please take a look at this Stackoverflow answer. It talks about logging in conjunction with the SOLID design principles.
Decorators
The
SOLID principles give us important guidance when it comes to writing maintainable software. The 'O' of the 'SOLID' acronym 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 for instance the use of an generic
ICommandHandler<TCommand> interface.
Tip: This article describes an architecture based on the use of the ICommandHandler<TCommand> interface.
Take the plausible scenario where we want to validate all commands that get executed by an
ICommandHandler<TCommand> implementation. The Open/Closed principle states that we want to do this, without having to alter each and every implementation. We can do this using a (single) 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)
{
// validate the supplied command (throws when invalid).
this.validator.ValidateObject(command);
// forward the (valid) command to the real
// command handler.
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 the real implementation directly into a consumer, we can (let Simple Injector) inject a validator decorator that wraps 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 IServiceProvider container;
public DataAnnotationsValidator(Container container)
{
this.container = container;
}
void IValidator.ValidateObject(object instance)
{
var context = new ValidationContext(instance,
this.container, null);
// Throws an exception when instance is invalid.
Validator.ValidateObject(instance, context);
}
}
The implementations of the
ICommandHandler<T> interface can be registered using the
RegisterManyForOpenGeneric extension method:
container.RegisterManyForOpenGeneric(
typeof(ICommandHandler<>),
typeof(ICommandHandler<>).Assembly);
By using the following extension method from the
SimpleInjector.Extensions namespace, 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<>),
typeof(ICommandHandler<>).Assembly);
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<T> in this case) wraps the real instance, the second decorator (
DeadlockRetryCommandHandlerDecorator<T> 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<>),
context => !context.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 implication for using it.
Tip: This extension method allows registering decorators that can be applied based on runtime conditions (such as the role of the current user).
Decorators with Func<T> factories
In certain scenarios, it is needed to postpone building part of the object graph. For instance when a service needs to control the lifetime of a dependency, need multiple instances, when instances need to be
executed on a different thread, or when instances need to be created in a certain
scope or (security) context.
When building a 'normal' object graph with dependencies, you can easily delay building a part of the graph by letting a service depend on a factory. This allows building that part of the object graph to be postponed until the time the type starts using the factory. When working with decorators however, injecting a factory to postpone the creation of the decorated instance will not work. Take for instance a
AsyncCommandHandlerDecorator<T> that allows executing a command handler on a different thread. We could let the
AsyncCommandHandlerDecorator<T> depend on a
CommandHandlerFactory<T>, and let this factory call back into the container to retrieve a new
ICommandHandler<T>. Unfortunately this would fail, since requesting an
ICommandHandler<T> would again wrap this instance with a new
AsyncCommandHandlerDecorator<T>, and we'd end up causing a stack overflow.
Since this is a scenario that is really hard to solve without framework support,
Simple Injector allows injecting a
Func<T> delegate into registered decorators. This delegate functions as a factory for the creation of the decorated instance. Taking the
AsyncCommandHandlerDecorator<T> as example, it could be implemented as follows:
public class AsyncCommandHandlerDecorator<T>
: ICommandHandler<T>
{
private readonly Func<ICommandHandler<T>> factory;
public AsyncCommandHandlerDecorator(
Func<ICommandHandler<T>> factory)
{
this.factory = factory;
}
public void Handle(T command)
{
// Execute on different thread.
ThreadPool.QueueUserWorkItem(_ =>
{
// Create new handler in this thread.
var handler = this.factory();
handler.Handle(command)
});
}
}
This special decorator can be registered just as any other decorator:
container.RegisterDecorator(
typeof(ICommandHandler<>),
typeof(AsyncCommandHandlerDecorator<>),
c => c.ImplementationType.Name.StartsWith("Async"));
However, since the
AsyncCommandHandlerDecorator<T> solely has singleton dependencies (the
Func<T> is a singleton), and creates a new decorated instance each time it’s called, we can even register it as a singleton itself:
container.RegisterSingleDecorator(
typeof(ICommandHandler<>),
typeof(AsyncCommandHandlerDecorator<>),
c => c.ImplementationType.Name.StartsWith("Async"));
When mixing this with other (synchronous) decorators, you'll get an extremely powerful and pluggable system:
container.RegisterManyForOpenGeneric(
typeof(ICommandHandler<>),
typeof(ICommandHandler<>).Assembly);
container.RegisterDecorator(
typeof(ICommandHandler<>),
typeof(TransactionCommandHandlerDecorator<>));
container.RegisterDecorator(
typeof(ICommandHandler<>),
typeof(DeadlockRetryCommandHandlerDecorator<>));
container.RegisterSingleDecorator(
typeof(ICommandHandler<>),
typeof(AsyncCommandHandlerDecorator<>),
c => c.ImplementationType.Name.StartsWith("Async"));
container.RegisterDecorator(
typeof(ICommandHandler<>),
typeof(ValidationCommandHandlerDecorator<>));
This configuration has an interesting mix of decorator registrations. The registration of the
AsyncCommandHandlerDecorator<T> allows (some of) the command handlers to be executed on the background (while others -who's name does not start with 'Async'- still run synchronously), but before execution, all commands are validated synchronously (to allow communicating validation errors to the caller). And all handlers (even the asynchronous ones) are executed in a transaction and the operation is retried when the database rolled back because of a deadlock).
Decorated collections
When registering a decorator, Simple Injector will automatically decorate any collection with elements of that service type:
container.RegisterAll<IEventHandler<CustomerMovedEvent>>(
typeof(CustomerMovedEventHandler),
typeof(NotifyStaffWhenCustomerMovedEventHandler));
container.RegisterDecorator(
typeof(IEventHandler<>),
typeof(ValidationEventHandlerDecorator<>),
c => SomeCondition);
The previous registration registers a collection of
IEventHandler<CustomerMovedEvent> services. Those services are decorated with a
ValidationEventHandlerDecorator<TEvent> when the supplied predicate holds.
For collections of elements that are created by the container (container controlled), the predicate is checked for each element in the collection. For collections of uncontrolled elements (a list of items that is not created by the container), the predicate is checked once for the whole collection. This means that controlled collections can be partially decorated. Taking the previous example for instance, you could let the
CustomerMovedEventHandler be decorated, while leaving the
NotifyStaffWhenCustomerMovedEventHandler undecorated (determined by the supplied predicate).
When a collection is uncontrolled, it means that the lifetime of its elements are unknown to the container. The following registration is an example of an uncontrolled collection:
IEnumerable<IEventHandler<CustomerMovedEvent>> handlers =
new IEventHandler<CustomerMovedEvent>[]
{
new CustomerMovedEventHandler(),
new NotifyStaffWhenCustomerMovedEventHandler(),
};
container.RegisterAll<IEventHandler<CustomerMovedEvent>>(handlers);
Although this registration contains a list of singletons, the container has no way of knowing this. The collection could easily have been a dynamic (an ever changing) collection. In this case, the container calls the registered predicate once (and supplies the predicate with the
IEventHandler<CusotmerMovedEvent> type) and if the predicate returns true, each element in the collection is decorated with a decorator instance.
Warning: In general you should prevent registering uncontrolled collections. The container knows nothing about them, and can't help you in doing diagnostics. Since the lifetime of those items is unknown, the container will be unable to wrap a decorator with a lifestyle other than transient. Best practice is to register container-controlled collections which is done by using one of the RegisterAll overloads that take a collection of System.Type instances.
Interception
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 might 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.
Property injection
Simple Injector does not inject any properties into types that get resolved by the container. In general there are two ways of doing property injection, and both are not supported by default for reasons explained below.
Implicit property injectionSome 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, or when the property doesn’t have a public setter, the property will be skipped. Simple Injector does not do this, and for good reason. We think that
implicit property injection is simply too uuhh... implicit :-). Silently skipping properties that can't be mapped can lead to a DI configuration that can't be verified easily and can therefore result in an application that fails at runtime instead of failing when the container is verified.
Explicit property injectionWe strongly feel that explicit property injection is a much better way to go. With explicit property injection the container is forced to inject a property and the process will fail immediately when a property can't be mapped or injected. Some containers (such as Unity and Ninject) allow explicit property injection by default by allowing properties to be decorated with attributes. Problem with this is that this forces the application to take a dependency on the framework, which is something that should be prevented.
Because Simple Injector does not encourage its users to take a dependency on the container (except for the startup path of course), Simple Injector does not contain any attributes that allow explicit property injection and it can therefore not explicitly inject properties out-of-the-box.
Besides this, the use of property injection should be very exceptional and in general constructor injection should always be used. If a constructor gets too many parameters (constructor over-injection anti-pattern), it is an indication of a violation of the
Single Responsibility Principle (SRP). SRP violations often lead to maintainability issues. So instead of patching constructor over-injection with property injection, the root cause should be analyzed and the type should be refactored, probably with
Facade Services. Another common reason to use properties is because those dependencies are optional. Instead of using optional property dependencies, best practice is to inject empty implementations (a.k.a.
Null Object pattern) into the constructor.
Enabling property injectionSimple Injector contains two ways to enable property injection. First of all the
RegisterInitializer<T> method can be used to inject properties (especially configuration values) on a per-type basis. Take for instance the following code snippet:
container.RegisterInitializer<HandlerBase>(handlerToInitialize =>
{
handlerToInitialize.ExecuteAsynchronously = true;
});
In the previous example a
Action<T> delegate is registered that will be called every time the container creates a type that inherits from
HandlerBase. In this case, the handler will set a configuration value on that class.
Note: although this method can also be used injecting services, please note that the Diagnostic Services will be unable to see and analyze this dependency.
The second way to inject properties is by implementing a custom
IPropertySelectionBehavior. The
property selection behavior is a general extension point provided by the container, to override the library's default behavior. The following code for instance enables explicit property injection using attributes, using any customly defined attribute:
using System;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using SimpleInjector.Advanced;
public static class AttributedPropertyInjectionExtensions
{
public static void AutowirePropertiesWithAttribute<TAttribute>(
this ContainerOptions options)
where TAttribute : Attribute
{
options.PropertySelectionBehavior =
new AttributePropertyInjectionBehavior(
options.PropertySelectionBehavior,
typeof(TAttribute));
}
private sealed class AttributePropertyInjectionBehavior
: IPropertySelectionBehavior
{
private readonly IPropertySelectionBehavior behavior;
private readonly Type attribute;
public AttributePropertyInjectionBehavior(
IPropertySelectionBehavior baseBehavior,
Type attributeType)
{
this.behavior = baseBehavior;
this.attribute = attributeType;
}
public bool SelectProperty(Type type, PropertyInfo prop)
{
return this.IsPropertyDecorated(prop) ||
this.behavior.SelectProperty(type, prop);
}
private bool IsPropertyDecorated(PropertyInfo p)
{
return p.GetCustomAttributes(this.attribute, true).Any();
}
}
}
The previous code snippet can be used as follows:
var container = new Container();
container.Options.AutowirePropertiesWithAttribute<MyInjectAttribute>();
This enables explicit property injection on all properties that are marked with the
MyInjectAttribute and it will fail when the property cannot be injected for whatever reason.
Tip: Properties injected by the container through the IPropertySelectionBehavior will be analyzed by the Diagnostic Services.
Note: The IPropertySelectionBehavior extension mechanism can also be used to implement implicit property injection. Doing so however is not advised because of the reasons given above.
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.