Simple Injector v4 has been released

For the last months we’ve been working on the next major release of Simple Injector, and it is finally here. We have removed legacy methods, simplified working with the library, and fixed many bugs and quirks.

In contrast to the impact that v3 had for developers, we expect most developers to update without having to make any code changes when upgrading from the latest v3.x to v4.0. There are quite some breaking changes through, but most of them are in more specialized parts of the library that you use when extending Simple Injector, such as writing custom Lifestyles, which is something most developers don’t do.

Our goal has always been to let the API guide you as much as possible through the breaking changes and how to fix them. In most cases removed parts of the API still exist, but are marked with [Obsolete(error: true)] attribute with expressive messages that explain what to do instead. This will cause your compiler to show a compilation error with (hopefully) a clear message describing the action to take. This should make it easier for you to migrate from v3.x to v4.0.

Before you upgrade to v4.0, please make sure you upgrade to the latest v3.x version of Simple Injector first.

With the release of v4.0 we moved to .NET Standard in favour of PCL. This means we removed support for PCL in version 4. Since most new platforms embrace the new .NET Standard, this shouldn’t be a problem. As long as your platform supports .NET Standard, Simple Injector v4 will run happily.

New Features

With this release we introduced many small and big simplifications to the API, some of which are:

  • The integration of the common LifetimeScopeLifestyle and ExecutionContextScopeLifestyle as part of the core library. These lifestyles have been renamed to the more obvious ThreadScopedLifestyle and AsyncScopedLifestyle, and the old SimpleInjector.Extensions.* NuGet packages have been deprecated.
  • The deprecation of framework-specific lifestyles WebApiRequestLifestyle and AspNetRequestLifestyle in favor of the new built-in AsyncScopedLifestyle.
  • The automatic and transparent reuse of registrations for classes that are registered with multiple interfaces. Simple Injector detected these kinds of problems, calling them Torn Lifestyles, but in Simple Injector v4 we completely removed this problem altogether, making it something the user hardly ever has to think about.
  • Several overloads added to simplify common scenarios.

On top of that, we removed some small parts of the API that could cause ambiguity and could lead to hidden, hard to detect errors. In some situations, e.g. when making conditional registrations, the user was able to make decisions on the service type of the consuming component, but this was unreliable, because such component could be registered with multiple interfaces. This could make the conditional registration invalid, where it was impossible for Simple Injector to warn the user about this. We removed these ambiguous properties and force the user to use the property containing the implementation type instead. We added some convenient extension methods on System.Type to make it easier to extract an abstraction from such implementation type, namely: IsClosedTypeOf<T>, GetClosedTypeOf<T> and GetClosedTypesOf<T>.

We improved the Diagnostic sub system once more. The biggest improvement is the detection of Short Circuited Dependencies. This is something that we were doing since v2, but there were situations in the past where Short Circuited Dependencies weren’t detected. We fixed that in this release.

For a complete list of all the breaking changes, new features and bug fixes, please view the release notes.

Working around the ASP.NET Core DI abstraction

For the last couple of years, Microsoft has been building the latest version of the .NET platform, branded .NET Core. One of the core components of this new framework is a DI library. Unfortunately, Microsoft made the mistake of defining a public abstraction for its DI library. In our previous blog post we described why the existence of this abstraction leads to all sorts of problems.

The goal of this blog post is to explain how you can effectively limit exposure to this abstraction and instead apply proven practices that promote structure, design and maintainability within your application. The summary of this blog post is the following:

TLDR;
Refrain from using a self-developed or 3rd party provided adapter for the .NET Core DI abstraction. Isolate the registration of application components from the framework and 3rd party components. Pursue a SOLID way of working and allow your application registrations to be verified and diagnosed by Simple Injector, without concern for incompatibilities with the framework and 3rd party components.

Microsoft wants its users to start off using the default container and then replace, if you want, with a 3rd party DI library. This advice of having one container instance that builds up both framework components, 3rd party components and application components stems from the idea that it is useful for framework components to be injected into application components. Having a single container makes it easy for the container build up object graphs that are a mixture of application and framework components.

Although developers might find this appealing, it’s important to realize that this a violation of the Dependency Inversion Principle (DIP), which states that:

abstracts are owned by the upper/policy layers.

In other words, in order to conform to the DIP, your application code should not depend on framework abstractions. Typically, code that depends on framework abstractions should exist wholly in the Composition Root. The DIP and ISP promote the use of abstractions tailored to your application’s needs and the creation of adapter implementations. Instead of having a framework or external library dictate the size and shape of abstractions, the application under development should define what’s best for its particular needs. Not only does this result in clean and testable code, it makes the code more flexible and reusable.

The SOLID principles are of great guidance here, and since the DIP states that our application (upper) layer should only depend on its own abstractions, building up mixed object graphs is an anti-pattern. Having one container build up mixed object graphs leads developers to violate the SOLID principles and will undoubtedly cause pain in the long run.

