Castle Core Documentation

repository·master·Indexed 25 days ago

https://github.com/castleproject/core

Foundational abstractions for the Castle Project, featuring Castle DynamicProxy for high-performance runtime proxy generation, Castle DictionaryAdapter for creating strongly typed wrappers around untyped data structures, and common logging services. The library supports .NET 10, .NET 8, .NET 9, .NET Standard 2.0, and .NET Framework 4.6.2+.

Tokens
9.7K
Snippets
15
Records
56
Agent score
81%

What's inside Castle Core

  1. Overview of Castle Core features

    master

    Castle Core is a library providing common Castle Project abstractions. Its primary features include:

    • Logging services: Common abstractions for logging.
    • Castle DynamicProxy: A lightweight runtime proxy generator.
    • Castle DictionaryAdapter: An adapter for dictionary-based operations.
  2. Use Castle DynamicProxy to add behavior to objects

    master
    Castle DynamicProxy allows you to generate proxy objects that transparently add or alter behavior to existing objects. This is commonly used for implementing interception, mocking frameworks (like Moq or NSubstitute), and lazy loading (like in Entity Framework Core).
  3. What is Castle DictionaryAdapter

    master

    DictionaryAdapter is a lightweight tool that generates strongly typed wrappers on top of IDictionary and its generic counterparts on the fly. It provides features such as support for INotifyPropertyChanged, editability, and error handling.

    It is particularly useful for wrapping untyped dictionaries commonly found in web applications, such as Session, Form, QueryString, Context.Items, or configuration settings from app.settings/web.settings.

  4. Use Castle DictionaryAdapter for strongly typed wrappers

    master

    Castle DictionaryAdapter allows you to generate strongly typed wrappers around untyped data structures, such as dictionaries or XML chunks (e.g., configuration files). It can optionally provide support for:

    • Change notification
    • Cancellation
    • Error notification
    • Other advanced features
  5. Apply the Single Responsibility Principle to interceptors

    master

    When using DynamicProxy, avoid creating monolithic interceptor classes that handle multiple concerns (e.g., combining logging, security, and parameter verification). Instead, leverage the ability to apply multiple interceptors to a single method call.

    Recommended patterns for interceptor design:

    • General Purpose Interceptors: Create interceptors that perform a single task (like logging) and apply them globally to all intercepted methods.
    • Class-Specific Interceptors: Create interceptors for specific hierarchies (e.g., classes inheriting from a common base) that focus on one responsibility.
    • Method-Specific Interceptors: Create interceptors that target a single method on a specific class or interface.

    Use interceptor selectors to match these specialized interceptors to their intended targets, ensuring each interceptor remains focused on a single responsibility.

  6. Optimize performance by reusing ProxyGenerator instances

    master

    When working in long-running processes (such as web sites or Windows services) that require creating many dynamic proxies, you must reuse the same ProxyGenerator instance.

    Creating new instances of ProxyGenerator for every proxy will bypass the internal caching mechanism, leading to:

    • High CPU usage
    • Constant increase in memory consumption
  7. How by-reference parameters behave during DynamicProxy interception

    master

    When using DynamicProxy to intercept methods that contain by-reference parameters (ref or out in C#, ByRef in Visual Basic), changes made to these parameters during the interception process are buffered and not immediately reflected in the caller's variables.

    The Lifecycle of a By-Ref Parameter in Interception

    1. Interception Start: DynamicProxy copies all arguments from the caller into the IInvocation.Arguments array. For ref and out parameters, this is a copy by value. The indices in the Arguments array are distinct storage locations and are not immediate aliases to the caller's variables.
    2. During Interception: If you modify a ref parameter using IInvocation.Arguments or IInvocation.SetArgumentValue, the change only affects the internal buffer. The caller's original variable will not see this change while the interception is still in progress.
    3. Interception End: Once the interception completes, DynamicProxy copies the final values from the Arguments buffer back to the original ref and out parameters. Only at this point does the caller's variable reflect the updated value.

    Key Takeaway: Modifying a by-reference parameter during interception will be reflected in the aliased variable eventually, but not immediately.

  8. How the interception pipeline works

    master

    Castle DynamicProxy uses an interception pipeline to inject behavior into proxied objects. When a method is called on a proxy, the call passes through a series of interceptors before reaching the target object.

    The Pipeline Flow

    1. Method Call: A caller invokes a method on the proxy.
    2. Interceptor Execution: Each interceptor receives an IInvocation object containing metadata about the call (e.g., MethodInfo, parameters, references to the proxy and target).
    3. Proceeding: An interceptor calls invocation.Proceed() to pass control to the next interceptor in the pipeline or to the target object itself.
      • You can call Proceed() multiple times (e.g., for retry logic).
      • You can omit Proceed() to short-circuit the pipeline (preventing the target method from being called).
    4. Target Invocation: When the final interceptor calls Proceed(), the actual method on the target object is executed.
    5. Return/Exception Flow: The call travels back up the pipeline, allowing interceptors to inspect the ReturnValue or handle exceptions thrown by the target.
    6. Completion: The proxy returns the value stored in invocation.ReturnValue to the original caller.
  9. Performance implications of proxy generation hooks and interceptor selectors

    master

    While IProxyGenerationHook and IInterceptorSelector provide fine-grained control, they can impact performance by decreasing the efficiency of proxy type caching.

    Extensive use of these features may increase the number of unique proxy types generated, which can lead to higher memory usage and slower performance due to cache misses. Before implementing these, consider if a simple if check inside the interceptor is a 'lesser evil' than increasing the number of required proxies tenfold. Always use a profiler in production-like scenarios to verify the impact on your specific use case.

  10. How logging works in Castle Core

    master
    Castle Core does not include a built-in logging framework. Instead, it uses the ILogger and ILoggerFactory abstractions to decouple Castle libraries from specific logging implementations. This allows you to plug in any logging framework you prefer by providing an implementation of ILoggerFactory.