MitchCodes.DIDynamicProxy.DotNet 1.0.0

dotnet add package MitchCodes.DIDynamicProxy.DotNet --version 1.0.0
                    
NuGet\Install-Package MitchCodes.DIDynamicProxy.DotNet -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="MitchCodes.DIDynamicProxy.DotNet" Version="1.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="MitchCodes.DIDynamicProxy.DotNet" Version="1.0.0" />
                    
Directory.Packages.props
<PackageReference Include="MitchCodes.DIDynamicProxy.DotNet" />
                    
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 MitchCodes.DIDynamicProxy.DotNet --version 1.0.0
                    
#r "nuget: MitchCodes.DIDynamicProxy.DotNet, 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 MitchCodes.DIDynamicProxy.DotNet@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=MitchCodes.DIDynamicProxy.DotNet&version=1.0.0
                    
Install as a Cake Addin
#tool nuget:?package=MitchCodes.DIDynamicProxy.DotNet&version=1.0.0
                    
Install as a Cake Tool

MitchCodes.DIDynamicProxy.DotNet

MitchCodes.DIDynamicProxy.DotNet is a library designed to simplify the integration of dynamic proxies into .NET applications using Dependency Injection (DI) with Castle.Core IInterceptors and Castle.Core.AsyncInterceptor IAsyncInterceptors. This library is aimed at developers looking to enhance their applications with aspects such as logging, caching, security, and more, by intercepting method calls on objects at runtime. By leveraging the functionality provided, developers can apply cross-cutting concerns to their services without modifying the actual code of their classes, thus adhering to the Open/Closed principle.

Features

  • Easy registration of classes for proxying within the Microsoft.Extensions.DependencyInjection DI container.
  • Support for synchronous and asynchronous method interception using Castle.Core interceptors.
  • Support for dependency injection inside the interceptors.
  • Flexible configuration for interceptors, allowing for both instance-based and type-based registration.
  • Integration with Castle.Core.AsyncInterceptor for handling asynchronous method calls seamlessly.
  • Utility methods for service lifetime management, making it straightforward to control the scope of proxied services.
  • Convenient abstract classes for creating interceptors that are controlled via an Attribute

Requirements

  • .NET 6 and up

Getting Started

To use MitchCodes.DIDynamicProxy.DotNet in your project, first install the package from NuGet:

Install-Package MitchCodes.DIDynamicProxy.DotNet

Usage

Setting up interceptors and proxied services through Dependency Injection

  1. Register app-level interceptors wherever you configure your services

    This example demonstrates how to register a LogInterceptor and CacheAsyncInterceptor at the app-level that are applied to every proxied service added by this library. The interceptors can be registered as scoped, transient, singleton or singleton with a provided instance.

    using MitchCodes.DIDynamicProxy.DotNet.Extensions;
    using Microsoft.Extensions.DependencyInjection;
    
    public void ConfigureServices(IServiceCollection services)
    {
        // Assuming LogInterceptor is an IInterceptor implementation
        services.AddInterceptorSingleton<LogInterceptor>();
    
        // Assuming CacheAsyncInterceptor is an IAsyncInterceptor implementation
        services.AddAsyncInterceptorSingleton<CacheAsyncInterceptor>();
    }
    
  2. Register proxied services wherever you configure your services

    This example demonstrates how you can register services that will be created as a proxy. Any app-level interceptors will be resolved and added automatically during the creation of the proxy. Additionally, you can configure your services by either directly adding instances or by registering types of interceptors that will be added specifically to the service.

    public void ConfigureServices(IServiceCollection services)
    {
        // Assuming LogInterceptor is an IInterceptor implementation
        services.AddInterceptorSingleton<LogInterceptor>();
    
        // Register your service and its proxy as before
        services.AddSingletonDynamicProxy<IMyService, MyService>();
    
        // Register another service with some specific interceptors
        services.AddSingletonDynamicProxy<IMyOtherService, MyOtherService>(new DynamicProxiedServiceSettings()
        {
            ProxyAsyncInterceptorTypes = new List<Type>() { typeof(CacheAsyncInterceptor) }
        });
    }
    

