Simple Injector allows much of its default behavior to be changed or extended. This chapter describes the available extension points and shows examples of how to use them. Do note that in most cases we advice developers to stick with the default behavior, because this behavior is based on best practices.

There are some exceptional circumstances, though, where you don’t control the amount of public constructors a type has. Code generators for instance, can have this annoying side effect. Earlier versions of the T4MVC template for instance did this.

In these rare cases we need to override the way Simple Injector does its constructor overload resolution. This can be done by creating custom implementation of IConstructorResolutionBehavior. The default behavior can be replaced by setting the Container.Options.ConstructorResolutionBehavior property.

Simple Injector will call into the registered IConstructorResolutionBehavior when the type is registered to allow the IConstructorResolutionBehavior implementation to verify the type. The implementation is called again when the registered type is resolved for the first time.

The following example changes the constructor resolution behavior to always select the constructor with the most parameters (the greediest constructor):

The following bit more advanced example changes the constructor resolution behavior to always select the constructor with the most parameters from the list of constructors with only resolvable parameters:

publicclassMostResolvableParametersConstructorResolutionBehavior:IConstructorResolutionBehavior{privatereadonlyContainercontainer;publicMostResolvableParametersConstructorResolutionBehavior(Containercontainer){this.container=container;}privateboolIsCalledDuringRegistrationPhase=>!this.container.IsLocked(); [DebuggerStepThrough]publicConstructorInfoGetConstructor(TypeimplementationType){varconstructor=this.GetConstructors(implementationType).FirstOrDefault();if(constructor!=null)returnconstructor;thrownewActivationException(BuildExceptionMessage(implementationType));}privateIEnumerable<ConstructorInfo>GetConstructors(Typeimplementation)=>fromctorinimplementation.GetConstructors()letparameters=ctor.GetParameters()wherethis.IsCalledDuringRegistrationPhase||implementation.GetConstructors().Length==1||ctor.GetParameters().All(this.CanBeResolved)orderbyparameters.Lengthdescendingselectctor;privateboolCanBeResolved(ParameterInfoparameter)=>this.GetInstanceProducerFor(newInjectionConsumerInfo(parameter))!=null;privateInstanceProducerGetInstanceProducerFor(InjectionConsumerInfoi)=>this.container.Options.DependencyInjectionBehavior.GetInstanceProducer(i,false);privatestaticstringBuildExceptionMessage(Typetype)=>!type.GetConstructors().Any()?TypeShouldHaveAtLeastOnePublicConstructor(type):TypeShouldHaveConstructorWithResolvableTypes(type);privatestaticstringTypeShouldHaveAtLeastOnePublicConstructor(Typetype)=>string.Format(CultureInfo.InvariantCulture,"For the container to be able to create {0}, it should contain at least "+"one public constructor.",type.ToFriendlyName());privatestaticstringTypeShouldHaveConstructorWithResolvableTypes(Typetype)=>string.Format(CultureInfo.InvariantCulture,"For the container to be able to create {0}, it should contain a public "+"constructor that only contains parameters that can be resolved.",type.ToFriendlyName());}// Usagevarcontainer=newContainer();container.Options.ConstructorResolutionBehavior=newMostResolvableConstructorBehavior(container);

The previous examples changed the constructor overload resolution for all registered types. This is usually not the best approach, because this promotes ambiguity in design of our classes. Because ambiguity is usually only a problem in code generation scenarios, it’s best to only override the behavior for types that are affected by the code generator.

Attribute-based property injection and implicit property injection are not supported by Simple Injector out of the box. With attribute-based property injection the container injects properties that are decorated with an attribute. With implicit property injection the container automatically injects all properties that can be mapped to a registration, but silently skips other properties. An extension point is provided to change the library’s default behavior, which is to not inject any property at all.

Out of the box, Simple Injector does allow explicit property injection based on registration of delegates using the RegisterInitializer method:

This enables property injection on a per-type basis and it allows configuration errors to be spot by the C# compiler and is especially suited for injection of configuration values. Downside of this approach is that the Diagnostic Services will not be able to analyze properties injected this way and although the RegisterInitializer can be called on base types and interfaces, it is cumbersome when applying property injection on a larger scale.

Tip: Instead of using property injection to configure components with primitive dependencies, as shown in the last example, consider wrapping the primitive value into a Parameter Object (e.g. FileLoggerSettings) and inject that Parameter Object into the consumer’s constructor instead.

The Simple Injector API exposes the IPropertySelectionBehavior interface to change the way the library does property injection. The example below shows a custom IPropertySelectionBehavior implementation that enables attribute based property injection using any custom attribute:

This enables explicit property injection on all properties that are marked with the supplied attribute (in this case MyInjectAttribute). In case a property is decorated that can’t be injected, the container will throw an exception.

Tip: Dependencies injected by the container through the IPropertySelectionBehavior will be analyzed by the Diagnostic, just like any constructor dependency is analyzed.

Implicit property injection can be enabled by creating an IPropertySelectionBehavior implementation that queries the container to check whether the property’s type to be registered in the container:

Warning: Silently skipping properties that can’t be mapped can lead to a DI configuration that can’t be easily verified and can therefore result in an application that fails at runtime instead of failing when the container is verified. Prefer explicit property injection—or better—constructor injection whenever possible.