Instead of aiming for one DI library that builds everything up (one container to rule them all), we should keep these two worlds separate: framework components should be built up by the framework’s container, application components should be built up using our container of choice. To integrate or bridge the two worlds we define small focused adapters on each side. Use the framework’s provided extension points to intercept the creation of root types and forward the creation of those types to your container. On the other side of the container divide we define implementations for application-tailored abstractions, which call-back into framework and 3rd party library code. A well designed framework will have all the necessary abstractions in place for us to intercept. ASP.NET Core MVC already contains all the required hooks. 3rd party tool developers should follow the same practice.

Some developers feel uncomfortable with the notion of two containers in single application. But if you view the built-in framework container as a configuration system for the framework, having an independent container for your own application components is a non-issue. Every framework has its own configuration system. ASP.NET Web Forms has its own configuration system (mainly XML based) and MVC & Web API have their own code-first configuration systems. In a sense, nothing much has changed; ASP.NET Core still has a configuration system, be it one that includes an internal container-like structure. Apparently this container gives them a lot of flexibility, which is great. But we never had the need to completely swap out a framework’s configuration system before, so why should we need to for ASP.NET Core?

So how does this work? If we don’t want to swap out the built-in configuration system for .NET Core, what should we do? As said before, good practice is to use the framework’s supplied extension points and override as necessary to redirect/intercept the creation of certain types.

The main interception point for ASP.NET Core MVC is the IControllerActivator abstraction. This abstraction allows intercepting the creation of MVC controller types. An implementation for Simple Injector is trivial:

public sealed class SimpleInjectorControllerActivator : IControllerActivator
{
    private readonly Container container;
    public SimpleInjectorControllerActivator(Container c) { container = c; }

    public object Create(ControllerContext c) =>
       container.GetInstance(c.ActionDescriptor.ControllerTypeInfo.AsType());

    public void Release(ControllerContext c, object controller) { }
}

To replace the built-in controller activator, you configure the Core container:

services.AddSingleton<IControllerActivator>(
    new SimpleInjectorControllerActivator(container));

Although trivial to implement, we do provide an out-of-the-box implementation for you in our ASP.NET Core MVC integration package to make your life easier. As a matter of fact, over time we will supply you with with all the convenient methods that allow you to make bootstrapping as seamless as possible. We might not provide you with integration packages for all existing frameworks, but plugging in Simple Injector will always be trivial when the designers provided you with the correct interception points.

What this means is that all framework components and 3rd party components can keep being composed by the built-in DI container and your application will register and resolve your components through Simple Injector.

Many developers incorrectly assume that having one container for the framework’s internal configuration and another for the application components will mean re-registering hundreds of framework and 3rd party library components in the application container, but this is simply not necessary. First of all, as we already established, those registrations shouldn’t be in the application container because no application component should directly depend on those abstractions. Secondly, your application will only need to interact with a handful of those services at most, so you’ll handle the abstractions you are actually interested in.

Examples

Let’s say you have a component that needs access to the HttpContext instance, because you want to extract the name of the user from the current request being executed. Since the HttpContext can be acquired using the Microsoft.AspNetCore.Http.IHttpContextAccessor abstraction, your component requires this abstraction as a constructor argument and your code might look something like this:

public sealed class CustomerRepository : ICustomerRepository
{
    private readonly IUnitOfWork uow;
    private readonly IHttpContextAccessor accessor;

    public CustomerRepository(IUnitOfWork uow, IHttpContextAccessor accessor)
    {
        this.uow = uow;
        this.accessor = accessor;
    }

    public void Save(Customer entity)
    {
        entity.CreatedBy = this.accessor.HttpContext.User.Identity.Name;
        this.uow.Save(entity);
    }
}

There are, however, several problems with this approach:

  • The component now takes a dependency on an ASP.NET Core MVC abstraction, which makes it impossible to reuse this component outside the context of ASP.NET Core MVC.
  • The component has explicit knowledge about how to get the user name for the application.
  • The code that gets the user name will likely be duplicated throughout the application.
  • The component becomes much harder to test, because of the train wreck in the Save method.

One of the main problems is that the IHttpContextAccessor abstraction isn’t designed for the specific needs of this component. The needs of this component are not to access the current HttpContext, its need is to get the name of the user on whose behalf the code is running. We should create a specific abstraction for that specific need:

public interface IUserContext
{
    string Name { get; }
}

With this abstraction, we can simplify our component to the following:

public sealed class CustomerRepository : ICustomerRepository
{
    private readonly IUnitOfWork uow;
    private readonly IUserContext userContext;

    public CustomerRepository(IUnitOfWork uow, IUserContext userContext)
    {
        this.uow = uow;
        this.userContext = userContext;
    }