Implementing Interceptors

  • Synchronous Interceptor

    Implement the IInterceptor interface for synchronous method interception. Here is a simple logging interceptor:

    public class LogInterceptor : IInterceptor
    {
        public void Intercept(IInvocation invocation)
        {
            Console.WriteLine($"Before executing {invocation.Method.Name}");
            invocation.Proceed();
            Console.WriteLine($"After executing {invocation.Method.Name}");
        }
    }
    
  • Asynchronous Interceptor

    For asynchronous method interception, implement the IAsyncInterceptor interface. Here is a sample async interceptor:

    public class AsyncInterceptor : IAsyncInterceptor
    {
        public void InterceptSynchronous(IInvocation invocation) => this.InternalInterceptSynchronous(invocation);
    
        public void InterceptAsynchronous(IInvocation invocation) => invocation.ReturnValue = this.InternalInterceptAsynchronous(invocation);
    
        public void InterceptAsynchronous<TResult>(IInvocation invocation) => invocation.ReturnValue = this.InternalInterceptAsynchronous<TResult>(invocation);
    
        private void InternalInterceptSynchronous(IInvocation invocation)
        {
            // Step 1. Do something prior to invocation.
    
            invocation.Proceed();
    
            // Step 2. Do something after invocation.
        }
    
        private async Task InternalInterceptAsynchronous(IInvocation invocation)
        {
            // Step 1. Do something prior to invocation.
    
            invocation.Proceed();
            var task = (Task)invocation.ReturnValue;
            await task;
    
            // Step 2. Do something after invocation.
        }
    
        private async Task<TResult> InternalInterceptAsynchronous<TResult>(IInvocation invocation)
        {
            // Step 1. Do something prior to invocation.
    
            invocation.Proceed();
            var task = (Task<TResult>)invocation.ReturnValue;
            var result = await task;
    
            // Step 2. Do something after invocation.
    
            return result;
        }
    }
    

    Read more about implementing IAsyncInterceptor here.

  • Attribute-based interception

    This library provides two abstract classes for creating interceptors that can be applied at the function-level of a proxied service: AbstractFunctionAttributeInterceptor<TAttribute> and AbstractFunctionAttributeAsyncInterceptor<TAttribute>. Using these classes, you can restrict the interception logic to only occur when the TAttribute attribute is applied to the function that is being invoked. The InterceptSynchronous, InterceptAsynchronous and InterceptAsynchronous<TResult> abstract functions are only called when the function being invoked has the attribute. The attribute is provided in the abstract functions. Below is an example of creating an attribute interceptor, using it in a service and registering it:

    public class LogAttribute : Attribute
    {
        public string CustomMessage { get; set; }
    }
    
    public class LogAsyncInterceptor : AbstractFunctionAttributeAsyncInterceptor<LogAttribute>
    {
        private readonly ILogger<LogAsyncInterceptor>? _logger;
    
        public LogAsyncInterceptor(ILogger<LogAsyncInterceptor> logger) => this._logger = logger;
    
        public override void InterceptSynchronous(IInvocation invocation, LogAttribute attribute)
        {
            // Step 1. Do something prior to invocation.
            this.LogBefore(invocation, attribute);
    
            invocation.Proceed();
    
            // Step 2. Do something after invocation.
            this.LogAfter(invocation, attribute);
        }
    
        public override async Task InterceptAsynchronous(IInvocation invocation, LogAttribute attribute)
        {
            // Step 1. Do something prior to invocation.
            this.LogBefore(invocation, attribute);
    
            invocation.Proceed();
            var task = (Task)invocation.ReturnValue;
            await task;
    
            // Step 2. Do something after invocation.
            this.LogAfter(invocation, attribute);
        }
    
        public override async Task<TResult> InterceptAsynchronous<TResult>(IInvocation invocation, LogAttribute attribute)
        {
            // Step 1. Do something prior to invocation.
            this.LogBefore(invocation, attribute);
    
            invocation.Proceed();
            var task = (Task<TResult>)invocation.ReturnValue;
            var result = await task;
    
            // Step 2. Do something after invocation.
            this.LogAfter(invocation, attribute);
    
            return result;
        }
    
        private void LogBefore(IInvocation invocation, LogAttribute attribute)
        {
            this._logger?.LogInformation($"Hello from before {invocation.Method.Name}: {attribute.CustomMessage}");
        }
    
        private void LogAfter(IInvocation invocation, LogAttribute attribute)
        {
            this._logger?.LogInformation($"Hello from after {invocation.Method.Name}: {attribute.CustomMessage}");
        }
    }
    
    public interface IEchoService
    {
        Task<string> EchoAsync(string input);
        Task<string> EchoNoLogAsync(string input);
    }
    
    public class EchoService : IEchoService
    {
        [Log(CustomMessage = "Echo")]
        public async Task<string> EchoAsync(string input)
        {
            await Task.Delay(1000);
    
            return input;
        }
    
        // just here as a demo
        public async Task<string> EchoNoLogAsync(string input)
        {
            await Task.Delay(1000);
    
            return input;
        }
    }
    
    // in Program.cs or whereever you configure your services
    public void ConfigureServices(IServiceCollection services)
    {
        // Since LogAsyncInterceptor is added at the app-level, all proxies will use this interceptor for all functions
        //  However, the requirement for the attribute means that for all other functions without the attribute, the interceptor will do nothing
        services.AddAsyncInterceptorScoped<LogAsyncInterceptor>();
    
        // Register your service and its proxy as before
        services.AddScopedDynamicProxy<IEchoService, EchoService>();
    }
    

Remarks

  • Unless you know you only are going to intercept syncronous functions, only need to do work before proceeding, or are going to manually handle Task return types from the intercepted function, I recommend to use IAsyncInterceptor. Read more about the benefits of IAsyncInterceptor here.
Product Compatible and additional computed target framework versions.
.NET net6.0 is compatible.  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 173 8/5/2025