Getting Started with PyNurseInjector
Installation
PyNurseInjector is available as NuGet packages. You'll need to install the core package and, if you're using ASP.NET Core, the ASP.NET Core integration package.
Core Library
# Package Manager Console
Install-Package DotNurse.Injector
# .NET CLI
dotnet add package DotNurse.Injector
# PackageReference
<PackageReference Include="DotNurse.Injector" Version="2.5.0" />
ASP.NET Core Integration
# Package Manager Console
Install-Package DotNurse.Injector.AspNetCore
# .NET CLI
dotnet add package DotNurse.Injector.AspNetCore
# PackageReference
<PackageReference Include="DotNurse.Injector.AspNetCore" Version="2.5.0" />
Basic Setup
Setting up PyNurseInjector in your ASP.NET Core application is straightforward. You need to configure it in two places: Program.cs and Startup.cs.
1. Enable Property Injection (Program.cs)
using DotNurse.Injector.AspNetCore;
var builder = WebApplication.CreateBuilder(args);
// Enable PyNurseInjector property injection
builder.Host.UseDotNurseInjector();
// Add services to the container
builder.Services.AddControllers();
var app = builder.Build();
// Configure the HTTP request pipeline
app.UseRouting();
app.MapControllers();
app.Run();
2. Register Services (Startup.cs or Program.cs)
using DotNurse.Injector;
// Option 1: Register all services from a namespace
builder.Services.AddServicesFrom("MyApp.Services");
// Option 2: Register only attributed services
builder.Services.AddServicesByAttributes();
// Option 3: Both!
builder.Services.AddServicesFrom("MyApp.Services");
builder.Services.AddServicesByAttributes();
Namespace-Based Registration
One of the most powerful features of PyNurseInjector is the ability to automatically register all services from a namespace.
Basic Namespace Registration
// Register all types from a namespace as Transient (default)
services.AddServicesFrom("MyApp.Services");
// Specify a different lifetime
services.AddServicesFrom("MyApp.Repositories", ServiceLifetime.Scoped);
// Include sub-namespaces
services.AddServicesFrom("MyApp", deep: true);
How It Works
PyNurseInjector scans the specified namespace and:
- Finds all classes that implement interfaces
- Registers each implementation with its interface(s)
- Skips types marked with [DontRegister]
- Respects [ServiceLifeTime] attributes
Example Project Structure
MyApp.Services/
├── UserService.cs → IUserService
├── ProductService.cs → IProductService
├── OrderService.cs → IOrderService
└── EmailService.cs → IEmailService
// One line registers all of them!
services.AddServicesFrom("MyApp.Services");
Attribute-Based Registration
For more control over individual services, use attributes to specify exactly how each service should be registered.
Available Attributes
[RegisterAs] Attribute
// Register with a specific interface
[RegisterAs(typeof(IEmailService))]
public class EmailService : IEmailService, INotificationService
{
// Will only be registered as IEmailService
}
// Register with multiple interfaces
[RegisterAs(typeof(IEmailService))]
[RegisterAs(typeof(INotificationService), ServiceLifetime.Singleton)]
public class EmailService : IEmailService, INotificationService
{
// Registered as both interfaces with different lifetimes
}
[ServiceLifeTime] Attribute
// Override the default lifetime
[ServiceLifeTime(ServiceLifetime.Singleton)]
public class ConfigurationService : IConfigurationService
{
// Registered as Singleton instead of default Transient
}
[DontRegister] Attribute
// Exclude from automatic registration
[DontRegister]
public class InternalHelper : IHelper
{
// Won't be registered even if in scanned namespace
}
Enabling Attribute Registration
// Only register types that have [RegisterAs] attribute
services.AddServicesByAttributes();
// Or scan specific assemblies
services.AddServicesByAttributes(typeof(MyService).Assembly);
Property Injection
PyNurseInjector allows you to inject dependencies directly into properties and fields, eliminating constructor clutter.
Basic Property Injection
public class ProductController : Controller
{
[InjectService]
public IProductService ProductService { get; private set; }
[InjectService]
public ILogger<ProductController> Logger { get; private set; }
// No constructor needed!
public IActionResult Index()
{
Logger.LogInformation("Loading products");
var products = ProductService.GetAll();
return View(products);
}
}
Field Injection
public class OrderService : IOrderService
{
[InjectService]
private readonly IProductService productService;
[InjectService]
protected IUserService userService;
public Order CreateOrder(int userId, List<int> productIds)
{
var user = userService.GetById(userId);
var products = productService.GetByIds(productIds);
// Create order logic...
}
}
Important: Property injection requires calling UseDotNurseInjector() on your host builder. Without this, the [InjectService] attributes will be ignored.
Configuration Options
PyNurseInjector provides extensive configuration options for fine-tuning service registration.
DotNurseInjectorOptions
services.AddServicesFrom("MyApp.Services", ServiceLifetime.Scoped, options =>
{
// Select which interface to use when multiple exist
options.SelectInterface = interfaces =>
interfaces.FirstOrDefault(i => i.Name.StartsWith("I"));
// Filter implementations
options.SelectImplementation = type =>
!type.Name.Contains("Legacy");
// Only register types inheriting from a base class
options.ImplementationBase = typeof(BaseService);
// Also register the implementation type itself
options.SelfRegister = true;
});
Configuration Examples
Repository Pattern Configuration
services.AddServicesFrom("MyApp.Data.Repositories", ServiceLifetime.Scoped, options =>
{
options.ImplementationBase = typeof(BaseRepository);
options.SelectInterface = interfaces =>
interfaces.FirstOrDefault(i => i.IsGenericType &&
i.GetGenericTypeDefinition() == typeof(IRepository<>));
});
Service Layer Configuration
services.AddServicesFrom("MyApp.Services", ServiceLifetime.Transient, options =>
{
options.SelectImplementation = type =>
type.Name.EndsWith("Service") && !type.IsAbstract;
options.SelfRegister = false; // Only register interfaces
});
Best Practices
1. Organize by Feature
MyApp/
├── Features/
│ ├── Users/
│ │ ├── IUserService.cs
│ │ ├── UserService.cs
│ │ └── UserRepository.cs
│ └── Products/
│ ├── IProductService.cs
│ ├── ProductService.cs
│ └── ProductRepository.cs
2. Use Consistent Naming
// Good: Interface and implementation names match
public interface IUserService { }
public class UserService : IUserService { }
// Avoid: Inconsistent naming
public interface IUserManager { }
public class UserSvc : IUserManager { } // Hard to auto-register
3. Explicit Lifetime for Stateful Services
[ServiceLifeTime(ServiceLifetime.Singleton)]
public class CacheService : ICacheService
{
private readonly Dictionary<string, object> _cache = new();
// Singleton because it maintains state
}
4. Use [DontRegister] for Internal Types
[DontRegister]
internal class ServiceHelper
{
// Internal utility class not meant for DI
}
5. Combine Registration Methods
// Register most services by namespace
services.AddServicesFrom("MyApp.Services");
// Register special cases with attributes
services.AddServicesByAttributes();
// Manual registration for third-party types
services.AddSingleton<IMemoryCache, MemoryCache>();