42

In my Startup class I use the ConfigureServices(IServiceCollection services) method to set up my service container, using the built-in DI container from Microsoft.Extensions.DependencyInjection.

I want to validate the dependency graph in an unit test to check that all of the services can be constructed, so that I can fix any services missing during unit testing instead of having the app crash at runtime. In previous projects I've used Simple Injector, which has a .Verify() method for the container. But I haven't been able to find anything similar for ASP.NET Core.

Is there any built-in (or at least recommended) way of verifying that the entire dependency graph can be constructed?

(The dumbest way I can think of is something like this, but it will still fail because of the open generics that are injected by the framework itself):

startup.ConfigureServices(serviceCollection); var provider = serviceCollection.BuildServiceProvider(); foreach (var serviceDescriptor in serviceCollection) { provider.GetService(serviceDescriptor.ServiceType); } 
5
  • 4
    I suspect the answer is that if you need a feature like this to use a 3rd party DI container that has it. Alternatively, you could look at the source code of a DI container that has it and build your own extension to Microsoft.DependencyInjection to provide it. Commented Mar 7, 2018 at 10:20
  • 4
    "In previous projects I've used Simple Injector". Why not use Simple Injector again? It seems to solve your problem elegantly. Commented Mar 7, 2018 at 11:43
  • 1
    @Steven true! Since this is a new project I'm trying to use the built-in functionality as much as possible, but if it turns out there's no good way to do this validation in the framework, using Simple Injector is probably the solution. Commented Mar 12, 2018 at 14:07
  • Did you find a good solution for this? I've just posted the same question Commented Mar 21, 2018 at 10:14
  • 1
    @mcintyre321 finally got it in aspnetcore 3 stackoverflow.com/a/60374778/5112433 Commented Feb 24, 2020 at 11:19

5 Answers 5

40

A built-in DI container validation was added in ASP.NET Core 3 and it is enabled only in the Development environment by default. If something is missing, the container throws a fatal exception on startup.

Keep in mind that controllers aren't created in the DI container by default, so a typical web app won't get much from this check until the controllers are registered in the DI:

public void ConfigureServices(IServiceCollection services) { services.AddControllers() .AddControllersAsServices(); } 

To disable/customize the validation, add a IHostBuilder.UseDefaultServiceProvider call:

public class Program { public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) //... .UseDefaultServiceProvider((context, options) => { options.ValidateOnBuild = false; }); 

This validation feature has several limitations, read more here: https://andrewlock.net/new-in-asp-net-core-3-service-provider-validation/

Sign up to request clarification or add additional context in comments.

1 Comment

AddControllersAsServices is the key here. Without it controllers are not registered thus not validated.
7

Here is a unit test that you can add to your project:

using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Hosting; using [X/N]Unit; namespace MyProject.UnitTests { public class DITests { [Fact or Test] public void AllServicesShouldConstructSuccessfully() { Host.CreateDefaultBuilder() .ConfigureWebHostDefaults(webBuilder => { webBuilder .UseDefaultServiceProvider((context, options) => { options.ValidateOnBuild = true; }) .UseStartup<Startup>(); }).Build(); }) } } 

Comments

2

Actually, I just used the example from your question with a few modifications and it worked pretty well for me. The theory behind filtering by classes in my namespace is that those will end up asking for everything else I care about.

My test looked a lot like this:

[Test or Fact or Whatever] public void AllDependenciesPresentAndAccountedFor() { // Arrange var startup = new Startup(); // Act startup.ConfigureServices(serviceCollection); // Assert var exceptions = new List<InvalidOperationException>(); var provider = serviceCollection.BuildServiceProvider(); foreach (var serviceDescriptor in services) { var serviceType = serviceDescriptor.ServiceType; if (serviceType.Namespace.StartsWith("my.namespace.here")) { try { provider.GetService(serviceType); } catch (InvalidOperationException e) { exceptions.Add(e); } } } if (exceptions.Any()) { throw new AggregateException("Some services are missing", exceptions); } } 

3 Comments

Can you please update your answer and add where your serviceCollection and services you iterate through are coming from
Wouldn't it be a good idea to dispose the provider, in case any of the services implement IDisposable?
I would also create a service scope for creating scoped services.
0

I had same problem in one of my project. My resolve:

  1. add methods like AddScopedService, AddTransientService and AddSingletonService, that add service to DI and then add it to some List. Use this methods instead of AddScoped, AddSingleton and AddTransient

  2. on startup application frist time i iterate by this list and call GetRequiredService. If any service can't be resolved, application will not start

  3. I had CI: auto build and deploy on commit to develop branch. So if someone merge changes that broke DI, application fail and we all know about it.

  4. If u whant to do it faster, u can use TestServer in Dmitry Pavlov's answer with my solution together

Comments

0

There is no built-in .Verify() method for the Microsoft.Extensions.DependencyInjection container. However, you can create a custom method that performs the same functionality. You're on the right track with your code snippet, but you'll need to make a few adjustments to handle open generics, as you mentioned.

Here's an example of how you could set up a utility method for validating your dependency graph:

 //example public static class ServiceCollectionExtensions { public static void ValidateDependencyGraph(this IServiceCollection services) { var serviceProvider = services.BuildServiceProvider(); foreach (var serviceDescriptor in services) { // Skip open generics if (serviceDescriptor.ServiceType.IsGenericTypeDefinition) { continue; } // Skip singletons that have already been instantiated if (serviceProvider.GetService(serviceDescriptor.ServiceType) is IDisposable disposable) { disposable.Dispose(); } else { serviceProvider.GetRequiredService(serviceDescriptor.ServiceType); } } } } 

Now, you can use this extension method in your unit tests to validate your dependency graph:

public void TestDependencyGraph() { var serviceCollection = new ServiceCollection(); var startup = new Startup(); startup.ConfigureServices(serviceCollection); serviceCollection.ValidateDependencyGraph(); } 

This will give you an idea of whether your dependency graph can be constructed successfully. However, keep in mind that some services may have runtime dependencies that cannot be verified during unit testing. So, it's still important to test your application in a real-world scenario.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.