    public void Save(Customer entity)
    {
        entity.CreatedBy = userContext.Name;
        uow.Save(entity);
    }
}

What we have achieved here is that we:

  • Decoupled our component from the framework code; it can be reused outside of ASP.NET.
  • Prevented this component to have explicit knowledge about how to retrieve the current user’s name.
  • Prevented this code from being duplicated throughout the application.
  • Reduced test complexity.
  • Made the code simpler.

Since we have decoupled our component from the framework code, we can now reuse the component. For instance, it’s quite common to want to run part of our code base in a background Windows Service where there is obviously no HttpContext. To make this work we will create an adapter implementation for IUserContext that is specific to the type of application we are building. For our ASP.NET application, we will need an adapter implementation that contains the original code that retrieves the user’s name. For a Windows Service, we might return the name of the system user.

Here’s our adapter implementation for ASP.NET:

public sealed class AspNetUserContext : IUserContext
{   
    private readonly IHttpContextAccessor accessor;
    public AspNetUserContext(IHttpContextAccessor a) { accessor = a; }
    public string Name => accessor.HttpContext.Context.User.Identity.Name;
}

As you can see, this adapter implementation is straightforward, all it does is getting the HttpContext for the current request and the user name is determined from the context, as we saw before.

This component can be registered in our application container as follows:

container.RegisterSingleton<IUserContext>(new AspNetUserContext(
    app.ApplicationServices.GetRequiredService<IHttpContextAccessor>()));

The app variable here is ASP.NET Core’s IApplicationBuilder abstraction that gets injected into the Startup.Configure method.

What we see here is that our AspNetUserContext adapter depends directly on the IHttpContextAccessor abstraction. We can do this because IHttpContextAccessor is one of the framework’s abstractions that we know for sure is registered as a singleton. For most framework and 3rd party services however, we will have no idea what lifestyle it is registered with, and therefore, resolving them directly using the ApplicationServices property of IApplicationBuilder is a pretty bad idea.

Due to another design flaw, ASP.NET Core allows resolving scoped instances through the ApplicationServices property, but returns those components as singletons! In other words, if we were to request any framework and 3rd party services through ApplicationServices, the chances are that we would get a stale instance that would break our application at runtime – and ASP.NET Core will not inform us of our mistake. Instead of throwing an exception the ASP.NET Core will fail silently and leave our application in a potentially invalid state, maybe causing an ObjectDisposedException or worse. This is actually yet another incompatibility with Simple Injector; Simple Injector blocks these types of invalid resolves by throwing an exception.

Instead of using the ApplicationServices property, it would be better to resolve services using the HttpContext.RequestServices property. The following adapter shows an example when dealing with framework dependencies with a lifestyle that is either not singleton or unknown:

public sealed class AspNetAuthorizerAdapter : IAuthorizer
{
    private readonly Func<IAuthorizationService> provider;

    public AspNetAuthorizerAdapter(Func<IAuthorizationService> provider)
    {
        this.provider = provider;
    }

    // Implementation here
}

Here we have an adapter for a hypothetical IAuthorizer abstraction. Instead of depending on ASP.NET’s IAuthorizationService directly, this adapter depends on Func<IAuthorizationService>, which allows the correctly scoped service to be resolved at runtime. This adapter can be registered as follows:

container.RegisterSingleton(
    new AspNetAuthorizerAdapter(
        GetAspNetServiceProvider<IAuthorizationService>(app)));

The AspNetAuthorizationAdapter is created and registered as singleton. The registration makes use of the convenient GetAspNetServicesProvider<T> helper method that allows creating the provider delegate:

private static Func<T> GetAspNetServiceProvider<T>(IApplicationBuilder app) {
    var accessor =
        app.ApplicationServices.GetRequiredService<IHttpContextAccessor>();
    return () => {
        if (accessor.HttpContext == null)
            throw new InvalidOperationException("No HttpContext");
        return accessor.HttpContext.RequestServices.GetRequiredService<T>();
    };
}

When supplied with an IApplicationBuilder instance, the GetAspNetServiceProvider method will create a Func<T> that allows resolving the given service type T from the RequestServices collection according to its proper scope.

NOTE: With the introduction of ASP.NET Core integration package for Simple Injector v4, we added GetRequestService<T>() and GetRequiredRequestService<T>() extension methods on IApplicationBuilder that allow retrieving request services just like the previous GetAspNetServiceProvider<T>() method does.

Using logging in your application

Besides a DI library, .NET Core ships with a logging library out-of-the-box. Any application developer can use the built-in logger abstraction directly in their applications. But should they? If you look at the ILogger abstraction supplied to us by Microsoft, it’s hard to deny that the abstraction is very generic in nature and might very well not suit your application’s specific needs. The previous arguments still hold: application code should be in control over the abstraction.

The SOLID principles guide us defining an application-specific abstraction for logging. The exact shape of this abstraction will obviously differ from application to application (but look at this example for inspiration). Again, a simple adapter implementation can do the transition from application code to framework code:

// your application's logging abstraction
public interface ILog { void Log(LogEntry e); }

public sealed class DotNetCoreLoggerAdapter : ILog
{
    private readonly Microsoft.Extensions.Logging.ILogger logger;
    public DotNetCoreLoggerAdapter(ILogger logger) { this.logger = logger; }

    public void Log(LogEntry e) => 
        logger.Log(ToLevel(e.Severity), 0, e.Message, e.Exception,
            (s, _) => s);

    private static LogLevel ToLevel(LoggingEventType s) =>
        s == LoggingEventType.Warning ? LogLevel.Warning :
        s == LoggingEventType.Error ? LogLevel.Error :
        LogLevel.Critical;
}

This DotNetCoreLoggerAdapter can be registered as singleton as follows:

container.RegisterSingleton<ILog>(
    new DotNetCoreLoggerAdapter(loggerFactory.CreateLogger("Application")));

Conclusion

By creating application specific abstractions, we prevent our code from taking unnecessary dependencies on external code, making them more flexible, testable and maintainable. We can define simple adapter implementations for the abstractions we need to use, while hiding the details of connecting to external code. This allows our application to use our container of choice (and supports a container-less approach). This approach is part of a set of principles and practices that is been taught by experts like Robert C. Martin and others for decades already. Don’t ignore these practices, embrace them and be both productive and successful.

What’s wrong with the ASP.NET Core DI abstraction?

For the last couple of years Microsoft has been building the latest version of the .NET platform: .NET Core. .NET Core is a complete redesign of the .NET platform, with the goals of being truly cross-platform and cloud friendly. We’ve been following the development of .NET Core closely and have released .NET Core compatible versions of Simple Injector since RC1. With the release of Simple Injector v3.2 we now officially support .NET Core.

As you may be aware Microsoft has added its own DI library as one of its core components. Some would yell “finally!”. The omission of such a component has spawned many open source DI libraries for .NET. Simple Injector obviously being one of them.

Don’t get me wrong here, we applaud Microsoft for promoting DI as a core practice in .NET and it will likely lead to many more developers practicing DI, which is a win for our industry. The problem, however, starts with the abstraction Microsoft has defined on top of their built-in DI container. Compared to the previous Resolve abstractions, such as IDependencyResolver and IServiceProvider, this new abstraction adds a Register API on top IServiceCollection. With the definition of this abstraction, it is Microsoft’s vision that other (more feature rich) DI libraries could plug-in into the platform, while application developers, 3rd party tool builders and framework developers use the standardized abstraction to add their registrations. This would allow application developers a standard for integrating their DI library of choice.

At first sight having an abstraction might seem like sound advice, a common saying in our industry is that there are few problems in software that can’t be solved by adding a (extra) layer of abstraction. In this instance though their reasoning is flawed. DI experts have been warning Microsoft about this problem from the beginning, without success. Mark Seemann quite accurately described the problems with this approach in general here, where IMO the main points of his reasoning are:

  • It pulls in the direction of the lowest common denominator
  • It stifles innovation
  • It makes it more difficult to avoid using a DI container
  • It introduces versioning hell
  • If adapters are supplied by contributors, the adapters may have varying quality levels, and may not support the latest version of the Conforming Container.

These are real issues we are facing today with the new .NET Core DI abstraction. DI containers often have very unique and incompatible features when it comes to their registration API. Simple Injector, for instance, is very carefully designed in a way that enables the identification of numerous configuration errors. One very prominent example (there are many more) is Simple Injector’s diagnostic abilities. This is one of the features that is fundamentally incompatible with the expectations that consumers of the DI abstraction will have. So what are the expectations consumers will have of the new abstraction?

Consumers of the DI abstraction can be divided into three groups. Framework components, 3rd party libraries and application developers; especially framework components and 3rd party libraries, which are now expected to add their own registrations through the common abstraction. Since it is nigh on impossible for these two groups of developers to test their code with all the available adapters they will test their code with the built-in container. And while using the built in container these developers will (and arguably should) implicitly expect the standardized behaviour of the built-in container – no matter which adapter is used. In other words, it is the built-in container that defines both the contract and the behaviour of the abstraction. Every implemented adapter must be an exact superset of the built-in container. Deviating from the norm is not allowed because it would break 3rd party tools that depend on the behaviour of the default, built-in, container.

