PyNurseInjector Features
PyNurseInjector provides a comprehensive set of features designed to simplify dependency injection in .NET applications while maintaining flexibility and performance.
Namespace-Based Registration
The most powerful feature of PyNurseInjector is the ability to automatically discover and register services based on namespaces.
How It Works
PyNurseInjector scans the specified namespace and automatically registers all classes that implement interfaces. This eliminates the need for manual registration of each service.
// Register all services in a namespace
services.AddServicesFrom("MyApp.Services");
// Register with specific lifetime
services.AddServicesFrom("MyApp.Repositories", ServiceLifetime.Scoped);
// Include sub-namespaces
services.AddServicesFrom("MyApp", deep: true);
Benefits
- Zero manual registration required
- Automatically discovers new services
- Reduces startup configuration code
- Enforces consistent namespace organization
Advanced Usage
// Register from multiple namespaces
services.AddServicesFrom("MyApp.Core.Services");
services.AddServicesFrom("MyApp.Infrastructure.Repositories");
services.AddServicesFrom("MyApp.External.Adapters");
// Use lambda for dynamic namespace selection
services.AddServicesFrom(type => type.Namespace?.StartsWith("MyApp.") ?? false);
Comprehensive Attribute System
PyNurseInjector provides a rich set of attributes for fine-grained control over service registration.
[RegisterAs] Attribute
Explicitly specify which interfaces a class should be registered as.
// Single interface registration
[RegisterAs(typeof(IUserService))]
public class UserService : IUserService, IDisposable
{
// Only registered as IUserService, not IDisposable
}
// Multiple interface registration with different lifetimes
[RegisterAs(typeof(IEmailService), ServiceLifetime.Singleton)]
[RegisterAs(typeof(INotificationService), ServiceLifetime.Scoped)]
public class EmailService : IEmailService, INotificationService
{
// Registered as both interfaces with different lifetimes
}
[ServiceLifeTime] Attribute
Override the default lifetime for a specific service.
[ServiceLifeTime(ServiceLifetime.Singleton)]
public class ConfigurationService : IConfigurationService
{
// Always registered as Singleton
}
// Can be combined with RegisterAs
[RegisterAs(typeof(ICacheService))]
[ServiceLifeTime(ServiceLifetime.Singleton)]
public class MemoryCacheService : ICacheService { }
[DontRegister] Attribute
Exclude specific types from automatic registration.
[DontRegister]
public class LegacyService : IService
{
// Won't be registered even if in scanned namespace
}
[DontRegister]
internal class TestHelper : IHelper
{
// Useful for test utilities in production namespaces
}
Property & Field Injection
Eliminate constructor clutter with automatic property and field injection.
[InjectService] Attribute
Mark properties or fields to be automatically injected by the DI container.
public class OrderController : Controller
{
// Property injection
[InjectService]
public IOrderService OrderService { get; private set; }
// Field injection
[InjectService]
private readonly ILogger<OrderController> logger;
[InjectService]
protected IEmailService emailService;
public async Task<IActionResult> CreateOrder(OrderDto dto)
{
logger.LogInformation("Creating order");
var order = await OrderService.CreateAsync(dto);
await emailService.SendOrderConfirmation(order);
return Ok(order);
}
}
Benefits
- Cleaner, more readable classes
- No constructor parameter lists
- Easy to add/remove dependencies
- Works with inheritance hierarchies
Supported Access Modifiers
Property/field injection works with:
- Public properties and fields
- Protected properties and fields
- Private properties and fields (with setter)
- Readonly fields
Flexible Lifetime Management
PyNurseInjector provides multiple ways to control service lifetimes.
Supported Lifetimes
Transient
New instance created every time
ServiceLifetime.Transient
Scoped
One instance per request/scope
ServiceLifetime.Scoped
Singleton
Single instance for app lifetime
ServiceLifetime.Singleton
Lifetime Configuration Methods
1. Global Default
// Set default lifetime for namespace
services.AddServicesFrom("MyApp.Services", ServiceLifetime.Scoped);
2. Per-Type Override
[ServiceLifeTime(ServiceLifetime.Singleton)]
public class CacheService : ICacheService { }
3. Per-Interface Override
[RegisterAs(typeof(IService), ServiceLifetime.Singleton)]
[RegisterAs(typeof(IDisposable), ServiceLifetime.Transient)]
public class MyService : IService, IDisposable { }
Custom Configuration Options
Fine-tune registration behavior with powerful configuration options.
DotNurseInjectorOptions
Interface Selection
services.AddServicesFrom("MyApp.Services", options =>
{
// Choose primary interface when multiple exist
options.SelectInterface = interfaces =>
interfaces.FirstOrDefault(i => i.Name.StartsWith("I"));
// Or use custom logic
options.SelectInterface = interfaces =>
interfaces.OrderBy(i => i.Name.Length).FirstOrDefault();
});
Implementation Filtering
services.AddServicesFrom("MyApp.Services", options =>
{
// Exclude certain implementations
options.SelectImplementation = type =>
!type.Name.Contains("Mock") && !type.Name.Contains("Test");
// Only include specific patterns
options.SelectImplementation = type =>
type.Name.EndsWith("Service") || type.Name.EndsWith("Repository");
});
Base Type Filtering
services.AddServicesFrom("MyApp.Data", options =>
{
// Only register types inheriting from BaseRepository
options.ImplementationBase = typeof(BaseRepository);
});
Self Registration
services.AddServicesFrom("MyApp.Services", options =>
{
// Also register implementation type itself
options.SelfRegister = true;
});
// Allows both:
services.GetService<IUserService>();
services.GetService<UserService>();
Advanced Type Exploration
PyNurseInjector provides powerful type discovery capabilities.
Expression-Based Discovery
// Register using lambda expressions
services.AddServicesFrom(type =>
type.Namespace != null &&
type.Namespace.StartsWith("MyApp.") &&
type.Name.EndsWith("Service"));
// Complex filtering
services.AddServicesFrom(type =>
{
var hasInterface = type.GetInterfaces().Any();
var isPublic = type.IsPublic;
var notAbstract = !type.IsAbstract;
var hasAttribute = type.GetCustomAttribute<ServiceAttribute>() != null;
return hasInterface && isPublic && notAbstract && hasAttribute;
});
Assembly Scanning
// Scan specific assemblies
services.AddServicesFromAssembly(typeof(UserService).Assembly);
// Scan multiple assemblies
var assemblies = new[]
{
typeof(UserService).Assembly,
typeof(ProductRepository).Assembly
};
foreach (var assembly in assemblies)
{
services.AddServicesFromAssembly(assembly);
}
Performance Optimizations
PyNurseInjector is designed with performance in mind.
Optimizations
- Reflection Caching: Type information is cached after first scan
- Lazy Loading: Services are only instantiated when needed
- Minimal Overhead: Property injection adds minimal runtime overhead
- Smart Scanning: Efficient namespace filtering reduces scan time
Benchmark Results
| Operation | PyNurseInjector | Manual Registration | Difference |
|---|---|---|---|
| Startup (100 services) | 12ms | 8ms | +4ms |
| Service Resolution | 0.1μs | 0.1μs | ~0μs |
| Property Injection | 0.3μs | N/A | +0.3μs |
Compatibility
PyNurseInjector is designed to work seamlessly with the .NET ecosystem.
Framework Support
- .NET 5.0+
- .NET Core 3.1
- ASP.NET Core 3.1+
- .NET Standard 2.0+
Integration with Other DI Features
// Works alongside manual registration
services.AddServicesFrom("MyApp.Services");
services.AddSingleton<IExternalService, ExternalService>();
// Compatible with Options pattern
services.Configure<MyOptions>(configuration.GetSection("MyOptions"));
// Works with hosted services
services.AddHostedService<BackgroundWorker>();
// Supports factory patterns
services.AddSingleton<IServiceFactory>(provider =>
new ServiceFactory(provider));
Third-Party Container Support
PyNurseInjector works with the default .NET DI container. For third-party containers like Autofac or Unity, use their respective registration methods after PyNurseInjector's initial registration.