Simple Injector does not allow injecting primitive types (such as integers and string) into constructors. The IDependencyInjectionBehavior interface is defined by the library to change this default behavior.

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 you the change of registering that type. Simple Injector supports this scenario with the ResolveUnregisteredType event. Unregistered-type resolution enables many advanced scenarios. The library itself uses this event for implementing enabling support for decorators.

By default, when registering a type without explicitly specifying a lifestyle, that type is registered using the Transient lifestyle. This behavior can be overridden and this is especially useful in batch-registration scenarios.

Here are some examples of registration calls that all register types as Transient:

Most of these methods have overloads that allow supplying a different lifestyle. This works great in situations where you register a single type (using one of the Register method overloads for instance), and when all registrations need the same lifestyle. This is less suitable for cases where you auto-register a set of types where each type needs a different lifestyle.

In this case you need to override the way Simple Injector does lifestyle selection. There are two ways of overriding the lifestyle selection.

Overriding the lifestyle selection can done globally by changing the Container.Options.DefaultLifestyle property, as shown in the following example:

container.Options.DefaultLifestyle=Lifestyle.Singleton;

Any registration that’s not explicitly supplied with a lifestyle, will get this lifestyle. In this case all registrations will be made as Singleton.

A more common need is to select the lifestyle based on some context. This can be done by creating custom implementation of ILifestyleSelectionBehavior.

When no lifestyle is explicitly supplied by the user, Simple Injector will call into the registered ILifestyleSelectionBehavior when the type is registered to allow the ILifestyleSelectionBehavior implementation to select the proper lifestyle. The default behavior can be replaced by setting the Container.Options.LifestyleSelectionBehavior property.

Intercepting the creation of types allows registrations to be modified. This enables all sorts of advanced scenarios where the creation of a single type or whole object graphs gets altered. Simple Injector contains two events that allow altering the type’s creation: ExpressionBuilding and ExpressionBuilt. Both events are quite similar but are called in different stages of the building pipeline.

The ExpressionBuilding event gets called just after the registration’s expression has been created that new up a new instance of that type, but before any lifestyle caching has been applied. This event can, for instance, be used for Context-based injection.

The ExpressionBuilt event gets called after the lifestyle caching has been applied. After lifestyle caching is applied much of the information that was available about the creation of that registration during the time ExpressionBuilding was called, is gone. While ExpressionBuilding is especially suited for changing the relationship between the resolved type and its dependencies, ExpressionBuilt is especially useful for applying decorators or applying interceptors.

Some frameworks insist in creating some of the classes you write and want to manage their lifetime. A notorious example of this is the pre-v4.7.2 ASP.NET Web Forms. One of the symptoms you often see with those frameworks is that the classes that the framework creates need to have a default constructor.

This disallows Simple Injector to create those instances and inject dependencies into their constructor. But Simple Injector can still be asked to initialize such instance according the container’s configuration. This is especially useful when overriding the default property injection behavior.

The following code snippet shows how an external instance can be initialized:

This allows any properties and initializers to be applied, but obviously doesn’t allow the lifestyle to be changed, or any decorators to be applied.

By calling the GetRegistration method, the container will create and cache an InstanceProducer instance that is normally used to create the instance. Note however, that the GetRegistration method restricts the shape of the type to initialize. Since GetRegistration is used in cases where Simple Injector creates types for you, Simple Injector will, therefore, check whether it can create that type. This means that if this type has a constructor with arguments that Simple Injector can’t inject (for instance because there are primitive type arguments in there), an exception will be thrown.

In that particular case, instead of requesting an InstanceProducer from the container, you need to create a Registration class using the Lifestyle class:

Simple Injector allows registering a delegate that will be called every time an instance is resolved directly from the container. This allows executing code just before and after an object graph gets resolved. This allows plugging in monitoring or diagnosing the container.

The following example shows the Options.RegisterResolveInterceptor method in action:

container.Options.RegisterResolveInterceptor(CollectResolvedInstance,c=>true);privatestaticobjectCollectResolvedInstance(InitializationContextcontext,Func<object>instanceProducer){// Invoke the delegate that calls into Simple Injector to get the requested service.objectinstance=instanceProducer();// Collect request specific data for display to the user.List<InstanceInitializationData>list=GetListForCurrentRequest(ResolvedInstances);list.Add(newInstanceInitializationData(context,instance));// Return the resolve instance.returninstance;}

The example above shows the registration code from the Glimpse plugin component. It registers an interception delegate to the CollectResolvedInstance method by calling container.Options.RegisterResolveInterceptor. The c => true lambda informs Simple Injector that the CollectResolvedInstance method should always be applied for every service that is being resolved. This makes sense for the Glimpse plugin, because the user would want to get a complete view of what is being resolved during that request.

When a user calls Container.GetInstance or InstanceProducer.GetInstance, instead of creating the requested instance, Simple Injector will call the CollectResolvedInstance method and supplies to that method:

An InitializationContext that contains information about the service that is requested.

An Func<object> delegate that allows the requested instance to be created.

The InitializationContext allows access to the InstanceProducer and Registration instances that describe the service’s registration. These two types enable detailed analysis of the resolved service, if required.

An InstanceProducer instance is responsible of caching the compiled factory delegate that allows the creation of new instances (according to their lifestyle) that is created. This factory delegate is a Func<object>. In case a resolve interceptor gets applied to an InstanceProducer, instead of calling that Func<object>, the InstanceProducer will call the resolve interceptor, while supplying that original Func<object> to the interceptor.