-
Notifications
You must be signed in to change notification settings - Fork 1
Open
Description
Description
Add support for Lazy<T> injection, enabling deferred initialization of services. This allows services to be created only when first accessed, which is useful for expensive operations, circular dependency breaking, and performance optimization.
Motivation
Some services are expensive to initialize or create circular dependencies. The Lazy<T> pattern provides a way to defer initialization until the service is actually needed. This is a standard pattern in .NET dependency injection and should be supported automatically.
Usage Scenario
Deferred Initialization for Expensive Services
public interface IReportGenerator
{
string GenerateReport(string type);
}
[RegisterAs<IReportGenerator>]
[Singleton]
public class ReportGenerator : IReportGenerator
{
private readonly HeavyReportEngine _engine;
public ReportGenerator()
{
// Expensive initialization - takes 5 seconds
_engine = new HeavyReportEngine();
}
public string GenerateReport(string type) => _engine.Generate(type);
}
public interface IReportService
{
void ProcessReports();
}
[RegisterAs<IReportService>]
[Scoped]
public class ReportService : IReportService
{
private readonly Lazy<IReportGenerator> _generator;
// Lazy<T> is injected instead of IReportGenerator
public ReportService(Lazy<IReportGenerator> generator)
{
_generator = generator;
// ReportGenerator is NOT created yet
}
public void ProcessReports()
{
// ReportGenerator is created HERE on first access
var report = _generator.Value.GenerateReport("daily");
}
}Breaking Circular Dependencies
public interface IOrderService { }
public interface IPaymentService { }
[RegisterAs<IOrderService>]
[Scoped]
public class OrderService : IOrderService
{
// Use Lazy to break circular reference
public OrderService(Lazy<IPaymentService> paymentService)
{
_paymentService = paymentService;
}
}
[RegisterAs<IPaymentService>]
[Scoped]
public class PaymentService : IPaymentService
{
// Direct dependency on IOrderService would create circular reference
public PaymentService(IOrderService orderService)
{
_orderService = orderService;
}
}Implementation Notes
- Support
Lazy<TService>injection whereTServiceis a registered service - The DI container should automatically register
Lazy<T>wrappers for all services - When
Lazy<T>is requested, return aLazy<T>that delegates to the actual service resolution - Ensure lazy services respect their registered lifetime (singleton, scoped, transient)
- Support with keyed services:
Lazy<IService>with a specific key - For scoped services wrapped in
Lazy<T>, ensure they follow scoping rules properly - Add unit tests for:
- Lazy initialization timing
- Multiple lazy requests for the same service
- Keyed lazy services
- Mixed lifetime scenarios (lazy singleton wrapping scoped, etc.)
- Add integration test in
RegistrationsTestsWithStandardAssemblyName.cs - Document that accessing
Lazy<T>.Valuetriggers resolution at that point
Technical Approach
The source generator should:
- Detect when
Lazy<TService>is used in constructor parameters - Ensure
TServiceis a registered service (or generate error) - For keyed services, support
Lazy<TService>with the same key detection - Generate registration code that provides
Lazy<T>instances via factory methods
Example generated code pattern:
services.AddScoped(typeof(Lazy<IMyService>), provider =>
new Lazy<IMyService>(() => provider.GetRequiredService<IMyService>()));Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels