|
1 | 1 | # SimpleInjection |
| 2 | + |
| 3 | +A lightweight dependency injection library for C# that combines simple DI container functionality with powerful source generation for content management. |
| 4 | + |
| 5 | +## Features |
| 6 | + |
| 7 | +### 🚀 Simple Dependency Injection |
| 8 | +- **Attribute-based registration** - Mark classes with `[Singleton]`, `[Scoped]`, or `[Transient]` |
| 9 | +- **Automatic service discovery** - No manual registration required |
| 10 | +- **Constructor injection** - Automatic dependency resolution |
| 11 | +- **Scope management** - Built-in scoped service lifetime management |
| 12 | + |
| 13 | +### ⚡ Content Source Generation |
| 14 | +- **Automatic enum generation** - Creates enums from your content collections |
| 15 | +- **Type-safe access** - Generated helper methods for accessing content by enum or index |
| 16 | +- **Performance optimized** - Uses `NamedComparer<T>` for fast dictionary lookups |
| 17 | +- **Roslyn analyzers** - Enforces best practices and catches common mistakes |
| 18 | + |
| 19 | +## Quick Start |
| 20 | + |
| 21 | +### 1. Install the Package |
| 22 | +```bash |
| 23 | +dotnet add package SimpleInjection |
| 24 | +``` |
| 25 | + |
| 26 | +### 2. Dependency Injection Usage |
| 27 | + |
| 28 | +Mark your classes with lifetime attributes: |
| 29 | + |
| 30 | +```csharp |
| 31 | +[Singleton] |
| 32 | +public class DatabaseService |
| 33 | +{ |
| 34 | + public void Connect() => Console.WriteLine("Connected to database"); |
| 35 | +} |
| 36 | + |
| 37 | +[Scoped] |
| 38 | +public class UserService |
| 39 | +{ |
| 40 | + private readonly DatabaseService _database; |
| 41 | + |
| 42 | + public UserService(DatabaseService database) |
| 43 | + { |
| 44 | + _database = database; |
| 45 | + } |
| 46 | + |
| 47 | + public void GetUser() => _database.Connect(); |
| 48 | +} |
| 49 | +``` |
| 50 | + |
| 51 | +Initialize and use the host: |
| 52 | + |
| 53 | +```csharp |
| 54 | +var host = Host.Initialize(); |
| 55 | + |
| 56 | +// Get singleton services directly |
| 57 | +var dbService = host.Get<DatabaseService>(); |
| 58 | + |
| 59 | +// Create scopes for scoped services |
| 60 | +using var scope = host.CreateScope(); |
| 61 | +var userService = scope.Get<UserService>(); |
| 62 | +``` |
| 63 | + |
| 64 | +### 3. Content Generation Usage |
| 65 | + |
| 66 | +Define your content classes: |
| 67 | + |
| 68 | +```csharp |
| 69 | +// Your content item must implement INamed |
| 70 | +public record Material(string Name, string Color, int Durability) : INamed; |
| 71 | + |
| 72 | +// Your content collection must implement IContent<T> |
| 73 | +[Singleton] |
| 74 | +public partial class Materials : IContent<Material> |
| 75 | +{ |
| 76 | + public Material[] All { get; } = |
| 77 | + [ |
| 78 | + new("Steel", "Gray", 100), |
| 79 | + new("Wood", "Brown", 50), |
| 80 | + new("Gold", "Yellow", 25) |
| 81 | + ]; |
| 82 | +} |
| 83 | +``` |
| 84 | + |
| 85 | +The source generator automatically creates: |
| 86 | + |
| 87 | +```csharp |
| 88 | +// Generated enum |
| 89 | +public enum MaterialsType |
| 90 | +{ |
| 91 | + Steel, |
| 92 | + Wood, |
| 93 | + Gold |
| 94 | +} |
| 95 | + |
| 96 | +// Generated helper methods |
| 97 | +public partial class Materials |
| 98 | +{ |
| 99 | + public Material Get(MaterialsType type) => All[(int)type]; |
| 100 | + public Material this[MaterialsType type] => All[(int)type]; |
| 101 | + public Material GetById(int id) => All[id]; |
| 102 | + public Material Steel => All[0]; |
| 103 | + public Material Wood => All[1]; |
| 104 | + public Material Gold => All[2]; |
| 105 | +} |
| 106 | +``` |
| 107 | + |
| 108 | +Use the generated code: |
| 109 | + |
| 110 | +```csharp |
| 111 | +var materials = host.Get<Materials>(); |
| 112 | + |
| 113 | +// Type-safe access using enums |
| 114 | +var steel = materials[MaterialsType.Steel]; |
| 115 | +var wood = materials.Get(MaterialsType.Wood); |
| 116 | + |
| 117 | +// Direct property access |
| 118 | +var gold = materials.Gold; |
| 119 | + |
| 120 | +// Index-based access |
| 121 | +var firstMaterial = materials.GetById(0); |
| 122 | +``` |
| 123 | + |
| 124 | +## Advanced Features |
| 125 | + |
| 126 | +### Performance Optimizations |
| 127 | + |
| 128 | +The library includes `NamedComparer<T>` for efficient dictionary operations with `INamed` keys: |
| 129 | + |
| 130 | +```csharp |
| 131 | +// Use ToNamedDictionary extension method |
| 132 | +var materialDict = materials.All.ToNamedDictionary(m => m.Durability); |
| 133 | + |
| 134 | +// Or explicitly specify the comparer |
| 135 | +var dict = new Dictionary<Material, int>(new NamedComparer<Material>()); |
| 136 | +``` |
| 137 | + |
| 138 | +### SubContent Collections |
| 139 | + |
| 140 | +For hierarchical content organization: |
| 141 | + |
| 142 | +```csharp |
| 143 | +public class WeaponStats : ISubContent<Material, int> |
| 144 | +{ |
| 145 | + public Dictionary<Material, int> ByKey { get; } |
| 146 | + public int this[Material material] => ByKey[material]; |
| 147 | +} |
| 148 | +``` |
| 149 | + |
| 150 | +### Roslyn Analyzers |
| 151 | + |
| 152 | +The package includes analyzers that help you: |
| 153 | +- **NC001**: Ensures `Dictionary<TKey, TValue>` uses `NamedComparer<T>` for `INamed` keys |
| 154 | +- **TND001**: Suggests using `ToNamedDictionary()` instead of `ToDictionary()` for `INamed` keys |
| 155 | + |
| 156 | +## Requirements |
| 157 | + |
| 158 | +- .NET 8.0 or .NET 9.0 |
| 159 | +- C# with nullable reference types enabled (recommended) |
| 160 | + |
| 161 | +## How It Works |
| 162 | + |
| 163 | +1. **Service Discovery**: The host scans all loaded assemblies for classes marked with lifetime attributes |
| 164 | +2. **Dependency Resolution**: Constructor parameters are automatically resolved from registered services |
| 165 | +3. **Source Generation**: The generator scans for classes implementing `IContent<T>` and generates enums and helper methods |
| 166 | +4. **Code Analysis**: Roslyn analyzers ensure best practices for dictionary usage with named keys |
| 167 | + |
| 168 | +## Best Practices |
| 169 | + |
| 170 | +- Use `[Singleton]` for stateless services and shared resources |
| 171 | +- Use `[Scoped]` for services that should be unique per operation/request |
| 172 | +- Use `[Transient]` for lightweight, stateless services that need fresh instances |
| 173 | +- Always implement `INamed` for content objects to enable source generation |
| 174 | +- Use the generated enums for type-safe content access |
| 175 | +- Leverage `ToNamedDictionary()` for performance when working with `INamed` collections |
| 176 | + |
| 177 | +## License |
| 178 | + |
| 179 | +MIT License - see the license file for details. |
0 commit comments