Ok, some more options to help you improve your code. Assuming the following model:
public class DataBaseLayer
{
private readonly string _connectionString;
private readonly string _userName;
private readonly ILogger _logger;
private readonly ITypeMapper _mapper;
public DataBaseLayer(
string connectionString,
string userName,
ILogger logger,
ITypeMapper mapper)
{
_connectionString = connectionString;
_userName = userName;
_logger = logger;
_mapper = mapper;
}
}
public interface ITypeMapper
{
}
public class TypeMapper : ITypeMapper
{
}
public interface ILogger
{
}
public class ConsoleLogger : ILogger
{
}
public class FileLogger : ILogger
{
}
1) Reduce the number of magic strings in the code
First thing would be to try to reduce the number of magic strings in your code, such as "connectionString" and "userName":
container.RegisterInstance(typeof(string), "userName", "my username");
container.RegisterInstance(typeof(string), "connectionString", "my connection string");
So, instead of repeating things like this all over the code...
new InjectionConstructor(
new ResolvedParameter<string>("connectionString"),
new ResolvedParameter<string>("userName"),
/* .... */
);
You can create your own parameter classes that derive from ResolveParameter:
public class UsernameParameter : ResolvedParameter<string>
{
public static UsernameParameter Instance => new UsernameParameter();
public static string ParameterName => "userName";
private UsernameParameter() : base(ParameterName)
{
}
}
public class ConnectionStringParameter : ResolvedParameter<string>
{
public static ConnectionStringParameter Instance => new ConnectionStringParameter();
public static string ParameterName => "connectionString";
private ConnectionStringParameter() : base(ParameterName)
{
}
}
Why bother? This way you don't have to repeat the strings "connectionString" and "userName", and if you need to change them all you have to do is to edit the ParameterName property in the corresponding class.
Registering the strings using ConnectionStringParameter and UsernameParameter:
container.RegisterInstance(typeof(string), UsernameParameter.ParameterName, "my username");
container.RegisterInstance(typeof(string), ConnectionStringParameter.ParameterName, "my connection string");
Using the parameters:
new InjectionConstructor(
ConnectionStringParameter.Instance,
UsernameParameter.Instance,
/* .... */
);
2) Use extension methods or factories to remove code duplication
For example, let's suppose you want to register different instances of DataBaseLayer. The only difference between these instances is the type of logger used. Registering the instances of ILogger:
container.RegisterType<ILogger, FileLogger>("file");
container.RegisterType<ILogger, ConsoleLogger>("console");
Registering the instances of DataBaseLayer using an extension method:
container.RegisterDataBaseLayer(instanceName: "fileDatabaseLayer", loggerType: "file");
container.RegisterDataBaseLayer(instanceName: "consoleDatabaseLayer", loggerType: "console");
Implementation of the RegisterDataBaseLayer extension method:
public static class UnityContainerExtensions
{
public static IUnityContainer RegisterDataBaseLayer(
this IUnityContainer container,
string instanceName,
string loggerType)
{
container.RegisterType<DataBaseLayer>(
instanceName,
new InjectionConstructor(
ConnectionStringParameter.Instance,
UsernameParameter.Instance,
new ResolvedParameter<ILogger>(loggerType),
new ResolvedParameter<ITypeMapper>()
)
);
return container;
}
}
3) Group your parameters if possible
Instead of ....
public class FooService
{
public FooService(string userName, string password)
{
// ...
}
}
Do:
public class FooService
{
public FooService(Credentials credentials)
{
// ...
}
}
public class Credentials
{
public string Username { get; set; }
public string Password { get; set; }
}
This will make your life easier when registering types using an IoC container and reduce your Bootstrapper code.
4) Create unit tests for your Bootstrapper/IoC container
This is extremely important and can save you some headaches. If you feel your code is fragile and might crash if/when a class is changed (e.g. changing or adding a new constructor), then you should add some tests for every type and/or named instance you register.
You should at least check if the container is able to resolve a particular instance, without throwing exceptions.