geoder101.Microsoft.Extensions.DependencyInjection 1.0.1-alpha

This is a prerelease version of geoder101.Microsoft.Extensions.DependencyInjection.
There is a newer prerelease version of this package available.
See the version list below for details.
dotnet add package geoder101.Microsoft.Extensions.DependencyInjection --version 1.0.1-alpha
                    
NuGet\Install-Package geoder101.Microsoft.Extensions.DependencyInjection -Version 1.0.1-alpha
                    
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="geoder101.Microsoft.Extensions.DependencyInjection" Version="1.0.1-alpha" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="geoder101.Microsoft.Extensions.DependencyInjection" Version="1.0.1-alpha" />
                    
Directory.Packages.props
<PackageReference Include="geoder101.Microsoft.Extensions.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 geoder101.Microsoft.Extensions.DependencyInjection --version 1.0.1-alpha
                    
#r "nuget: geoder101.Microsoft.Extensions.DependencyInjection, 1.0.1-alpha"
                    
#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 geoder101.Microsoft.Extensions.DependencyInjection@1.0.1-alpha
                    
#: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=geoder101.Microsoft.Extensions.DependencyInjection&version=1.0.1-alpha&prerelease
                    
Install as a Cake Addin
#tool nuget:?package=geoder101.Microsoft.Extensions.DependencyInjection&version=1.0.1-alpha&prerelease
                    
Install as a Cake Tool

Microsoft.Extensions.DependencyInjection.Decorator

Extension methods for Microsoft.Extensions.DependencyInjection that enable the Decorator pattern. Easily wrap registered services with decorators while preserving service lifetimes (Transient, Scoped, Singleton).

Features

  • Simple API - Intuitive extension methods for IServiceCollection
  • Lifetime Preservation - Maintains the original service lifetime (Transient, Scoped, Singleton)
  • Multiple Decorators - Chain multiple decorators on the same service
  • Factory Support - Access IServiceProvider for advanced scenarios
  • Generic Support - Works with open generic types
  • Zero Dependencies - Only requires Microsoft.Extensions.DependencyInjection

Installation

dotnet add package geoder101.Microsoft.Extensions.DependencyInjection

Quick Start

Basic Decorator

using Microsoft.Extensions.DependencyInjection;

var services = new ServiceCollection();

// Register your service
services.AddTransient<INotificationService, EmailNotificationService>();

// Decorate it with logging
services.Decorate<INotificationService, LoggingNotificationDecorator>();

var provider = services.BuildServiceProvider();
var notificationService = provider.GetRequiredService<INotificationService>();
// Returns LoggingNotificationDecorator wrapping EmailNotificationService

Decorator with Factory

services.AddScoped<IOrderProcessor, OrderProcessor>();

// Use factory to access IServiceProvider
services.Decorate<IOrderProcessor>((provider, inner) =>
{
    var logger = provider.GetRequiredService<ILogger<OrderProcessorDecorator>>();
    return new OrderProcessorDecorator(inner, logger);
});

Chaining Multiple Decorators

services.AddTransient<ICalculator, Calculator>();

// Add multiple decorators - they wrap in order
services.Decorate<ICalculator, CachingCalculatorDecorator>();
services.Decorate<ICalculator, LoggingCalculatorDecorator>();
services.Decorate<ICalculator, ValidationCalculatorDecorator>();

// Result: ValidationCalculatorDecorator -> LoggingCalculatorDecorator -> CachingCalculatorDecorator -> Calculator

API Reference

Decorate<TService>()

Decorates all registered services of type TService with the same type.

public static IServiceCollection Decorate<TService>(
    this IServiceCollection services,
    Func<TService, TService>? configure = null)

Decorate<TService, TDecorator>()

Decorates all registered services of type TService with a decorator of type TDecorator.

public static IServiceCollection Decorate<TService, TDecorator>(
    this IServiceCollection services,
    Func<TDecorator, TService>? configure = null)
    where TDecorator : TService

Decorate<TService>() with Factory

Decorates using a factory function that has access to the service provider.

public static IServiceCollection Decorate<TService>(
    this IServiceCollection services,
    Func<IServiceProvider, TService, TService> decoratorFactory)

Decorate<TService, TDecorator>() with Factory

Decorates with a decorator type using a factory function.

public static IServiceCollection Decorate<TService, TDecorator>(
    this IServiceCollection services,
    Func<IServiceProvider, TDecorator, TService> decoratorFactory)
    where TDecorator : TService

