Reputation:
During the DI container setup I have multiple services that rely on a manual setup. E.g. some of them require a database connection string. So instead of writing
services.AddTransient(typeof(TheInterface), typeof(TheImplementation));
I have to do
services.AddTransient<TheInterface>(serviceProvider =>
{
// gather all the constructor parameters here
return new TheImplementation(/* pass in the constructor parameters */);
});
The constructor parameters are always the same. Instead of writing this multiple times I thought about creating a collection of those services and looping through that collection. I know I can create new instances from a type by using the Activator
class but I'm struggling with the conversion from a type to a generic. The following code is a sample code for demo purposes
public void SetupServices(IServiceCollection services, string databaseConnectionString)
{
IDictionary<Type, Type> servicesTypes = new Dictionary<Type, Type>()
{
{ typeof(MyInterface), typeof(MyImplementation) }
// ... more services here
};
foreach (KeyValuePair<Type, Type> servicesType in servicesTypes)
{
services.AddTransient <??? /* servicesType.Key does not work here */> (serviceProvider =>
{
return Activator.CreateInstance(servicesType.Value, databaseConnectionString /*, other params */);
});
}
}
The position I'm struggling is this line
services.AddTransient <??? /* servicesType.Key does not work here */>
How would I convert the service interface type to a generic one? I'm not even sure if this loop is worth the effort... but currently I have 5 services which could use it.
Upvotes: 1
Views: 1717
Reputation: 1183
I'm not sure doing a generic version of it is the correct way to go. Could your 'manual setup' of your services may be fixed using the options pattern? As it looks like you're trying to pass through a database connection string.
It could look something like
// Options object
public class DatabaseOptions {
public string ConnectionString {get;set;}
}
// Startup.cs
public void ConfigureServices(IServiceCollection services)
{
[...]
// From configuration (appsettings.json)
services.Configure<DatabaseOptions>(Configuration.GetSection("Database"));
// OR directly
services.Configure<DatabaseOptions>(x => x.ConnectionString = "SomeString");
}
// The service
public class SomeService {
public SomeService(IOptions<DatabaseOptions> dbOptions) {
var connectionString = dbOptions.Value.ConnectionString;
}
}
Upvotes: 2
Reputation: 12171
You can register type using ServiceDescriptor
:
foreach (KeyValuePair<Type, Type> servicesType in servicesTypes)
{
services.Add(new ServiceDescriptor(
serviceType.Key, // interface
serviceProvider => Activator.CreateInstance(servicesType.Value, databaseConnectionString /*, other params */), // implementation
ServiceLifetime.Transient); // lifetime
}
}
Upvotes: 1