Let's assume you have a project (e.g. .NET Core) using dependency injection. A database repository relies on a database connection string.
public abstract class BaseRepository
{
// use this when accessing the database
private readonly string connectionString;
protected BaseRepository(string connectionString)
{
this.connectionString = connectionString;
}
}
You don't want to pass this variable on every call so you simply add it during the DI container setup
// connectionString is taken from a config file
services.AddTransient<IUsersRepository>(serviceProvider => new UsersRepository(connectionString));
But let's assume you have a class that could be used for multiple cases e.g. a class that hashes some things. I created a sample class for this question
public class KeyEncryptor
{
private readonly KeyDerivationPrf keyDerivationPrf;
private readonly int iterationCount;
private readonly int desiredKeyLength;
public KeyEncryptor(KeyDerivationPrf keyDerivationPrf, int iterationCount, int desiredKeyLength)
{
this.keyDerivationPrf = keyDerivationPrf;
this.iterationCount = iterationCount;
this.desiredKeyLength = desiredKeyLength;
}
public byte[] Encrypt(string content, byte[] saltBytes)
{
return KeyDerivation.Pbkdf2(content, saltBytes, keyDerivationPrf, iterationCount, desiredKeyLength);
}
public bool Compare(string rawContent, byte[] encryptedKey, byte[] saltBytes)
{
byte[] encryptedContentKey = Encrypt(rawContent, saltBytes);
string encryptedContentKeyText = Convert.ToBase64String(encryptedContentKey);
string encryptedKeyText = Convert.ToBase64String(encryptedKey);
return encryptedContentKeyText.Equals(encryptedKeyText);
}
}
As you can see here many parameters are passed from the config file to the constructor. Now one could say
If you would remove those constructor parameters and expect them as method parameters your class would be more flexible
but
- then other classes would have to pass in the whole configuration values on every call
- then I would also have to pass in the connection string every time to the repository to make it uniform
You might know that the connection string or hashing algorithm never changes so you set it once during startup. But others might say keep it as flexible as possible, don't bother passing in 10 parameters on every call.
So when should you set constants during DI setup and when expect them on the fly as parameters? Is there a rule to follow?