Search code examples
c#asp.net-coresimple-injector

Is there a way to pre-build all of my services that are registered with the Simple Injector container?


I am trying to improve the initial request time of my server right after deployment or restart by the IIS Manager. As I was looking for a way to do that I came across this article Reducing initial request latency by pre-building services in a startup task in ASP.NET Core. However, my project uses the Simple Injector (SI) library - I am not sure how (if it's even possible) to instruct SI to pre-build my registered services which should improve the first request time.

Has anyone tried that before?

That is my Startup.cs

public class Startup
{
    private IHostingEnvironment _env;
    private static readonly Container _container = new Container();

    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; private set; }

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddMemoryCache();
        services.AddSession();
        services.Configure<AzureBlobSettings>(settings =>
            Configuration.GetSection("AzureBlobSettings").Bind(settings));

        IntegrateSimpleInjector(services);
    }

    private void IntegrateSimpleInjector(IServiceCollection services)
    {
        _container.Options.DefaultScopedLifestyle = new AsyncScopedLifestyle();

        services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();

        services.AddSingleton<IControllerActivator>(
            new SimpleInjectorControllerActivator(_container));
        services.AddSingleton<IViewComponentActivator>(
            new SimpleInjectorViewComponentActivator(_container));

        services.EnableSimpleInjectorCrossWiring(_container);
        services.UseSimpleInjectorAspNetRequestScoping(_container);
    }

    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        _env = env;
        InitializeContainer(app, env);

        // standard config

        _container.Verify();

        // standard config

        app.UseMvc(routes =>
        {
            routes.MapRoute(
                name: "default",
                template: "{controller=Home}/{action=Index}/{id?}"
                );
            routes.MapRoute(
                name: "openSpotfire",
                template:
                "{controller=OpenAnalytics}/{action=AnalyticsView}/{id?}/{name?}"
                );
        });
    }

    private void InitializeContainer(
        IApplicationBuilder app, IHostingEnvironment env)
    {
        // Add application presentation components:
        _container.RegisterMvcControllers(app);
        _container.RegisterMvcViewComponents(app);

        // Add application services. 
        ServiceConfiguration.ConfigureService(_container, Configuration, env);

        // Allow Simple Injector to resolve services from ASP.NET Core.
        _container.AutoCrossWireAspNetComponents(app);
    }
}

That is my ServiceConfiguration.cs

public static class ServiceConfiguration
{
    public static void ConfigureService(
        Container c, IConfiguration configuration, IHostingEnvironment env)
    {
        //Cross Cutting Concerns from nuget
        container.Register<CurrentUser>(Lifestyle.Scoped);
        container.Register<IUserProfileService, CachedUserProfileService>(
                Lifestyle.Scoped);
        container.Register<ISharedItemBuilderFactory, SharedItemBuilderFactory>(
            Lifestyle.Scoped);
        container.Register<IEmailer, DbMailer>(Lifestyle.Scoped);
        container.Register<IRecipientService, RecipientService>(Lifestyle.Scoped);
        container.Register<ISpotfireUserDataService, SpotfireUserDataService>(
            Lifestyle.Scoped);
        container.Register<IWorkbookManagementService, WorkbookManagementService>(
            Lifestyle.Scoped);
        container.Register<ILogger, NLogLogger>(Lifestyle.Scoped);

        // CCC Settings
        container.Register(() => new DbMailConnection
        {
            ConnectionString = configuration["AppSettings:ConnectionString"],
            Profile = configuration["AppSettings:DbMailProfile"]
        }, Lifestyle.Singleton);
    }
}

Solution

  • [is it possible] to instruct SI to pre-build my registered services which should improve the first request time.

    Yes, there is. You should call Container.Verify() after you configured the container.

    Among other things, verify iterates through all known registrations, creates and compiles their expression trees.

    By default, Verify does a number of things:

    • It builds all expression trees for all registrations
    • It compiles those expression trees into delegates
    • It calls those delegates to ensure the instances can be created. With this, it forces the JIT compiler to compile the delegates
    • It will iterate container-controlled collections, as they behave as streams in Simple Injector
    • It will ensure decoratees are created in case the decorator contains a decoratee factory.
    • It will run full diagnostics on your object graph

    There is an overload of Verify available that allows you to suppress the last, diagnostics step. But do note that you should typically not suppress diagnostics.

    Besides reducing the time it takes to execute the first few requests, calling verify is advised, because it checks the health of your configuration by running the diagnostic services. This is a unique feature that separates Simple Injector from all other DI Containers for .NET.

    For more information, see the documentation Verify the container’s configuration and on diagnostics.