Simple Injector’s diagnostic and verification abilities is one of the many features that make Simple Injector users extremely productive. It detects problems that would be detected much later in the development cycle when using a different DI library. But running the diagnostics on both application and 3rd party registrations will cause problems because it is unlikely that all the external parties will automatically “play nice” with Simple Injector’s diagnostics. There is every chance they will define registrations that Simple Injector finds suspicious even though they have (hopefully) tested the registrations are fine for their specific case with the default container. It would be impossible for a hypothetical adapter for Simple Injector to distinguish between 3rd party registrations and application registrations. Switching off diagnostics completely would remove one of Simple Injector’s most important safety nets, whilst leaving the diagnostics system in place would likely cause false-positives from the 3rd party tooling that would each need to be suppressed by application developers. Since these 3rd party registrations are mostly hidden to the application developer, working around these issues could be daunting, frustrating and sometimes even impossible. One might argue that it would be good for Simple Injector to detect problems with 3rd party tools, but contacting those same developers to explain the “problem” would probably lead to fingers being pointed at us, since we “obviously” provided the user with an “incompatible” adapter.

Simple Injector’s diagnostic abilities is just one of the many incompatibilities that we would face when writing an adapter for .NET Core’s DI abstraction. Other incompatibilities include:

To make a fully compatible adapter for Simple Injector requires removing many prominent features, and thereby changing the existing behaviour of the Simple Injector library to something that would violate the guiding principles that underpin our vision. This is not an attractive solution. Not only would it introduce major breaking changes, it would remove features and behaviours that make Simple Injector unique and it is this complete set of features that many developers love about Simple Injector. In this sense having an adapter “stifles innovation” as Mark describes. With Simple Injector we made many innovations and not only would the adapter make Simple Injector almost useless to its users, it would restrict us from future improvement and innovation. Some might view Simple Injector’s philosophy as radical, but we think otherwise; we designed Simple Injector in a way that we think serves our users best. And the NuGet download count on the Simple Injector package indicates that many developers agree with us. Conforming to a defined adapter would prevent us from serving our users.

Although Simple Injector’s view may diverge from the norm more than most other containers, the simple act of defining this common abstraction blocks future DI libraries with an even more radical or innovative viewpoint from being used at all – it stifles innovation for future libraries. Just imagine one of the other containers introducing the same kind of verification that Simple Injector provides? Such feature can’t be introduced without breaking the contract of the DI abstraction. The mere act of having such an adapter can block progress in our industry.

With this explanation, I hope I’ve also made it clear that Microsoft’s DI abstraction isn’t even the lowest common denominator, because the lowest common denominator implies compatibility with all DI libraries! As I expressed here the chances are that none of the existing DI libraries are fully compatible with the defined abstraction. This is partly down to the fact that although the built-in container defines the contract of the abstraction, the abstraction test project lacks a solid number of test cases that fully define the exact behaviour in all scenarios. Up until now all adapter implementations have been guessing and hoping for the best, that their adapter is mostly in sync with the built-in container’s behaviour. The Autofac maintainers for instance, just realized they have some quite severe incompatibility issues and eventually came to the same conclusion as we did.

This wouldn’t be so bad if Microsoft’s DI library was a feature rich implementation that contained features like Simple Injector’s verification and diagnostic services so that we all use the same fully featured DI library. Sadly, the implementation is far from feature rich, Microsoft itself has described their implementation as a

A minimalistic DI container [that] is useful in the cases when you don’t need any advanced injection capabilities

To make matters worse, since the built-in container defines the contract of the abstraction, adding new features to the built-in container will break all existing adapters! Any 3rd party developer who uses the abstraction will only test with the built-in library and when this library depends on a feature added to the built-in container that is not yet supported by an adapter, things will fail and the application developer is screwed. This is one aspect of the versioning hell that Mark Seemann discusses in his blog post. One will hope that, at the very least, Microsoft will bump the major version number any time they make a change. Not only is their current implementation “minimalistic”, it can never evolve to a feature rich completely usable DI container, because they’ve painted themselves in a corner: every future change is breaking change that will piss everyone off.

A better solution is to avoid using the abstraction and its adapters entirely. As Mark Seemann quite accurately explained here and here, reusable libraries and a framework may not need to use a DI container at all. Unfortunately, the mere act of defining an abstraction will make it much harder to avoid using it. By defining an abstraction and actively promoting its use, Microsoft is leading thousands of 3rd party library developers and framework developers to stop thinking about defining the right library and the right framework abstractions (Mark Seemann’s articles clearly describes this). They no longer think about this because Microsoft leads them to believe that the whole world needs one common abstraction. We have seen new factory interfaces for MVC appear very late in the game (such as the IViewComponentActivator abstraction prior to RC2). And if we see the MVC team make these kinds of mistakes till very late in the development cycle, what can we expect from all those developers who are starting to build on top of the new .NET platform?

Conclusion

The definition of a DI abstraction is a painful mistake by Microsoft that will haunt us for many years to come. It has already stifled innovation, has introduced versioning hell and frustrates many developers. The abstraction is incompatible with many DI libraries and, against expert advice, Microsoft chose to retain the abstraction, dividing the world into incompatible and partially compatible containers, leading to endless issue reports for the adapter libraries that implement the DI abstraction and 3rd party libraries that use the abstraction.

