Dependency Injection Integration
NPipeline integrates seamlessly with dependency injection (DI) containers, allowing you to use constructor injection for node dependencies.
Automatic Node Resolution
When you add a node to a pipeline, NPipeline automatically:
- Creates an instance of the node
- Resolves all constructor dependencies from the DI container
- Injects them into the node
You can also pre-configure node instances for advanced scenarios:
// Pre-configure a specific node instance
var customTransform = new MyTransform(specialConfig);
var builder = new PipelineBuilder();
builder.AddTransformNode(customTransform); // Use the pre-configured instance
This is useful when:
- Node setup is complex and can't be done through DI alone
- You need to share state between multiple nodes
- You're migrating legacy code
Example:
public class ProcessOrderTransform : ITransformNode<Order, ProcessedOrder>
{
private readonly IPaymentService _paymentService;
private readonly INotificationService _notificationService;
// Constructor dependencies are automatically injected!
public ProcessOrderTransform(
IPaymentService paymentService,
INotificationService notificationService)
{
_paymentService = paymentService;
_notificationService = notificationService;
}
public async Task<ProcessedOrder> ExecuteAsync(
Order input,
PipelineContext context,
CancellationToken cancellationToken)
{
var paymentResult = await _paymentService.ChargeAsync(input.Amount, cancellationToken);
await _notificationService.SendAsync($"Payment: {paymentResult.Status}", cancellationToken);
return new ProcessedOrder { /* ... */ };
}
}
DI Container Integration
Setting Up with Microsoft.Extensions.DependencyInjection:
var services = new ServiceCollection();
// Register your dependencies
services.AddScoped<IPaymentService, PaymentService>();
services.AddScoped<INotificationService, NotificationService>();
services.AddScoped<IOrderRepository, OrderRepository>();
var serviceProvider = services.BuildServiceProvider();
// Pipeline builder uses the service provider
var pipeline = PipelineBuilder.WithServiceProvider(serviceProvider)
.AddSourceNode<OrderSourceNode>()
.AddTransformNode<ProcessOrderTransform>()
.AddTransformNode<ValidateOrderTransform>()
.AddSinkNode<OrderSinkNode>()
.BuildPipeline();
var context = PipelineContext.Default;
var result = await runner.ExecuteAsync(pipeline, context);
Scoped Dependencies
Each pipeline execution can have its own scope for scoped dependencies:
using (var scope = serviceProvider.CreateScope())
{
var scopedServices = scope.ServiceProvider;
var pipeline = PipelineBuilder.WithServiceProvider(scopedServices)
.AddSourceNode<OrderSourceNode>()
.AddTransformNode<ProcessOrderTransform>()
.AddSinkNode<OrderSinkNode>()
.BuildPipeline();
await runner.ExecuteAsync(pipeline, context);
// Scoped services are disposed here
}
Advanced Pattern: Composite Services
Use DI to compose complex behavior:
public class EnrichedOrderTransform : ITransformNode<Order, EnrichedOrder>
{
private readonly IOrderEnricher _enricher;
private readonly ICacheService _cache;
public EnrichedOrderTransform(
IOrderEnricher enricher,
ICacheService cache)
{
_enricher = enricher;
_cache = cache;
}
public async Task<EnrichedOrder> ExecuteAsync(
Order input,
PipelineContext context,
CancellationToken cancellationToken)
{
var cached = await _cache.GetAsync(input.Id, cancellationToken);
if (cached != null)
return cached;
var enriched = await _enricher.EnrichAsync(input, cancellationToken);
await _cache.SetAsync(input.Id, enriched, cancellationToken);
return enriched;
}
}
Benefits
Testability:
// Test with mock dependencies
var mockPaymentService = new Mock<IPaymentService>();
var mockNotificationService = new Mock<INotificationService>();
var transform = new ProcessOrderTransform(
mockPaymentService.Object,
mockNotificationService.Object);
// Test the transform in isolation
Flexibility:
// Swap implementations based on environment
if (isDevelopment)
{
services.AddSingleton<IPaymentService, MockPaymentService>();
}
else
{
services.AddSingleton<IPaymentService, StripePaymentService>();
}
Next Steps
- Error Handling Architecture - Learn how DI works with error handlers
- Extension Points - Create custom nodes with DI support