I have an application where I'm using pure DI to construct my object graph. I would like to use a factory so I can create and destroy instances of a certain class at run time. The trouble is that this class has a lot (say >10) of dependencies on shared resources.
For example, to log in to a phone switch, you first need a socket connection to the switch itself, of which there can only be one per application. Then it allocates a number of call slots, each one to handle a session that can take phone calls one at a time. For each call slot, it allocates a phone extension from the extension pool, and all phone operations in the call slot must use the same extension. Then it needs to get an agent ID from the agent pool, and all operations in the call slot must use the same agent ID. Then it can finally login using the extension and agent ID. Finally it sends messages on some application level sockets to multiple other applications that want to know when login succeeds or fails.
Since I want these Login objects created on the fly, it seems like I should make a factory class in the Composition Root. It would take all these shared resources as constructor dependencies. But as the number of things that must happen at login grows, the factory class constructor parameter list is getting pretty big.
Here is some code. It is simpler than the actual application in that there are only 3 shared resources, but should convey the idea. To avoid long parameter lists in the create methods, I put the shared resources as class level variables and just access them directly in the create methods.
static SharedService1 service1;
static SharedService2 service2;
static SharedService3 service3;
static void Main(string[] args)
{
service1 = new SharedService1();
service2 = new SharedService2();
service3 = new SharedService3();
SessionOwner owner = new SessionOwner(
CreateLoginSession()
);
//...
}
static LoginSession CreateLoginSession()
{
return new LoginSession(
CreateLoginExecutor(),
CreateLogoutExecutor()
);
}
static LoginExecutor CreateLoginExecutor()
{
return new LoginExecutor(
service1,
service2
);
}
static LogoutExecutor CreateLogoutExecutor()
{
return new LogoutExecutor(
new Foo(service2),
new Bar(service3)
);
}
Now I have determined that it needs to delay creation of the LoginSession object. A factory seemed like the right solution. The following does work:
class Program
{
static SharedService1 service1;
static SharedService2 service2;
static SharedService3 service3;
static void Main2(string[] args)
{
service1 = new SharedService1();
service2 = new SharedService2();
service3 = new SharedService3();
DelayedSessionOwner owner = new DelayedSessionOwner(
new LoginSessionFactory(service1, service2, service3)
);
//...
}
}
class LoginSessionFactory
{
private SharedService1 Service1;
private SharedService2 Service2;
private SharedService3 Service3;
public LoginSessionFactory(SharedService1 service1, SharedService2 service2, SharedService3 service3)
{
Service1 = service1;
Service2 = service2;
Service3 = service3;
}
public LoginSession CreateLoginSession()
{
return new LoginSession(
CreateLoginExecutor(),
CreateLogoutExecutor()
);
}
private LoginExecutor CreateLoginExecutor()
{
return new LoginExecutor(
Service1,
Service2
);
}
private LogoutExecutor CreateLogoutExecutor()
{
return new LogoutExecutor(
new Foo(Service2),
new Bar(Service3)
);
}
}
But now all shared resources are parameters in the constructor of the factory. It might look ok here when there are only 3 shared services. But the real example has many more. Here are some solutions I thought of, none of which feel quite right.
- Use a DI container and pass it into the factory constructor in a single parameter. I know using a container vs. Pure DI has been debated (see here and here). I have done some work with a container on this application. The registration phase got really messy and hard for my coworkers to follow. This could be me not doing it right, but I had a hard time in a case like my phone example, where the application is a Windows service that can make multiple calls at once and so it had multiple instances of the same class.
- Put all the shared resources into a class, pass an instance of that to the factory, and let the factory use whichever resources it needs. Sort of like a really simple container, but specific to this application so the services would be accessible as basic properties. Then there could be a class that holds resources shared across the application, and another class, with a new instance for each call slot, that holds resources that are shared across calls on the same slot.
- Change the factory class to a factory method inside the main Composition Root class. Since there will be several of these factories, this could make the Composition Root class pretty big. I guess I could make it a partial class and split it up into multiple files.
- Just accept that the constructor list is going to be long because it really is using all the things in the list.
Any other ideas?