Dynamic connection string to Web Api

asp.net-web-api connection-string entity-framework-6

Accepted Answer

I would utilize the same controller and look for the connection string based on the information provided rather than hosting different Web API instances for each client. Hosting numerous instances would be more complicated, and considering that the connection string is the only difference stated, I do not believe the complexity would be justified.

Finding out which client is calling will be the first step in obtaining the proper connection string. Tokens, headers, request data, or routing could all be used for this. I will use routing because it is the most straightforward and widely available to clients; nevertheless, be sure to thoroughly analyze your requirements when determining how you will make the selection.

[Route( "{clientId}" )]
public Foo Get( string clientId ) { /* ... */ }

Next, we must obtain the appropriateDbContext for the customer. We want to continue utilizing DI, however it is challenging because we don't know what connection string is required to build the object until the Controller has been formed. Therefore, rather than injecting the actual object, some sort of factory is needed. In this instance, we'll refer to it as aFunc<string, IUnitOfWork> with the knowledge that it receives a string representing the "clientId" and returns a suitably instantiatedIUnitOfWork . As an alternative, we could do this via a named interface.

[RoutePrefix("foo")]
public class FooController : ApiController
{  
    private Func<string, IUnitOfWork> unitOfWorkFactory;

    public FooController( Func<string, IUnitOfWork> unitOfWorkFactory )
    {
        this.unitOfWorkFactory = unitOfWorkFactory;
    }

    [Route( "{clientId}" )]
    public Foo Get( string clientId )
    {
        var unitOfWork = unitOfWorkFactory(clientId);
        // ...
    }
}

The only thing left to do is set up our dependency injection container to provide us that.Func<string, IUnitOfWork> . This could differ greatly depending on the implementation. One approach of accomplishing it in Autofac is as follows.

protected override void Load( ContainerBuilder builder )
{
    // It is expected `MyDbContext` has a constructor that takes the connection string as a parameter
    // This registration may need to be tweaked depending on what other constructors you have.
    builder.Register<MyDbContext>().ForType<DbContext>().InstancePerRequest();

    // It is expected `UnitOfWork`'s constructor takes a `DbContext` as a parameter
    builder.RegisterType<UnitOfWork>().ForType<IUnitOfWork>().InstancePerRequest();

    builder.Register<Func<string, Bar>>(
        c =>
            {
                var dbContextFactory = c.Resolve<Func<string, DbContext>>();
                var unitOfWorkFactory = c.Resolve<Func<DbContext, IUnitOfWork>>();

                return clientId =>
                    {
                        // You may have injected another type to help with this
                        var connectionString = GetConnectionStringForClient(clientId);
                        return unitOfWorkFactory(dbContextFactory(connectionString));
                    };
            });
 }

Since comments suggest that Autofac is currently in use, it is used even though alternative containers could produce equivalent results.

With that, the controller should be able to be created, and each request will utilize the proper connection string.


An illustration of registering based on a related project

builder.Register<Func<string, IEmployeeService>>(
    c =>
        {
            var dbContextFactory = c.Resolve<Func<string, IMainContext>>();
            var unitOfWorkFactory = c.Resolve<Func<IMainContext, IUnitOfWork>>();
            var repositoryFactory = c.Resolve<Func<IMainContext, IEmployeeRepository>>();
            var serviceFactory = c.Resolve<Func<IUnitOfWork, IEmployeeService>>();

            return clientId =>
                {
                    // You may have injected another type to help with this
                    var connectionString = GetConnectionStringForClient(clientId);

                    IMainContext dbContext = dbContextFactory(connectionString);
                    IUnitOfWork unitOfWork = unitOfWorkFactory(dbContext);
                    IEmployeeRepository employeeRepository = repositoryFactory(dbContext);
                    unitOfWork.employeeRepositoty = employeeRepository;

                    return serviceFactory(unitOfWork);
                };
        });

When you have chosen the client, you should consider updating (or building a new) container so that you may rely more on it if you find that the registration becomes too onerous as a result of the requirement to perform some wiring manually.

4
6/23/2015 7:06:58 PM

Popular Answer

ZZZ_tmp


Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow