Search code examples
c#asp.net-coreioc-containerlamar

Inject different implementions in Controller based on Route


I've got an ASP.NET Core application and I'd like to use different strategies based on the Route selected. As an example if someone navigates to /fr/Index I want to inject the French translation implementation into my Controller. Likewise when someone navigates to /de/Index I'd like the German translation injected.

This is to avoid having every single action on my Controller read the "language" parameter and pass it on.

From a higher level, I'd like to have something like that:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    // Stuff here
    app.MapWhen(
        context => context.Request.Query["language"] == "fr", 
        builder =>
        {
            builder.Register<ILanguage>(FrenchLanguageImplementation);
        });

    app.MapWhen(
        context => context.Request.Query["language"] == "de",
        builder =>
        {
            builder.Register<ILanguage>(GermanLanguageImplementation);
        });
}

Unfortunately, it doesn't look like I get the IoC container resolving context at that level.

PS: I'm using Lamar as IoC.


Solution

  • You can use the AddScoped overload on IServiceCollection (or ServiceRegistry, which also implements IServiceCollection) to provide a factory-based service registration to the DI container. Here's an example implementation of ConfigureContainer, with explanatory comments inline:

    public void ConfigureContainer(ServiceRegistry services)
    {
        // ...
    
        // Register IHttpContextAccessor for use in the factory implementation below.
        services.AddHttpContextAccessor();
    
        // Create a scoped registration for ILanguage.
        // The implementation returned from the factory is tied to the incoming request.
        services.AddScoped<ILanguage>(sp =>
        {
            // Grab the current HttpContext via IHttpContextAccessor.
            var httpContext = sp.GetRequiredService<IHttpContextAccessor>().HttpContext;
            string requestLanguage = httpContext.Request.Query["language"];
    
            // Determine which implementation to use for the current request.
            return requestLanguage switch
            {
                "fr" => FrenchLanguageImplementation,
                "de" => GermanLanguageImplementation,
                _ => DefaultLanguageImplementation
            };
        });
    }
    

    Disclaimer: Up until testing the information in this answer, I've never used Lamar, so this Lamar-specific setup is taken from the docs and a best-guess effort. Without Lamar, the first line in the sample code would be public void ConfigureServices(IServiceCollection services) with no other changes.