### Basic Setup for Asynchronous Interception Source: https://github.com/castleproject/core/blob/master/docs/dynamicproxy-async-interception.md This code demonstrates the basic setup for asynchronous interception using Castle Project Core. It shows how to create a proxy for an interface and an example interceptor. ```csharp var serviceProxy = proxyGenerator.CreateInterfaceProxyWithoutTarget(new AsyncInterceptor()); // Examples will show how interception gets triggered: int result = await serviceProxy.GetAsync(); public interface IService { Task DoAsync(); Task GetAsync(int n); } class AsyncInterceptor : IInterceptor { // Examples will mostly focus on this method's implementation: public void Intercept(IInvocation invocation) { ... } } ``` -------------------------------- ### DictionaryAdapter Setup Source: https://github.com/castleproject/core/blob/master/docs/dictionaryadapter-customize-keys.md Provides the initial setup code for creating a DictionaryAdapter instance. ```csharp var dictionary = new Hashtable(); var adapter = new DictionaryAdapterFactory.GetAdapter(dictionary); ``` -------------------------------- ### Install markdownlint Source: https://github.com/castleproject/core/blob/master/docs/CONTRIBUTING.md Install the markdownlint gem to check documentation syntax. ```bash gem install mdl ``` -------------------------------- ### Interface and Class Example Source: https://github.com/castleproject/core/blob/master/docs/dynamicproxy-leaking-this.md Demonstrates a simple interface and class pair where the `Bar` method returns `this`, setting up a scenario for `this` leakage when proxied. ```csharp public interface IFoo { IFoo Bar(); } public class Foo : IFoo { public IFoo Bar() { return this; } } ``` -------------------------------- ### Basic Logging with ILogger and ConsoleFactory Source: https://context7.com/castleproject/core/llms.txt Implement logging in your application using Castle's ILogger abstraction. Inject ILogger via the constructor and use a LoggerFactory to create logger instances. This example uses ConsoleFactory for console output. ```csharp using Castle.Core.Logging; public class OrderProcessor { private readonly ILogger _logger; // Inject logger through constructor public OrderProcessor(ILogger logger) { _logger = logger ?? NullLogger.Instance; } public void ProcessOrder(Order order) { _logger.InfoFormat("Processing order {0} for customer {1}", order.Id, order.CustomerId); try { // Process the order ValidateOrder(order); _logger.Debug("Order validation passed"); CalculateTotals(order); _logger.DebugFormat("Order total: {0:C}", order.Total); SaveOrder(order); _logger.InfoFormat("Order {0} processed successfully", order.Id); } catch (ValidationException ex) { _logger.WarnFormat(ex, "Validation failed for order {0}", order.Id); throw; } catch (Exception ex) { _logger.ErrorFormat(ex, "Failed to process order {0}", order.Id); throw; } } } // Using ConsoleLogger var consoleFactory = new ConsoleFactory(); var consoleLogger = consoleFactory.Create(typeof(OrderProcessor)); var processor1 = new OrderProcessor(consoleLogger); // Using TraceLogger var traceFactory = new TraceLoggerFactory(); var traceLogger = traceFactory.Create("OrderProcessor"); var processor2 = new OrderProcessor(traceLogger); // Using NullLogger (for testing or when logging not needed) var processor3 = new OrderProcessor(NullLogger.Instance); ``` -------------------------------- ### Selective Method Interception with IProxyGenerationHook Source: https://context7.com/castleproject/core/llms.txt Implement IProxyGenerationHook to control which methods are intercepted, optimizing performance by skipping unnecessary interceptions. This example intercepts public methods not starting with 'Get'. ```csharp using Castle.DynamicProxy; public class OnlyPublicMethodsHook : IProxyGenerationHook { public void MethodsInspected() { } public void NonProxyableMemberNotification(Type type, MemberInfo memberInfo) { } public bool ShouldInterceptMethod(Type type, MethodInfo methodInfo) { // Only intercept public methods that don't start with "Get" return methodInfo.IsPublic && !methodInfo.Name.StartsWith("Get"); } // Override Equals/GetHashCode for proper proxy caching public override bool Equals(object obj) => obj is OnlyPublicMethodsHook; public override int GetHashCode() => typeof(OnlyPublicMethodsHook).GetHashCode(); } public class CountingInterceptor : IInterceptor { public int CallCount { get; private set; } public void Intercept(IInvocation invocation) { CallCount++; invocation.Proceed(); } } public class DataService { public virtual string GetData() => "data"; // Not intercepted (starts with Get) public virtual void SaveData(string data) { } // Intercepted public virtual void ProcessData() { } // Intercepted } var generator = new ProxyGenerator(); var options = new ProxyGenerationOptions(new OnlyPublicMethodsHook()); var interceptor = new CountingInterceptor(); var proxy = generator.CreateClassProxy(options, interceptor); proxy.GetData(); // Not intercepted proxy.SaveData("x"); // Intercepted proxy.ProcessData(); // Intercepted Console.WriteLine($"Intercepted calls: {interceptor.CallCount}"); // 2 ``` -------------------------------- ### DictionaryAdapter for Nested Structures Source: https://context7.com/castleproject/core/llms.txt Use DictionaryAdapter with the Component attribute to manage complex nested configurations. This example demonstrates creating an adapter for nested interfaces like IDatabaseSettings and ICacheSettings. ```csharp using Castle.Components.DictionaryAdapter; public interface IDatabaseSettings { string Host { get; set; } int Port { get; set; } string Name { get; set; } } public interface ICacheSettings { bool Enabled { get; set; } int DurationMinutes { get; set; } } public interface IApplicationConfig { string AppName { get; set; } [Component] IDatabaseSettings Database { get; } [Component] ICacheSettings Cache { get; } } var dictionary = new Dictionary { ["AppName"] = "MyApplication", ["Database_Host"] = "db.example.com", ["Database_Port"] = 3306, ["Database_Name"] = "production", ["Cache_Enabled"] = true, ["Cache_DurationMinutes"] = 60 }; var factory = new DictionaryAdapterFactory(); var config = factory.GetAdapter(dictionary); Console.WriteLine($"App: {config.AppName}"); Console.WriteLine($"DB: {config.Database.Host}:{config.Database.Port}/{config.Database.Name}"); Console.WriteLine($"Cache: {(config.Cache.Enabled ? "Enabled" : "Disabled")}, {config.Cache.DurationMinutes}min"); // Nested adapters are also writable config.Database.Host = "new-db.example.com"; Console.WriteLine($"Updated dictionary: {dictionary["Database_Host"]}"); // new-db.example.com ``` -------------------------------- ### Customize Proxy Generation with Mixins Source: https://context7.com/castleproject/core/llms.txt Use `ProxyGenerationOptions` to add mixins to proxies, allowing them to implement additional interfaces. This example demonstrates adding an `IDisposableService` mixin to a proxy. ```csharp using Castle.DynamicProxy; public interface IService { string Process(string input); } public interface IDisposableService : IDisposable { bool IsDisposed { get; } } // Mixin implementation public class DisposableMixin : IDisposableService { public bool IsDisposed { get; private set; } public void Dispose() => IsDisposed = true; } public class ServiceInterceptor : IInterceptor { public void Intercept(IInvocation invocation) { if (invocation.Method.Name == "Process") { invocation.ReturnValue = $"Processed: {invocation.Arguments[0]}"; } else { invocation.Proceed(); } } } var generator = new ProxyGenerator(); var options = new ProxyGenerationOptions(); options.AddMixinInstance(new DisposableMixin()); var proxy = generator.CreateInterfaceProxyWithoutTarget(options, new ServiceInterceptor()); // Use main interface var result = ((IService)proxy).Process("data"); Console.WriteLine(result); // "Processed: data" // Use mixin interface var disposable = (IDisposableService)proxy; Console.WriteLine(disposable.IsDisposed); // false disposable.Dispose(); Console.WriteLine(disposable.IsDisposed); // true ``` -------------------------------- ### Intercepting Task Methods Without Return Value (Async/Await) Source: https://github.com/castleproject/core/blob/master/docs/dynamicproxy-async-interception.md This example demonstrates intercepting an awaitable method returning a Task using async/await within the interceptor. The Intercept method delegates to an async method, allowing the use of await for asynchronous operations. ```csharp // calling code: await serviceProxy.DoAsync(); // interception: public void Intercept(IInvocation invocation) { invocation.ReturnValue = InterceptAsync(invocation); } private async Task InterceptAsync(IInvocation invocation) { // In this method, you have the comfort of using `await`: var response = await httpClient.PostAsync(...); ... } ``` -------------------------------- ### Configure NLog and Serilog Logging Source: https://context7.com/castleproject/core/llms.txt Demonstrates how to initialize NLog and Serilog factories and use the ILogger abstraction for logging operations. ```csharp using Castle.Core.Logging; using Castle.Services.Logging.NLogIntegration; using Castle.Services.Logging.SerilogIntegration; // NLog Integration public class NLogExample { public static void Configure() { // Create NLog factory var nlogFactory = new NLogFactory(); // Or extended factory for more features var extendedNLogFactory = new ExtendedNLogFactory(); var logger = nlogFactory.Create(typeof(NLogExample)); logger.Info("Application started"); logger.DebugFormat("Debug info: {0}", "details"); logger.ErrorFormat("Error occurred: {0}", "message"); } } // Serilog Integration public class SerilogExample { public static void Configure() { // Create Serilog logger first var serilogLogger = new Serilog.LoggerConfiguration() .MinimumLevel.Debug() .WriteTo.Console() .WriteTo.File("logs/app.log", rollingInterval: RollingInterval.Day) .CreateLogger(); // Create Castle factory with Serilog logger var serilogFactory = new SerilogFactory(serilogLogger); var logger = serilogFactory.Create(typeof(SerilogExample)); logger.Info("Serilog integrated with Castle.Core"); logger.DebugFormat("Processing item {0}", 42); } } // Generic service using ILogger abstraction public class GenericService { private readonly ILogger _logger; public GenericService(ILoggerFactory factory) { _logger = factory.Create(GetType()); } public void DoWork() { _logger.Trace("Entering DoWork"); // Most verbose _logger.Debug("Debug information"); _logger.Info("Processing started"); _logger.Warn("Warning condition"); _logger.Error("Error occurred"); _logger.Fatal("Fatal error"); // Most severe } } ``` -------------------------------- ### Implement Service for plain C# demonstration Source: https://github.com/castleproject/core/blob/master/docs/dynamicproxy-by-ref-parameters.md Implementation of IService to show standard aliasing behavior. ```csharp sealed class Service : IService { private ExecuteDelegate execute; public Service(ExecuteDelegate execute) => this.execute = execute; public void Execute(ref int n) => this.execute?.Invoke(ref n); } delegate void ExecuteDelegate(ref int n); ``` -------------------------------- ### Run markdownlint Source: https://github.com/castleproject/core/blob/master/docs/CONTRIBUTING.md Execute the linter within the documentation directory using the specified style file. ```bash cd doc mdl --style=markdownlint.rb . ``` -------------------------------- ### Instantiating Proxy with Target Source: https://github.com/castleproject/core/blob/master/docs/dynamicproxy-internals.md When instantiating a proxy created with a target, the target object is passed to the proxy's constructor. ```csharp var proxy = new FooProxy(target, interceptors /* passed to `CreateClassProxy` */); return (Foo)proxy; ``` -------------------------------- ### Conditional Interception Logic in Interceptor Source: https://github.com/castleproject/core/blob/master/docs/dynamicproxy-fine-grained-control.md Avoid placing method filtering logic directly within interceptors. Instead, use IProxyGenerationHook to exclude methods from proxying or IInterceptorSelector to choose specific interceptors for a method. This example demonstrates the less efficient, conditional approach within an interceptor. ```csharp public void Intercept(IInvocation invocation) { if (invocation.TargetType != typeof(Foo)) { invocation.Proceed(); return; } if (invocation.Method.Name != "Bar") { invocation.Proceed(); return; } if (invocation.Method.GetParameters().Length != 3) { invocation.Proceed(); return; } DoSomeActualWork(invocation); } ``` -------------------------------- ### Using KeyPrefixAttribute for Key Prefixes Source: https://github.com/castleproject/core/blob/master/docs/dictionaryadapter-customize-keys.md Shows how to use KeyPrefixAttribute to add a common prefix to all dictionary keys derived from an interface. ```csharp [KeyPrefix("Person")] public interface IPerson { string Name {get; set;} } ``` ```csharp adapter.Name == dictionary["PersonName"]; ``` -------------------------------- ### Create Class Proxy with Target Source: https://github.com/castleproject/core/blob/master/docs/dynamicproxy-internals.md Use `CreateClassProxyWithTarget` to generate a proxy that forwards calls to a provided target instance. This is useful when you need to proxy an existing object. ```csharp public abstract class Foo { public abstract void Bar(); } sealed class FooTarget : Foo { public override void Bar() { /* ... */ } } var proxy = proxyGenerator.CreateClassProxyWithTarget(target: new FooTarget(), new SomeInterceptor()); ``` -------------------------------- ### Import DictionaryAdapter Namespace Source: https://github.com/castleproject/core/blob/master/docs/dictionaryadapter.md Required namespace import after referencing Castle.Core.dll. ```csharp using Castle.Components.DictionaryAdapter; ``` -------------------------------- ### Default Property to Key Mapping Source: https://github.com/castleproject/core/blob/master/docs/dictionaryadapter-customize-keys.md Demonstrates the default behavior where property names directly map to dictionary keys. ```csharp adapter.Name = "Stefan"; ``` ```csharp dictionary["Name"] = "Stefan"; ``` ```csharp var name = adapter.Name; ``` ```csharp var name = (string)dictionary["Name"]; ``` -------------------------------- ### Modify Foo class to accept arguments Source: https://github.com/castleproject/core/blob/master/docs/dynamicproxy-internals.md Demonstrates changing a parameterless method to accept an integer argument. ```diff public abstract class Foo { - public abstract void Bar(); + public abstract void Bar(int arg); } ``` -------------------------------- ### Interface Proxy Generation With Target Source: https://github.com/castleproject/core/blob/master/docs/dynamicproxy-internals.md Shows the generated code for an interface proxy with a target. The `__target` field holds the provided target object, and the constructor parameters reflect its inclusion. ```csharp namespace Castle.Proxies { public partial class FooProxy : Foo { /* ... */ private IInterceptor[] __interceptors; private Foo __target; public FooProxy( IInterceptor[] interceptors, Foo target) : base() { __target = target; __interceptors = interceptors; } /* ... */ } } ``` -------------------------------- ### Generated Class Proxy with Target Structure Source: https://github.com/castleproject/core/blob/master/docs/dynamicproxy-internals.md When a proxy is created with a target, the generated `FooProxy` class includes a `__target` field and the constructor accepts the target instance. The `Bar` method now uses the `__target` for invocation. ```csharp namespace Castle.Proxies { public partial class FooProxy : Foo { /* ... */ private IInterceptor[] __interceptors; private Foo __target; public FooProxy(Foo target, IInterceptor[] interceptors) : base() { __target = target; __interceptors = interceptors; } public override void Bar() { var invocation = new Foo_Bar(target: __target, proxy: this, interceptors: __interceptors, proxiedMethod: token_Bar, arguments: new object[0]); invocation.Proceed(); } } } namespace Castle.Proxies.Invocations { public partial class Foo_Bar : CompositionInvocation { public Foo_Bar(Foo target, object proxy, IInterceptor[] interceptors, MethodInfo proxiedMethod, object[] arguments) : base(target, proxy, interceptors, proxiedMethod, arguments) { } public override void InvokeMethodOnTarget() { EnsureValidTarget(); (target as Foo).Bar(); } } } ``` -------------------------------- ### Create Interface Proxy With Target Source: https://github.com/castleproject/core/blob/master/docs/dynamicproxy-internals.md Use this method to create an interface proxy when a target object is available and its methods should be invoked. The target object is passed to the proxy's constructor. ```csharp var proxy = proxyGenerator.CreateInterfaceProxyWithTarget(target: new FooTarget(), new SomeInterceptor()); ``` -------------------------------- ### Create Class Proxies with DynamicProxy Source: https://context7.com/castleproject/core/llms.txt Generates inheritance-based proxies for classes, allowing interception of virtual methods. Ensure the ProxyGenerator instance is reused for optimal performance. ```csharp using Castle.DynamicProxy; // Define a class with virtual methods public class Calculator { public virtual int Add(int a, int b) => a + b; public virtual int Multiply(int a, int b) => a * b; } // Create an interceptor to add logging behavior public class LoggingInterceptor : IInterceptor { public void Intercept(IInvocation invocation) { Console.WriteLine($"Calling: {invocation.Method.Name}({string.Join(", ", invocation.Arguments)})"); invocation.Proceed(); // Call the actual method Console.WriteLine($"Result: {invocation.ReturnValue}"); } } // Usage - reuse ProxyGenerator instance for performance var generator = new ProxyGenerator(); var proxy = generator.CreateClassProxy(new LoggingInterceptor()); var result = proxy.Add(5, 3); // Output: // Calling: Add(5, 3) // Result: 8 ``` -------------------------------- ### Define Abstract Class for Proxying Source: https://github.com/castleproject/core/blob/master/docs/dynamicproxy-internals.md Define an abstract class that will be used as a base for generating a proxy. The `Bar` method is abstract and must be implemented by the proxy. ```csharp public abstract class Foo { public abstract void Bar(); } ``` -------------------------------- ### Create Interface Proxies with Target Source: https://context7.com/castleproject/core/llms.txt Generates composition-based proxies that wrap an existing object instance. Interceptors can forward calls to the target or implement cross-cutting concerns like caching. ```csharp using Castle.DynamicProxy; public interface IOrderService { Order GetOrder(int id); void SaveOrder(Order order); } public class OrderService : IOrderService { public Order GetOrder(int id) => new Order { Id = id, Total = 99.99m }; public void SaveOrder(Order order) => Console.WriteLine($"Saved order {order.Id}"); } // Interceptor for caching public class CachingInterceptor : IInterceptor { private readonly Dictionary _cache = new(); public void Intercept(IInvocation invocation) { var key = $"{invocation.Method.Name}:{string.Join(",", invocation.Arguments)}"; if (invocation.Method.Name.StartsWith("Get") && _cache.TryGetValue(key, out var cached)) { Console.WriteLine($"Cache hit for {key}"); invocation.ReturnValue = cached; return; } invocation.Proceed(); if (invocation.Method.Name.StartsWith("Get")) { _cache[key] = invocation.ReturnValue; Console.WriteLine($"Cached result for {key}"); } } } // Create proxy with target var generator = new ProxyGenerator(); var target = new OrderService(); var proxy = generator.CreateInterfaceProxyWithTarget(target, new CachingInterceptor()); var order1 = proxy.GetOrder(42); // Hits target, caches result var order2 = proxy.GetOrder(42); // Returns from cache ``` -------------------------------- ### Modify Foo class to use out parameter Source: https://github.com/castleproject/core/blob/master/docs/dynamicproxy-internals.md Demonstrates changing an integer parameter to an out parameter. ```diff public abstract class Foo { - public abstract void Bar( int arg); + public abstract void Bar(out int arg); } ``` -------------------------------- ### Generate Class Proxy with Interceptor Source: https://github.com/castleproject/core/blob/master/docs/dynamicproxy-internals.md Use `proxyGenerator.CreateClassProxy` to generate a proxy for the `Foo` class, specifying an interceptor to handle method calls. ```csharp var proxy = proxyGenerator.CreateClassProxy(new SomeInterceptor()); ``` -------------------------------- ### Custom KeyPostfix Attribute Implementation Source: https://github.com/castleproject/core/blob/master/docs/dictionaryadapter-customize-keys.md Provides a C# implementation for a custom KeyPostfixAttribute that appends a specified string to dictionary keys. ```csharp [AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)] public class KeyPostfixAttribute : DictionaryBehaviorAttribute, IDictionaryKeyBuilder { private String postfix; public KeyPrefixAttribute(string keyPrefix) { this.postfix = keyPrefix; } String IDictionaryKeyBuilder.GetKey(IDictionaryAdapter dictionaryAdapter, String key, PropertyDescriptor property) { return key + postfix; } } ``` -------------------------------- ### Implement Interceptor with IInvocation Source: https://context7.com/castleproject/core/llms.txt Shows how to access method details, arguments, and return values within an IInterceptor implementation using IInvocation. ```csharp using Castle.DynamicProxy; public class DetailedInterceptor : IInterceptor { public void Intercept(IInvocation invocation) { // Get method information var method = invocation.Method; Console.WriteLine($"Method: {method.DeclaringType.Name}.{method.Name}"); // Access arguments var args = invocation.Arguments; for (int i = 0; i < args.Length; i++) { Console.WriteLine($" Arg[{i}]: {args[i]} ({method.GetParameters()[i].Name})"); } // Get/set specific argument var firstArg = invocation.GetArgumentValue(0); invocation.SetArgumentValue(0, ModifyArgument(firstArg)); // Access generic arguments if method is generic if (invocation.GenericArguments != null) { Console.WriteLine($" Generic args: {string.Join(", ", invocation.GenericArguments.Select(t => t.Name))}"); } // Access proxy and target var proxy = invocation.Proxy; var target = invocation.InvocationTarget; // The target object (if any) var targetType = invocation.TargetType; // The target type var targetMethod = invocation.MethodInvocationTarget; // Method on target // Call the actual method invocation.Proceed(); // Access/modify return value var returnValue = invocation.ReturnValue; Console.WriteLine($" Return value: {returnValue}"); invocation.ReturnValue = ModifyReturnValue(returnValue); // For async scenarios, capture proceed info var proceedInfo = invocation.CaptureProceedInfo(); // Can call proceedInfo.Invoke() later in continuations } } public interface ICalculator { int Add(int a, int b); T GetDefault(); } var generator = new ProxyGenerator(); var proxy = generator.CreateInterfaceProxyWithoutTarget(new DetailedInterceptor()); proxy.Add(5, 3); // Output: // Method: ICalculator.Add // Arg[0]: 5 (a) // Arg[1]: 3 (b) // Return value: 8 ``` -------------------------------- ### Using KeyAttribute for Unrelated Keys Source: https://github.com/castleproject/core/blob/master/docs/dictionaryadapter-customize-keys.md Illustrates how to use the KeyAttribute to specify a completely different dictionary key for a property. ```csharp public interface IPerson { [Key("PersonId")] string Name {get; set;} } ``` ```csharp adapter.Name == dictionary["PersonId"]; ``` -------------------------------- ### Proxy Usage Leading to Leak Source: https://github.com/castleproject/core/blob/master/docs/dynamicproxy-leaking-this.md Illustrates how using a proxy for `IFoo` can lead to a bug where the second call to `Bar` is made on the target object, not the proxy. ```csharp var foo = GetFoo(); // returns proxy var bar = foo.Bar(); bar.Bar(); ``` -------------------------------- ### Generated proxy code for method arguments Source: https://github.com/castleproject/core/blob/master/docs/dynamicproxy-internals.md Shows the generated proxy implementation that transfers arguments into the IInvocation.Arguments array. ```diff namespace Castle.Proxies { public partial class FooProxy : Foo { /* ... */ - public override void Bar() + public override void Bar(int arg) { var invocation = new Foo_Bar(target: __target, proxy: this, interceptors: __interceptors, proxiedMethod: token_Bar, - arguments: new object[0]); + arguments: new object[1] { arg }); invocation.Proceed(); } } } ``` -------------------------------- ### Implement a custom IInterceptor Source: https://github.com/castleproject/core/blob/master/docs/dynamicproxy-introduction.md A basic implementation of the IInterceptor interface that wraps a method call with logging and exception handling. ```csharp [Serializable] public class Interceptor : IInterceptor { public void Intercept(IInvocation invocation) { Console.WriteLine("Before target call"); try { invocation.Proceed(); } catch(Exception) { Console.WriteLine("Target threw an exception!"); throw; } finally { Console.WriteLine("After target call"); } } } ``` -------------------------------- ### Generated FooProxy Class Source: https://github.com/castleproject/core/blob/master/docs/dynamicproxy-internals.md This is a simplified representation of the `FooProxy` class generated by DynamicProxy. It implements the `Foo` abstract class and handles method interceptions. ```csharp using System; using System.Reflection; using Castle.DynamicProxy; using Castle.DynamicProxy.Internals; namespace Castle.Proxies { public partial class FooProxy : Foo { private static MethodInfo token_Bar; static FooProxy() { token_Bar = (MethodInfo)MethodBase.GetMethodFromHandle(/* method handle for `Foo.Bar` */, typeof(Foo).TypeHandle); } private IInterceptor[] __interceptors; public FooProxy(IInterceptor[] interceptors) : base() { __interceptors = interceptors; } public override void Bar() { var invocation = new Foo_Bar(targetType: typeof(Foo), proxy: this, interceptors: __interceptors, proxiedMethod: token_Bar, arguments: new object[0]); invocation.Proceed(); } } } ``` -------------------------------- ### Using TypeKeyPrefixAttribute for Interface-Specific Prefixes Source: https://github.com/castleproject/core/blob/master/docs/dictionaryadapter-customize-keys.md Demonstrates using TypeKeyPrefixAttribute to prefix dictionary keys with the full interface name, preventing key collisions. ```csharp namespace Acme.Crm; [TypeKeyPrefix] public interface IPerson { string Name {get; set;} } ``` ```csharp adapter.Name == dictionary["Acme.Crm.IPerson#Name"]; ``` -------------------------------- ### Correct async interception with CaptureProceedInfo Source: https://github.com/castleproject/core/blob/master/docs/dynamicproxy-async-interception.md To correctly handle `invocation.Proceed()` in async methods, use `invocation.CaptureProceedInfo()` before the `await`. The returned object can then be used to invoke the rest of the interception pipeline or the target method after the `await` completes. ```csharp // calling code--as before: await serviceProxy.DoAsync(); // interception: public void Intercept(IInvocation invocation) { invocation.ReturnValue = InterceptAsync(invocation); } private async Task InterceptAsync(IInvocation invocation) { // If we want to `Proceed` at any point in this method, // we need to capture how far in the invocation pipeline // we're currently located *before* we `await`: var proceed = invocation.CaptureProceedInfo(); await ...; // At this point, interception is completed and we have // a "stale" invocation that has been reset to the very // beginning of the interception pipeline. However, // that doesn't mean that we cannot send it onward to the // remaining interceptors or to the proxy target object: proceed.Invoke(); // At this point, a later interceptor might have over- // written `invocation.ReturnValue`. As explained earlier, // while the calling code will no longer observe this // value, we could inspect it to set our own task's // result (if we returned a `Task`). } ``` -------------------------------- ### Customize Dictionary Key Mapping Source: https://context7.com/castleproject/core/llms.txt Apply attributes like KeyPrefix, Key, and KeySubstitution to control how interface properties map to underlying dictionary keys. ```csharp using Castle.Components.DictionaryAdapter; // Use KeyPrefix to add common prefix to all keys [KeyPrefix("App_")] public interface IPrefixedSettings { string DatabaseHost { get; set; } // Maps to "App_DatabaseHost" int DatabasePort { get; set; } // Maps to "App_DatabasePort" } // Use Key attribute for explicit key mapping public interface ILegacySettings { [Key("DB_HOST")] string DatabaseHost { get; set; } [Key("DB_PORT")] int DatabasePort { get; set; } [Key("MAX_CONN")] int MaxConnections { get; set; } } // Use KeySubstitution for character replacement public interface IXmlSettings { [KeySubstitution("_", ".")] string Database_Connection_String { get; set; } // Maps to "Database.Connection.String" [KeySubstitution("_", ":")] string Logging_Level { get; set; } // Maps to "Logging:Level" } // Use TypeKeyPrefix for namespace-based keys [TypeKeyPrefix] public interface MyApp.Config.IServiceSettings { string Endpoint { get; set; } // Maps to "MyApp.Config.IServiceSettings#Endpoint" } var factory = new DictionaryAdapterFactory(); // Example with legacy settings var legacyDict = new Dictionary { ["DB_HOST"] = "localhost", ["DB_PORT"] = 5432, ["MAX_CONN"] = 100 }; var legacy = factory.GetAdapter(legacyDict); Console.WriteLine($"Host: {legacy.DatabaseHost}"); // localhost Console.WriteLine($"Port: {legacy.DatabasePort}"); // 5432 ``` -------------------------------- ### Interface Proxy Generation Without Target Source: https://github.com/castleproject/core/blob/master/docs/dynamicproxy-internals.md Illustrates the generated code for an interface proxy without a target. Note the presence of a `__target` field, which is unused in this scenario, and the use of `InterfaceMethodWithoutTargetInvocation`. ```csharp namespace Castle.Proxies { public partial class FooProxy : Foo { /* ... */ private IInterceptor[] __interceptors; private object __target; public FooProxy(IInterceptor[] interceptors, object target) : base() { __interceptors = interceptors; __target = target; } public override void Bar() { var invocation = new InterfaceMethodWithoutTargetInvocation(target: null, proxy: this, interceptors: __interceptors, proxiedMethod: token_Bar, arguments: new object[0]); invocation.Proceed(); } } } ``` -------------------------------- ### Generated proxy code for out parameters Source: https://github.com/castleproject/core/blob/master/docs/dynamicproxy-internals.md Shows the generated proxy implementation that handles copying updated out parameter values back from the IInvocation.Arguments array. ```diff namespace Castle.Proxies { public partial class FooProxy : Foo { // ... - public override void Bar( int arg) + public override void Bar(out int arg) { + // arg = default(int); var invocation = new Foo_Bar(targetType: typeof(Foo), proxy: this, interceptors: __interceptors, proxiedMethod: token_Bar, arguments: new object[1] { arg }); + try + { invocation.Proceed(); + } + finally + { + var arguments = invocation.Arguments; + arg = (int)arguments[0]; + } } } } ``` -------------------------------- ### Handling Method Parameters in Proxies with Target Source: https://github.com/castleproject/core/blob/master/docs/dynamicproxy-internals.md When the proxied method has parameters, the generated proxy's method signature and the invocation arguments are updated accordingly. The `InvokeMethodOnTarget` method retrieves and passes arguments to the target method. ```csharp public abstract class Foo { public abstract void Bar(int arg); } namespace Castle.Proxies { public partial class FooProxy : Foo { /* ... */ public override void Bar(int arg) { var invocation = new Foo_Bar(target: __target, proxy: this, interceptors: __interceptors, proxiedMethod: token_Bar, arguments: new object[1] { arg }); invocation.Proceed(); } } } namespace Castle.Proxies.Invocations { public partial class Foo_Bar : CompositionInvocation { /* ... */ public override void InvokeMethodOnTarget() { EnsureValidTarget(); (target as Foo).Bar((int)GetArgumentValue(0)); } } } ``` -------------------------------- ### Read from Dictionary via Adapter Source: https://github.com/castleproject/core/blob/master/docs/dictionaryadapter.md Create an adapter using DictionaryAdapterFactory to read values from a dictionary. ```csharp var dictionary = new Hashtable(); var factory = new DictionaryAdapterFactory(); var adapter = factory.GetAdapter(dictionary); dictionary["Message"] = "Hello world!"; Debug.Assert(adapter.Message == "Hello world!"); ``` -------------------------------- ### Updated Generated Code for Proceeding Source: https://github.com/castleproject/core/blob/master/docs/dynamicproxy-internals.md Shows the diff in generated code when the base class method is no longer abstract. A new callback method `Bar_callback` is generated to handle calling the base method due to accessibility restrictions. ```diff namespace Castle.Proxies { public partial class FooProxy : Foo { /* ... */ - public override void Bar() + protected override void Bar() { /* ... */ } + public override void Bar_callback() + { + base.Bar(); + } } } namespace Castle.Proxies.Invocations { public partial class Foo_Bar : InheritanceInvocation { /* ... */ public override void InvokeMethodOnTarget() { - ThrowOnNoTarget(); + (proxyObject as FooProxy).Bar_callback(); } } } ``` -------------------------------- ### Create Strongly-Typed Dictionary Wrappers Source: https://context7.com/castleproject/core/llms.txt Use DictionaryAdapterFactory to map dictionary keys to interface properties for compile-time safety. Supports both Hashtable and generic Dictionary types. ```csharp using Castle.Components.DictionaryAdapter; using System.Collections; public interface IAppSettings { string ConnectionString { get; set; } int MaxRetries { get; set; } bool EnableCaching { get; set; } TimeSpan Timeout { get; set; } } // Create adapter from dictionary var dictionary = new Hashtable { ["ConnectionString"] = "Server=localhost;Database=mydb", ["MaxRetries"] = 3, ["EnableCaching"] = true, ["Timeout"] = TimeSpan.FromSeconds(30) }; var factory = new DictionaryAdapterFactory(); var settings = factory.GetAdapter(dictionary); // Read values with strong typing Console.WriteLine($"Connection: {settings.ConnectionString}"); Console.WriteLine($"Max Retries: {settings.MaxRetries}"); Console.WriteLine($"Caching: {settings.EnableCaching}"); // Write values settings.MaxRetries = 5; Console.WriteLine($"Dictionary value: {dictionary["MaxRetries"]}"); // 5 // Works with generic dictionaries too var genericDict = new Dictionary { ["ConnectionString"] = "Server=prod;Database=live", ["MaxRetries"] = 10, ["EnableCaching"] = false, ["Timeout"] = TimeSpan.FromMinutes(1) }; var prodSettings = factory.GetAdapter(genericDict); Console.WriteLine($"Prod Connection: {prodSettings.ConnectionString}"); ``` -------------------------------- ### Select Interceptors at Runtime with DynamicProxy Source: https://context7.com/castleproject/core/llms.txt Implement IInterceptorSelector to dynamically determine which interceptors execute for specific methods. Requires a ProxyGenerator and ProxyGenerationOptions configuration. ```csharp using Castle.DynamicProxy; public class LoggingInterceptor : IInterceptor { public void Intercept(IInvocation invocation) { Console.WriteLine($"LOG: {invocation.Method.Name}"); invocation.Proceed(); } } public class AuthorizationInterceptor : IInterceptor { public void Intercept(IInvocation invocation) { Console.WriteLine($"AUTH: Checking permission for {invocation.Method.Name}"); invocation.Proceed(); } } public class MethodSelector : IInterceptorSelector { public IInterceptor[] SelectInterceptors(Type type, MethodInfo method, IInterceptor[] interceptors) { // Apply auth interceptor only to methods starting with "Save" or "Delete" if (method.Name.StartsWith("Save") || method.Name.StartsWith("Delete")) { return interceptors; // Return all interceptors } // For other methods, only use logging return interceptors.Where(i => i is LoggingInterceptor).ToArray(); } } public interface IDocumentService { Document GetDocument(int id); void SaveDocument(Document doc); void DeleteDocument(int id); } var generator = new ProxyGenerator(); var options = new ProxyGenerationOptions { Selector = new MethodSelector() }; var interceptors = new IInterceptor[] { new LoggingInterceptor(), new AuthorizationInterceptor() }; var proxy = generator.CreateInterfaceProxyWithTarget(target, options, interceptors); proxy.GetDocument(1); // Only LoggingInterceptor runs proxy.SaveDocument(doc); // Both interceptors run proxy.DeleteDocument(1); // Both interceptors run ``` -------------------------------- ### Modify Base Class Method for Proceeding Source: https://github.com/castleproject/core/blob/master/docs/dynamicproxy-internals.md Change the `Bar` method in the base class `Foo` from abstract to `protected virtual`. This allows `invocation.Proceed` to call the base class implementation. ```diff public abstract class Foo { - public abstract void Bar(); + protected virtual void Bar() { } } ``` -------------------------------- ### Define DictionaryAdapter Interface Source: https://github.com/castleproject/core/blob/master/docs/dictionaryadapter.md Define a plain interface to represent the dictionary structure. ```csharp public interface IHelloWorld { string Message { get; } } ``` -------------------------------- ### Handle Async Methods with Task Return Values Source: https://context7.com/castleproject/core/llms.txt Implement an interceptor to correctly manage Task return values for asynchronous methods. Ensure proper pipeline behavior by using CaptureProceedInfo before awaiting. ```csharp using Castle.DynamicProxy; public interface IAsyncRepository { Task GetUserAsync(int id); Task SaveUserAsync(User user); } public class AsyncTimingInterceptor : IInterceptor { public void Intercept(IInvocation invocation) { var returnType = invocation.Method.ReturnType; if (returnType == typeof(Task)) { invocation.ReturnValue = InterceptAsync(invocation); } else if (returnType.IsGenericType && returnType.GetGenericTypeDefinition() == typeof(Task<>)) { invocation.ReturnValue = InterceptAsyncWithResult(invocation); } else { invocation.Proceed(); } } private async Task InterceptAsync(IInvocation invocation) { // Capture proceed info BEFORE any await var proceed = invocation.CaptureProceedInfo(); var sw = Stopwatch.StartNew(); proceed.Invoke(); // Await the actual task await (Task)invocation.ReturnValue; Console.WriteLine($"{invocation.Method.Name} completed in {sw.ElapsedMilliseconds}ms"); } private async Task InterceptAsyncWithResult(IInvocation invocation) { var proceed = invocation.CaptureProceedInfo(); var sw = Stopwatch.StartNew(); proceed.Invoke(); var result = await (Task)invocation.ReturnValue; Console.WriteLine($"{invocation.Method.Name} returned {result} in {sw.ElapsedMilliseconds}ms"); return result; } private Task InterceptAsyncWithResult(IInvocation invocation) { var resultType = invocation.Method.ReturnType.GetGenericArguments()[0]; var method = typeof(AsyncTimingInterceptor) .GetMethod(nameof(InterceptAsyncWithResult), BindingFlags.NonPublic | BindingFlags.Instance) .MakeGenericMethod(resultType); return (Task)method.Invoke(this, new object[] { invocation }); } } ``` -------------------------------- ### Create Interface Proxy Without Target Source: https://context7.com/castleproject/core/llms.txt Use `ProxyGenerator.CreateInterfaceProxyWithoutTarget` to create proxies for interfaces when no actual target implementation is available. Interceptors must provide all behavior, which is useful for mocking and testing. ```csharp using Castle.DynamicProxy; public interface IEmailService { void SendEmail(string to, string subject, string body); bool IsValidEmail(string email); } // Interceptor that provides implementation public class MockEmailInterceptor : IInterceptor { public List<(string To, string Subject, string Body)> SentEmails { get; } = new(); public void Intercept(IInvocation invocation) { switch (invocation.Method.Name) { case "SendEmail": var to = (string)invocation.Arguments[0]; var subject = (string)invocation.Arguments[1]; var body = (string)invocation.Arguments[2]; SentEmails.Add((to, subject, body)); Console.WriteLine($"Mock: Would send email to {to}"); break; case "IsValidEmail": var email = (string)invocation.Arguments[0]; invocation.ReturnValue = email.Contains("@") && email.Contains("."); break; default: throw new NotImplementedException($"Method {invocation.Method.Name} not mocked"); } } } var generator = new ProxyGenerator(); var interceptor = new MockEmailInterceptor(); var mockService = generator.CreateInterfaceProxyWithoutTarget(interceptor); mockService.SendEmail("test@example.com", "Hello", "World"); var isValid = mockService.IsValidEmail("test@example.com"); // Returns true Console.WriteLine($"Emails sent: {interceptor.SentEmails.Count}"); // 1 ``` -------------------------------- ### Invoke IService in plain C# Source: https://github.com/castleproject/core/blob/master/docs/dynamicproxy-by-ref-parameters.md Demonstrates that changes to ref parameters are immediately reflected in the aliased variable. ```csharp int n = 0; var service = new Service( execute: (ref int alias) => { Console.WriteLine(n); // => 0 alias = 42; Console.WriteLine(n); // => 42 }); Console.WriteLine(n); // => 0 service.Execute(ref n); Console.WriteLine(n); // => 42 ``` -------------------------------- ### Instantiate and Cast Generated Proxy Source: https://github.com/castleproject/core/blob/master/docs/dynamicproxy-internals.md After the proxy types are generated, an instance of `FooProxy` is created with the provided interceptors and cast to the base type `Foo`. ```csharp var proxy = new FooProxy(interceptors /* passed to `CreateClassProxy` */); return (Foo)proxy; ``` -------------------------------- ### Create Class Proxy with Interceptor Source: https://github.com/castleproject/core/blob/master/docs/dynamicproxy-internals.md Use ProxyGenerator to create a class proxy for a given type, applying a standard interceptor. Ensure the target type and interceptor are correctly instantiated. ```csharp var proxy = proxyGenerator.CreateClassProxy(new StandardInterceptor()); ``` -------------------------------- ### Implement IInterceptor for DynamicProxy Source: https://github.com/castleproject/core/blob/master/docs/dynamicproxy-by-ref-parameters.md Interceptor implementation that delegates the invocation logic to a provided action. ```csharp sealed class Interceptor : IInterceptor { private Action intercept; public Interceptor(Action intercept) => this.intercept = intercept; public void Intercept(IInvocation invocation) => this.intercept?.Invoke(invocation); } ``` -------------------------------- ### Define IService interface Source: https://github.com/castleproject/core/blob/master/docs/dynamicproxy-by-ref-parameters.md Interface used for demonstrating by-reference parameter behavior. ```csharp public interface IService { void Execute(ref int n); } ``` -------------------------------- ### Log4net Integration with Castle Logging Source: https://context7.com/castleproject/core/llms.txt Integrate Castle's logging abstraction with the log4net framework using Log4netFactory. This allows leveraging log4net's features like configuration files and different appenders. Ensure log4net is configured in your application startup. ```csharp using Castle.Core.Logging; using Castle.Services.Logging.Log4netIntegration; // Configure log4net (typically in application startup) // log4net.Config.XmlConfigurator.Configure(); public class PaymentService { private readonly ILogger _logger; public PaymentService(ILoggerFactory loggerFactory) { _logger = loggerFactory.Create(typeof(PaymentService)); } public PaymentResult ProcessPayment(Payment payment) { using (_logger.ThreadStacks["PaymentId"].Push(payment.Id.ToString())) { _logger.InfoFormat("Starting payment processing for amount {0:C}", payment.Amount); if (payment.Amount <= 0) { _logger.Warn("Invalid payment amount"); return new PaymentResult { Success = false, Error = "Invalid amount" }; } try { // Process payment var result = ChargeCard(payment); if (result.Success) _logger.Info("Payment processed successfully"); else _logger.WarnFormat("Payment declined: {0}", result.Error); return result; } catch (Exception ex) { _logger.Fatal("Payment processing failed with unhandled exception", ex); throw; } } } } // Setup with log4net var loggerFactory = new Log4netFactory(); var paymentService = new PaymentService(loggerFactory); // For extended logging features (MDC, NDC support) var extendedFactory = new ExtendedLog4netFactory(); var extendedLogger = extendedFactory.Create(typeof(PaymentService)); ``` -------------------------------- ### Invoke IService with DynamicProxy interception Source: https://github.com/castleproject/core/blob/master/docs/dynamicproxy-by-ref-parameters.md Demonstrates that changes to ref parameters via IInvocation are not immediately reflected in the aliased variable. ```csharp int n = 0; var service = new ProxyGenerator().CreateInterfaceProxyWithoutTarget(new Interceptor( intercept: invocation => { Console.WriteLine(n); // => 0 invocation.SetArgumentValue(0, 42); Console.WriteLine(n); // => 0 !!! })); Console.WriteLine(n); // => 0 service.Execute(ref n); Console.WriteLine(n); // => 42 ```