Search code examples
.netperformancedesign-patternsdependency-injectionstartup

.NET Dependency Injection Auto-Registration vs Performance


My company uses Unity container with Code As Configuration in desktop application. After reading book Dependency Injection in .NET I thought to propose Auto-Registration using Convention over Configuration as the book says use Composition Root to compose the application’s object graph and you should prefer Auto-Registration (to avoid violating DRY and to get more manageable code).

But I also watched pluralsight course Building High-Performance Windows 8 Applications and author said:

assembly load is expensive process as far as performance concern and it is important to avoid unnecessary loading of an assembly in startup path.

I am worried about performance using Auto-Registration in startup as it requires to use reflection and loading of all assemblies.

I like to understand what is the best approach. Has anyone faced performance issue and dropped Auto-Registration or found intermediate option?


Solution

  • If you use a DI Container like Unity, you have basically two options of registering your dependencies:

    • Convention over Configuration using Auto-Registration
    • Explicitly register

    With Auto-Registration you typically apply Assembly Scanning and reflect over the types in a given set of assemblies. This Batch-Registers all those types in the Container.

    With Explicitly register on the other hand, you list all types one-by-one in code (or XML, or any other form).

    Both options however will cause all assemblies that contain the types to register, to be loaded at application start-up.

    This is something that is typically hard to prevent when using a DI Container, since the DI Container needs those mappings before-hand. The typical use is to configure the container during start-up, and after that only resolve from it.

    With Pure DI on the other hand, it is much easier to delay loading of assemblies until those types are used for the first time.

    Say for instance you're building a MVC web application with Pure DI, it might look a bit like this:

    public static Controller CreateController(Type type)
    {
        var dbContext = new CommerceContext(this.connectionString);
    
        if (type == typeof(HomeController))
            return 
                new HomeController(
                    new ProductRepository(
                        dbContext));
    
        if (type == typeof(LogoutController))
            return
                new LogoutController(
                    new UserRepository(
                        dbContext));
    
        else
            return base.CreateController(type);
    }
    

    Now let's say that ProductRepository and UserRepository each live in different assemblies. Now when the CreateController method is called for the first time, which will be very shortly after start-up, this method will be JITted. When a method gets JITted, all assemblies of all referenced types will be loaded. In this case that would mean that both the assembly of ProductRepository and that of UserRepository would both be loaded.

    Since HomeController is very likely created very soon after the application starts, it wouldn't make much sense to try to delay the loading of the ProductRepository assembly, but it might make sense for UserRepository, since the LogoutController might be created much later in the application's lifetime.

    We can prevent UserRepository assembly to be loaded, by extracting it to its own method:

    public static Controller CreateController(Type type)
    {
        ...
        if (type == typeof(LogoutController))
            return
                new LogoutController(
                    CreateUserRepository(dbContext));
        ...
    }
    
    // Extracted to method to enable lazy assembly loading
    private IUserRepository CreateUserRepository(CommerceContext context) 
    {
        return new UserRepository(dbContext));
    }
    

    By delegating the creation of the UserRepository class to the CreateUserRepository method, we can prevent its assembly from being loaded too early. This obviously only works when no other types from that assembly are needed earlier. If, for instance, the IUserRepository interface is defined in the same assembly, this means the assembly will still be loaded when CreateController gets JITted, since CreateController depends on IUserRepository.

    This example however is a bit contrived, since for a web application it usually makes little sense to do this kind of lazy loading of assemblies, since there are other techniques to prevent the user from noticing the application start-up time.

    This brings me to the following, although it might be important to delay loading of applications in your Desktop application, you really need to measure this first. In many applications this will absolutely not be a problem. I worked myself on several desktop applications that all used DI and a DI Container and did pre-loading of all assemblies at start up, in the performance wasn't a problem in most cases.

    In the other (single) case where this actually a problem was, we solved this by showing a fancy splash screen to show the user the application was being loaded.

    That said, even when using a DI container, there are ways to delay loading assemblies, but this is a rather sophisticated practice, which I would not pursuit unless, there is an actual performance penalty and can't be solved in any other way.