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
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Kiwify.Kiwi.DependencyInjection" Version="1.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Kiwify.Kiwi.DependencyInjection" Version="1.0.0" />
                    
Directory.Packages.props
<PackageReference Include="Kiwify.Kiwi.DependencyInjection" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Kiwify.Kiwi.DependencyInjection --version 1.0.0
                    
#r "nuget: Kiwify.Kiwi.DependencyInjection, 1.0.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Kiwify.Kiwi.DependencyInjection@1.0.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Kiwify.Kiwi.DependencyInjection&version=1.0.0
                    
Install as a Cake Addin
#tool nuget:?package=Kiwify.Kiwi.DependencyInjection&version=1.0.0
                    
Install as a Cake Tool

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 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

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