Is there any way to achieve a generic factory when the class it returns requires a parameter in the constructor? All my factories look like this:
public static class UserServiceFactory
{
public static UserService GetService()
{
UserRepository userRepository = new UserRepository();
return new UserService(userRepository);
}
}
I tried something like this:
public static TServiceClass GetService<TServiceClass>()
where TServiceClass : class, new()
{
TServiceClass serviceClass = null;
string repositoryName = typeof (TServiceClass).ToString().Replace("Service", "Repository");
Type type = Type.GetType(repositoryName);
if (type != null)
{
object repository = Activator.CreateInstance(type);
serviceClass = (TServiceClass)Activator.CreateInstance(typeof (TServiceClass), new[]{repository});
}
return serviceClass;
}
However, this of course won't work because I can't use a class that has no parameterless constructor as a generic parameter, but I feel like I am close. I thought about passing the Service class like GetService(Type serviceClassType)
but then I can't declare the method's return type and would have to cast it when I call it, which I want to avoid.
Is there another approach? Is this even possible?
You could do something like this, and take both types as type parameters:
public static TService GetService<TRepository, TService>() where TRepository:new()
{
var repository = new TRepository();
return (TService)Activator.CreateInstance(typeof(TService), repository);
}
Or, if you want to rely on convention that the service and repository are the same name (just for illustration):
public static TService GetService<TService>()
{
var repositoryName = String.Concat(typeof(TService).Namespace, Type.Delimiter, typeof(TService).Name.Replace("Service", "Repository"));
object repository = Activator.CreateInstance(Type.GetType(repositoryName));
return (TService)Activator.CreateInstance(typeof(TService), repository);
}
This feels not obvious and unintuitive, and perhaps there are a better, more robust solutions available.
Perhaps using an inversion of control container would suit as a better approach.
Using a container, I would just resolve UserService
and let the container inject the appropiate repository. If UserService
is a dependency from something else, let the DI container resolve that, and a modern container will resolve dependencies of dependencies.