DI requires little bit more information about how to resolve required types/instances.
You mention only one part of the problem - registration of multiple instances of same type, but what about how those instances will be consumed.
Consumption will affect on how instances should be registered.
In a case you need different instances for different cases - you can create a singleton wrapper which will contain all required instances with correspondent name
public class Wrapper
{
public IRepository Orders { get; }
public IRepository Deliveries { get; }
public IRepository Products { get; }
public Wrapper(IRepository orders, IRepository deliveries, IRepository products)
{
Orders = orders;
Deliveries = deliveries;
Products = products
}
}
IRepository will be registered as Transient, but wrapper as Singleton
services.AddTransient<IRepository, SqlRepository>();
services.AddSingleton<Wrapper>();
Whenever you need one of those instances you will inject a wrapper as a dependency and use required instance through well-named property
public class Consumer
{
private readonly Wrapper _data;
public Consumer(Wrapper data) => _data = data;
public void DoSomething()
{
var myProducts = _data.Products.GetMy();
// ...
}
}
Because DI need to know how to instantiate different instances of same class, we need to create some workaround.
Another approach is to create different types for every required instance and which will derive from original one and register them as singleton types.
public class SqlRepository
{
public void GetData() { }
}
public class Orders : SqlRepository
{
// Nothing extra
}
public class Products : SqlRepository
{
// Nothing extra
}
And registration
services.AddSingleton<SqlRepository>();
services.AddSingleton<Orders>();
services.AddSingleton<Products>();
So consumers will have required type as a dependency and only one instance for every type will be instantiated within DI.