Skip to content

Lightweight C# DI container with source generators - automatic service discovery, type-safe content enums, and Roslyn analyzers for optimal performance

License

Notifications You must be signed in to change notification settings

DerekGooding/SimpleInjection

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

40 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

SimpleInjection

A lightweight dependency injection library for C# that combines simple DI container functionality with powerful source generation for content management.

Features

πŸš€ Simple Dependency Injection

  • Attribute-based registration - Mark classes with [Singleton], [Scoped], or [Transient]
  • Automatic service discovery - No manual registration required
  • Constructor injection - Automatic dependency resolution
  • Scope management - Built-in scoped service lifetime management

⚑ Content Source Generation

  • Automatic enum generation - Creates enums from your content collections
  • Type-safe access - Generated helper methods for accessing content by enum or index
  • Performance optimized - Uses NamedComparer<T> for fast dictionary lookups
  • Roslyn analyzers - Enforces best practices and catches common mistakes

Quick Start

1. Install the Package

dotnet add package SimpleInjection

2. Dependency Injection Usage

Mark your classes with lifetime attributes:

[Singleton] public class DatabaseService { public void Connect() => Console.WriteLine("Connected to database"); } [Scoped] public class UserService { private readonly DatabaseService _database; public UserService(DatabaseService database) { _database = database; } public void GetUser() => _database.Connect(); }

Initialize and use the host:

var host = Host.Initialize(); // Get singleton services directly var dbService = host.Get<DatabaseService>(); // Create scopes for scoped services using var scope = host.CreateScope(); var userService = scope.Get<UserService>();

3. Content Generation Usage

Define your content classes:

// Your content item must implement INamed public record Material(string Name, string Color, int Durability) : INamed; // Your content collection must implement IContent<T> [Singleton] public partial class Materials : IContent<Material> { public Material[] All { get; } = [ new("Steel", "Gray", 100), new("Wood", "Brown", 50), new("Gold", "Yellow", 25) ]; }

The source generator automatically creates:

// Generated enum public enum MaterialsType { Steel, Wood, Gold } // Generated helper methods public partial class Materials { public Material Get(MaterialsType type) => All[(int)type]; public Material this[MaterialsType type] => All[(int)type]; public Material GetById(int id) => All[id]; public Material Steel => All[0]; public Material Wood => All[1]; public Material Gold => All[2]; }

Use the generated code:

var materials = host.Get<Materials>(); // Type-safe access using enums var steel = materials[MaterialsType.Steel]; var wood = materials.Get(MaterialsType.Wood); // Direct property access var gold = materials.Gold; // Index-based access var firstMaterial = materials.GetById(0);

Advanced Features

Performance Optimizations

The library includes NamedComparer<T> for efficient dictionary operations with INamed keys:

// Use ToNamedDictionary extension method var materialDict = materials.All.ToNamedDictionary(m => m.Durability); // Or explicitly specify the comparer var dict = new Dictionary<Material, int>(new NamedComparer<Material>());

SubContent Collections

For hierarchical content organization:

public class WeaponStats : ISubContent<Material, int> { public Dictionary<Material, int> ByKey { get; } public int this[Material material] => ByKey[material]; }

Roslyn Analyzers

The package includes analyzers that help you:

  • NC001: Ensures Dictionary<TKey, TValue> uses NamedComparer<T> for INamed keys
  • TND001: Suggests using ToNamedDictionary() instead of ToDictionary() for INamed keys

Requirements

  • .NET 8.0 or .NET 9.0
  • C# with nullable reference types enabled (recommended)

How It Works

  1. Service Discovery: The host scans all loaded assemblies for classes marked with lifetime attributes
  2. Dependency Resolution: Constructor parameters are automatically resolved from registered services
  3. Source Generation: The generator scans for classes implementing IContent<T> and generates enums and helper methods
  4. Code Analysis: Roslyn analyzers ensure best practices for dictionary usage with named keys

Best Practices

  • Use [Singleton] for stateless services and shared resources
  • Use [Scoped] for services that should be unique per operation/request
  • Use [Transient] for lightweight, stateless services that need fresh instances
  • Always implement INamed for content objects to enable source generation
  • Use the generated enums for type-safe content access
  • Leverage ToNamedDictionary() for performance when working with INamed collections

License

MIT License - see the license file for details.