Our view is that as an application developer, you should refrain from using an adapter and in the next article I will explain more thoroughly how to approach this and why, even without an incompatible container, it is the smarter way forward.

Stay tuned

When should you use a container?

A DI container is a tool that allows constructing the graphs of classes that contain an application’s behaviour (a.k.a. components or injectables). When we apply the Dependency Injection pattern in our systems the DI container can simplify the process of object construction and can, when used correctly, improve the maintainability of the start-up path (a.k.a. the Composition Root) of our application. But a DI container is not mandatory when we apply Dependency Injection.

Applying Dependency Injection without a DI container is called Pure DI. When we use Pure DI we define the structure of our object graph explicitly in code and this code is still centralized in the Composition Root just as it is when using a DI container. Dependency Injection does not discourage the use of the new keyword to construct components; it promotes the centralization of the use of the new keyword.

In this article, Mark Seemann shows the advantage of Pure DI over using a container: with Pure DI the compiler can verify the object graph. Mark makes some good points that for smaller applications Pure DI can be more beneficial than the use of containers, while larger applications can take advantage of convention over configuration which can help a lot in making your Composition Root maintainable. Mark even shows how Pure DI can help in finding configuration mistakes like Captive Dependencies.

The primary benefit of Pure DI is that it allows your code to fail-fast (in this case the system fails at compile-time). Detecting failures early is crucial when it comes to lowering development cost, because tracking down bugs is obviously much easier in a system that fails fast.

Although I do agree with Mark’s reasoning, it’s important to realize that Pure DI isn’t a silver bullet that detects all configuration mistakes. On the contrary, it’s quite easy to overlook problems such as Captive Dependencies as the Composition Root starts to grow. If you were to switch from a DI container to Pure DI and you were expecting your code to fail-fast, you might be in for an unpleasant surprise when the first bugs appear. This can happen because the C# compiler can only do a few simple checks on your behalf, such as:

  • Check whether the number of arguments supplied to a constructor match
  • Check whether the types supplied to a constructor match

The compiler is unable to perform the following checks:

  • Are null values supplied to constructors
  • Do constructor invocations fail
  • Are dependencies injected into a component with a longer lifetime (the so called Captive Dependencies)
  • Are dependencies that are expected to have a certain lifestyle, created more than once for the duration of that lifetime? (Problems known as Torn Lifestyle and Ambiguous Lifestyle)
  • Are disposable components not disposed when they go out of scope.

All these issues are relatively easy to spot when the number of components in the application is really small, but once that number starts to grow it’s very easy to lose track. A really strict coding style within your Composition Root does help but can easily go wrong when a team of developers is maintaining the Composition Root (opposed to having one single DI expert who has a really close watch on these types of issues).

It’s hard to define a threshold in application size for when a DI container outweighs Pure DI. In the business systems I help create, we almost always use a DI container for the central application (which often is a web application), while using Pure DI for small (background) Windows Services since they typically use just a fraction of the total business layer. Once the Composition Root starts to grow, tools that can verify and diagnose the correctness of the Composition Root become extremely valuable.

It is unfortunate that most DI containers have a limited set of capabilities when it comes to verifying their configuration (causing your application to fail silently). Simple Injector deals with all the previously stated issues and more. In Simple Injector it’s just a matter of following good practices and calling Container.Verify() once you have completed the configuration of the container. Verification of your configuration gives you an increased level of confidence that all known object graphs are wired correctly at application start-up. Simple Injector can give more certainty than Pure DI, while keeping the benefits of, among other things, convention over configuration.

Simple Injector v3 is here!

After months of preparation and development we have finally released Simple Injector v3.0. In version 3 we are breaking from the past: we have removed legacy methods, simplified parts of the API and added some compelling new features.

We expect that almost every developer will have to make changes to his/her composition root when upgrading to v3. We did our best to make the upgrade process easy but please be prepared to make changes to your code.

The driver for making these breaking changes is that parts of the API have evolved over time and in doing so have grown confusing (e.g. RegisterOpenGeneric and RegisterManyForOpenGeneric). In the pursuit of keeping Simple Injector simple we felt obligated to improve the consistency of the API. These decisions have not been taken lightly because we hate breaking your code. Our driving force is, however, a simpler and more compelling library for everyone.

Our goal was to let the API guide you as much as possible through the breaking changes and how to fix them. In most cases removed parts of the API still exist, but are marked with [Obsolete(error: true)] attribute with expressive messages that explain what to do instead. This will cause your compiler to show a compilation error with (hopefully) a clear message describing the action to take. This should make it easier for you to migrate from v2.x to v3.0.

Before you upgrade to v3.0, please make sure you upgrade to the latest 2.8 version of Simple Injector first. Some beta testers reported that there were some changes between minor versions of the 2.x branch that broke code and/or unit tests. Upgrading in two steps should make the process much easier.

