Search code examples
c#inversion-of-controlowinsimple-injector

How to configure a dynamic connection string on a OWIN enviroment based on HTTP header?


I'm kind of new using OWIN and IoC and now I need to implement a dynamic context that is resolved by Simple Injector based on a HTTP header that identifies who is calling my API. This approach may not be the best one, so I'm also open for another possible solutions.

Here's my OWIN startup class:

public void Configuration(IAppBuilder app)
{
    var httpConfig = new HttpConfiguration();
    var container = new Container();
    string connectionString = string.Empty;

    container.Options.DefaultScopedLifestyle = new WebApiRequestLifestyle();

    ConfigureOAuthTokenGeneration(app);
    ConfigureOAuthTokenConsumption(app);

    app.Use(async (context, next) => 
    {
        var customHeader = context.Request.Headers.Get("customHeader");
        connectionString = "conStr";//get con based on the header, from a remote DB here
        await next.Invoke();
    });

    app.UseOwinContextInjector(container);

    container.Register<DbContext>(() => new MyDynamicContext(connectionString), 
        Lifestyle.Scoped);
    //container.Register ...;

    container.Verify();

    httpConfig.DependencyResolver = new SimpleInjectorWebApiDependencyResolver(container);

    ConfigureWebApi(httpConfig);
    app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
    app.UseWebApi(httpConfig);
}

The problem I'm facing is that the connection string is always empty when the DI framework creates the instance.

Any help will be appreciated.


Solution

  • In your case, both the MyDynamicContext and the connection string are runtime values. As explained here, runtime data should not be injected into components. Instead, you should define an abstraction that allows retrieving the runtime value after the object graph has been constructed.

    In your case, it would be best to have an abstraction that allows retrieving the DbContext, for instance:

    public interface IDbContextProvider {
        DbContext Context { get; }
    }
    

    Consumers of the DbContext can make use of the IDbContextProvider abstraction to get the current context. An implementation might look as follows:

    class DbContextProvider : IDbContextProvider {
        private readonly Func<DbContext> provider;
        public DbContextProvider(Func<DbContext> provider) { this.provider = provider; }
        public DbContext Context { get { return this.provider(); } }
    }
    

    And the registration of this DbContextProvider can look as follows:

    var provider = new DbContextProvider(() => GetScopedItem(CreateContext));
    container.RegisterSingleton<IDbContextProvider>(provider);
    
    static T GetScopedItem<T>(Func<T> valueFactory) where T : class {
        T val = (T)HttpContext.Current.Items[typeof(T).Name];
        if (val == null) HttpContext.Current.Items[typeof(T).Name] = val = valueFactory();
        return val;
    }    
    
    static DbContext CreateContext() {
        var header = HttpContext.Request.Headers.Get("custHeader");
        string connectionString = "conStr";//get con based on the header, from a remote DB here
        return new MyDynamicContext(connectionString);
    }