Part of Kiwi Foundation - frameworks, libraries, and tools for .NET applications.
Platform-level libraries that bring a declarative, attribute-driven model to configuration loading and dependency injection. Declare behavior where it is defined, not in startup code.
| Library | Package | Purpose |
|---|---|---|
| Kiwi Config | Kiwify.Kiwi.Configuration |
Bind IConfiguration to strongly-typed objects using attributes |
| Kiwi DI | Kiwify.Kiwi.DependencyInjection |
Auto-register services and configs using attributes |
The two libraries are independent but designed to compose. Use Kiwi Config alone, Kiwi DI alone, or both together.
Configuration only:
dotnet add package Kiwify.Kiwi.Configuration
Dependency Injection (includes Configuration transitively):
dotnet add package Kiwify.Kiwi.DependencyInjection
Or via PackageReference:
<!-- Configuration only -->
<PackageReference Include="Kiwify.Kiwi.Configuration" Version="1.0.0" />
<!-- DI (includes Configuration) -->
<PackageReference Include="Kiwify.Kiwi.DependencyInjection" Version="1.0.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="10.0.7" />Before:
// Scattered, stringly-typed, disconnected from the classes it wires
services.AddSingleton(new DatabaseConfig
{
Host = config["database:host"] ?? "localhost",
Port = int.Parse(config["database:port"] ?? "5432"),
});
services.AddScoped<IOrderService, OrderService>();
services.AddSingleton<ICacheService>(sp => {
var cfg = sp.GetRequiredService<DatabaseConfig>();
return new RedisCacheService(cfg.Host, cfg.Port);
});After:
// One call. Everything declared on the classes themselves.
services.AddKiwiServices(configuration);[ConfigSection("database")]
[ConfigService]
public class DatabaseConfig
{
[ConfigKey("host", "localhost")]
public string Host { get; private set; } = string.Empty;
[ConfigKey("port", 5432)]
public int Port { get; private set; }
}
[Service(ServiceLifetime.Scoped)]
public class OrderService : IOrderService { ... }
[Service(ServiceLifetime.Singleton)]
[ConstructFrom(typeof(DatabaseConfig), nameof(DatabaseConfig.Host), nameof(DatabaseConfig.Port))]
public class RedisCacheService : ICacheService
{
public RedisCacheService(string host, int port) { ... }
}// Load config into a typed object -- no DI container required
var cfg = configuration.LoadConfiguration<MyConfig>();
// With ILogger for diagnostics
var cfg = configuration.LoadConfiguration<MyConfig>(logger);| Attribute | Target | Purpose |
|---|---|---|
[ConfigSection("key")] |
Class | Declares config root key |
[ConfigKey("key", default)] |
Property | Binds to a config value with optional default |
[ConfigKey("/key")] |
Property | Absolute path - ignores parent section prefix |
[ConfigObject] |
Property | Embeds a nested config object |
[ConfigKey("key", Required = true)] |
Property | Throws at startup if key is absent |
Default value priority: config value -> [ConfigKey] default -> GetDefault{Prop}() method -> type zero value
// One-call setup - scans assembly, loads configs, registers services
services.AddKiwiServices(configuration);
// With named conditions for complex logic
services.AddKiwiServices(configuration, options => {
options.AddCondition("IsProduction", sp => ...);
});| Attribute | Target | Purpose |
|---|---|---|
[ConfigService] |
Class | Auto-load as config singleton |
[Service(Lifetime)] |
Class | Auto-register service against its interfaces |
[Service(..., ConfigKey = "F:X")] |
Class | Register only when config["F:X"] == "true" |
[Service(..., Condition = "name")] |
Class | Register only when named condition is true |
[Service(..., Negate = true)] |
Class | Register only when condition is false |
[Service(..., Key = "k")] |
Class | Keyed service registration |
[RegistersFor(typeof(T))] |
Generic class | One registration per concrete type |
[ConstructFrom(typeof(C), "Prop")] |
Class | Extract config properties as constructor args |
src/
Kiwify.Kiwi.Configuration/ -- Configuration library
Kiwify.Kiwi.DependencyInjection/ -- DI library
docs/
getting-started.md -- Step-by-step guide (Config + DI together)
config/
overview.md -- What Kiwi Config is and why it exists
binding.md -- Attributes, type conversions, defaults, required fields
advanced.md -- Nested config, logging, performance, real-world example
dependency-injection/
overview.md -- What Kiwi DI is, mental model, AddKiwiServices phases
registration.md -- [Service], conditions, fallbacks, keyed services
advanced.md -- [RegistersFor], [ConstructFrom], pre-loaded configs
articles/
kiwi-config-intro.md -- Narrative introduction to Kiwi Config
kiwi-di-intro.md -- Narrative introduction to Kiwi DI
samples/
config/
Sample.BasicConfiguration/ -- [ConfigSection], [ConfigKey], defaults, type conversion
Sample.AdvancedConfiguration/ -- Nested objects, absolute paths, collections, GetDefault
di/
Sample.BasicDI/ -- [Service], [ConfigService], lifetimes
Sample.ConditionalRegistration/ -- Feature flags, fallbacks, named conditions
Sample.GenericServices/ -- [RegistersFor] with generic types
Sample.ConstructFrom/ -- Property extraction into constructors
Each sample is a self-contained console application.
Configuration samples:
dotnet run --project samples/config/Sample.BasicConfiguration
dotnet run --project samples/config/Sample.AdvancedConfigurationDependency Injection samples:
dotnet run --project samples/di/Sample.BasicDI
dotnet run --project samples/di/Sample.ConditionalRegistration
dotnet run --project samples/di/Sample.GenericServices
dotnet run --project samples/di/Sample.ConstructFrom| Getting Started | New to Kiwi Platform? Start here. Covers both libraries together. |
| Kiwi Config - Overview | What Kiwi Config is and why it exists |
| Kiwi Config - Binding | Attributes, type conversions, defaults, required fields |
| Kiwi Config - Advanced | Nested config, logging, performance, real-world example |
| Kiwi DI - Overview | What Kiwi DI is, mental model, AddKiwiServices phases |
| Kiwi DI - Registration | [Service], conditions, fallbacks, keyed services |
| Kiwi DI - Advanced | [RegistersFor], [ConstructFrom], pre-loaded configs |
MIT