Real-World Examples

Logging Decorator

public interface IOrderService
{
    Task<Order> PlaceOrderAsync(OrderRequest request);
}

public class OrderService : IOrderService
{
    public async Task<Order> PlaceOrderAsync(OrderRequest request)
    {
        // Implementation
    }
}

public class LoggingOrderServiceDecorator : IOrderService
{
    private readonly IOrderService _inner;
    private readonly ILogger<LoggingOrderServiceDecorator> _logger;

    public LoggingOrderServiceDecorator(IOrderService inner, ILogger<LoggingOrderServiceDecorator> logger)
    {
        _inner = inner;
        _logger = logger;
    }

    public async Task<Order> PlaceOrderAsync(OrderRequest request)
    {
        _logger.LogInformation("Placing order for customer {CustomerId}", request.CustomerId);
        try
        {
            var result = await _inner.PlaceOrderAsync(request);
            _logger.LogInformation("Order {OrderId} placed successfully", result.Id);
            return result;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Failed to place order for customer {CustomerId}", request.CustomerId);
            throw;
        }
    }
}

// Registration
services.AddScoped<IOrderService, OrderService>();
services.Decorate<IOrderService>((provider, inner) =>
{
    var logger = provider.GetRequiredService<ILogger<LoggingOrderServiceDecorator>>();
    return new LoggingOrderServiceDecorator(inner, logger);
});

Caching Decorator

public class CachingProductServiceDecorator : IProductService
{
    private readonly IProductService _inner;
    private readonly IMemoryCache _cache;

    public CachingProductServiceDecorator(IProductService inner, IMemoryCache cache)
    {
        _inner = inner;
        _cache = cache;
    }

    public async Task<Product> GetProductAsync(int id)
    {
        var cacheKey = $"product_{id}";

        if (_cache.TryGetValue(cacheKey, out Product? cachedProduct))
            return cachedProduct!;

        var product = await _inner.GetProductAsync(id);
        _cache.Set(cacheKey, product, TimeSpan.FromMinutes(5));
        return product;
    }
}

// Registration
services.AddScoped<IProductService, ProductService>();
services.Decorate<IProductService>((provider, inner) =>
{
    var cache = provider.GetRequiredService<IMemoryCache>();
    return new CachingProductServiceDecorator(inner, cache);
});

Retry Decorator with Polly

public class RetryPaymentServiceDecorator : IPaymentService
{
    private readonly IPaymentService _inner;
    private readonly IAsyncPolicy _retryPolicy;

    public RetryPaymentServiceDecorator(IPaymentService inner, IAsyncPolicy retryPolicy)
    {
        _inner = inner;
        _retryPolicy = retryPolicy;
    }

    public async Task<PaymentResult> ProcessPaymentAsync(PaymentRequest request)
    {
        return await _retryPolicy.ExecuteAsync(() => _inner.ProcessPaymentAsync(request));
    }
}

// Registration
services.AddScoped<IPaymentService, PaymentService>();
services.Decorate<IPaymentService>((provider, inner) =>
{
    var retryPolicy = Policy
        .Handle<HttpRequestException>()
        .WaitAndRetryAsync(3, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)));

    return new RetryPaymentServiceDecorator(inner, retryPolicy);
});

Service Lifetime Behavior

The decorator pattern respects and preserves the original service's lifetime:

Original Lifetime Decorator Behavior
Transient New instance created each time
Scoped Same instance within a scope
Singleton Same instance throughout application lifetime
// Singleton example
services.AddSingleton<ICache, MemoryCache>();
services.Decorate<ICache, LoggingCacheDecorator>();

var provider = services.BuildServiceProvider();
var cache1 = provider.GetRequiredService<ICache>();
var cache2 = provider.GetRequiredService<ICache>();
// cache1 and cache2 are the same instance

License

This project is licensed under the MIT License - see the LICENSE.txt file for details.

Credits

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  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 (1)

Showing the top 1 NuGet packages that depend on geoder101.Microsoft.Extensions.DependencyInjection:

Package Downloads
geoder101.MoqProxy.DependencyInjection.Microsoft

Microsoft.Extensions.DependencyInjection integration for MoqProxy. Enables wrapping DI-registered services with Moq proxies for testing - verify calls to real implementations, spy on service interactions, and test integration scenarios without modifying production code.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.6-alpha 115 10/20/2025
1.0.2-alpha 120 10/19/2025
1.0.1-alpha 115 10/19/2025