Besides the clean-up of the API, Simple Injector is now much stricter when it comes to diagnosing your configuration. When calling Verify() Simple Injector 3 will automatically run its diagnostics and it will throw an exception when any diagnostic error occurs. Even without calling Verify() Simple Injector will always check for pesky Lifestyle Mismatches when resolving instances from the container and will throw an exception when such a mismatch is detected. These exceptions are intended to provide the quickest feedback on configuration mistakes that we believe you should resolve. From experience we know that this can save you from wasting many hours debugging problems later.

Breaking Changes

The most prominent breaking changes are the changes to the public API and these can prevent your code from compiling.

Here is a cheat sheet containing a mapping for the most prominent API changes. On the left side are the old v2 API calls, on the right side the related method to use in v3. Note that in most cases the compiler errors will guide you through the process.

v2 API v3 API
RegisterSingle RegisterSingleton
RegisterAll RegisterCollection
RegisterSingleDecorator RegisterDecorator(Lifestyle.Singleton)
RegisterAllOpenGeneric RegisterCollection
RegisterManyForOpenGeneric Register / RegisterCollection
RegisterOpenGeneric Register
RegisterSingleOpenGeneric Register(Lifestyle.Singleton)

For a complete list of all the breaking changes, please see the release notes.

New Features

As well as the breaking changes there are many other big and small improvements to the library. The most prominent of these are:

  • the addition of a Lifestyle.Scoped property;
  • support for conditional and contextual registrations using the new RegisterConditional methods.
  • Register overloads now accept open generic types.
  • RegisterCollection(Type, IEnumerable<Registration>) now accepts open generic types.
  • Container.Register(Type, IEnumerable<Assembly>) and Container.RegisterCollection(Type, IEnumerable<Assembly>) overloads have been added to simplify batch registration.
  • the Container class now implements IDisposable to allows disposing singletons.

The new Lifestyle.Scoped is a small feature that can make your Composition Root much cleaner. Most applications use a combination of three lifestyles: Transient, Singleton, and some scoped lifestyle that is particularly suited for that application type. For example an ASP.NET MVC application will typically use the WebRequestLifestyle; a Web API application will use the WebApiRequestLifestyle. Instead of using the appropriate RegisterXXX extension method of the appropriate integration package, you can now do the following:

var container = new Container();
// Just define the scoped lifestyle once.
container.Options.DefaultScopedLifesyle = new WebRequestLifestyle();

container.Register<IUserContext, AspNetUserContext>(Lifestyle.Scoped);
container.Register<IUnitOfWork, DbContextUnitOfWork>(Lifestyle.Scoped);

Not only does this make your code much cleaner, it also makes it easier to pass the container on to some methods that add some layer-specific configuration to the container. For instance.

public static void BootstrapBusinessLayer(Container container) {
    // Registrations specific to the business layer here:
    container.Register<IUnitOfWork, DbContextUnitOfWork>(Lifestyle.Scoped);
}

The Lifestyle.Scoped property makes it easy for the business layer bootstrapper to add registrations using the application’s scoped lifestyle without having to know which lifestyle it actually is. This simplifies reuse of this bootstrapper across the applications in your solution.

Another great improvement is the addition of the RegisterConditional methods. These method overloads allow conditional registration of types and registration of contextual types. Take the following conditional registrations:

container.RegisterConditional<ILogger, NullLogger>(
    c => c.Consumer.ImplementationType == typeof(HomeController));
container.RegisterConditional<ILogger, SqlLogger>(c => !c.Handled);

This particular combination of registrations ensures that a NullLogger is injected into HomeController and all other components get a SqlLogger.

For advanced scenarios one of the RegisterConditional overloads accepts an implementation type factory delegate. Take a look at the following example:

container.RegisterConditional(typeof(ILogger),
    c => typeof(Logger<>).MakeGenericType(c.Consumer.ImplementationType),
    Lifestyle.Singleton,
    c => true);

This example registers a non-generic ILogger abstraction that will be injected as a closed generic version of Logger<T>, where T is determined based on its context (in this case the consuming component). In other words when your HomeController depends on an ILogger it will actually get a Logger<HomeController>.

For a complete list of all the breaking changes, new features and bug fixes, please view the release notes.

Happy injecting.

Wanted: Beta Testers

With Simple Injector 3 we are breaking with the past and working hard to simplify the API to better represent our ideals and, of course, the name of our library. This means that we are introducing breaking changes in v3 that will undoubtedly impact any developer migrating from v2.

The driver for making these breaking changes is that areas of the API have evolved over time and in so doing have grown confusing (e.g. RegisterOpenGeneric and RegisterManyForOpenGeneric). In the pursuit of keeping Simple Injector simple we felt obligated to improve the consistency of the API. These decisions have not been taken lightly because we hate breaking your code. Our driving force is, however, a Simple(r) Injector for everyone.

