I am trying to refactor an existing solution (didn't write it) to use DI and Autofac, and have run into a bit of a problem. The solution in question supports a number of SQL database types (MSSQL, MySQL, PostgreSQL, potentially more.), and the user can have a number of databases of various kinds connected in the app (ie. they may have both an MSSQL and PostgreSQL DB connected). When a DB is connected to the app the type is stored in an enum. When actions are taken on the DBs in question it currently uses the following static factory to return a Datalayer:
public static class DatabaseFactoryController
{
public static IDatalayer GetDatalayer(ConnType databaseType)
{
switch (databaseType)
{
case ConnType.Mssql:
return new MssqlModel());
case ConnType.Postgre:
return new PostgreModel());
case ConnType.MySql:
return new MysqlModel());
default:
throw new ArgumentOutOfRangeException(nameof(databaseType));
}
}
}
Now the problem is that other controllers in the code need to have the proper datalayer injected for the DB the user is currently accessing like eg:
public class SizeController : IDataController
{
private readonly IDatalayer _datalayer;
public SizeController(IDatalayer datalayer)
{
_datalayer = datalayer;
}
public Response<SizeInfo> GetData(IRequest request)
{
<actions taken here using datalayer>
}
}
But how can I wire up Autofac to dynamically inject the right datalayer in the controllers, for the DB chosen for any given call (instantiation of the controller) without going to a ServiceLocator. I'm pretty sure it has to be possible but I'm pretty new to Autofac so maybe that's why I can't seem to make it work based on the documentation. I've tried following this: https://benedict-chan.github.io/blog/2014/08/13/resolving-implementations-at-runtime-in-autofac/
builder.RegisterType<MssqlDatalayer>()
.As<IDatalayer>().Keyed<IDatalayer>(ConnType.Mssql);
builder.RegisterType<PostgreDatalayer>()
.As<IDatalayer>().Keyed<IDatalayer>(ConnType.Postgre);
builder.Register<Func<ConnType, IDatalayer>>(c =>
{
var componentContext = c.Resolve<IComponentContext>();
return (roleName) =>
{
var dataService = componentContext.ResolveKeyed<IDatalayer>(roleName);
return dataService;
};
});
builder.RegisterType<SizeController>().As<IDataController>()
but in that implementation I fail to see how I pick the implementation to inject into the constructor at runtime. Maybe I'm missing something massively obvious, or maybe I need to fundamentally refactor the code. Any input will be valued as I've been stuck on this problem for a while now.