Kiwify.Kiwi.DependencyInjection
1.0.0
Prefix Reserved
dotnet add package Kiwify.Kiwi.DependencyInjection --version 1.0.0
NuGet\Install-Package Kiwify.Kiwi.DependencyInjection -Version 1.0.0
<PackageReference Include="Kiwify.Kiwi.DependencyInjection" Version="1.0.0" />
<PackageVersion Include="Kiwify.Kiwi.DependencyInjection" Version="1.0.0" />
<PackageReference Include="Kiwify.Kiwi.DependencyInjection" />
paket add Kiwify.Kiwi.DependencyInjection --version 1.0.0
#r "nuget: Kiwify.Kiwi.DependencyInjection, 1.0.0"
#:package Kiwify.Kiwi.DependencyInjection@1.0.0
#addin nuget:?package=Kiwify.Kiwi.DependencyInjection&version=1.0.0
#tool nuget:?package=Kiwify.Kiwi.DependencyInjection&version=1.0.0
Kiwi DI
Attribute-driven dependency injection for .NET. Replace scattered startup registration code with attributes that live next to the classes they describe - registration, conditional activation, and config-driven wiring in a single AddKiwiServices call.
Includes Kiwify.Kiwi.Configuration transitively.
The Problem with Standard .NET DI
Every service you add means another line in Program.cs or Startup.cs:
// This grows without bound as the application grows
services.AddScoped<IOrderService, OrderService>();
services.AddScoped<IPaymentService, PaymentService>();
services.AddSingleton<ICacheService, RedisCacheService>();
services.AddSingleton<IEmailService, SmtpEmailService>();
// ... 40 more lines ...
// Feature flags require conditional logic here, not where the service lives
if (configuration["Features:Analytics"] == "true")
services.AddSingleton<IAnalyticsService, FullAnalyticsService>();
else
services.AddSingleton<IAnalyticsService, NoOpAnalyticsService>();
The deeper issue is the disconnection. A class exists in one file; its DI registration exists in another. To understand how a service is wired - its lifetime, conditions, and interfaces - you have to look in two places. To toggle a feature, you edit both the service and the startup file.
The Kiwi DI Approach
The class declares its own registration rules. Startup code becomes one call, regardless of how many services are added.
// Each service declares itself - no corresponding startup entry needed
[Service(ServiceLifetime.Scoped)]
public class OrderService : IOrderService { ... }
[Service(ServiceLifetime.Singleton, ConfigKey = "Features:Analytics")]
public class FullAnalyticsService : IAnalyticsService { ... }
[Service(ServiceLifetime.Singleton, ConfigKey = "Features:Analytics", Negate = true)]
public class NoOpAnalyticsService : IAnalyticsService { ... }
// Startup: this is all you write, forever
services.AddKiwiServices(configuration);
AddKiwiServices scans the assembly, evaluates conditions, and registers everything. Adding a new service means adding a class with the right attributes - the startup file does not change.
Installation
<PackageReference Include="Kiwify.Kiwi.DependencyInjection" Version="1.0.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="10.0.7" />
Kiwify.Kiwi.Configuration is included transitively.
Quick Start
Step 1 - Define config and services with attributes:
using Kiwify.Kiwi.Platform.Configuration.Attributes;
using Kiwify.Kiwi.Platform.DependencyInjection;
using Kiwify.Kiwi.Platform.DependencyInjection.Attributes;
using Microsoft.Extensions.DependencyInjection;
// Config class: auto-loaded from appsettings.json and registered as singleton
[ConfigSection("app")]
[ConfigService]
public class AppConfig
{
[ConfigKey("name", "MyApp")]
public string Name { get; private set; } = string.Empty;
[ConfigKey("port", 5000)]
public int Port { get; private set; }
}
// Service class: registered against IGreetingService automatically
public interface IGreetingService { void Greet(); }
[Service(ServiceLifetime.Singleton)]
public class GreetingService : IGreetingService
{
private readonly AppConfig _config;
public GreetingService(AppConfig config) => _config = config;
public void Greet() => Console.WriteLine($"Hello from {_config.Name} on port {_config.Port}");
}
Step 2 - One call at startup:
var services = new ServiceCollection();
services.AddKiwiServices(configuration);
var provider = services.BuildServiceProvider();
provider.GetRequiredService<IGreetingService>().Greet();
// Output: Hello from ProductionApp on port 8080
A service is registered against all non-System.* interfaces it implements by default. If it implements no interfaces, it is registered as its concrete type.
Conditional Registration
Toggle implementations with a config flag. No startup code changes required.
// Active when Features:Cache = "true"
[Service(ServiceLifetime.Singleton, ConfigKey = "Features:Cache")]
public class RedisCache : ICache { ... }
// Active when Features:Cache is not "true" (the fallback)
[Service(ServiceLifetime.Singleton, ConfigKey = "Features:Cache", Negate = true)]
public class InMemoryCache : ICache { ... }
For logic beyond a config flag - environment detection, credential availability, capacity checks - register a named predicate:
services.AddKiwiServices(configuration, options =>
{
options.AddCondition("IsProduction", sp =>
sp.GetRequiredService<IHostEnvironment>().IsProduction());
});
[Service(ServiceLifetime.Singleton, Condition = "IsProduction")]
public class ProductionTelemetry : ITelemetry { ... }
[Service(ServiceLifetime.Singleton, Condition = "IsProduction", Negate = true)]
public class NoOpTelemetry : ITelemetry { ... }
Generic Services
Register one generic class against many concrete types with [RegistersFor]:
[Service(ServiceLifetime.Singleton)]
[RegistersFor(typeof(EmailHandler))]
[RegistersFor(typeof(SmsHandler), Key = "sms")]
[RegistersFor(typeof(AuditHandler), Key = "audit", ConfigKey = "Features:Audit")]
public class NotificationManager<THandler> where THandler : class
{
public NotificationManager(THandler handler) { ... }
}
This produces three registrations from one class declaration. Adding support for a new handler type is one line.
Constructor Args from Config
When a component needs raw scalar values rather than a reference to the full config object, [ConstructFrom] moves the extraction rule to the class itself:
[Service(ServiceLifetime.Singleton)]
[ConstructFrom(typeof(AppConfig), nameof(AppConfig.Host), nameof(AppConfig.Port))]
public class TcpListener
{
public TcpListener(string host, int port) { ... }
}
Keyed Services
Use Key to register multiple implementations of the same interface under distinct names:
[Service(ServiceLifetime.Scoped, Key = "primary")]
public class PrimaryDatabase : IDatabase { ... }
[Service(ServiceLifetime.Scoped, Key = "replica")]
public class ReplicaDatabase : IDatabase { ... }
var primary = provider.GetRequiredKeyedService<IDatabase>("primary");
var replica = provider.GetRequiredKeyedService<IDatabase>("replica");
Feature Summary
| Feature | Native MS DI | Scrutor | Kiwi DI |
|---|---|---|---|
| Assembly scanning | manual | ✓ | ✓ |
| Attribute-based registration | - | - | ✓ |
| Co-located definition + registration | - | - | ✓ |
| Config-driven conditional registration | manual | - | ✓ |
| Named conditions (complex logic) | manual | - | ✓ |
| Fallback / negation pattern | manual | - | ✓ |
| Generic type expansion | manual | - | ✓ |
| Constructor args from config properties | manual | - | ✓ |
| Pre-loaded config support | manual | - | ✓ |
| Boilerplate required | high | low | minimal |
Part of Kiwi Foundation
Kiwi DI is one library in Kiwi Foundation, a suite of .NET frameworks built around a single idea: developers should focus on business logic, not infrastructure boilerplate.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 is compatible. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net5.0
- Kiwify.Kiwi.Configuration (>= 1.0.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.0.0 | 101 | 5/6/2026 |