I've got a very big list of ISample
registrations in my MS.DI container, which I inject as an IEnumerable<ISample>
. But at runtime I typically need a very few. MS.DI, however, always creates all items of the collection immediately, which causes a performance problem in my application.
Is there a way to allow items of an injected IEnumerable<T>
to be loaded lazily, making it function as a stream, rather than a pre-populated array?
Here's my sample code that demonstrates the problem:
services.AddTransient<ISample, SampleA>();
services.AddTransient<ISample, SampleB>();
services.AddTransient<ISample, SampleC>();
public class SampleA : ISample
{
public Guid Id = FirstGuid;
public SampleA()
{
Console.WriteLine("SampleA created.");
}
}
public class SampleB : ISample
{
public Guid Id = SecondGuid;
public SampleB()
{
Console.WriteLine("SampleB created.");
}
}
public class SampleC : ISample
{
public Guid Id = ThirdGuid;
public SampleC()
{
Console.WriteLine("SampleC created.");
}
}
In other class I use service provider for creating an instance of any of these classes.
public ISample GetInstance(Guid Id)
{
return _serviceProvider.GetServices<ISample>().FirstOrDefault(d => d.Id==Id);
}
What's the best way to prevent all items to be pre-populated?
For solve this issue I would suggest using Dictionary<Guid, Type>
and add all your implementations to it by their Ids, this what I've tried and works fine:
public interface ISample
{
public Guid Id { get; }
}
public class SampleA : ISample
{
public Guid Id => Guid.Parse("3f30ae05-b88e-4abf-85b5-22e7ce4b639f");
public SampleA()
{
Console.WriteLine("SampleA created.");
}
}
public class SampleB : ISample
{
public Guid Id => Guid.Parse("c4a5b853-433b-4889-af41-cb99a8c71c4a");
public SampleB()
{
Console.WriteLine("SampleB created.");
}
}
public class SampleC : ISample
{
public Guid Id => Guid.NewGuid();
public SampleC()
{
Console.WriteLine("SampleC created.");
}
}
and in your startup register them like this:
services.AddTransient<SampleA>();
services.AddTransient<SampleB>();
services.AddTransient<SampleC>();
var dic = new Dictionary<Guid, Type>()
{
{Guid.Parse("3f30ae05-b88e-4abf-85b5-22e7ce4b639f"), typeof(SampleA)},
{Guid.Parse("c4a5b853-433b-4889-af41-cb99a8c71c4a"), typeof(SampleB)},
{Guid.Parse("c4a5b853-433b-4889-af41-cb99a8c71c4a"), typeof(SampleC)},
};
//you will inject this
services.AddScoped<Func<Guid, ISample>>
(provider => (id) => provider.GetService(dic[id]) as ISample);
and I you class inject it in this way:
public class UseDI
{
private readonly Func<Guid, ISample> _funcSample;
public UseDI(Func<Guid, ISample> funcSample)
{
_funcSample= funcSample;
}
public ISample GetInstance(Guid Id)
{
return _funcSample(Id);
}
}
I have tested this var sampleB=GetInstance(Guid.Parse("c4a5b853-433b-4889-af41-cb99a8c71c4a"))
and only SampleB constructor run.