We are doing our best to make the transition as seamless as possible and we need feedback on this effort. We are looking for Simple Injector users to test the beta of Simple Injector 3 and tell us what they think.

If you can help then please upgrade your NuGet packages to 3.0.0-beta1 3.0.0-beta2 3.0.0-beta3 3.0.0-beta4 and try to compile your code (it will probably fail!). Our main strategy is to guide users with obsolete messages that should be presented as descriptive compiler errors. Once you have fixed any errors please call Verify() on your container, (as you are hopefully already doing) and test your application to ensure everything resolves as expected. To discuss any element of beta testing please join us on gitter. We would usually expect the upgrade process to take no more than a half hour.

Please note that we don’t expect you to do our testing for us. Simple Injector has an extensive suite of unit tests and we don’t expect to have introduced many new bugs.

Here’s a list of the most prominent breaking changes we are introducing:

  • Calling Verify() will now automatically diagnose the container’s configuration for common configuration mistakes and exceptions will be thrown. We felt that not enough developers were explicitly calling Analyzer.Analyze() to check for diagnostic warnings, leading to a common source of bugs. So we decided to integrate this into Verification in the hope that this will guide our users to the pit of success. The old Verification behaviour can be triggered by calling Verify(VerificationOption.VerifyOnly).
  • Even if you don’t call Verify(), the v3 the container will always check for lifestyle mismatches when resolving an instance and will throw an exception if there is a mismatch in the object graph. This more strict behaviour can be suppressed globally but (for obvious reasons) we advise against doing so.
  • The RegisterSingle methods have been renamed to RegisterSingleton as we felt there was some ambiguity in the name RegisterSingle, especially in combination with methods like RegisterAll, RegisterCollection and RegisterManyForOpenGeneric. We considered removing these method completely, but the benefits of doing so did not compare favourably to the pain of fixing this as a breaking change.
  • The RegisterAll methods have been renamed to RegisterCollection. Just as with the RegisterSingle methods, new developers experienced confusion in their naming. The new name expresses more clearly what the methods do.
  • The RegisterManyForOpenGeneric extension methods from the SimpleInjector.Extensions namespace have been replaced with new Register and RegisterCollection overloads on the Container. When doing one-to-one mappings, Register(Type, IEnumerable<Assembly>) can be used, and when registering collections RegisterCollection(Type, IEnumerable<Assembly>) can be used.
  • The RegisterSingleDecorator extension methods have been removed. Decorators can be registered by calling RegisterDecorator while supplying the Lifestyle.Singleton.
  • The RegisterOpenGeneric extension methods have been removed. The Container.Register methods have been extended to accept open generic types. This removes the superficial difference between the registration of open generic types and other registrations. (This difference originally had a technical background, but we allowed the internal difference to effect the user experience.) Do note that there is a behavioural difference between RegisterOpenGeneric and Container.Register. RegisterOpenGeneric registers each generic type as fall-back, which means it will apply that type if no other registration exists for that type. Conversely, Container.Register will not register a fall-back, it will, instead, test for overlapping registrations. If the fall-back behaviour is required, the new RegisterConditional methods can be used. The new RegisterConditional methods allow supplying a delegate that allows signaling a registration as fall-back registration.
  • IConstructorVerificationBehavior and IConstructorVerificationBehavior have been merged and replaced with IDependencyInjectionBehavior.

In the majority of cases the compiler error messages should guide you through the migration. If you find a scenario that is unclear or it takes time to figure out please let us know. We want to make the migration as seamless as possible.

Besides the above list of breaking changes, we have some compelling new features that may be of interest:

  • A Lifestyle.Scoped property was added to simplify registration of scoped lifestyles, since in most applications you would typically only use one specific scoped lifestyle. You can now use Register<IService, Impl>(Lifestyle.Scoped) instead of having to call RegisterPerWebRequest<IService, Impl>() for example. This also simplifies reuse in your composition root, when the same configuration is reused over multiple application types (such as MVC and WCF).
  • As noted above RegisterConditional methods are added to the Container to allow registering types based on contextual information such as information about the consumer in which they are injected.
  • Batch registration made easier by adding overloads of the Register method that accept either a collection of types or a collection of assemblies.
  • As explained above, the Register methods now accept open generic types.
  • The container now implements IDisposable. This allows cached singletons to be disposed when the container gets disposed.
  • RegisterCollection(Type, IEnumerable<Registration>) now accepts open generic service types as well.

Welcome to the Simple Injector weblog

The Simple Injector weblog is where we – the Simple Injector contributors – will write about Simple Injector, Dependency Injection, best practice and software design in general.

Simple Injector is strongly opinionated, and so are we; we love talking about software design and principles and from now on we’ll be doing it right here.

We will keep you updated about new Simple Injector features, releases, background stories and anything else we decide to write about.

Welcome to The Simple Injector Blog!