Reputation: 19141
We are trying to figure out how to setup Dependency Injection for situations where service classes can have different dependencies based on how they are used. In our specific case, we have a web app where 95% of the time the connection string is the same for the entire Request (this is a web application), but sometimes it can change.
For example, we might have 2 classes with the following dependencies (simplified version - service actually has 4 dependencies):
public LoginService (IUserRepository userRep)
{
}
public UserRepository (IContext dbContext)
{
}
In our IoC container, most of our dependencies are auto-wired except the Context for which I have something like this (not actual code, it's from memory ... this is StructureMap): x.ForRequestedType().Use() .WithCtorArg("connectionString").EqualTo(Session["ConnString"]);
For 95% of our web application, this works perfectly. However, we have some admin-type functions that must operate across thousands of databases (one per client). Basically, we'd want to do this:
public CreateUserList(IList<string> connStrings)
{
foreach (connString in connStrings)
{
//first create dependency graph using new connection string
????
//then call service method on new database
_loginService.GetReportDataForAllUsers();
}
}
My question is: How do we create that new dependency graph for each time through the loop, while maintaining something that can easily be tested?
Upvotes: 2
Views: 196
Reputation: 19141
We ended up just creating a concrete context and injecting that, then changing creating a wrapper class that changed the context's connection string. Seemed to work fine.
Upvotes: 0
Reputation: 48583
So most UserRepository
methods use a single connection string obtained from session, but several methods need to operate against a list of connection strings?
You can solve this problem by promoting the connection string dependency from IContext
to the repository and adding two additional dependencies - a context factory and a list of all the possible connections strings the repository might need to do its work:
public UserRepository(IContextFactory contextFactory,
string defaultConnectionString,
List<string> allConnectionStrings)
Then each of its methods can build as many IContext
instances as they need:
// In UserRepository
public CreateUserList() {
foreach (string connString in allConnectionStrings) {
IContext context = contextFactory.CreateInstance(connString);
// Build the rest of the dependency graph, etc.
_loginService.GetReportDataForAllUsers();
}
}
public LoginUser() {
IContext context = contextFactory.CreateInstance(defaultConnectionString);
// Build the rest of the dependency graph, etc.
}
Upvotes: 0
Reputation: 8557
Within the loop, do:
container.With("connectionString").EqualTo(connString).GetInstance<ILoginService>()
where "connectionString" is the name of a string constructor parameter on the concrete implementation of ILoginService.
Upvotes: 0
Reputation: 45435
To defer the creation of an object until runtime, you can use a factory:
public interface ILoginServiceFactory
{
ILoginService CreateLoginService(string connectionString);
}
Usage:
public void CreateUserList(IList<string> connStrings)
{
foreach(connString in connStrings)
{
var loginService = _loginServiceFactory.CreateLoginService(connString);
loginService.GetReportDataForAllUsers();
}
}
Upvotes: 2