# PostSharp: full content > The pages of https://postsharp.net that are written in Markdown, in the section order of https://postsharp.net/llms.txt. Each one is also served on its own, at its URL plus `.md`. ## Section: PostSharp # PostSharp Technical FAQ > Answers to the most frequent technical questions about PostSharp: how it transforms your assemblies, what it costs at run time, and which platforms and tools it supports. Canonical: https://postsharp.net/postsharp/faq.html How PostSharp works, what it costs at run time, and where it fits in your toolchain. For licensing and purchasing, see the [pricing FAQ](https://postsharp.net/pricing/faq). --- ## Section: Metalama # Alternatives to Metalama Canonical: https://postsharp.net/metalama/alternatives/ This section analyzes alternatives to Metalama for several applications. It tries to fairly present the pros and cons of each approach. Each comparison page below covers a single low-level approach: one for generating code, another for modifying it, another for validating it, another for offering fixes. Metalama's value is that it unifies all of these in one integrated tool, so teams don't have to combine several tools that don't talk to each other. | Application | Alternatives | |---|---| | [Aspect-oriented programming](https://postsharp.net/metalama/alternatives/aspect-oriented-programming) | MSIL rewriting, middleware-based run-time approaches, and Roslyn-based frameworks. | | [Code generation](https://postsharp.net/metalama/alternatives/code-generation) | Pre-build scripts, T4, MSIL rewriting, run-time generation, Roslyn code generators, Generative AI. | | [Architecture verification](https://postsharp.net/metalama/alternatives/architecture-verification) | Code analyzers, architectural unit tests, custom Roslyn analyzers, custom scripts. | --- # Alternatives for Architecture Verification > The document discusses various methods for architecture verification, including code style analyzers, architectural unit tests, custom Roslyn analyzers, and custom scripts. Canonical: https://postsharp.net/metalama/alternatives/architecture-verification.html ## Code style analyzers and linters One of the most common ways to improve your code quality is to add code linters (called _analyzers_ in .NET) to your projects. However, they can't help you validate your architecture. .NET comes with a [large set of analyzers](https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/overview) that analyze code style, portability issues, performance pitfalls, and even detect some security problems in your code. Third-party libraries like xUnit include their own analyzers, and code quality companies like Sonar release [even more linters](https://www.nuget.org/packages/SonarAnalyzer.CSharp/). These analyzers are very good, and you should probably use some of them in any project. However, they only enforce _pre-made_ rules. Sometimes, you can configure some parameters of these rules using `.editorconfig`. For instance, you can configure your code style preferences, such as choosing `var` over explicit types or specifying your preferred casing. _You cannot verify architecture_ with these rules. Code quality is a complex topic, and you need more than one tool in your kit.
### Benefits - **Simple.** There is nothing to code. Customize the rules to your code style. - **Behaves like native C# warnings.** You can suppress them or escalate them using `#pragma warning`, `.editorconfig`, `WarnAsError`, etc. - **Immediate feedback as you type.** There's no need to build the project to refresh the warnings.
### Inconveniences - **Cannot enforce custom rules.** They are limited to predefined rules, sometimes with parameters that can be configured through `.editorconfig`.
### Use cases - Check code style and standards. - Verify pitfalls, best practices, and security violations.
## Architectural unit tests Probably the easiest way to get started with architectural verification is to use [ArchUnitNET](https://github.com/TNG/ArchUnitNET), an architecture verification _unit test_ framework. As its name suggests, this framework allows you to write unit tests (using a framework like xUnit) that verify your architecture. You can create one test method per rule, which can either succeed or fail, like a test. The framework decompiles your assemblies using [Cecil](https://github.com/jbevain/cecil) and lets you define architectural rules using a fluent API. The principal benefit of this approach is that it feels very familiar thanks to its integration with the practice of unit testing. However, it does not report errors and warnings in a way that feels "native." Defects do not appear directly in the code editor as squiggles but in the test runner. An unfortunate side effect is that you cannot easily manage them using `#pragma warning disable` and `.editorconfig`. Therefore, this approach is only viable if you go for a zero-warning quality strategy.
### Benefits - **Can enforce custom architecture rules.** ArchUnitNET is designed for architectural verification. You can write your own rules using a C# fluent API. - **Familiar unit testing experience.** Developers on your team already understand and use unit tests.
### Inconveniences - **Failed tests instead of warnings.** Developers are used to squiggles in case of structural errors in code, and instead, they will get a failed test. - **No link to source code.** Although error messages are well-written, you cannot simply navigate from the unit test to the suspect code declaration. - **Cannot manage warnings.** There are no warnings but failed tests, so you cannot easily manage them using `#pragma warning disable`, `.editorconfig`, or server-side tools like Sonar or Qodana.
### Use cases - Zero warning strategy.
## Custom Roslyn analyzers If you value real-time code analysis and native warnings that can be managed using `#pragma warning disable`, `.editorconfig`, and code quality services such as Sonar or Qodana, your only alternative to Metalama is to build a set of custom analyzers. Indeed, Metalama Architecture Verification is itself built on top of Roslyn analyzers. Without the Metalama layer, you will have to code directly against the Roslyn API. It's going to be much more complex.
### Benefits - **Behaves like native C# warnings.** Developers get the same look and feel with architecture verification warnings as with any other compiler or analyzer warnings. - **Immediate feedback as you type.** No need to run a build or a test. You get feedback within seconds. - **Easy distribution.** You can easily include your analyzers in your NuGet packages. An advantage compared to Metalama is that you don't need a reference to a third-party package.
### Inconveniences - **Complex.** Implementing simple rules is relatively straightforward, but building more complex architecture rules can quickly become much more intricate and require you to build a framework on top of the Roslyn API.
### Use cases - Validation of API usage for packages with a large distribution.
## Custom scripts Heading in a completely different direction, you can create custom scripts that query your code for specific patterns you don't want to see. You can run these scripts in a small console app or use an interactive tool like LINQPad. The `System.Reflection` namespace is not sufficient to navigate through the code references. A good option is to use [Metalama's introspection API](https://doc.metalama.net/preview/conceptual/introspection/linqpad) through the `Metalama.Framework.Workspaces` namespace.
### Benefits - **Maximum freedom.** It's your process. You have complete control over the execution, inputs, and outputs of the program. - **Rich architecture validation API** using `Metalama.Framework.Workspaces`. - **Interactive queries** with tools like LINQPad.
### Inconveniences - **Not native C# warnings.** No integration with `.editorconfig`, `#pragma warning`, `WarnAsError`, etc.
### Use cases - Interactive queries - Experiments - Custom workflows
## Comparison | Feature | Metalama | Standard linters | Custom analyzers | Architectural unit tests | Custom build scripts |---------|----------|-------------------|-------------------|--------------------------|--------------------- | Can enforce custom rules | Yes | No | Yes | Yes | Yes | Behaves like native C# warnings. | Yes | Yes | Yes | No | No | Simple, capable API suitable for architecture verification | Yes | No | No | Yes | Yes[^1] | Understands aspects | Yes | No | No | No | Yes[^1] [^1]: Possible using `Metalama.Framework.Workspaces`. --- # Alternatives for AOP > The document compares aspect-oriented programming frameworks for .NET, detailing approaches like MSIL rewriting, Roslyn-based, and middleware. Canonical: https://postsharp.net/metalama/alternatives/aspect-oriented-programming.html Today's aspect-oriented frameworks all rely on one of the following approaches: * [MSIL Rewriting](https://postsharp.net/metalama/alternatives/msil-rewriting) is the process of modifying the binary assembly during the build process. An additional step is added to this process just after the C# compiler. This approach was pioneered by PostSharp when the C# compiler was a black box. Many tools still rely on it. * **Roslyn-based** frameworks interact directly with the compiler without requiring an additional process. They are based on official Roslyn extension points such as analyzers and code generators, plus unofficial extension points added by [Metalama.Compiler](https://github.com/metalama/Metalama.Compiler), an open-source fork of Roslyn, allowing arbitrary code transformations. [Metalama](https://postsharp.net/metalama) is the only framework in this category at the moment. Roslyn-based frameworks can give you real-time feedback as you are typing, while MSIL-based ones require you to rebuild. * [Middleware-based frameworks](https://postsharp.net/metalama/alternatives/middleware) generally rely on a dependency injection framework and generate dynamic proxies at runtime. They are limited to intercepting interface methods and adding new interfaces to types. Here is an overview of the main aspect-oriented frameworks available for .NET. This list is followed by a table comparing their features. ## PostSharp [PostSharp](https://postsharp.net/postsharp), developed since 2004, was the first complete implementation of AOP concepts in .NET. It is based on [MSIL rewriting](https://postsharp.net/metalama/alternatives/msil-rewriting). PostSharp became a source of inspiration for several MSIL-based AOP frameworks. PostSharp includes a broad set of ready-made aspects. It has complete documentation. It comes with a Visual Studio extension that provides visibility into the transformations performed by aspects. ## Metalama [Metalama](https://postsharp.net/metalama), PostSharp's open-source patterns & architecture toolkit for C#, was first released in 2023. Based on Roslyn, Metalama works both at design time (within the IDE) and at compile time. It is the most complete implementation of aspect-oriented principles, as the comparison table below shows. Metalama uses a C#-to-C# template language coined _T#_. T# is 100% C#-compatible, so you can get full IntelliSense support with any IDE. Since Metalama generates C# and not MSIL, you can preview and even debug the code generated by your aspects. Metalama shares the same Visual Studio extension as PostSharp. ## AspectInjector Like PostSharp, [AspectInjector](https://github.com/pamidur/aspect-injector) is based on [MSIL rewriting](https://postsharp.net/metalama/alternatives/msil-rewriting). While far from PostSharp in terms of features, AspectInjector supports most code overriding and introduction features expected from an AOP framework. ## Rougamo [Rougamo](https://github.com/inversionhourglass/Rougamo) is another compile-time AOP framework based on [MSIL rewriting](https://postsharp.net/metalama/alternatives/msil-rewriting). Its code transformation abilities are limited. It implements an AspectJ-inspired pointcut mechanism to select code to be modified. ## AspectCore [AspectCore](https://github.com/dotnetcore/AspectCore-Framework) is an aspect-oriented framework based on [dynamic proxies](https://postsharp.net/metalama/alternatives/middleware). This approach operates at runtime by generating a _proxy_ type between the consumer and the implementation of an interface. It works only with components served by a dependency injection framework. ## Fody [Fody](https://github.com/Fody/Fody) is an extensible tool for weaving .NET assemblies. While it is not an aspect framework in itself, [some](https://github.com/vescon/MethodBoundaryAspect.Fody) [plug-ins](https://github.com/Fody/MethodDecorator) allow for PostSharp-style decoration of methods, enabling the implementation of some simple aspects. Fody has a long list of [plug-ins](https://github.com/Fody/Home/blob/master/pages/addins.md) that implement specific code transformations. Because these transformations must be coded directly in MSIL and not in C#, Fody does not fully qualify as an aspect-oriented framework. ## Comparison | | Metalama | PostSharp | AspectInjector | Rougamo | AspectCore | |-----------------------------------------------|----------|-----------|----------------|---------|-----------------| | Technology | Roslyn | MSIL | MSIL | MSIL | Dynamic Proxies | | Override virtual members | Yes | Yes | Yes | Yes | Yes | | Override non-virtual members | Yes | Yes | Yes | Yes | No | | Implement interfaces | Yes | Yes | Yes | No | Yes | | Introduce new members | Yes | Yes | Yes | No | No | | Reference introduced members from source code | Yes | No | No | No | No | | Allocationless context passing | Yes | No | Yes | No | No | | IDE: Aspect Explorer | Yes | Yes | No | No | No | | IDE: CodeLens | Yes | Yes | No | No | No | | View/Debug Generated C# | Yes | No | No | No | No | | Large library of pre-built aspects | Yes | Yes | No | No | No | --- # Alternatives for Code Generation > This document discusses various approaches for code generation in .NET, highlighting benefits, inconveniences, and use cases for each method. Canonical: https://postsharp.net/metalama/alternatives/code-generation.html There are many approaches to code generation in .NET, and Metalama is not necessarily the best choice for all use cases. In particular, Metalama should _not_ be used to generate code based on a _slow_ source like a database or a web API. ## Pre-build scripts, programs, or tasks When your code generation logic does not depend on the source code itself, it is convenient to write this as a script-like C# program that simply writes its output with a `TextWriter`. You can choose to execute the program upon different triggering events: - On demand, if the input data does not change often. In this case, you should store the output in source control. - As a [pre-build event](https://learn.microsoft.com/en-us/visualstudio/ide/how-to-specify-build-events-csharp).
### Benefits - **Simple and familiar**. After all, it's just C#. - **Very simple to debug**. - When executed on demand, it can handle a slow generation process (such as generating code from a database schema) without affecting the build time.
### Inconveniences - **No source code access**. The control logic cannot rely on your C# source code to generate code. - **Cannot override handwritten code.** You can generate new files, but you cannot inject new logic into existing source code. - **Purely text-based**. No error checking, syntax coloring, or syntax completion for generated code. - **Not real-time**. You need to rebuild after modifying the source files to see new errors or use newly generated methods.
### Use cases - Generate data objects from databases, XML schemas, or UML models. - Generate service proxies for REST, SOAP, gRPC, or Web APIs. - Generate C# wrappers for non-.NET libraries.
In theory, your script could also access the source code by loading it using a [Roslyn workspace](https://learn.microsoft.com/en-us/dotnet/csharp/roslyn-sdk/work-with-workspace). However, in this case, you might want to consider using Metalama or Roslyn code generators instead. ## T4 (Text Template Transformation Toolkit) [T4](https://learn.microsoft.com/en-us/visualstudio/modeling/code-generation-and-t4-text-templates) is a Microsoft technology for the generation of text files based on templates. T4 templates are a mixture of text blocks and control logic that can generate a text file. The control logic is written as fragments of program code in C#. The generated file can be text, such as a web page, a resource file, or program source code in any language. T4 uses a similar syntax as ASP.NET WebForms, with control blocks delimited by `<%` and `%>`. T4 templates can be executed as a pre-build event or on demand. They are a special case of pre-build scripts and share the same benefits and inconveniences. Writing a T4 template is more convenient than writing a pure C# program when there is relatively little control logic. When the template is dominated by control logic, writing C# code is often more productive.
### Benefits - **Simple**. The control logic is C#, with the familiar `<%` and `%>` delimiters. - **Real-time feedback.** With an optional Visual Studio extension, templates can be automatically executed when the input file is modified. - When executed on demand, it can handle a slow generation process (such as generating code from a database schema) without affecting the build time.
### Inconveniences - **No source code access**. The control logic cannot rely on your C# source code to generate code. - **Cannot override handwritten code.** You can generate new files, but you cannot inject new logic into existing source code. - **Purely text-based**. No error checking, syntax coloring, or syntax completion for the non-control part of the code.
#### Use cases The same use cases as for pre-build scripts.
## MSIL Rewriting Tools based on MSIL Rewriting run after the C# compiler. They decompile the assembly written by the compiler, run it through some plug-ins that generate new code into it, and compile it back to a binary assembly. Examples of such tools include PostSharp and Fody. MSIL rewriting was the only possible approach that allowed mixing generated logic with handwritten logic back in the days when the C# compiler was a black box. Roslyn-based tools now do the same work at the source level.
### Benefits - **Code model access**. The generator can reflect the source code through decompilation. - **Can override handwritten code.** Ability to add new behaviors to existing methods.
### Inconveniences - **No generated code access.** Source code cannot reference generated classes or members. - **No real-time feedback.** You need to rebuild after modifying the source files to see new errors. - **Complex.** Unless a general-purpose AOP framework like PostSharp is used, it's difficult to implement generators using MSIL.
### Use cases - General aspect-oriented programming (all aspects) with PostSharp. - Limited aspect-oriented programming (decorators, interceptors, mixins) with Fody. - INotifyPropertyChanged, contracts.
## Run-time generation Instead of generating code at build time, you can do it at run time, typically during application start-up, using the `System.Reflection.Emit` namespace. Many well-known libraries, including different serializers and the `Regex` class, use this approach instead of using `System.Reflection` alone to improve performance.
### Benefits - **Partial code model access.** Control logic has partial access to the code model using `System.Reflection`. However, it cannot access the method bodies, but only the metadata.
### Inconveniences - **Cannot override handwritten code.** Generators can only create new types. - **AoT Incompatible.** Ahead-of-time (AoT) compilation, required by .NET Native, does not support `System.Reflection.Emit`. - **Slower startup.** Emitting code at run time causes a performance overhead at each application startup. - **Complex.** Must emit IL code. - **No generated code access.** Source code cannot reference generated types.
### Use cases - Generate dynamic proxies (Castle.DynamicProxy). - Generate object mappers (AutoMapper). - Generate serializers/deserializers. - Generate `Regex` implementations.
## Roslyn Code Generators Roslyn generators are plug-ins of the C# compiler that generate code based on the source code or other files in the project. They are executed in real-time as you type code in the IDE, and at build time. Before Roslyn generators existed, `System.Reflection.Emit` was the only "official" way to generate code based on source code. The main problem was that this approach is not compatible with .NET Native and AoT. When Microsoft decided to improve support for AoT in .NET, they had to improve the code generation scenario, and came up with Roslyn Code Generators.
### Benefits - **Full source code access.** The generator logic can inspect the whole source code. - **Real-time feedback.** The new code is generated immediately as you type. - **Generated code access.** Source code can reference generated classes or members, with full support for IntelliSense.
### Inconveniences - **Cannot override handwritten code.** Generators can only create new types or extend existing `partial` ones. - **Cannot report errors or warnings.** Roslyn code generators have no mechanism to report errors or warnings. A separate Roslyn analyzer must be created. - **Complex.** The Roslyn code generator is a low-level one optimized for performance. Implementing complex generation logic with this API can be tricky. - **Purely text-based.** No error checking, syntax coloring, or syntax completion for generated code.
### Use cases - Native AoT readiness: Regex, ASP.NET request handlers, serialization/deserialization, ... - Generate C# code from other source languages (XAML, Blazor/CSHTML).
## Comparison The following table summarizes the abilities and limitations of the different approaches to code generation for C#: | Feature | Metalama | Pre-build script, T4 | MSIL Rewriting | Run-time | Roslyn generators | |---------|----------|----------------------|----------------|----------|-------------------| | Can reference source code from the generator | Yes | No | Yes | Yes | Yes | | Can reference generated code from source code | Yes | Yes | No | No | Yes | | Gives immediate feedback as you type | Yes | No | No | No | Yes | | Can override handwritten code | Yes | No | Yes | No | No | | Offers simple, strongly-typed template language | Yes | No | No | No | No | | Can report errors and warnings to source code | Yes | No | Yes | No | No | ## And generative AI? Generative AI (GenAI) is often cited as a code generation technology, but it plays a completely different role. The most significant difference between GenAI and the code generation tools discussed in this article is that **code generated by GenAI must be maintained as source code**. Here, we are talking about techniques that generate throwaway code: code that never needs to be maintained. Since most of the total cost of ownership of a codebase comes from maintenance and not its initial writing, GenAI replaces handwritten code rather than code generation tools. GenAI is a good way to get a first draft in a language or area you don't master, but it is not a replacement for deterministic code generation tools. --- # Fody > Fody is an extensible tool for weaving .NET assemblies, offering open-source plugins and method decorators but has limitations in complexity and traceability. Canonical: https://postsharp.net/metalama/alternatives/fody.html [Fody](https://github.com/Fody/Fody) is an extensible tool for weaving .NET assemblies. Manipulating the IL of an assembly as part of a build requires a significant amount of plumbing code. This plumbing code involves knowledge of both the MSBuild and Visual Studio APIs. Fody aims to eliminate that plumbing code through an extensible add-in model. ## What are Fody weavers? Fody weavers are plug-ins to the Fody platform. They generally implement a single feature. Fody weavers are packaged separately from Fody itself. ## Fody strengths - **Open-source plug-ins**: Fody offers a variety of open-source plugins, making it easier to implement common programming patterns. These plugins are user-friendly, with ConfigureAwait, PropertyChanged, and a resource-embedding tool being particularly popular. - **Method decorators**: Fody's MethodDecorator simplifies the implementation of simple patterns. ## Fody limitations - **Complexity**: Using existing plugins and method decorators is straightforward, but creating custom patterns at the MSIL level can be intricate, particularly for non-trivial applications. - **Cannot reference new members from source code**: Since Fody processes output from the C# compiler, the source code cannot directly reference any members introduced by Fody. - **Lack of aspect traceability**: The code transformations Fody applies aren't visible in the Integrated Development Environment (IDE). To view these changes, one must use a decompiler to examine the compiled application. - **No commercial support**: Both Fody and `Mono.Cecil`, the underlying library, are maintained by volunteers; no professional support offering stands behind them. ## Fody alternatives - [Metalama.Compiler](https://github.com/metalama/Metalama.Compiler) is a fork of Roslyn that adds an extension point allowing for arbitrary code modifications using the Roslyn code model instead of MSIL instructions. - [Aspect-oriented frameworks](https://postsharp.net/metalama/alternatives/aspect-oriented-programming) for .NET are generally more capable and simpler to customize than Fody add-ins. - [Metalama](https://postsharp.net/metalama) is the most complete AOP framework for .NET, as this [feature comparison](https://postsharp.net/metalama/alternatives/aspect-oriented-programming) details. It can be extended using [Metalama.Framework.Sdk](https://doc.metalama.net/conceptual/sdk) to support code transformations using the Roslyn code model. [Metalama.Community.Costura](https://github.com/metalama/Metalama.Community/tree/HEAD/src/Metalama.Community.Costura) is a port of the popular `Costura.Fody` weaver built with `Metalama.Framework.Sdk`. --- # Middleware and Dependency Injection > This page shows how AOP can be implemented using middleware or dependency injection. Canonical: https://postsharp.net/metalama/alternatives/middleware.html Many .NET libraries, such as dependency injection frameworks, ASP.NET action filters, and WCF behaviors, offer features akin to Aspect-Oriented Programming (AOP). Particularly noteworthy are those based on dependency injection frameworks. These frameworks, when an interface is requested, do not directly return the implementation; instead, they provide a _proxy type_, sometimes called a _dynamic proxy_ because it is generated at runtime during the application startup. This proxy, acting as an intermediary, implements the same interface, injects the desired aspects, and then proceeds to call the actual service implementation. This approach is user-friendly and integrates well into the .NET ecosystem. However, it's important to recognize its limitations. These libraries offer a somewhat limited perspective on the capabilities of AOP, especially when compared to what is achievable with compiler-based AOP methods. ## Implementations * [AspectCore](https://github.com/dotnetcore/AspectCore-Framework) is a basic but popular aspect-oriented framework based on dynamic proxies. It integrates with the `Microsoft.Extensions.DependencyInjection` namespace and Autofac. Some documentation is available in Chinese. ## Strengths - **Simplified learning curve**: Middleware-based AOP frameworks align well with concepts like dependency injection and application server architectures. Their configuration mechanisms are intuitive, often mirroring the underlying principles of these environments, which makes them a natural fit. With a focus primarily on interception, these frameworks are more straightforward to understand. Their simplicity extends both to their use and to their internal implementation. This makes them accessible to a broader range of development teams, including those without specialized AOP expertise. - **Runtime flexibility**: A key advantage of middleware-based AOP is its runtime adaptability. Aspects can be added, modified, or removed as the application runs, offering significant flexibility. This dynamic configurability, achievable through code or configuration files, allows for tailored application behavior under varying conditions. Furthermore, the ability to selectively apply aspects, such as excluding certain aspects during unit testing, enhances development and testing processes. - **No need for special compilation**: One of the major conveniences of middleware-based AOP is the elimination of a specialized compilation step. Aspects are woven in at runtime, simplifying the build and deployment pipeline and reducing overall complexity in application setup. - **Serviceability**: The simpler framework structure of middleware-based AOP becomes a significant advantage when it comes to maintenance and bug fixing. In contrast, compiler-based AOP frameworks are often considerably more complex, representing several man-years of development effort. This complexity can make them less serviceable by application developers, who may find it challenging to understand and modify such intricate systems. ## Limitations Compared to statically-weaved AOP (whether implemented as MSIL or AST rewriting), middleware-based AOP presents the following limitations: - **Limited code transformation features**: Proxy-based frameworks can only intercept calls at component boundaries, such as interfaces or virtual methods. Compiler-based frameworks, on the other hand, can override any declaration, including private fields or methods, or helper classes that are not part of the component facade. - **Risk of over-modularization**: Because these libraries are only able to inject behaviors at component boundaries, you may be tempted to over-modularize just to make use of the injection abilities of the framework. This can increase the complexity and decrease the performance of the application. - **A universal hammer for cross-cutting concerns**: Dependency injection excels at _composing_ an application, wiring services together at their interface and constructor boundaries, and it remains complementary to AOP. The problem is that it is often stretched to also handle cross-cutting concerns such as logging, caching, or validation. Because DI can only intervene at those boundaries, the concern ends up scattered: introduced through proxies where an interface happens to exist, and reimplemented by hand everywhere it does not. An aspect is the better fit here, because it can weave the behavior directly into the method body, or into a non-virtual or private member, exactly where the concern belongs. - **Performance overhead**: Libraries performing runtime injection tend to be slower because they do their work at runtime. - **Lack of traceability and intelligibility**: Proxy-based AOP libraries lack visibility into the aspects being applied to the code. Compile-time frameworks have the potential to provide clearer visibility, allowing you to observe which code is being executed and comprehend the impact of aspects without executing or debugging the program. ## Conclusion In conclusion, middleware-based AOP frameworks in .NET provide user-friendly integration, runtime flexibility, and simplified serviceability without needing special compilation. However, they are limited in their transformation capabilities, risk inducing over-modularization, and may introduce performance overheads and challenges in aspect traceability. On the other hand, compiler-based AOP methods offer broader aspect-weaving abilities and better performance, albeit with increased complexity. The choice between the two approaches should be based on the project's specific needs, balancing simplicity and integration against comprehensive aspect control and efficiency. --- # MSIL Rewriting > MSIL Rewriting modifies binary assemblies post-C# compilation. Tools like PostSharp, Mono.Cecil, Fody, AspectInjector, and Rougamo use this approach. Canonical: https://postsharp.net/metalama/alternatives/msil-rewriting.html _MSIL Rewriting_ is the process of modifying the binary assembly during the build process. An additional rewriting step is added to this process just after the C# compiler. These tools can add MSIL instructions, as well as new types and members, during the build. The following tools are based on MSIL rewriting: * [PostSharp](https://postsharp.net/postsharp) implements its own MSIL rewriting stack. * [Mono.Cecil](https://github.com/jbevain/cecil) is a standalone library that can be used to modify .NET assemblies. * [Fody](https://postsharp.net/metalama/alternatives/fody) is based on `Mono.Cecil`. It integrates with MSBuild and implements a plug-in system so that MSIL transformations can be packaged and combined. * [AspectInjector](https://github.com/pamidur/aspect-injector) and [Rougamo](https://github.com/inversionhourglass/Rougamo) are AOP frameworks based on `Mono.Cecil`. --- # PostSharp > PostSharp is the proven MSIL-based aspect-oriented framework, actively maintained since 2004. Metalama is the same team's open-source patterns & architecture toolkit for C#, built on Roslyn: source-level transformations, real-time feedback in the IDE, and an MIT-licensed core. Canonical: https://postsharp.net/metalama/alternatives/postsharp.html [PostSharp](https://postsharp.net/postsharp) is the proven MSIL-based aspect-oriented framework that PostSharp Technologies has built and supported since 2004. Metalama is the same team's open-source patterns & architecture toolkit for C#: the same idea of factoring out cross-cutting concerns, built on Roslyn instead of MSIL rewriting. Both products are actively maintained, supported, and updated for each new .NET release; the difference is _how_ they transform your code and what that buys you. ## MSIL rewriting versus Roslyn PostSharp works by [rewriting the compiled MSIL](https://postsharp.net/metalama/alternatives/msil-rewriting): it runs after the C# compiler and modifies the binary assembly. The transformation happens below the source level: aspects are applied at build time, you debug your original source, and inspecting the woven code takes a decompiler. Metalama works at the source level, as a Roslyn source generator. The result is ordinary C# that the compiler, debugger, and IDE all understand, which is what makes its design-time experience possible: * **You can read and step into the generated code**, because it is C#, not IL. * **You get real-time feedback in the IDE** (CodeLens, a source ↔ transformed [diff](https://postsharp.net/metalama/features/design-time-feedback), and diagnostics as you type), rather than waiting for a post-build weaving step. * **Hand-written code can reference generated declarations**, because they exist as Roslyn symbols during compilation. * **The core is open source** (MIT), where PostSharp is commercial. ## Which one to choose - **For new projects on the current .NET stack, start with Metalama.** It is in active development and gives you the source-level, design-time, open-core experience above. - **Existing PostSharp codebases need no migration.** PostSharp is actively maintained and [updated for each new version](https://postsharp.net/support/policies/versions) of .NET, C#, and Visual Studio. A few of its capabilities are not yet in Metalama, and Metalama is a redesign on Roslyn rather than a drop-in binary-compatible port. Either way, you are working with the same team, the same support channels, and the same long-term commitment to .NET developer tools. --- # Roslyn Code Generators > Roslyn source generators can only add new code. Metalama, built on the same Roslyn foundation, also overrides, wraps, and composes over your existing code, and adds a real design-time experience. Canonical: https://postsharp.net/metalama/alternatives/roslyn-generators.html Roslyn source generators are the built-in way to generate C# code at compile time. They are a good fit for _additive_ generation: producing new files or filling in `partial` members from attributes, schemas, or other inputs. Their hard limit is that they can **only add code**: a generator cannot change the body of a method you wrote, wrap an existing property, or modify an existing type. Metalama is built on the same Roslyn foundation, but its aspects can also transform existing declarations. ## What a source generator can and cannot do A source generator inspects the compilation and contributes additional source files. Because it never touches your existing syntax trees, it can: * add new types, or new members to a `partial` type; * read attributes and other code to decide what to generate; * report diagnostics. It cannot: * change the body of an existing method, or add code around it (for logging, caching, retries, validation…); * override or wrap an existing property or field; * see the output of another generator, so several generators cannot compose on the same declaration. ## How Metalama differs Metalama runs as a Roslyn source generator under the hood, but exposes a higher-level model (_aspects_) that can **override, wrap, introduce, and compose over existing members**, not just append new ones. That is what makes cross-cutting patterns (logging, caching, `INotifyPropertyChanged`, contracts) possible without editing every method by hand. In fairness, a source generator is only one of Roslyn's extension points. You can also write _analyzers_ to report diagnostics, and _code-fix_ or _refactoring providers_ to offer corrections in the IDE. Between them they cover more ground than a generator alone. But they are **not integrated with each other in any way**: the generator, the analyzer, and the code fix are three independent plug-ins that each start from the raw syntax tree and semantic model and know nothing of one another. Nothing connects a diagnostic to the code a generator emitted, or a code fix to a generated declaration. You wire the three together by hand and keep them in sync yourself. Metalama exposes generation, validation, and code fixes through a single aspect model, so a validation rule can reason about what an aspect generated, and an aspect can ship its own code fixes. | Capability | Roslyn Source Generators | Metalama | |---|---|---| | Add new types and members | Yes | Yes | | Reference generated code from hand-written code | Yes | Yes | | Modify or override existing methods | No | Yes | | Wrap existing members (logging, caching, retry) | No | Yes | | Compose several transformations on one declaration | No | Yes, with deterministic [ordering](https://postsharp.net/metalama/features/aspect-composition) | | Report diagnostics | Yes | Yes | | Suggest code fixes | No | Yes | ## When a plain source generator is enough If all you need is purely additive generation (say, generating DTOs, serializers, or registration code from a schema), a hand-written source generator has no extra dependency and is a fine choice. Reach for Metalama when you need to transform _existing_ code, apply a concern across many declarations, or want the design-time tooling and tested libraries. --- # When to Use Metalama > This web section lists different use cases of Metalama. Canonical: https://postsharp.net/metalama/applications/ Metalama is not for every project. Consider using Metalama if one of the following situations applies to your project: * **Large projects.** Your project contains dozens of entities and hundreds of properties or methods requiring the same behaviors. * **Large teams.** There is a large team of developers, and it is challenging to align everyone on consistent patterns and practices. * **Long lifecycle.** You must maintain the project for several years, so an investment in long-term quality makes sense. ## When _not_ to use Metalama Conversely, *avoid* using Metalama if: * **Small and simple projects.** If your project is trivial, the added complexity might outweigh the benefits. As a rule of thumb, each aspect should be applied at least 20 times to justify its use. * **Inexperienced team.** Don't embark on a Metalama journey if you lack senior developers or architects on your team. Like any sharp tool, Metalama requires maturity and wisdom in selecting use cases; misuse can introduce unnecessary complexity. ## Which factors are almost irrelevant * **Code-generation overhead.** Metalama expands aspects into plain C# at compile time (there is no run-time engine), so the generated code runs exactly like the same logic written by hand, and sometimes faster: idiomatic hand-written C# favors compact, readable patterns, while a generator can emit an uglier but faster one. The functionality an aspect adds still costs what it would cost either way; Metalama itself adds no run-time overhead. * **Compilation time.** Metalama typically adds 25% to your `dotnet build` time, which is probably not a big difference for most projects. If it does seem important, it might mean Metalama's benefits won't outweigh the added complexity. ## What are the principal use cases? | Category | Description | |----------|----------| | [Principles At Work](https://postsharp.net/metalama/applications/principles) | See how Metalama can help implement fundamental software development principles like Clean Code, SOLID, DRY, Aspect-Oriented Programming, Architecture Verification, or Refactoring.| | [Classics](https://postsharp.net/metalama/applications/classics) | See typical and concrete use cases of Metalama in software application development. | | [DevOps](https://postsharp.net/metalama/applications/devops) | Learn how Metalama can help move from a prototype to a production-ready application with technical features like logging, metrics, caching, exception handling, and resilience. | | [Alternatives](https://postsharp.net/metalama/alternatives) | See alternatives of Metalama in different areas of application. | --- # Architecture Erosion > Architecture erosion is the gap between your planned and actual architecture. See why it happens, what it costs, and how to enforce design rules at compile time. Canonical: https://postsharp.net/metalama/applications/architecture-erosion.html "*Software architecture erosion refers to the gap between the planned and actual architecture of a software system as observed in its implementation... [It] can result in lower quality, increased complexity, and harder-to-maintain software. As these changes happen, it becomes more and more difficult to understand the originally planned software architecture*", Sean Barow. ## Why does it happen? Architecture erosion occurs when the planned architecture of a software system does not align with its actual architecture during its implementation. Although the code can look clean at first sight, it is more complex than it should be. This discrepancy may arise due to a lack of automatic validation of source code against architecture, design, and pattern guidelines. Architecture erosion occurs when architecture is expressed in a non-executable form, such as documentation. As erosion intensifies, the architectural qualities of the codebase deteriorate, which can result in brittle code that is challenging to repair or enhance. The **broken window syndrome** may also occur, which is the idea that visible signs of neglect, unaddressed warnings, or other violations can encourage further neglect or disrepair. This can ultimately lead to a further decline in overall code quality and an increase in technical debt. ## What does it cost to you? Architecture erosion can have significant consequences on the quality and complexity of the software system, resulting in increased costs and reduced productivity. - **Increasing complexity.** As the architecture erodes, the codebase becomes more complex and less organized. This makes it increasingly difficult for developers to understand and modify the code. Keeping complexity down is the holy grail of any sizable software project. High complexity is a frequent source of bugs. - **Cumulative technical debt:** Architecture erosion often leads to the accumulation of technical debt. This debt represents the cost of addressing issues that have been deferred or ignored due to time constraints or complexity. Over time, technical debt can compound, resulting in higher maintenance costs to address it. - **Higher maintenance costs.** With time, the architecture no longer fulfills its role. A decrease in modularity and separation of concerns will cause a decrease in the ability of the architecture to respond to changes in requirements, increasing maintenance costs. - **Failure to meet new requirements.** Ultimately, architecture decay may lead to the inability to implement new features in the codebase. ## What causes architecture erosion? Researchers and practitioners have identified the following causes of architecture erosion: - **Architecture design defects.** The architecture designed in the first phase of the project might be incorrect, forcing developers to work around it. - **Inappropriate architecture changes.** When the project is in maintenance, new requirements may not be compatible with the original architecture. Unless the design is updated, developers will be forced to depart from the original architectural design. - **Disconnection between architects and developers.** Developers may not be aware of the intended architecture, especially if they were not involved in the architecture team, if the architecture was incompletely documented, or simply not communicated to them. - **Knowledge vaporization.** The initial developers may leave the team, taking their architectural knowledge with them. This cause is even more acute if the architecture is not properly documented. - **Documentation decay.** Over time, as the codebase evolves, the existing architectural documentation may no longer accurately reflect the current state of the system. This discrepancy makes it harder for developers to rely on documentation for understanding and modifying the code. ## How Metalama helps The root cause of erosion is that architecture lives as non-executable documentation, which rots as the code evolves. Metalama lets you express your architecture rules as high-level C# code: **architecture as executable code** rather than diagrams and text that no compiler ever checks. Rules that are executed cannot silently drift out of date the way documentation does. Unlike architecture unit tests, which only run on demand or in the CI pipeline, give poor error messages, and ignore `#pragma warning` suppression, Metalama's rules run continuously in the IDE with real-time feedback, reported as ordinary C# warnings on the exact line that violates them. ## Learn more To learn more about architecture erosion, check the following references: * [Understanding architecture erosion: the practitioners’ perspective](https://arxiv.org/pdf/2103.11392) * [Understanding software architecture erosion: A systematic mapping study](https://arxiv.org/pdf/2112.10934) --- # Architecture Verification for .NET > Metalama is a tool for architecture verification, ensuring that your C# codebase complies with the intended software architecture. Canonical: https://postsharp.net/metalama/applications/architecture-verification.html Architecture is often defined in non-executable forms such as text and diagrams. Since it cannot be automatically enforced, it is verified during _code reviews_, which can be slow and frustrating for both sides. As a result, the _real_ architecture of the code departs from the _intended_ one, a phenomenon known as _architecture erosion_. Metalama lets you validate code **usages**, **references**, and **dependencies**, enabling you to enforce architectural rules directly in code, a concept we call **Architecture as Code**. This feature requires a Metalama Professional license. Metalama lets you define rules tailored to your architecture and business domain and enforce them in the IDE with real-time feedback. Expressing architecture in an _executable_ way allows you to validate the codebase in real-time. Most issues can be identified and resolved immediately, making code reviews smoother and less focused on mundane issues. It's like having Roslyn analyzers custom-made for your project, but without the complexity of authoring raw analyzers. Typically, the following rules can be automatically enforced in a codebase: - Restrict dependencies between classes, class families, and namespaces. - Verify naming conventions based on the base class or implemented interface. - Ensure that code is implemented according to pattern guidelines. ## Benefits Here are six reasons why you _must_ consider automatically verifying your codebase against your design: - **Stop architecture decay.** Keep your code aligned with the defined blueprints. Your codebase retains its initial architectural qualities, allowing for low maintenance costs and the ability to implement new requirements. The _broken window_ syndrome stops and reverses. - **Reduce codebase complexity.** By validating the high-level architecture along with implementation patterns and other coding conventions, you further reduce the complexity of your codebase, making it easier for software developers to understand. - **Scale expertise.** Instead of repeatedly pointing out the same issues in code reviews, architects can codify their guidance once and apply it everywhere, instantly. - **Get immediate feedback.** Developers receive real-time notifications of issues while typing, eliminating the need to wait for the next build or code review. - **Streamline code reviews.** With fewer trivial violations to address, code reviews become more efficient and less frustrating. By addressing most minor issues in real time, code reviews can focus on essential aspects. - **Validate code that aspects have transformed.** Because Metalama's code analysis is integrated with its aspect framework, a validation rule can account for what an aspect did to the code. Other analyzers get confused by generated or enhanced code; Metalama sees the same code your aspects produce. If they can author your architecture as executable code, architects stop being gatekeepers and start accelerating the entire team. ## Features ### Custom attributes: hand-pick declarations Here is how to enforce a naming convention where all classes implementing `IDocumentFactory` must be suffixed `Factory`: ```csharp [DerivedTypesMustRespectNamingConvention( "*Factory" )] public interface IDocumentFactory { IDocument CreateDocument( DocumentId documentId ); } ``` In the following snippet, we prevent the constructor of the `Invoice` class from being used from a different type than `InvoiceFactory`, enforcing the Factory pattern. ```csharp public class Invoice { [CanOnlyBeUsedFrom( Types = [ typeof(InvoiceFactory) ] ) ] public Invoice( DocumentId documentId ) { } } ``` ### Fabrics: select in bulk The following code enforces that `internal` members of the current namespace are not used by a different namespace. ```csharp namespace MyNamespace; internal class Fabric : NamespaceFabric { public override void AmendNamespace( INamespaceAmender amender ) { amender.InternalsCanOnlyBeUsedFrom( r => r.CurrentNamespace() ); } } ``` Here we are using a project fabric to check that floating-point numbers are not used in the Invoicing namespace. ```csharp internal class Fabric : ProjectFabric { public override void AmendProject( IProjectAmender amender ) { amender .SelectReflectionTypes( typeof(float), typeof(double) ) .CannotBeUsedFrom( r => r.Namespace( "**.Invoicing" ), "Use decimal numbers instead." ); } } ``` ### Behaves like native C# warnings Warnings reported by Metalama behave like native C# warnings. This has several benefits: - They are represented by the familiar squiggles directly in the code editor. - They can be suppressed or escalated using `#pragma warning`, `.editorconfig`, or `WarnAsError`. - They can be managed by tools like Sonar or Qodana. ### Immediate editor feedback Metalama runs within the IDE and verifies your code as you type. You receive feedback in seconds. No need to wait for a build or the CI/CD pipeline to complete. ### Understands aspects Other verification tools may fail to understand the code after it has been enhanced by aspects. Metalama Architecture Validation is fully integrated with the Metalama Aspect Framework. ## Resources - Reference documentation: [Verifying architecture](https://doc.metalama.net/conceptual/architecture). - Source code: [Metalama.Extensions.Architecture](https://github.com/metalama/Metalama.Extensions/tree/HEAD/src/Metalama.Extensions.Architecture) - NuGet package: [Metalama.Extensions.Architecture](https://www.nuget.org/packages/Metalama.Extensions.Architecture) --- # Aspect-Oriented Programming in C# > Aspect-Oriented Programming (AOP) extends Object-Oriented Programming with a paradigm that focuses on separating the implementation of repetitive code patterns from the main business logic. This page presents the main characteristics of AOP as well as different implementations for C# and .NET. Canonical: https://postsharp.net/metalama/applications/aspect-oriented-programming.html Aspect-Oriented Programming (AOP) extends Object-Oriented Programming with concepts and tools that focus on separating the implementation of repetitive code patterns from the main business logic. Without AOP, repetitive code patterns are implemented manually in source code, resulting in additional (and tedious) work, excessive code complexity, and more defects. Telemetry from PostSharp shows that AOP typically removes around 15% of the source code, and up to 50% in pattern-heavy projects. Because the cost of building and maintaining software grows roughly with its size, less code means lower cost and fewer defects, along with code that is cleaner and easier to read. Aspect-oriented programming relies on three concepts: * _Aspects_ are a mechanism to encapsulate patterns of repetitive code. Aspects know how to mechanically modify your source code to add new behaviors. Aspects are like custom attributes that alter the way your code executes, e.g., by adding caching, exception handling, or immutability. Aspects work like templates that you can apply to hand-written code. They must be applied to a declaration, typically a method, property, or class. Aspects are made of several primitive transformations named _advice_. For instance, overriding a method, adding an interface to a class, or intercepting field accesses are different kinds of advice. In addition to advising the code, good aspect frameworks enable you to analyze source code and report errors and warnings. * Several targeting mechanisms to apply aspects to source code, including: * Custom attributes, * Code queries executed at compile time, * Inheritance from base types to derived types. * A composition mechanism ensuring that several aspects (e.g., logging and caching) can be safely and predictably added to the same declaration. The process of applying one or many aspects to source code is often called weaving. ## Is AOP still useful in C#? AOP was formalized in the early 2000s as a way to improve the modularity of classic object-oriented languages at a time when Java and C# were still in their infancy. In the meantime, both languages and development practices have evolved. The generalization of the use of higher-order functions (delegates, anonymous methods, lambdas) and dependency injection has greatly improved modularity, covering some of the initial use cases of aspect-oriented programming. Therefore, some developers question the usefulness of AOP in today's applications. The academic roots of aspect-oriented programming did not help. Esoteric jargon like _advice_, _pointcut_, and _joinpoint_ make AOP sound overly complex. Fortunately, .NET implementations, following PostSharp's lead, never followed this theoretical framework. After all, we don't even position Metalama as an aspect-oriented framework! But despite the progress of programming languages and software engineering practices, we still think that the gap identified by the early AOP researchers has not been completely addressed by classic languages. We're talking about the abstraction gap between human reasoning and programming languages. Software architects think in terms of patterns and may say, "Add logging to all public methods of public classes in namespace `X`, and implement `INotifyPropertyChanged` for all classes derived from `BaseEntity`." But C# has no `pattern` keyword. The language does not have a way to do "for each declaration `X`, add behavior `Y`." Therefore, to save developers from implementing these requirements by hand, languages like C# must be extended to support this kind of meta-programming feature. Whether we call these meta-programming features AOP or just code generation is eventually just a secondary question. ## What are the typical uses of AOP in modern .NET? In .NET, examples of code patterns that can be handled by AOP are: * logging (AOP's "Hello, World" example), profiling, * security, * caching, * transaction handling, * observability (`INotifyPropertyChanged`), * change tracking, * design patterns such as Builder, Memento, or Factory, * multi-threading, * equality, * `ToString`, * WPF dependency properties and commands, * storage (including object-database mapping and serialization), * pulling dependencies. ## Example Logging is the "hello, world" example of aspect-oriented programming. Here is a logging aspect implemented with Metalama: ```csharp public class LogAttribute : OverrideMethodAspect { Console.WriteLine( $"{meta.Target.Method}: starting.") try { return meta.Proceed(); } finally { Console.WriteLine( $"{meta.Target.Method}: completed.") } } ``` One way to add the aspect to a method is to add a custom attribute: ```csharp [Log] public void Greet() { Console.WriteLine("Hello, world."); } ``` If we want to target select targets using a code query instead of hand-picking one, we can use a fabric: ```csharp internal class Fabric : ProjectFabric { public override void AmendProject( IProjectAmender amender ) { amender .SelectMany( c => c.Types ) .SelectMany( t => t.Methods ) .AddAspectIfEligible(); } } ``` Here is the _transformed_ code, i.e., the one that gets executed: ```csharp public void Greet() { // [] Console.WriteLine( $"{meta.Target.Method}: starting.") try { // [] Console.WriteLine("Hello, world."); // [] } finally { Console.WriteLine( $"{meta.Target.Method}: completed.") } // [] } ``` ## Benefits of aspect-oriented programming ### Benefit 1. Boosting development efficiency Utilizing aspects in development frees up time for more meaningful tasks. By employing Metalama, you shift part of the implementation work from your team to the compiler, because patterns typically follow implementation guidelines: algorithmic processes that machines excel at handling. Telemetry from PostSharp projects, consistent with customer reports, shows source-code savings of around 15% on average, ranging from 10% to 50% depending on project type. With Metalama, you create an aspect once and save time (and source code) each time it is applied. Because the [COCOMO II](https://csse.usc.edu/tools/COCOMOII.php) cost model has software cost grow roughly in proportion to its size, a 10-50% smaller codebase translates into a comparable saving in development and maintenance effort, with the same functionality and better reliability. This approach allows developers to concentrate on inventive and value-adding tasks, increasing job satisfaction by eliminating the need to write repetitive low-level code. Metalama enables architects and senior developers to address overarching architectural challenges and streamline implementation, acting as talent multipliers within the team. New or junior team members can quickly ramp up and contribute more effectively, as they can focus on specific tasks without being burdened by low-level technical details. ### Benefit 2. Enhancing application reliability Utilizing AOP enhances application reliability in several ways. * First, less boilerplate code leads to fewer bugs. As best-selling author Steve McConnell points out, in a typical business application, one defect exists for every 40 lines of code, so reducing the source code also reduces the number of defects. The saving may be more than proportional: academic research (Eaddy et al., _Do Crosscutting Concerns Cause Defects?_, IEEE Transactions on Software Engineering, 2008) found that the crosscutting code aspects factor out is disproportionately associated with defects. Moreover, patterns implemented with aspects require testing only once, not every time they are applied to business code. * Second, AOP allows you to code at a higher level of abstraction, with most low-level details reliably handled by the compiler. This results in simpler source code that is easier to reason about, thereby reducing the number of defects. AOP enables faster failure detection, often as soon as build time. Consider multithreading, for example. Instead of making field-level decisions with locks or events, you can make architectural decisions to apply the [Synchronized] threading model to the base class, with the aspect taking care of the rest. * Lastly, AOP makes it affordable to prepare your app for production. You can add comprehensive logging to your entire codebase in minutes and implement features such as caching or exception handling without cluttering your business logic. AOP is the ideal tool to enhance the reliability of your applications. ### Benefit 3. Taming code complexity Utilizing aspects effectively reduces code complexity. By clearly separating technical details from business logic, code becomes cleaner, simpler, and easier to read. This clarity allows new team members to quickly understand the code, diagnose problems, fix bugs, and add new functionalities. > “Sure, we achieved some considerable savings in terms of LOC. But more importantly, the new code is much less complex and much easier to maintain. This is what really saves time and money in the long run.” > Daniel Wolf, Project Manager, mobileX AG ### Benefit 4. Streamlining maintenance Ultimately, the primary advantage of using AOP is reducing maintenance costs and extending the codebase's lifespan before a complete rewrite becomes necessary. Maintenance accounts for an estimated 55% to 95% of a software system's total cost, and research by Minelli, Mocci and Lanza (_I Know What You Did Last Summer_, IEEE ICPC, 2015) found that developers spend around 70% of their time reading and understanding existing code rather than writing it. Keeping complexity low therefore has an outsized impact on the maintenance team's productivity. ## Features checklist ### Code transformation (advising) The main role of an aspect is to transform code. Therefore, the most critical factor when choosing an AOP framework is the kind of code transformation it supports. The more transformations supported, the more complex patterns you can automate. Here are the advice kinds supported by Metalama: - Overriding existing type members (including non-virtual, non-interface members). - Introducing new members to an existing type. - Introducing new types. - Implementing new interfaces in existing types. - Appending parameters to constructors and pulling (required for dependency injection). ### Design-time support In the past, you had to build your project to refresh errors and warnings. IDEs have come a long way and now provide real-time feedback to developers while typing. If your aspect introduces new members, types, or interfaces, it's often useful to reference them in _source_ code. If the new member is generated as a post-compilation step, this is not possible. Only Roslyn-based frameworks can bring this real-time development experience to aspect-oriented programming. ### Error reporting A secondary but important role of aspects is to report warnings and errors. For instance, you might want to report an error if the developer tries to use your caching aspect on a `void` method. As a bonus, look for the ability to suggest a code fix when you report a warning. ### Targeting mechanisms How will you choose which types, methods, or properties your aspects will apply to? Metalama supports the following mechanisms: * **Custom attributes**: You hand-pick the target declarations and add aspects as custom attributes. * **Inheritance**: Aspects applied to a base type or member automatically apply to derived types and overriding members. * **Programmatically**: Aspects can be added using a compile-time C# API that lets you query the code model using a familiar LINQ-style API. Other frameworks (including PostSharp) may also support adding aspects using a compile-time XML file, but we found this approach less practical than the purely programmatic one when designing Metalama. ### Debugging experience Can you still meaningfully debug your code after you enhance it with aspects? Can you debug the _generated_ code, or just the _source_ code? With Metalama, the default debugging experience is to debug the _source_ code, meaning you will step into the source code but step over the generated code. However, by switching to the `LamaDebug` configuration, you will be able to step into every line of code generated by the aspect. ### Testing How can you test that your aspects are working as expected? Do they consistently generate the code you expect? Do they report expected errors and warnings upon invalid code? Metalama provides a test framework that allows you to test that the generated code is as expected. ### IDE tooling The aim of aspect-oriented programming is to get the boilerplate out of sight and keep the source code clean. However, to understand the code, it's often useful to know _that_ an aspect affects your class or method, even if you don't want to be always bothered by _how_ it is affected, i.e., you don't want to always see the generated code. Metalama integrates with CodeLens, so you always get a hint when an aspect is applied to your code. If you want to see exactly how your code is affected, you can always open the _diff_ comparing your source code with the generated code. --- # Builder Pattern > The Builder pattern constructs complex objects step by step but requires much boilerplate code, which Metalama can help reduce. Canonical: https://postsharp.net/metalama/applications/builder.html The Builder pattern is a creational design pattern that allows you to construct complex objects step by step. It is especially useful when you need to create an immutable object with many optional parameters or properties. A variant of this pattern, the Abstract Builder, adds abstraction to the construction process. The main inconvenience of the Builder pattern is the large amount of repetitive code required to implement it. This can be virtually eliminated with a Metalama aspect. ## Example In the following example, we will use the `Song` class for the Builder pattern. The `Song` class has two required properties (`Artist` and `Title`) and two optional properties (`Duration` and `Genre`). ```cs // [] [GenerateBuilder] // [] public partial class Song { [Required] public string Artist { get; } [Required] public string Title { get; } public TimeSpan? Duration { get; } public string Genre { get; } = "General"; } ``` The `GenerateBuilder` aspect generates all the necessary code on the fly. We can use the `Song` class as follows: ```cs var songBuilder = new Song.Builder( "Joseph Kabasele", "Indépendance Cha Cha" ); songBuilder.Genre = "Congolese rumba"; var song = songBuilder.Build(); ``` Show me how it works! The [GenerateBuilder](https://doc.metalama.net/examples/builder) aspect generates a `Builder` class nested inside the `Song` class and a `ToBuilder` method to create a new `Builder` object. ```cs // [] public partial class Song { public Builder ToBuilder() => new Builder(this); public class Builder { public Builder(string artist, string title) { Artist = artist; Title = title; } internal Builder(Song source) { Artist = source.Artist; Title = source.Title; Duration = source.Duration; Genre = source.Genre; } public string Artist { get; set; } public TimeSpan ? Duration { get; set; } public string Genre { get; set; } = "General"; public string Title { get; set; } public Song Build() { var instance = new Song(Artist, Title, Duration, Genre) !; return instance; } } } // [] ``` That's a lot of boilerplate you want to avoid! ## Metalama benefits * **Improve productivity**: Any generated code is code you don't have to write and maintain. * **Reduce human errors**: Whenever you have to add new optional (or required) properties to the `Song` class, the aspect will take care of it. It's the best way to avoid having to remember to update the Builder class manually by adding new fields, properties, and the necessary mappings to move the value of that new Builder property to the `Song` class. ## Resources * Blog post: [Implementing the Builder pattern with Metalama](https://postsharp.net/blog/builder-pattern-with-metalama) * Example: [Implementing the Builder pattern without boilerplate](https://doc.metalama.net/examples/builder) --- # Caching > Metalama Caching is an aspect library for caching in C# and .NET, including invalidation. It minimizes repetitive code and errors. Canonical: https://postsharp.net/metalama/applications/caching.html Caching is probably the simplest way to improve the performance of an application. Maintaining a cache can be cumbersome, especially if you must add caching to dozens of methods and manage dependencies. Metalama makes this process simpler and less error-prone. Traditionally, setting up caching involves working directly with the caching framework's API. You need to create cache keys, check if items are already in the cache, and add new items when necessary. Additionally, when the original data changes, you must ensure the cache is updated accordingly. It is one of the most error-prone tasks in software development. With [Metalama Caching](https://www.nuget.org/packages/Metalama.Patterns.Caching), an open-source caching library based on Metalama, adding caching to a method can be as simple as adding a custom attribute. `Metalama.Patterns.Caching` is a production-ready, professionally supported, and open-source aspect library. ## Example Here is how caching can look using Metalama: ```cs internal partial class CustomerServices { // [] [Cache] // [] public Customer GetCustomer(int id) { Console.WriteLine($">> Retrieving the customer {id} from database..."); Thread.Sleep(1000); return new Customer { Id = id, Name = "Rubber Debugging Duck" }; } // [] [InvalidateCache(nameof(GetCustomer))] // [] public void UpdateCustomer(int id, string newName) { Console.WriteLine($">> Updating the customer {id} in database..."); Thread.Sleep(1000); } } ``` To initialize Metalama Cache, add this line to your startup code: ```csharp // Add the caching service. builder.Services.AddMetalamaCaching(); ``` ## Benefits * **Reduced boilerplate**: Metalama Caching enables you to cache the return value of a method as a function of its arguments with just a `[Cache]` custom attribute. To invalidate the cache, add the `[InvalidateCache]` aspect to the update methods. To use a custom class as a parameter of a cached method, apply the `[CacheKey]` aspect to mark the properties that uniquely identify the object. Consequently, your business code becomes shorter and more readable. * **Reduced bugs**: Manually generating cache keys with hand-written code is notoriously bug-prone. Metalama Caching eliminates this source of defects by implementing a reliable approach to key generation, combining object-oriented and aspect-oriented techniques. * **Reduced coupling**: Cache invalidation can be complex and often requires you to review your complete write methods every time you add caching to a read method. Cache dependencies act as an abstraction layer between read and write methods, reducing coupling between them. ## Features * `[Cache]` aspect for caching. * `[InvalidateCache]` aspect for cache invalidation. * `[CacheKey]` aspect to generate cache keys from objects. * Type-safe cache invalidation API. * Full support for dependency injection. * Support for dependencies. * Support for Redis. * Support for cache synchronization over a message bus. ## Resources * Reference documentation: [Metalama.Patterns.Caching](https://doc.metalama.net/patterns/caching) * NuGet package: [Metalama.Patterns.Caching](https://www.nuget.org/packages/Metalama.Patterns.Caching) * Source code: [Metalama.Patterns.Caching](https://github.com/metalama/Metalama.Patterns/tree/HEAD/src/Metalama.Patterns.Caching) * Example: [Create your own caching aspect](https://doc.metalama.net/examples/caching) * Blog post: [Five ways to add caching to your ASP.NET App](https://postsharp.net/blog/aspnet-caching) * Blog post: [Simplify your ASP.NET Aspire caching with Metalama](https://postsharp.net/blog/aspire-caching-metalama) --- # Change Tracking > The document explains implementing change tracking in .NET UIs using `IChangeTracking` and `IRevertibleChangeTracking` interfaces with Metalama. Canonical: https://postsharp.net/metalama/applications/change-tracking.html When programming UIs, especially "transactional" UIs with _Ok_ buttons, it's necessary to detect whether a data object has been modified. This feature is often called the _Dirty Flag_. It allows, for instance, enabling or disabling the _Save_ button. In .NET, this behavior is abstracted by the `IChangeTracking` interface. Another interface, `IRevertibleChangeTracking`, supports the _Reset_ feature. Here is the definition of these system interfaces: ```csharp public interface IChangeTracking { bool IsChanged { get; } void AcceptChanges(); } public interface IRevertibleChangeTracking : IChangeTracking { void RejectChanges(); } ``` Implementing these interfaces manually requires a lot of work because it is necessary to instrument every property setter or any method modifying fields. Generating code in properties and fields is exactly where Metalama excels. You can create an aspect that adds the `IChangeTracking` or `IRevertibleChangeTracking` interface to the target type and instruments all fields and properties to set the `IsChanged` property. ## Example Implementing `IChangeTracking` or even `IRevertibleChangeTracking` in the following classes can be as easy as adding a `[TrackChanges]` custom attribute. We made a slight modification to the .NET pattern: we require an `IsTrackingChanges` property to be enabled for change tracking to work. ```csharp // [] [TrackChanges] // [] public partial class Comment { public Guid Id { get; init; } public required string Author { get; set; } public required string Content { get; set; } } public class ModeratedComment : Comment { public bool? IsApproved { get; set; } } ``` Here is the aspect in action: ```csharp var comment = new ModeratedComment { Id = Guid.NewGuid(), Author = "Cicero", Content = "Non nobis solum nati sumus", // Must be the last one. IsTrackingChanges = true }; Console.WriteLine($"IsChanged={comment.IsChanged}"); comment.IsApproved = true; Console.WriteLine($"IsChanged={comment.IsChanged}"); comment.AcceptChanges(); Console.WriteLine($"IsChanged={comment.IsChanged}"); ``` The program prints the following text: ```text IsChanged=False IsChanged=True IsChanged=False ``` Show me how it works! The [TrackChanges](https://doc.metalama.net/examples/change-tracking/change-tracking-1) aspect transforms the `Comment` class into this: ```csharp public partial class Comment : ISwitchableChangeTracking, IChangeTracking { // [] private string _author; private string _content; // [] public required Guid Id { get; init; } // [] public required string Author { get => this._author; set { if (value != this._author) { this._author = value; OnChange(); } } } public required string Content { get => this._content; set { if (value != this._content) { this._content = value; OnChange(); } } } public bool IsChanged { get; private set; } public bool IsTrackingChanges { get; set; } public void AcceptChanges() { IsChanged = false; } protected void OnChange() { if (IsTrackingChanges) { IsChanged = true; } } // [] } ``` A [more complex variant](https://doc.metalama.net/examples/change-tracking/change-tracking-3) of the aspect can integrate with the `INotifyPropertyChanged` interface or implement the `RejectChanges` functionality. ## Metalama benefits - Enhance productivity and reduce boilerplate code by automating property and field instrumentation. - Improve code readability. - Ensure consistency and reduce human error in implementing change tracking. - Simplify maintenance by centralizing logic in one aspect class. ## Resources * Example: [Change tracking](https://doc.metalama.net/examples/change-tracking) --- # Classic Singleton Pattern > This document explains the Singleton pattern, highlighting its implementation and benefits using the Metalama framework. Canonical: https://postsharp.net/metalama/applications/classic-singleton.html The Singleton pattern is arguably one of the most famous creational design patterns. It focuses on ensuring that a class has only one instance and provides a global point of access to that instance. ## Example The following `PerformanceCounterManager` class is a classic Singleton example, designed to consistently gather performance counters across an entire application. The `[Singleton]` class attribute indicates it's a Singleton and is also an aspect that can be [developed using Metalama](https://doc.metalama.net/examples/singleton/singleton-1#aspect-implementation). It generates a static `Instance` property and reports an error if there is a public constructor. If needed, it also creates a private constructor. ```cs // [] [Singleton] // [] public class PerformanceCounterManager { private readonly ConcurrentDictionary _counters = new(); public void IncrementCounter(string name) => this._counters.AddOrUpdate(name, 1, (_, value) => value + 1); } ``` The `[Singleton]` aspect generates the following code: ```cs public class PerformanceCounterManager { // [] public static PerformanceCounterManager Instance { get; } = new(); private PerformanceCounterManager() {} // [] private readonly ConcurrentDictionary _counters = new(); public void IncrementCounter(string name) => this._counters.AddOrUpdate(name, 1, (_, value) => value + 1); } ``` ## Metalama benefits * **Better expressiveness**. The `[Singleton]` attribute makes the intent of the class clear and explicit. It's easier to understand the code and maintain it. * **Code consistency**. The generated code is always consistent with the pattern rules. Although the Singleton pattern has very little repetitive code, it is easy to forget to make the constructor private or to call the Instance method in the same way. * **Less code**. You're just saving two lines of code thanks to this aspect, so boilerplate reduction will not be your main motivation. ## Resources * Example: [Classic Singleton](https://doc.metalama.net/examples/singleton/singleton-1) --- # Classic Applications > Metalama aids in implementing features for mobile, desktop, or web apps using design patterns, UI code patterns, and defensive programming. Canonical: https://postsharp.net/metalama/applications/classics.html Whether you're building a mobile, desktop, or web app, Metalama can help implement a broad set of features. | Category | Patterns | |----------|----------| | [Classic Design Patterns](https://postsharp.net/metalama/applications/design-patterns) | Singleton, Memento, Factory, Builder, Decorator, Proxy | | [UI Code Patterns](https://postsharp.net/metalama/applications/ui) | Change Tracking, INotifyPropertyChanged, Memoization, Undo-Redo, Command | | [Defensive Programming](https://postsharp.net/metalama/applications/contracts) | Code contracts: pre-conditions, post-conditions, invariants, data annotations. | | [Classic Object Services](https://postsharp.net/metalama/applications/object-services) | Deep Cloning, ToString, Comparison | --- # Clean Code > This page lists several criteria of clean code and shows how Metalama can help improve many of them. Canonical: https://postsharp.net/metalama/applications/clean-code.html *Clean Code*, as defined by Robert C. Martin, emphasizes the importance of writing code that is easy to understand, maintain, and modify. There has been much discussion around the importance of writing clean code, a skill every developer should strive to acquire. It's crucial to understand that clean code matters most _at scale_, when the application reaches thousands of lines of code. ## Clean code tools There are several categories of tools: - **General code analysis tools** detect violations of general principles, industry standards, and best practices, but they cannot easily be customized to your specific situation. - **Architecture verification tools**, such as Metalama Architecture Verification, allow you to create your own rules. - **Code generation tools**, including aspect-oriented frameworks, allow you to generate redundant code on the fly during compilation, keeping your source code clean and concise. Your clean code pipeline will need _all_ of these tools. ## How can Metalama help? According to Martin, clean code possesses several characteristics. Here is how Metalama can help improve each of them.

Criteria

How Metalama can help

Complementary tools

Readability

Code should be well-organized, and names should clearly indicate their purpose.

  • Code is often cluttered by operational requirements like error handling, data binding, and caching. With Metalama, you can encapsulate these requirements as aspects and apply them to your code using custom attributes or fabrics. This helps you remove this clutter from the source code, making its essence more apparent.

  • Using Metalama Architecture Verification, you can enforce naming conventions, for instance, naming classes and members according to their role in the design pattern.

Code style and formatting.

Code cleanup.

Simplicity

Code should be as simple as possible, avoiding unnecessary complexity.

  • Metalama can seem like a source of complexity in the beginning, but when used wisely (not as a hacking tool) and at scale (dozens of uses of each aspect), it reduces complexity by achieving a better separation of concerns between business logic and non-technical requirements.

  • Metalama Architecture Verification or custom validation aspects can detect when code does not adhere to the desired architecture or pattern guidelines.

Efficiency

Code should be efficient in performance and resource usage.

  • Thanks to its C#-to-C# template technology, the code generated by Metalama is as fast as hand-written code.

  • Metalama allows easy implementation of performance-improving features such as caching or memoization, or it can instrument the code and collect performance information.

Profilers.

Benchmark and performance tests.

Testability

Code should be easy to test, meaning it's modular and allows for independent component testing.

  • Operational features such as logging or error handling can be difficult to test effectively as their implementation is replicated in several classes, each of which needs to be tested separately. However, by utilizing Metalama, these features can be implemented in a centralized location, known as an "aspect." Aspects can then be thoroughly tested using Metalama's testing framework.

Refactorability

The code should be written in a way that allows easy modification or refactoring without breaking functionality.

  • Using aspects can reduce the need for refactoring because features implemented with aspects can be changed or added without refactoring. When a pattern needs change, the only thing to modify is the aspect, not the hundreds of files using the aspect.

  • You can use Architecture Verification to raise warnings for code that requires refactoring. For instance, if you no longer want a component to be used except from a given namespace, you can report a warning for any undesired use.

  • Metalama aspects can be programmed to suggest custom refactorings. They are offered to developers as other C# refactorings.

Lack of Duplication

Avoid repetition of code (DRY Principle).

  • Metalama can further reduce repetitive code that cannot be efficiently abstracted by OOP by encapsulating cross-cutting concerns into aspects.

Code duplication detection tools.

Expressiveness

The code should clearly express the intentions of the developer.

  • By removing boilerplate, the business meaning of the source code is more apparent.

  • Instead of using code comments, you can express your design intent using architecture validation custom attributes or fabrics. Metalama will report a warning whenever this rule is violated.

Error Handling

Code should anticipate and properly handle potential errors.

Minimal Dependencies

Code should have minimal dependencies on other pieces of code.

  • Dependencies on technical services such as the logger or cache will be moved to aspects. Your source code will be free of these dependencies. During the build, Metalama will pull them into your code, making them runtime dependencies, but not source-code ones.

Standards Compliance

Code should adhere to industry standards and best practices.

  • Beyond industry-wide standards, Metalama Architecture Validation can help you validate the code against rules and conventions that are specific to your project.

Generic code analysis tools.

--- # Cloning > Learn how to use Metalama to automatically implement deep cloning for C# objects, eliminating repetitive ICloneable code and reducing bugs. Canonical: https://postsharp.net/metalama/applications/cloning.html The .NET runtime offers a method for _shallow_ cloning with `object.MemberwiseClone`. However, deeply cloning an object tree usually involves a lot of repetitive code, which can add both cost and bugs to your application. This is one of the many patterns you can fully automate with Metalama. To make an object deeply clonable, you typically need to: - Implement the `ICloneable` interface for the type. - Add a method `public T Clone()` and implement it, usually by calling `MemberwiseClone` and then invoking the `Clone` method on each field of a clonable type. - Implement `object ICloneable.Clone()` as an explicit interface implementation, which calls the public `Clone` method. Most of the time, this is purely algorithmic work. ## Example The [Cloneable example aspect](https://doc.metalama.net/examples/clone) covers all the steps mentioned above, and more. It requires you to mark the fields you wish to clone with the `[Child]` custom attribute, but feel free to modify the aspect code to change this behavior. You can apply this to any class: ```csharp // [] [Cloneable] // [] class Game { public Player Player { get; set; } // [] [Child] // [] public GameSettings Settings { get; set; } } ``` During compilation, the following code will be generated: ```csharp using System; [Cloneable] class Game // [] : ICloneable // [] { public Player Player { get; set; } [Child] public GameSettings Settings { get; set; } // [] public virtual Game Clone() { var clone = (Game)this.MemberwiseClone(); clone.Settings = ((GameSettings)this.Settings.Clone()); return clone; } object ICloneable.Clone() { return Clone(); } // [] } ``` If you make the class `partial`, Metalama will also generate the code at design time. This means that Intellisense will "see" that `Game` implements `ICloneable` and includes a public `Clone` method. ## Resources - Commented example: [Deep Cloning](https://doc.metalama.net/examples/clone) --- # Command > Metalama.Patterns.Wpf automates WPF command implementation, enhancing productivity by reducing boilerplate code and supporting `CanExecute` logic. Canonical: https://postsharp.net/metalama/applications/command.html One of the [best practices](https://postsharp.net/blog/wpf-best-practices-2024) in WPF is to implement the logic behind buttons and menu items as a Command (`ICommand`) instead of a simple event handler. However, WPF commands require some redundant code, especially to integrate the `CanExecute` logic. The [Command](https://doc.metalama.net/patterns/wpf/command) aspect of the `Metalama.Patterns.Wpf` open-source package solves this problem by automatically generating the plumbing code. `Metalama.Patterns.Wpf` is a production-ready, professionally-supported, and open-source aspect library. Available for WPF only. ## Example In the following example, the `[Command]` aspect will generate an `ExecuteSaveCommand` property based on the `ExecuteSave` method and the `CanExecuteSave` property. ```csharp [Observable] public partial class MainWindow : Window { public bool HasChanges { get; private set; }; // [] [Command] // [] public void ExecuteSave() { // Details skipped. } public bool CanExecuteSave => this.HasChanges; } ``` ## Benefits * **Write less code**. Minimize boilerplate code to implement ICommand properties. * **Keep your code clean and concise**. Your business logic is easier to read and less entangled. * **Reduce human errors**. Eliminate repetitive code that is prone to mistakes. * **Improve maintainability**. Keep your codebase clean and easier to understand. * **Integrates with existing projects**. Works with existing WPF projects without requiring major refactoring. ## Features * Idiomatically C#: Metalama works _with_ your code, not against it. * Supports simple, parameterized, async, and background commands. * Integrates with the `[Observable]` aspect to handle the `CanExecute` functionality without boilerplate. ## Resources * Blog post: [Implementing WPF Commands with Metalama](https://postsharp.net/blog/wpf-command-metalama). * Reference documentation: [WPF Commands](https://doc.metalama.net/patterns/wpf/command). * Source code: [Metalama.Patterns.Wpf](https://github.com/metalama/Metalama.Patterns/tree/HEAD/src/Metalama.Patterns.Wpf). * NuGet package: [Metalama.Patterns.Wpf](https://www.nuget.org/packages/Metalama.Patterns.Wpf). --- # Defensive Programming > Defensive Programming uses redundant validation checks to enhance software robustness, streamlined by Metalama's automatic code generation. Canonical: https://postsharp.net/metalama/applications/contracts.html **Defensive Programming** emphasizes adding redundant validation checks (such as preconditions, postconditions, and invariants) to ensure software remains robust against unexpected inputs or failures. Defensive programming prevents the propagation of defects from one component to another, making it easier to trace exceptions to their root cause. Manually implementing these practices is often labor-intensive due to the need for repetitive boilerplate code. Metalama streamlines this process by automatically generating validation code, enabling developers to reap its benefits without the associated overhead. Metalama [Code Contracts](https://doc.metalama.net/patterns/contracts), implemented in the `Metalama.Patterns.Contracts` package, are a simple mechanism to validate the value assigned to properties, fields, or parameters. When applied to Model or ViewModel properties, code contracts can be used to implement _user input validation_. Unlike the [System.ComponentModel.DataAnnotations](https://learn.microsoft.com/en-us/dotnet/api/system.componentmodel.dataannotations) namespace, Metalama's code contracts can be applied to _any_ class and not just ASP.NET ones. `Metalama.Patterns.Contracts` is a production-ready, professionally-supported, and open-source aspect library. ## Example The following snippet shows contracts applied to properties of the `Customer` class. ```csharp public class Customer { // [] [Phone] // [] public string? Phone { get; set; } // [] [Url] // [] public string? Url { get; set; } // [] [Range( 1900, 2100 )] // [] public int? BirthYear { get; set; } public string? FirstName { get; set; } // [] [Required] // [] public string LastName { get; set; } } ``` Here we have a contract applied to an `out` parameter. ```csharp public interface ICustomerService { // Returns the name of a given customer or null if it cannot be found, // but never returns an empty string. bool TryGetCustomerName( int id, [NotEmpty] out string? name ); } ``` This code will check the value of _all_ non-nullable parameters in the current project. ```csharp internal class Fabric : ProjectFabric { public override void AmendProject( IProjectAmender amender ) { amender.VerifyNotNullableDeclarations(); } } ``` ## Benefits * Validate user inputs while keeping your code concise and readable. * Implement defensive programming and prevent the propagation of bugs from one component to another. ## Features * Preconditions: input parameter values, field/property setter values. * Postconditions: output parameter values, return values, property getter values. * Invariants: method executed before exiting any public method. * Global null-checking with one line of code thanks to fabrics. * Customizable and localizable: change the `throw` statement at will. ## Resources * Reference documentation: [Code Contracts](https://doc.metalama.net/patterns/contracts). * Source code: [Metalama.Patterns.Contracts](https://github.com/metalama/Metalama.Patterns/tree/HEAD/src/Metalama.Patterns.Contracts). * NuGet package: [Metalama.Patterns.Contracts](https://www.nuget.org/packages/Metalama.Patterns.Contracts). --- # Decorator Pattern > The Decorator pattern adds behaviors to objects without altering their structure, using Metalama for static implementation. Canonical: https://postsharp.net/metalama/applications/decorator.html The Decorator pattern is a structural design pattern that allows you to add new behaviors to objects. It is a good alternative to subclassing because it allows you to add new functionalities to objects without changing their structure. Metalama seems like it was _invented_ to implement decorators. If you want to apply decorators _statically_ to your code, use the [OverrideMethodAspect](https://doc.metalama.net/api/metalama-framework-aspects-overridemethodaspect), [OverrideFieldOrPropertyAspect](https://doc.metalama.net/api/metalama-framework-aspects-overridefieldorpropertyaspect), or [OverrideEventAspect](https://doc.metalama.net/api/metalama-framework-aspects-overrideeventaspect) according to what you want to decorate. If you want to _dynamically_ apply decorators at runtime, see the Proxy pattern. ## Example The `Retry` aspect can be applied to a method as a custom attribute: ```cs // [] [Retry] // [] public void Send( Message message ) { Console.WriteLine( "Sending message..." ); // Simulate unreliable message sending if ( ++this._sendCount % 3 == 0 ) { Console.WriteLine( "Message sent successfully." ); } else { throw new IOException( "Failed to send message." ); } } ``` When you compile, Metalama generates the following code for you: ```cs [Retry] public void Send( Message message ) { // [] for ( var i = 0;; i++ ) { try { // [] Console.WriteLine( "Sending message..." ); // Simulate unreliable message sending if ( ++this._sendCount % 3 == 0 ) { Console.WriteLine( "Message sent successfully." ); } else { throw new IOException( "Failed to send message." ); } // [] } catch ( Exception e ) when ( i < 3 ) { var delay = this.Delay * Math.Pow( 2, i + 1 ); Console.WriteLine( $"Method Broker.Send has failed " + $" on {e.GetType().Name}. Retrying in {delay / 1000} seconds... ({i + 1}/3)" ); Thread.Sleep( (int) delay ); } } // [] } ``` Thanks to the `[Retry]` aspect, this method automatically retries when it fails. Show me how it works! The `[Retry]` aspect implements a decorator that retries the execution of a method upon exception: ```cs internal class RetryAttribute : OverrideMethodAspect { public int Attempts { get; set; } = 5; public double Delay { get; set; } = 1000; public override dynamic? OverrideMethod() { for ( var i = 0;; i++ ) { try { return meta.Proceed(); } catch ( Exception e ) when ( i < this.Attempts ) { var delay = this.Delay * Math.Pow( 2, i + 1 ); Console.WriteLine( $"Method {meta.Target.Method.DeclaringType.Name}.{meta.Target.Method} has failed " + $" on {e.GetType().Name}. Retrying in {delay / 1000} seconds... ({i + 1}/{this.Attempts})" ); Thread.Sleep( (int) delay ); } } } } ``` ## Metalama benefits * **Use decorators everywhere**: With Metalama, we’re not limited to virtual or interface methods (such as with the Proxy pattern); we can intercept anything, even static private fields. This allows you to use the Decorator pattern throughout your code. * **Keep your code clean**: It's a great way to add new functionalities to objects without changing their structure. It allows you to keep your code clean and maintainable. * **Improve your code quality**: The generated code is always consistent with the design pattern rules. Every time you apply the aspect, you can be sure that the code is compliant with the Decorator pattern. ## Resources * Blog post: [The Decorator Pattern in Modern C#](https://postsharp.net/blog/decorator-pattern) --- # Dependency Properties > Metalama.Patterns.Wpf is an open-source library for WPF, enhancing productivity by generating dependency properties without boilerplate code. Canonical: https://postsharp.net/metalama/applications/dependency-property.html Dependency properties enable advanced property behaviors such as data binding, animation, and styling. However, implementing dependency properties in WPF often requires writing repetitive and error-prone boilerplate code, which can slow down development and introduce bugs. `Metalama.Patterns.Wpf` addresses this issue by allowing developers to generate dependency properties directly from C# automatic properties. This eliminates the need for boilerplate code and keeps your code cleaner and more maintainable. `Metalama.Patterns.Wpf` is a production-ready, professionally supported, and open-source aspect library. Available for WPF only. ## Example ```csharp public partial class FancyTextBlock : UserControl { public FancyTextBlock() { InitializeComponent(); } // [] [Required] [DependencyProperty] // [] public string Text { get; set; } = "Hello, world."; // [] [DependencyProperty] // [] public Brush FancyBorderBrush { get; set; } public void OnFancyBorderBrushChanged() { /* Handle changes here. */} } ``` ## Benefits * **Write less code**. Generate dependency properties from C# automatic properties without boilerplate code. * **Reduce human errors**. Eliminate repetitive code that is prone to mistakes. * **Improve maintainability**. Keep your codebase clean and easier to understand. * **Fits existing projects**. Works with existing WPF projects without requiring major refactoring. * **Customization**. Add validation rules or `OnPropertyChanged` callbacks tailored to your needs. ## Features * Integrates with [Metalama.Patterns.Contracts](https://doc.metalama.net/patterns/contracts) to set validation rules. * Fully customizable: add your own validation or `OnPropertyChanged` callbacks. ## Resources * Blog post: [Implementing WPF Dependency Properties with Metalama](https://postsharp.net/blog/wpf-dependency-property-metalama). * Reference documentation: [WPF Dependency Properties](https://doc.metalama.net/patterns/wpf/dependency-property). * Source code: [Metalama.Patterns.Wpf](https://github.com/metalama/Metalama.Patterns/tree/HEAD/src/Metalama.Patterns.Wpf). * NuGet package: [Metalama.Patterns.Wpf](https://www.nuget.org/packages/Metalama.Patterns.Wpf). --- # Design Patterns > This article details how Metalama can help implement design patterns in C#, including the Memento, Singleton, Factory, Builder, Decorator, and Proxy patterns. Canonical: https://postsharp.net/metalama/applications/design-patterns.html Design patterns are repetitive by design. The more your pattern implementations are regular and predictable, the more you can reduce the cognitive complexity of your codebase. If it's repetitive, it can be automated! Metalama can help implement design patterns in two ways: * **Code generation.** Sometimes patterns require so much repetitive code, with almost no creativity required, that it's possible to algorithmically generate the required code. Good examples of this are the Memento or Builder patterns. * **Code verification.** For some other patterns, there are fewer opportunities to generate code. However, we still want to verify that handwritten code complies with the design pattern rules. And we can also use Metalama for this. See, for instance, the modern Singleton and Abstract Factory patterns. ## Benefits * **Less boilerplate code.** Some design patterns require a fair amount of repetitive code. It can be reduced to zero with Metalama. * **Uniform, predictable codebase.** A key idea of design patterns is that similar problems must have similar solutions, and that components in these solutions must have similar names. Metalama allows you to enforce pattern rules and conventions across your codebase. * **Enforce dependencies.** Design patterns define how different classes of a pattern are allowed to communicate with each other. For instance, in a factory pattern, the object constructor might be called only from the factory class or method. You can use Metalama to enforce these constraints. ## Design Patterns Metalama can be used to generate or verify code in the following patterns: | Pattern | How Metalama can help | |--------------------|-----------------------------------------------------------------------------| | [Classic Singleton](https://postsharp.net/metalama/applications/classic-singleton) | Generate the boilerplate and enforce constructors to be private. | | [Modern Singleton](https://postsharp.net/metalama/applications/modern-singleton) | Enforce constructors to be called only from the dependency container configuration code. | | [Memento](https://postsharp.net/metalama/applications/memento) | Generate all of the boilerplate code. There's a lot. | | [Factory](https://postsharp.net/metalama/applications/factory) | Enforce constructors to be called only from factory methods or classes. | | [Builder](https://postsharp.net/metalama/applications/builder) | Generate all of the boilerplate code. There's a lot. | | [Decorator](https://postsharp.net/metalama/applications/decorator) | Metalama's override feature is basically an automatic Decorator pattern. | | [Proxy](https://postsharp.net/metalama/applications/proxy) | Generate proxy classes at compile time instead of at run time. | --- # DevOps > Shows how Metalama can help make your C# application production-ready, an important aspect of DevOps. Canonical: https://postsharp.net/metalama/applications/devops.html There is a significant difference between a prototype and a production-ready application. The former is meant to serve as a proof-of-concept and should only run on your computer for a 10-minute demo. The latter must be able to run unattended 24/7 at minimal operating costs. Metalama helps you transition your app from prototype to production with less boilerplate and fewer errors. | Category | Applications | |--|--| | [Logging & Tracing](https://postsharp.net/metalama/applications/logging) | Method-level logging, including parameters and return values. | | [Metrics](https://postsharp.net/metalama/applications/metrics) | Method-level performance and reliability metrics. | | [Caching](https://postsharp.net/metalama/applications/caching) | Cache a method's return value as a function of its parameters. | | [Exception handling](https://postsharp.net/metalama/applications/exception-handling) | Implement resilience with or without Polly. Enrich exceptions with parameter values for easier diagnostics. | | [Security](https://postsharp.net/metalama/applications/security) | Authorization, audit, homomorphic encryption. | --- # Equality Comparison > Metalama automates C# equality comparison, generating IEquatable, GetHashCode, and operators to eliminate boilerplate code. Canonical: https://postsharp.net/metalama/applications/equatable.html There are times when .NET's default equality comparers are sufficient, but when they fall short, crafting a custom one means writing a host of class members, all of it repetitive code. With Metalama, mark the types or members that need an equality contract, and the code is generated during compilation. You might think implementing the `IEquatable` interface is enough, but for a consistent equality pattern, you must: - Add the `IEquatable` interface to the type. - Implement the `Equals(T)` public method. - Override the default `Equals(object)` method. - Override the default `GetHashCode()` method. - Add `==` and `!=` operators. With Metalama, all this can be generated using a single custom attribute. ## Examples In this example, the aspect generates code for the Equatable pattern whenever it detects an `[EqualityMember]` attribute in your code. For instance, you want two instances of the `Person` class to be uniquely identified by their `Id` property. ```csharp internal partial class Person { // [] [EqualityMember] // [] public int Id { get; init;} public string? Name { get; init; } public DateTime DateOfBirth { get; init; } } ``` Metalama generates the following code in the background: ```csharp using System; using System.Collections.Generic; namespace Metalama.Samples.Comparison1; internal partial class Person // [] : IEquatable // [] { // [] public bool Equals(Person other) { if (other == null) { return false; } if (object.ReferenceEquals(this, other)) { return true; } if (!EqualityComparer.Default.Equals(Id, other.Id)) { return false; } return true; } public override bool Equals(object? other) { if (object.ReferenceEquals(this, other)) { return true; } return other is Person typed && Equals(typed); } public override int GetHashCode() { var hashCode = default(HashCode); hashCode.Add(Id, EqualityComparer.Default); return hashCode.ToHashCode(); } public static bool operator ==(Person a, Person b) { return a == null && b == null || a != null && a.Equals(b); } public static bool operator !=(Person a, Person b) { return a == null ^ b == null || a != null && !a.Equals(b); } // [] } ``` The new members are also generated at design time, so IntelliSense will recognize their existence. ## Resources - Commented example: [Equatable](https://doc.metalama.net/examples/comparison) --- # Exception Handling > This article shows how to use Metalama to improve the resilience and performance of .NET/C# apps by adding exception handling, caching, and other policies. Canonical: https://postsharp.net/metalama/applications/exception-handling.html Resilience refers to a system's ability to recover gracefully from failures, handle unexpected situations, and continue delivering what it's supposed to do. To achieve this, engineers use key resilience patterns like circuit breakers, retries, fallbacks, and throttling. Adding resilience into a codebase usually requires writing boilerplate code, which is often tedious, unproductive, and error-prone. ## Resilience with Polly Polly is the go-to library for implementing resilience in .NET. It implements the most-used policies and is actively maintained, so there's rarely a reason to write your own. However, adding Polly to your code can still require some boilerplate code: you still need to pull dependencies and, for each method, wrap it into a delegate call. With Metalama, you can add Polly without any boilerplate. ### Benefits Compared to adding Polly manually, adding it with Metalama has the following benefits: - **Clean code**: There's no need to wrap your code in a delegate call, so it remains crystal clear and is easier to maintain. - **Modify the pattern**: You can replace Polly with another library or remove it altogether if you want. ### Example You can create an `[AddPolicy]` Metalama aspect that pulls the Polly dependency and wraps your method into a delegate code: ```cs public class CalculatorService { // [] [AddPolly( "MyPolicy" )] // [] public int Add( int a, int b ) { // Your code here return a + b; } } ``` See [this commented example](https://doc.metalama.net/examples/exception-handling/retry/retry-5) to learn how to create such aspects. Show me my transformed code! Here is what the `[AddPolly]` aspect may do to your code. ```cs internal class CalculatorService { private ILogger _logger; private IPolicyFactory _policyFactory; public CalculatorService(ILogger logger, IPolicyFactory? policyFactory) { this._logger = logger; this._policyFactory = policyFactory; } public int Add(int a, int b) { // [] object? ExecuteCore() { try { // [] // Your code here. return a + b; // [] } catch (Exception e) { _logger.LogWarning( $"CalculatorService.Add(a = {{{a}}}, b = {{{b}}}) has failed: {e.Message}"); throw; } } var policy = _policyFactory.GetPolicy(PolicyKind.Retry); return (int)policy.Execute(ExecuteCore); // [] } } ``` ### Resources * Example: [Implementing an auto-retry aspect that uses Polly](https://doc.metalama.net/examples/exception-handling/retry/retry-5) * Blog: [5 practical ways to add Polly to your application](https://postsharp.net/blog/polly) ## Resilience without Polly You can also create exception-handling aspects without Polly. See [these examples](https://doc.metalama.net/examples/exception-handling) to get some inspiration. ## Enriching exception stacks In diagnosing software problems, context is everything. Exception call stacks are a good example of this context, but they are not enough: they lack parameter values that give you an idea of the application state. You can use an aspect to **append the parameter values** to any `Exception` flying through your method. When reporting the exception, you can add the parameter values to the call stack. ### Example Suppose we have the following exception: ```text System.NullReferenceException: Object reference not set to an instance of an object at CustomerService.GetCustomerCreationDate(Int32) at HomeController.Index() ... ``` To continue your investigation, you would need to know for which value of `customerId` the `GetCustomer` method returns `null`. You create an `EnrichExceptionAttribute` aspect that automatically wraps the whole method body in a try-catch block and enriches the exception with the parameter values. You can [see the full code of that aspect here](https://doc.metalama.net/examples/exception-handling/enrich-exception#aspect-code). However, solely defining an aspect is not enough. You need to apply it to all your methods, so you add this class to your project: ```csharp internal class Fabric : ProjectFabric { public override void AmendProject(IProjectAmender amender) => amender .SelectTypes() .SelectMany(type => type.Methods) .AddAspectIfEligible(); } ``` Now, instead of just reporting `exception.ToString()`, you can append the result of `exception.GetContextInfo()` to get the following exception stack: ```text System.NullReferenceException: Object reference not set to an instance of an object at CustomerService.GetCustomerCreationDate(Int32) at HomeController.Index() ... with CustomerService.GetCustomerCreationDate(123) ``` Now you see (at the bottom of the block) for which value of `customerId` your code fails. Show me how it works! The problematic code is the following: ```csharp public string GetCustomerCreationDate( int customerId ) => _customerDb.GetCustomer( customerId ).CreationData; ``` When you build the project, the fabric added the [EnrichException](https://doc.metalama.net/examples/exception-handling/enrich-exception#aspect-code) aspect to any public method, including this one. Therefore, instead of your source code, the following code is executed: ```cs public string GetCustomerCreationDate( int customerId ) { try { return _customerService.GetCustomer( customerId ).CreationData; } catch (Exception e) { e.AppendContextFrame($"CustomerService.GetCustomerCreationDate({customerId})"); throw; } } ``` In this example, the `AppendContextFrame` is just a method that appends the parameter values to the exception message, providing additional context information that can help developers quickly identify the cause of the error. ### Metalama benefits - **Low-cost to adopt.** Because the exception-wrapping code is generated at compile time, enriching every method costs one aspect instead of per-method edits. - **Clean code.** The exception handling code is generated on-the-fly during compilation, so your code remains crystal clear and is easier to maintain. - **Add or remove anytime.** You can add or remove the feature at any time. You can also enable logging only in select build configurations. ### Resources * Example: [Enriching exceptions with parameter values](https://doc.metalama.net/examples/exception-handling/enrich-exception) --- # Factory Patterns > Factory and Abstract Factory patterns create objects without exposing logic, using Metalama for architecture verification. Canonical: https://postsharp.net/metalama/applications/factory.html The Factory and Abstract Factory are creational design patterns used to create objects without exposing the creation logic to the client. The main difference between the Factory and Abstract Factory is that the Factory pattern is a single method that creates objects, while the Abstract Factory is a super-factory that creates other factories. Although Metalama can be used to create a Factory aspect that generates a factory method for a class (or even an Abstract Factory class), it's generally not worth the effort unless you have a large number of highly trivial factories that just call the constructor. Instead, you can use [Metalama Architecture Verification](https://doc.metalama.net/conceptual/architecture) to verify that no code except the factories directly calls the constructor. By doing this, you're making the design intent _explicit_ and _executable_, reporting warnings in real-time in case of violation. ## Example Suppose we have a base interface `IShape` and want to verify that only the `ShapeFactory` class can create instances of the interface. We can add a `[UseFactory]` aspect to it. ```cs // [] [UseFactory( typeof(ShapeFactory) )] // [] public interface IShape { public Color Color { get; } } ``` It automatically applies to derived types: ```csharp public class Circle : IShape { public Circle( double radius, Color color ) { Radius = radius; Color = color; } public double Radius { get; } public Color Color { get; } } ``` We now get a warning whenever we try to directly use the constructor: ```csharp Drawing CreateDrawing() { // [] // WARNING! Use ShapeFactory to get an instance of this class. // [] var circle = new Circle( 1.1, Color.Red ); return new Drawing { circle }; } ``` Show me how it works! The `UseFactoryAttribute` class is an inheritable aspect that verifies that constructors of derived types are used only from a given type or from a unit test. ```cs [Inheritable] internal class UseFactoryAttribute : TypeAspect { private readonly Type _factoryType; public UseFactoryAttribute( Type factoryType ) { this._factoryType = factoryType; } public override void BuildAspect( IAspectBuilder builder ) => builder.Outbound.SelectMany( t => t.Constructors ) .CanOnlyBeUsedFrom( scope => scope.Namespace( "**.Tests" ) .Or() .Type( this._factoryType ), $"Use '{this._factoryType.Name}' to get an instance of this class." ); } ``` ## Metalama benefits * **Reduce human errors**. You can be confident that no rogue code is calling the constructor directly instead of getting an instance from the DI container. ![](https://postsharp.net/assets/images/solutions/factory-validation-9abb0b361e.png) ## Resources * Blog post: [The Factory design pattern in C#](https://postsharp.net/blog/factory-pattern) --- # INotifyPropertyChanged > Metalama's `Observable` aspect automates `INotifyPropertyChanged` implementation, enhancing productivity and reducing errors in UI applications. Canonical: https://postsharp.net/metalama/applications/inotifypropertychanged.html Most modern UI applications rely on binding data classes to UI classes. The `INotifyPropertyChanged` interface is the standard way to achieve this. However, implementing this interface manually can be cumbersome and error-prone, particularly when dealing with a large number of properties. Thanks to Metalama's [Observable](https://doc.metalama.net/patterns/observability) aspect from the `Metalama.Patterns.Observability` open-source package, you can implement the `INotifyPropertyChanged` interface automatically. The plumbing code is generated on-the-fly when you compile, so your source code remains clean and concise. `Metalama.Patterns.Observability` is a production-ready, professionally supported, and open-source aspect library. Available for all UI frameworks ## Example In the following code, the `[Observable]` aspect is applied to two classes: `Customer` (the model class) and `CustomerViewModel`. ```csharp // THAT'S ALL. These classes implement INotifyPropertyChanged // Thanks to the [Observable] aspect. // [] [Observable] // [] public partial class Customer { public string FirstName { get; set; } public string LastName { get; set; } } // [] [Observable] // [] public partial class CustomerViewModel { // YES. This property can change. public Customer Customer { get; set; } // YES. This property depends on a property of a property. public string FullName => $"{this.Customer.FirstName} {this.Customer.LastName}"; } ``` ## Benefits * **Write less code**. Eliminate almost all of the `INotifyPropertyChanged` repetitive code, as most cases are handled automatically. * **Avoid errors**. Never forget to call `OnPropertyChanged` again. * **Keep your code clean and concise**. Your business logic is no longer hidden behind the observability plumbing. ## Features * Idiomatically C#: Metalama works _with_ your code, not against it. * Recursive code analysis algorithm that detects dependencies of properties on fields and other properties within. * Support for complex dependencies, such as properties depending on properties of child objects or of the base class. * Customizable. Take control when needed to handle corner cases. * Compatible with MVVM frameworks. ## Resources * Blog post: [Implementing INotifyPropertyChanged with Metalama](https://postsharp.net/blog/inotifypropertychanged-metalama). * Reference documentation: [Metalama.Patterns.Observability](https://doc.metalama.net/patterns/observability). * Source code: [Metalama.Patterns.Observability](https://github.com/metalama/Metalama.Patterns/tree/HEAD/src/Metalama.Patterns.Observability). * NuGet package: [Metalama.Patterns.Observability](https://www.nuget.org/packages/Metalama.Patterns.Observability). --- # Logging & Tracing > This article presents techniques and resources to automatically implement logging and tracing in C# using Metalama. Canonical: https://postsharp.net/metalama/applications/logging.html Using aspects to implement high-resolution logging (also called _tracing_) is so popular that it has become Metalama's _hello, world_. There is probably no better example of repetitive code that should never be written by hand in the first place. Logging and tracing are often used as synonyms (including on this website), but they are slightly different: * *Logging* is the practice of generating and capturing _significant events_, errors, and messages generated by an application. Adding logging to an app is often done manually because it's difficult to automatically determine what constitutes a significant event. However, some events can be added automatically using a Metalama aspect: - A method throwing an exception, including its parameter values. - A method taking more time than expected to execute. * *Tracing* is the process of generating and capturing detailed information about the _execution flow_ of a program, focusing on individual method calls. Adding tracing to your app is very useful if you want to be prepared to collect detailed information about an issue in production without the ability to use a debugger. Implementing tracing in your app manually would be extremely time-consuming and error-prone. Using Metalama, this can be done in minutes. ## Features Metalama comes with several [examples of logging aspects](https://doc.metalama.net/examples/log) as we learned that many teams prefer to customize their logging and tracing logic. You will value the following Metalama features when implementing logging: - **Fabrics.** No need to add a custom attribute to hundreds of methods. Execute a build-time code query and add logging to hundreds of methods at a time. - **Dependency injection.** Aspects can automatically pull the `ILogger` class from the DI container. ## Example The following snippet adds logging to all public methods of public types except `ToString`: ```csharp internal class Fabric : ProjectFabric { public override void AmendProject(IProjectAmender amender) => amender .SelectTypes() .Where(type => type.Accessibility == Accessibility.Public) .SelectMany(type => type.Methods) .Where(method => method.Accessibility == Accessibility.Public && method.Name != "ToString") // [] .AddAspectIfEligible(); // [] } ``` Show me how it works! Fabric classes execute at compile time. The code above selects all public types and methods and adds a logging aspect to them. Suppose that you have this class in your project: ```cs internal class Calculator { public double Add(double a, double b) => a + b; } ``` If you select one of the most sophisticated versions of the [example logging aspects](https://doc.metalama.net/examples/log), it will transform the code into this during the build: ```cs public class Calculator { private ILogger _logger; public Calculator(ILogger logger) { this._logger = logger; } [Log] public double Add(double a, double b) { var isTracingEnabled = _logger.IsEnabled(LogLevel.Trace); if (isTracingEnabled) { _logger.LogTrace($"Calculator.Add(a = {a}, b = {b}) started."); } try { var result = a + b; if (isTracingEnabled) { _logger.LogTrace($"Calculator.Add(a = {a}, b = {b}) returned {result}."); } return (double) result; } catch (Exception e) when (_logger.IsEnabled(LogLevel.Warning)) { _logger.LogWarning($"Calculator.Add(a = {a}, b = {b}) failed: {e.Message}"); throw; } } } ``` As you can see, the aspect pulled a dependency, added a `try/catch` block, and a lot of boilerplate code. You probably don't want to write this code by hand, ever! ## Metalama benefits - **Less code to write.** No need to write these thousands of lines of code. - **Clean code.** The tracing code is generated on-the-fly during compilation, so your code remains crystal clear and is easier to maintain. - **High performance.** The generated code is as fast as hand-written code. - **Consistency.** Your logs stay consistent across the application: message structure, log level, log category, and destination. - **Add or remove anytime.** Detailed tracing is no longer an expensive feature that needs to be planned. You can add it or remove it at any time. You can also enable logging only in select build configurations. ## Resources * Example: [Implementing logging without boilerplate](https://doc.metalama.net/examples/log) * Blog article: [Serilog log levels, and how to use them](https://postsharp.net/blog/serilog-log-levels) * Blog article: [Adding Serilog to ASP.NET Core: a practical guide](https://postsharp.net/blog/serilog-aspnetcore) * Blog article: [Fast and Compact Structured Logging in C# Using String Interpolation](https://postsharp.net/blog/structured-logging-with-string-interpolation) --- # Maintaining Legacy Code > Aspects let you add behavior to a legacy codebase without extensive refactoring, and real-time code analysis guides safe modernization, extending a codebase's usable lifespan and helping you avoid the Big Rewrite. Canonical: https://postsharp.net/metalama/applications/maintenance.html An old codebase is not old because it is worthless. It is old because it works and has been paying its way for years. The problem is that every change now feels dangerous, and the people who understood the original design have moved on. Metalama lets you keep evolving such a codebase by adding behavior to it without extensive refactoring, and by flagging outdated patterns as you go. ## Why legacy code resists change Refactoring an aging codebase is risky, and the risk usually comes from the same few sources. - **No safety net.** When there are few or no automated tests, any significant change is a gamble. Nothing catches the regression until a user does. - **Vanished knowledge.** The developers who knew why the code was written a certain way leave the team and take that context with them. What remains is code nobody fully trusts to touch. - **Accumulated churn.** After years of edits by many hands, often on short assignments, the code becomes hard to read. Intent is buried under workarounds, and understanding a method takes longer than changing it. - **Outdated practices.** Patterns, APIs, and frameworks that were reasonable a decade ago are now deprecated or forbidden, but they are woven through the whole codebase. Left unaddressed, these forces push teams toward the **Big Rewrite**: starting over from scratch. It is tempting, but it is expensive and risky, and it diverts resources away from new features while the rewrite catches up to what the old system already did. ## How Metalama helps Metalama does not ask you to rewrite anything. It gives you two ways to keep a legacy codebase alive: adding behavior through aspects, and validating the code as you modernize it. ### Add behavior without refactoring With [aspect-oriented programming](https://postsharp.net/metalama/applications/aspect-oriented-programming), cross-cutting concerns such as logging, exception handling, security, or validation are factored into an [aspect](https://postsharp.net/metalama/applications/aspect-oriented-programming) instead of being scattered by hand through the business logic. - **Non-intrusive.** An aspect adds behavior at compile time without altering the code it applies to. On a legacy system, where changing the core logic is exactly what you want to avoid, that matters. - **Incremental.** You can start with a single method, then a namespace, then a whole project, applying aspects where maintenance is hardest and expanding from there. There is no big-bang migration to schedule. - **Centralized.** Because a pattern lives in one aspect, changing it means editing one place rather than hunting for every occurrence across the codebase. ### Guide modernization with real-time analysis Metalama's code-analysis side runs inside the IDE and at build time. You can express rules that flag deprecated APIs or forbidden patterns, and the violation is reported as you type rather than discovered months later. This is the same mechanism behind [architecture verification](https://postsharp.net/metalama/applications/architecture-verification). - **Find what to change.** Analysis rules highlight the exact places that still use an old pattern, turning a vague modernization goal into a concrete, finite list. - **Offer the fix.** Rules can supply code-fix suggestions, so the IDE points at the problem and proposes the correction. That makes a long [refactoring](https://postsharp.net/metalama/applications/refactoring) effort safer and less error-prone. ## Benefits - **Lower risk.** Aspects add behavior without touching the code you are afraid to touch, and real-time analysis stops outdated patterns from spreading further. - **Knowledge that stays.** Rules and patterns encoded as aspects and analyzers survive the departure of the people who wrote them. - **A longer usable life.** Modernizing incrementally, one aspect and one rule at a time, extends how long a codebase remains maintainable and defers or avoids the Big Rewrite altogether. --- # Memento Pattern > The Memento pattern captures an object's state without violating encapsulation, useful for undo/redo functionality and state restoration. It can be automatically implemented using Metalama. Canonical: https://postsharp.net/metalama/applications/memento.html The Memento pattern is a classic behavioral design pattern used to capture the internal state of an object without violating its encapsulation. It allows you to save the state of an object and later restore it. The Memento pattern is useful in many scenarios, such as implementing undo/redo functionality, saving and restoring the state of an object, or saving the state of an object to a file. As the Memento pattern involves a lot of boilerplate code, it is a good candidate for automatic creation by Metalama. ## Example In the following example, the `[Memento]` custom attribute is all you need to implement the Memento pattern on the `Fish` class. ```cs // [] [Memento] // [] public sealed partial class Fish { public string? Name { get; set; } public string? Species { get; set; } public DateTime DateAdded { get; set; } } ``` The aspect implements the `IMementoable` interface, including its `SaveToMemento` and `RestoreMemento` methods. ```cs var fish = new Fish() { Name = "Hannibal", Species = "Tilapia" }; // Save var memento = fish.SaveToMemento(); // Change fish.Species = "Shark"; // Undo change fish.RestoreMemento(memento); ``` Show me how it works! The aspect, once applied, will create an internal `Memento` class to capture the state of the `Fish` class. The `Fish` class will also have a `SaveToMemento` method and a `RestoreMemento` method to save and restore the state of the `Fish` object. ```cs // [] public partial class Fish : IMementoable { public void RestoreMemento(IMemento memento) { var typedMemento = (Memento) memento; this.Name = typedMemento.Name; this.Species = typedMemento.Species; this.DateAdded = typedMemento.DateAdded; } public IMemento SaveToMemento() => new Memento(this); private class Memento: IMemento { public Memento(Fish originator) { this.Originator = originator; this.Name = originator.Name; this.Species = originator.Species; this.DateAdded = originator.DateAdded; } public string? Name { get; } public string? Species { get; } public DateTime DateAdded { get; } public IMementoable? Originator { get; } } } // [] ``` You can find the complete `Memento` aspect source code [here](https://doc.metalama.net/preview/examples/memento/memento-1#complete-aspect). ## Metalama benefits * **Increase your productivity**: The pattern requires a lot of boilerplate code. Instead of writing it manually, an aspect can handle it for you, allowing you to focus exclusively on your business logic. * **Keep your code consistent**: The generated code is always consistent with the pattern rules. * **Enhance maintainability**: The generated code will always be up-to-date; you'll never forget to update the Memento class when you add a new property to the *Mementoable* class. ## Resources * Blog post: [The Memento Design Pattern in C#, Practically With Examples [2024]](https://postsharp.net/blog/memento) * Example: [Implementing the Memento pattern without boilerplate](https://doc.metalama.net/examples/memento) --- # Memoization > Memoization improves app performance by storing read-only property values, avoiding re-evaluation. Use Metalama.Patterns.Memoization for easy implementation. Canonical: https://postsharp.net/metalama/applications/memoization.html In high-performance apps or components, [memoization](https://doc.metalama.net/patterns/memoization) is the technique of "remembering" the value of a read-only property of an object to avoid subsequent evaluations. Memoization is helpful when a property always returns the same value for the same object. Unlike caching, memoization is implemented directly in the property rather than relying on an external caching component or a string-based key, making it fast. High-performance implementations of the memoization pattern can be tricky to implement due to multi-threading issues. The [Memoize](https://doc.metalama.net/patterns/memoization) aspect from the `Metalama.Patterns.Memoization` package turns a standard read-only property into a memoized one without any boilerplate. `Metalama.Patterns.Memoization` is a production-ready, professionally-supported, and open-source aspect library. Available for all UI frameworks. ## Benefits * Improve your application's performance. * Avoid common mistakes in implementing the memoization code pattern. * Keep your code clean and concise. ## Example In the following snippet, the `PatternBrush` property is memoized, ensuring it is created only once. ```csharp public class ExpensiveResourceViewModel : DependencyObject { // [] [Memoize] // [] public VisualBrush PatternBrush => new VisualBrush { TileMode = TileMode.Tile, Viewport = new Rect(0, 0, 20, 20), ViewportUnits = BrushMappingMode.Absolute, Visual = CreatePatternVisual() }; private DrawingVisual CreatePatternVisual() { var visual = new DrawingVisual(); using (var context = visual.RenderOpen()) { context.DrawRectangle(Brushes.LightGray, null, new Rect(0, 0, 20, 20)); context.DrawLine(new Pen(Brushes.Blue, 2), new Point(0, 0), new Point(20, 20)); context.DrawLine(new Pen(Brushes.Blue, 2), new Point(20, 0), new Point(0, 20)); } return visual; } } ``` ## Resources * Reference documentation: [Memoization](https://doc.metalama.net/patterns/memoization). * Source code: [Metalama.Patterns.Memoization](https://github.com/metalama/Metalama.Patterns/tree/HEAD/src/Metalama.Patterns.Memoization). * NuGet package: [Metalama.Patterns.Memoization](https://www.nuget.org/packages/Metalama.Patterns.Memoization). --- # Metrics > This article details how Metalama can help instrument C# applications to make them more observable in production by adding logging, tracing, exception handling, and metrics. Canonical: https://postsharp.net/metalama/applications/metrics.html .NET applications can be instrumented using the `System.Diagnostics.Metrics` APIs to track important metrics. Some metrics are included in standard .NET libraries, but you may want to add new custom metrics that are relevant to your applications and libraries. You can use a Metalama aspect to add metrics that are relevant to the execution of a method, for instance: - number of executions, - number of failures, - total execution time ## Example You can add metrics to code using custom attributes: ```cs public class HatShop { private int _executionCount; // [] [MeasureExecutionCount] [MeasureExecutionTime] [MeasureExceptionCount] // [] public void PlaceOrder() { this._executionCount++; if ( this._executionCount % 10 == 0 ) { throw new Exception(); } else { Console.WriteLine( "Ordering a hat." ); } } } ``` Alternatively, if you want to add metrics to several methods (for instance, all public methods) but don't want to add a custom attribute to each of them, you can also add the metrics using a fabric: ```csharp internal class Fabric : ProjectFabric { public override void AmendProject(IProjectAmender amender) => amender .SelectTypes() .Where(type => type.Accessibility == Accessibility.Public) .SelectMany(type => type.Methods) .Where(method => method.Accessibility == Accessibility.Public) .AddAspectIfEligible(); } ``` The metric aspects will generate new classes that define the metrics. For instance, for `HatShop`, it will generate `HatShopMetrics`. It will also generate an `IServiceCollection` extension method to add these metrics to your apps: ```csharp services.AddMetrics(); services.AddAutoGeneratedMetrics(); ``` Show me how it works! The aspects first generate the `HatShopMetrics` class: ```csharp public class HatShopMetrics { internal readonly Counter PlaceOrderExceptionCount; internal readonly Counter PlaceOrderExecutionCount; internal readonly Counter PlaceOrderExecutionTime; private IMeterFactory _meterFactory; private IMetricHost _metricHost; public HatShopMetrics(IMeterFactory meterFactory, IMetricHost metricHost ) { this._meterFactory = meterFactory ?? throw new System.ArgumentNullException(nameof(meterFactory)); this._metricHost = metricHost ?? throw new System.ArgumentNullException(nameof(metricHost)); var meter = _meterFactory.Create(_metricHost.ApplicationName, _metricHost.ApplicationVersion, _metricHost.Tags); this.PlaceOrderExceptionCount = meter.CreateCounter("PlaceOrder.ExceptionCount"); this.PlaceOrderExecutionCount = meter.CreateCounter("PlaceOrder.ExecutionCount"); this.PlaceOrderExecutionTime = meter.CreateCounter("PlaceOrder.ExecutionTime"); } } ``` It then modifies the `HatShop` class in two ways: 1. It pulls the `HatShopMetrics` dependency, and 2. It instruments the target methods. ```csharp public class HatShop { private HatShopMetrics _hatShopMetrics; private int _executionCount; public HatShop(HatShopMetrics hatShopMetrics = null) { this._hatShopMetrics = hatShopMetrics; } public void PlaceOrder() { var timestamp = Stopwatch.GetTimestamp(); try { this._hatShopMetrics?.PlaceOrderExecutionCount.Add(1); try { this._executionCount++; if (this._executionCount % 10 == 0) { throw new Exception(); } else { Console.WriteLine("Ordering a hat."); } } catch { this._hatShopMetrics?.PlaceOrderExceptionCount.Add(1); throw; } return; } finally { this._hatShopMetrics?.PlaceOrderExecutionTime.Add(Stopwatch.GetTimestamp() - timestamp); } } } ``` We have a lot of additional code here because there are three metrics. It also generates the `IServiceCollection` extension method: ```csharp public static class MetricRegistrations { public static void AddAutoGeneratedMetrics(this ServiceCollection serviceCollection) { serviceCollection.AddSingleton(typeof(HatShopMetrics)); } } ``` ## Resources - Example: [Metalama.Samples.Metrics](https://github.com/metalama/Metalama.Samples/tree/HEAD/examples/metrics) --- # Modern Singleton Pattern > The modern Singleton pattern uses Dependency Injection to manage instances, improving testability. Use Metalama to enforce architectural constraints. Canonical: https://postsharp.net/metalama/applications/modern-singleton.html The classic Singleton pattern has not aged well. It's considered an anti-pattern because it is difficult to test and incompatible with the Dependency Injection paradigm. A modern approach to the Singleton pattern is to use a Dependency Injection container to manage the lifecycle of the singleton instance (e.g., `IServiceCollection.AddSingleton`). The main problem with the modern Singleton is that it is challenging to ensure the constructor is used only in the appropriate context, typically from the `Startup` code and unit tests. Metalama can help you [enforce architectural constraints](https://doc.metalama.net/conceptual/architecture) and report violations directly in the code editor as warnings. ## Example The following `PerformanceCounterManager` class is a modern Singleton example. It has a public constructor but is registered as a Singleton in the DI container. ```cs // [] [Singleton] // [] public class PerformanceCounterManager : IPerformanceCounterManager { private readonly ConcurrentDictionary _counters = new(); private readonly IPerformanceCounterUploader _uploader; public PerformanceCounterManager( IPerformanceCounterUploader uploader ) { this._uploader = uploader; } public void IncrementCounter( string name ) => this._counters.AddOrUpdate( name, 1, ( _, value ) => value + 1 ); } ``` The `[Singleton]` aspect reports warnings when any class other than `Startup` or a unit test attempts to call the constructor. ```cs class MetricsCollection( IPerformanceCounterUploader uploader ) { // [] // WARNING: The class is a [Singleton]. Do not create new instances, instead use PerformanceCounterManager.Instance. // [] PerformanceCounterManager _performanceManager = new( uploader ); } ``` Show me how it works! Here is the code of the `[Singleton]` aspect: ```cs public class SingletonAttribute : TypeAspect { public override void BuildAspect( IAspectBuilder builder ) { builder.Outbound .SelectMany( t => t.Constructors ) .CanOnlyBeUsedFrom( scope => scope.Type( typeof(Startup) ).Or().Namespace( "**.Tests.**" ), description: "The class is a [Singleton]." ); } } ``` ## Metalama benefits * **Reduce human errors**. You can be confident that no improper code is calling the constructor directly instead of getting an instance from the DI container. ## Resources * Blog post: [The Singleton Pattern in C# Today Is Not Your Dad's One!](https://postsharp.net/blog/singleton-pattern) * Example: [Validating the modern Singleton](https://doc.metalama.net/examples/singleton/singleton-2) --- # Object Services > Learn how to use Metalama to generate common object-oriented services like deep cloning or ToString. Canonical: https://postsharp.net/metalama/applications/object-services.html Metalama can generate code for the common services you often need to add in class hierarchies to override their system implementations. While not as fancy as design patterns, they still contribute significantly to boilerplate code and potential defects. | Service | How Metalama can help | |---------|-----------------------| | [Cloning](https://postsharp.net/metalama/applications/cloning) | Automatically implements `ICloneable`, eliminating repetitive code and reducing errors. | | [ToString](https://postsharp.net/metalama/applications/tostring) | Generates the `ToString` method based on the type properties, with opt-in or opt-out strategies. | | [Equatable](https://postsharp.net/metalama/applications/equatable) | Implements the equality comparison pattern, allowing you to select the members that are part of the contract. | --- # Principles at Work > Shows how to use Metalama to follow industry best practices. Canonical: https://postsharp.net/metalama/applications/principles.html Software development practices encompass principles, methodologies, and guidelines that enable developers to write efficient, maintainable, scalable, and high-quality code. By adhering to these best practices, teams can enhance collaboration, minimize technical debt, and make software more readable and easier to modify. When you’ve pushed C# to its limits as an object-oriented language, Metalama takes it a step further by reducing boilerplate code and enhancing readability and maintainability even further. Metalama can help implement the following development principles: | Principle | How Metalama can help | |-----------|-------------| | [Clean Code](https://postsharp.net/metalama/applications/clean-code) | Metalama helps you adhere to Clean Code principles by further reducing boilerplate, enforcing architecture rules, and improving readability. It does so by centralizing cross-cutting concerns into aspects, which classic object-oriented programming cannot do. This results in simplified maintenance, a major objective of Clean Code principles. | | [SOLID & DRY](https://postsharp.net/metalama/applications/solid) | Metalama reinforces Single Responsibility and Dependency Inversion principles by moving cross-cutting responsibilities, including their dependencies, into aspects. It further eliminates repetitive code. | | [Aspect-Oriented Programming](https://postsharp.net/metalama/applications/aspect-oriented-programming) | Metalama modernizes AOP, embracing Roslyn for real-time transformation, transparent debugging, and maintainable, scalable C# development, breaking free from cumbersome academic concepts of the early 2000s. | | [Defensive Programming (Code Contracts)](https://postsharp.net/metalama/applications/contracts) | Asserting preconditions, postconditions, and invariants ensures that defects do not propagate from one component to another. Metalama reduces the validation boilerplate code, making it easier to reap the benefits of defensive programming without its costs. | | [Refactoring](https://postsharp.net/metalama/applications/refactoring) | Detect code that requires refactoring and report warnings. Author custom code fixes and live templates. | --- # Proxy Pattern > The Proxy pattern in C# provides substitutes for objects to add behavior and can be implemented via interfaces. Use Metalama to generate static proxies instead of dynamic ones. Canonical: https://postsharp.net/metalama/applications/proxy.html The Proxy pattern is a structural design pattern that lets you provide a substitute or placeholder for another object, typically to add new behavior. In C#, the proxied object is generally represented by an interface, although it's also possible to implement the pattern with virtual methods. Implementing the Proxy pattern involves duplicating all interface members, which requires a lot of boilerplate code. The proxy's added behavior can either be implemented separately in each of these members or can be abstracted. In this case, the abstraction is called an _interceptor_. There are three ways to generate proxy classes: * By hand, which is feasible only with a small number of interfaces and members. * Dynamically at runtime using libraries like [Castle DynamicProxy](https://github.com/castleproject/Core/blob/master/docs/dynamicproxy.md), which increases startup time and is not compatible with ahead-of-time compilation. * Statically at build time using Roslyn source generators or Metalama. ## Example Here's how a proxy aspect works in practice. This example is a slightly simplified version of [this sample aspect](https://github.com/metalama/Metalama.Samples/tree/HEAD/examples/Metalama.Samples.Proxy). Suppose we have an implementation class `OrderService` that we cannot modify, but we want to add logging to it. The `OrderService` implements the following interface: ```csharp public interface IOrderService { OrderId PlaceOrder(Order order); void CancelOrder(OrderId orderId); } ``` We generate a static proxy using the following code: ```csharp public class Fabric : ProjectFabric { public override void AmendProject(IProjectAmender amender) { amender .SelectReflectionType(typeof(IOrderService)) // [] .GenerateStaticProxy(); // [] } } ``` This instructs Metalama to generate the following code: ```csharp // [] public class OrderServiceProxy : IOrderService { private IOrderService _intercepted; private IInterceptor _interceptor; public SomeProxy(IInterceptor interceptor, IOrderService intercepted) { _interceptor = interceptor; _intercepted = intercepted; } public OrderId PlaceOrder(Order order) { var args = Tuple.Create(order); return _interceptor.Invoke(ref args, Invoke, nameof(PlaceOrder)); int Invoke(ref Tuple receivedArgs) { return _intercepted.PlaceOrder(receivedArgs.Item1); } } public void CancelOrder(OrderId orderId) { var args = Tuple.Create(orderId); _interceptor.Invoke(ref args, Invoke, nameof(CancelOrder)); return; void Invoke(ref Tuple receivedArgs) { _intercepted.CancelOrder(receivedArgs.Item1, receivedArgs.Item2); return default; } } } // [] ``` We can now use the proxy class as follows: ```csharp var orderServiceProxy = new OrderServiceProxy( new OrderService(), new LoggingInterceptor()); orderServiceProxy.PlaceOrder(order); ``` Show me more! An interceptor is a class implementing the following interface: ```csharp public interface IInterceptor { public TResult Invoke( ref TArgs args, InterceptorDelegate proceed, string methodName) where TArgs : struct, ITuple; } ``` For logging, we might use this code: ```csharp public interface LoggingInterceptor : IInterceptor { public TResult Invoke( ref TArgs args, InterceptorDelegate proceed, string methodName) where TArgs : struct, ITuple { Console.WriteLine( $"Executing {methodName}({string.Join(", ", args.ToArray())})"); return proceed(ref args); } } ``` ## Metalama benefits * **Fast application startup.** Static proxies are generated at compile time instead of at runtime, so your application starts faster. * **Compatible with AoT compilation.** No reflection is necessary at runtime. * **No boilerplate code.** Unlike with the handwritten approach, you write a single line of code. ## Resources * Source code: [Metalama.Samples.Proxy](https://github.com/metalama/Metalama.Samples/tree/HEAD/examples/Metalama.Samples.Proxy) --- # Refactoring > This article covers how Metalama can help with code refactoring in C# projects. Canonical: https://postsharp.net/metalama/applications/refactoring.html Refactoring is a broad topic that ranges from limited code changes that can be automated (such as making a field read-only) to large efforts that take several days. Metalama complements mainstream refactoring tools with the ability to spot code that requires refactoring and create your own custom live templates and code fixes. You will need Metalama Professional to use the approaches described in this article. ## Spot obsolete code and report warnings When you are in the middle of a large refactoring, it is common to lose track of which code has not yet been refactored. With Metalama, you can create rules that identify declarations that need to be refactored and report warnings. For your next large refactoring, consider this approach. At the beginning of the refactoring effort, you create [architecture verification](https://postsharp.net/metalama/applications/architecture-verification) rules for the new intended design. You will have dozens of warnings initially, and you can resolve them progressively until there are none. ### Example Typically, large refactorings are made necessary by a change in the desired architecture. Let's take an example. Suppose that the current state of your application is that the OrderFulfillment namespace directly calls methods of the Warehouse namespace. You want to refactor the codebase to use a Mediator pattern. This is a large refactoring, and you plan to work on it for several days. To avoid introducing too many issues in the process, you want to perform tasks by small increments, ensuring that the application builds and that the unit tests pass at each step. ## Add your own one-click refactorings You are certainly familiar with the lightbulb or screwdriver menu of your IDE, which allows you to perform simple changes in your code without typing it manually. And you've maybe wondered how to extend it. Metalama allows you to custom create code fixes and refactorings: * _Code fixes_ are attached to an error or warning and are suggestions of how to _fix_ an issue. * _Code refactorings_ do not depend upon any errors or warnings. You can use exactly the same set of techniques as when creating aspects. You can use the whole power of Metalama. The only difference is that the aspect will be applied at _design time_, when the user selects them in the refactoring menu, instead of at compile time. ### Live templates You can mark an aspect class as a [live template](https://doc.metalama.net/conceptual/aspects/ide/live-template) by using the [EditorExperience](https://doc.metalama.net/api/metalama-framework-aspects-editorexperienceattribute) custom attribute. This will export the aspect class to the refactoring menu. In this approach, it's important to define the [aspect eligibility](https://doc.metalama.net/conceptual/aspects/eligibility). ### Code fixes and refactorings You can use a fabric or an aspect to detect possible defects and [attach a code fix](https://doc.metalama.net/api/metalama-framework-diagnostics-idiagnostic-withcodefixes) to the error or warning. You can also detect refactoring opportunities and [suggest a refactoring](https://doc.metalama.net/api/metalama-framework-diagnostics-scopeddiagnosticsink-suggest) _without_ reporting any error or warning. ## Reducing the need for refactoring The best way to make refactoring easier is to have a good initial design that can evolve with time _without_ requiring extensive changes to accommodate new features. Take, for instance, caching. You might realize in the middle of a project that your cache key generation strategy is not good enough and needs to be refactored. If the caching strategy was implemented in dozens of places, you have a bigger refactoring problem than if it were implemented in a single place. By leveraging [on-the-fly code generation](https://postsharp.net/metalama/features/code-generation) early in your design, you reduce the need for large refactorings later in the project. When the need arises to modify the code pattern, the only thing to change is the aspect itself. --- # Security Canonical: https://postsharp.net/metalama/applications/security.html Metalama lets you add cross-cutting security concerns like authorization, auditing, and encryption to your applications without writing boilerplate code. ## Authorization Metalama can be used to implement authorization by weaving aspects into your code to enforce role-based or permission-based access control. For example, you can create an aspect that checks the user's roles or permissions before allowing access to specific methods or classes. ## Audit With Metalama, you can implement auditing by creating an aspect that logs method calls, parameter values, and execution results. This aspect can be applied to critical methods to ensure that all actions are recorded for compliance and debugging purposes. ## Transparent encryption Metalama can help implement transparent encryption by weaving aspects that encrypt and decrypt data at specific points in your code: for example, encrypting a field before it is persisted and decrypting it on read. The encryption logic stays out of your business code. --- # SOLID & DRY Principles in C# > This article explains how Metalama can help improve SOLID and DRY principles in .NET and C# codebases. Canonical: https://postsharp.net/metalama/applications/solid.html SOLID and DRY principles are universally accepted guidelines in software engineering. Metalama enables you to implement, improve, or promote these principles in your C# code. The _SOLID principles_ consist of five design principles that help create well-structured, maintainable, and scalable software: - **S**ingle Responsibility Principle (SRP) - **O**pen/Closed Principle (OCP) - **L**iskov Substitution Principle (LSP) - **I**nterface Segregation Principle (ISP) - **D**ependency Inversion Principle (DIP) DRY stands for _Don't Repeat Yourself_. It states that you should avoid duplicating code in your codebase. We'll show you how to use Metalama to implement the SRP, OCP, DIP, and DRY principles. ## Single Responsibility Principle (SRP) ***"A class should have only one reason to change."*** This means that a class should have only one responsibility. If not, it should be refactored into multiple classes, each with a single responsibility. Although we can't use Metalama to ensure that a class has only one responsibility, we can use it once you've identified which responsibility doesn't belong to the class. For example, you can use Metalama to move cross-cutting concerns into their own classes, such as logging, validation, and caching. For instance, let's say we have a `TaxCalculator` class with a `GetTaxAmount` method. ```cs public class TaxCalculator { public decimal GetTaxAmount(Invoice invoice) { Console.WriteLine($"Calculating tax for invoice ID {invoice.Id}"); if (invoice == null) throw new ArgumentNullException(nameof(invoice)); var taxPercent = GetTaxPercent(invoice.CategoryId); var taxAmount = invoice.Amount * taxPercent / 100; Console.WriteLine($"Tax calculated: {taxAmount:C} for invoice ID {invoice.Id}"); return taxAmount; } private decimal GetTaxPercent(int categoryId) { // Simulate fetching from a database return FetchTaxPercentFromDatabase(categoryId); } } ``` The `GetTaxAmount` method is responsible for calculating the tax amount, but it also logs the start and end of the method and logs any exceptions that occur. This means that the `GetTaxAmount` method has more than one responsibility. To remove the logging responsibility from the `GetTaxAmount` method, we can move all the logging code to a Metalama aspect (a separate class) like the `Log` attribute introduced in the [Implementing logging without boilerplate](https://doc.metalama.net/examples/log) article. Using that new `Log` attribute, the `TaxCalculator` class will look like this: ```cs public class TaxCalculator { // [] [Log] // [] public decimal GetTaxAmount(Invoice invoice) { if (invoice == null) throw new ArgumentNullException(nameof(invoice)); var taxPercent = GetTaxPercent(invoice.CategoryId); var taxAmount = invoice.Amount * taxPercent / 100; return taxAmount; } private decimal GetTaxPercent(int categoryId) { // Simulate fetching from a database return FetchTaxPercentFromDatabase(categoryId); } } ``` Now the `GetTaxAmount` method only has one responsibility: calculating the tax amount. The logging responsibility has been moved to the `Log` attribute. Another way to implement the Single Responsibility Principle is to use the Decorator design pattern. You can use this pattern to add the logging behavior to a class without changing its existing code. To implement this pattern with Metalama, you can follow the steps in the [The Decorator Pattern in Modern C# [2024]](https://postsharp.net/blog/decorator-pattern) article. ## Open/Closed Principle (OCP) ***A class should be open for extension but closed for modification.*** This means that you should be able to add new functionality to a class without changing its existing code. Aspects are a great way to add new functionalities to classes without changing their existing code. Consider the `TaxCalculator` class from the previous example. If you want to add logging behavior to the `GetTaxAmount` method, you can use the `Log` attribute to add this behavior without changing the `TaxCalculator` class. ```cs public class TaxCalculator { [Log] public decimal GetTaxAmount(Invoice invoice) { if (invoice == null) throw new ArgumentNullException(nameof(invoice)); var taxPercent = GetTaxPercent(invoice.CategoryId); var taxAmount = invoice.Amount * taxPercent / 100; return taxAmount; } [Log] [Cache] private decimal GetTaxPercent(int categoryId) { // Simulate fetching from a database return FetchTaxPercentFromDatabase(categoryId); } } ``` In this example, the `Cache` attribute (part of the [Metalama Patterns](https://doc.metalama.net/patterns/caching)) is used to add caching behavior to the `GetTaxPercent` method. The `TaxCalculator` class is open for extension because you can add new functionalities to it (using attributes in this case) but closed for modification because you don't need to change the existing code of the class. Metalama also provides something called [Fabrics](https://doc.metalama.net/conceptual/using/fabrics) to add multiple aspects at once using compile-time imperative code, as opposed to the declarative custom attributes we used in the previous example. Why should you need to do this? Because sometimes you need to add aspects dynamically, based on some conditions to several classes/methods at once. The following snippet adds logging to all public methods of public types (except `ToString`): ```cs internal class Fabric : ProjectFabric { public override void AmendProject(IProjectAmender amender) => amender .SelectTypes() .Where(type => type.Accessibility == Accessibility.Public) .SelectMany(type => type.Methods) .Where(method => method.Accessibility == Accessibility.Public && method.Name != "ToString") .AddAspectIfEligible(); } ``` ## Dependency Inversion Principle (DIP) ***High-level modules should not depend on low-level modules. Both should depend on abstractions.*** This means that you should depend on abstractions, not on concrete implementations. This is another principle that the use of Metalama aspects helps improve. Consider the `TaxCalculator` class from previous examples. The first version of the class had a dependency on the logging mechanism (in this case just a `Console.WriteLine` method, but it could be easily replaced by a more complex logging mechanism generating a stronger dependency). The final version of the `TaxCalculator` class, thanks to Metalama aspects, depends on the `Log` aspects, not on their concrete implementations. This means that the `TaxCalculator` class depends on abstractions, not on concrete implementations. You can start logging with the `Console.WriteLine` method and then switch to a more complex logging mechanism without changing the `TaxCalculator` class. ## Don't Repeat Yourself Principle (DRY) In C# development, there are many opportunities to repeat code. For example, you may encounter scenarios where you need to: 1. Write the same validation logic in multiple classes or methods. 2. Implement identical logging logic across different classes or methods. 3. Apply consistent caching logic in numerous classes or methods. 4. Implement the INotifyPropertyChanged interface in multiple classes. 5. Develop cumbersome code when using the Builder design pattern. Metalama helps you avoid code duplication by allowing you to define reusable aspects that can be applied to multiple classes or methods. This way, you can write the validation, logging, caching, and other logic once and apply it to multiple classes or methods. Here is an example that applies the DRY principle using Metalama. Suppose you have a `Person` class that implements the `INotifyPropertyChanged` interface. You can use Metalama to avoid writing the same boilerplate code in multiple classes. ```cs [Observable] public class Person { public string Name { get; set; } public int Age { get; set; } } ``` How much code have we saved? The use of the `[Observable]` attribute generates the boilerplate code required to implement the `INotifyPropertyChanged` interface. Take a look at how much code you saved using Metalama: ```cs public class Person : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; private string name; public string Name { get => name; set { if (name != value) { name = value; ``` ```csharp PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Name))); } } private int age; public int Age { get => age; set { if (age != value) { age = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Age))); } } } ``` The amount of repetitive code here is significant and likely something you'd want to minimize. --- # ToString > Learn how to use Metalama to automatically generate ToString methods for C# classes, improving debugging and logging with customizable formatting options. Canonical: https://postsharp.net/metalama/applications/tostring.html A well-crafted `ToString` implementation speeds up debugging and log analysis. Typically, it should return a concise description of the object, highlighting its identity and state. With Metalama, you can skip writing this repetitive code manually. ## Example Consider the [ToString example aspect](https://doc.metalama.net/examples/tostring/tostring-1), applied to a class. In this scenario, we adopt an opt-out approach, marking members with `[NotToString]` that we prefer to exclude. ```csharp // [] [ToString] // [] internal class MovingVertex { public double X { get; set; } public double Y { get; set; } public double DX { get; set; } public double DY { get; set; } // [] [NotToString] // [] public double Velocity => Math.Sqrt((this.DX * this.DX) + (this.DY * this.DY)); } ``` The aspect generates the following code: ```csharp public override string ToString() { return $"{{MovingVertex DX={DX}, DY={DY}, X={X}, Y={Y}}}"; } ``` To modify the code generation pattern, adjust the aspect class itself, `ToStringAttribute`. ## Resources - Commented example: [ToString](https://doc.metalama.net/examples/tostring/tostring-1) --- # Metalama for UI Apps > Shows different use cases of Metalama in writing UI apps (like WPF). Canonical: https://postsharp.net/metalama/applications/ui.html Metalama can save you a significant amount of redundant code and reduce errors when implementing UI apps, whether they run on desktop, mobile, or browser through WASM. UI apps are often complex, requiring a lot of boilerplate code to make them reactive to user input. Metalama aids in implementing observability (allowing the UI framework to react to data changes), input validation, memoization, and framework-specific constructs such as dependency properties or commands in WPF. | Pattern | How Metalama can help | |---------|-----------------------| | [Change Tracking](https://postsharp.net/metalama/applications/change-tracking) | Automatically implements `IChangeTracking` or `IRevertibleChangeTracking` interfaces, reducing boilerplate code. | | [INotifyPropertyChanged](https://postsharp.net/metalama/applications/inotifypropertychanged) | Automatically implements the `INotifyPropertyChanged` interface, eliminating repetitive code and reducing errors. | | [Memoization](https://postsharp.net/metalama/applications/memoization) | Turns standard read-only properties into memoized ones without boilerplate, improving performance. | | [Undo-Redo](https://postsharp.net/metalama/applications/undo-redo) | Implements the Memento pattern to support undo/redo functionality. | | [Command](https://postsharp.net/metalama/applications/command) | Generates plumbing code for WPF commands, integrating with `Observable` aspect for `CanExecute` logic. | | [Dependency Properties](https://postsharp.net/metalama/applications/dependency-property) | Turn C# auto properties into WPF dependency properties. Compatible with [Code Contracts](https://postsharp.net/metalama/applications/contracts).| --- # Undo-Redo > Undo/redo is a popular UI feature, often implemented using the Memento pattern from the "Gang of Four's" Design Patterns. Canonical: https://postsharp.net/metalama/applications/undo-redo.html Undo/redo is a consistently popular feature, especially in UIs that do not rely on an _Ok_ button. A common way to implement it is by following the Memento pattern, originally formalized in the "Gang of Four's" Design Patterns book. For details, see the [Memento Pattern](https://postsharp.net/metalama/applications/memento) on this website. --- # Features > Metalama is PostSharp's open-source patterns & architecture toolkit for C#: code generation, validation, and custom refactorings, built on Roslyn. Canonical: https://postsharp.net/metalama/features/ Metalama is PostSharp's open-source patterns & architecture toolkit for C#, built on Roslyn. It specializes in code generation (including aspect-oriented programming) and code validation. Additionally, it allows you to create custom code fixes or refactorings. To achieve the same results with raw Roslyn, you would need to combine four separate, low-level tools that don't work together: source generators (which can only _add_ code, never modify existing members), MSIL rewriters like Fody (which modify existing code, but after compilation, so the results aren't visible to your source or IntelliSense), analyzers (to report diagnostics), and code-fix providers (to offer IDE fixes). Metalama replaces all four with a single high-level API in which generation, validation, and fixes cooperate. For instance, a validation rule can account for the code an aspect generated. | Feature | Description | |----------|----------| | [Code Generation](https://postsharp.net/metalama/features/code-generation) | Metalama is the simplest and most capable code generation framework for C#. Where Roslyn source generators can only _add_ code, its aspects also override, wrap, and compose over existing members, so you can express transformations no other Roslyn-based approach can, with generated code integrated with your hand-written code. | | [Code Verification](https://postsharp.net/metalama/features/code-validation) | Metalama makes it easy to validate hand-written or AI-generated code in real time, reporting errors and warnings when rules are violated. Thanks to the architecture validation package, you can also verify dependencies between classes and namespaces. | | [Design-Time Feedback](https://postsharp.net/metalama/features/design-time-feedback) | Metalama provides real-time feedback as you type, eliminating the need to constantly rebuild to refresh your IDE. Unlike MSIL-based approaches, and thanks to its integration with Roslyn source generators, Metalama allows you to reference generated declarations from hand-written code. | | [Code Fix Toolkit](https://postsharp.net/metalama/features/code-fixes) | Boost your team's productivity by suggesting custom code fixes for any errors or warnings reported by your code verification logic. | | [Ready-to-Use Aspect Libraries](https://postsharp.net/metalama/features/aspect-libraries) | Metalama comes with many professionally built and tested aspects that you can pull from NuGet and start using immediately. | | [Visual Studio Tooling](https://postsharp.net/metalama/features/tooling) | See how your code is affected by aspects directly in Visual Studio with Code Lens integration, Aspect Diff, and Aspect Explorer. Get syntax highlighting for T#, Metalama's C#-to-C# template language. | | [Test Frameworks](https://postsharp.net/metalama/features/testing) | Test code generation patterns and the reporting of warnings and errors. | | [Transformed Code Debugging](https://postsharp.net/metalama/features/debugging) | Choose whether to step into the transformed code or stick to the source code during debugging. You can see exactly which code is executed. | | [Code Query API](https://postsharp.net/metalama/features/code-query) | Query your code as a database and explore code references using LINQPad. Access the Metalama object model to diagnose how aspects and fabrics affect your code. | | [Divorce](https://postsharp.net/metalama/features/divorce) | Metalama makes it easy to part ways when needed. Run the `metalama divorce` command to commit the generated code into your source code and return to Microsoft's plain compiler. | --- # Code Selection Mechanisms > Metalama provides code selection mechanisms, including custom attributes, fabrics, aspect inheritance, and cross-project fabrics for enhancing or validating declarations. Canonical: https://postsharp.net/metalama/features/applying.html From custom attributes to code queries, Metalama offers several ways to select the declarations you want to enhance or validate. These mechanisms include: * **Custom attributes** for the manual selection of declarations. * **Fabrics** for programmatic LINQ-like code queries. * **Aspect inheritance** for automatic propagation of aspects. * **Cross-project fabrics** for applying aspects across multiple projects. ## Custom attributes The simplest way to add an aspect to a class, member, or parameter is by using a custom attribute. By default, aspects derive from the `System.Attribute` class. Use this approach when you want to manually select each target, for example when applying a caching aspect. ### Example In the following snippet, `[Cache]` adds caching to the `GetOpenOrders` method. ```csharp [Cache] public IEnumerable GetOpenOrders( Guid accountId ) { ... } ``` ## Fabrics _Fabrics_ are special C# classes that run at compile time within the compiler or at design time within the IDE. With fabrics, you can programmatically select declarations in bulk using a LINQ-like code query. Use this approach when you want to select declarations based on rules that are easy to evaluate in the code model. ### Example The `Fabric` class runs in the compiler and adds an exception handler to all public methods of public types in the current project. ```csharp internal class Fabric : ProjectFabric { public override void AmendProject(IProjectAmender amender) => amender .SelectTypes() .Where( t => t.Accessibility == Accessibility.Public ) .SelectMany( type => type.Methods ) .Where( m => m.Accessibility == Accessibility.Public ) .AddAspectIfEligible(); } ``` ### Resources * Reference documentation: [Adding many aspects simultaneously](https://doc.metalama.net/conceptual/using/adding-aspects-with-fabrics). ## Aspect inheritance Some aspects can be marked as `[Inheritable]`. When applied to a class or interface (either via an attribute or a fabric), they are automatically applied to all derived classes. Aspect inheritance also works across projects. ### Example In the following example, the `[Observable]` aspect implements the `INotifyPropertyChanged` interface for the `Animal` class. The properties of both `Animal` and `Dog` are instrumented to raise the `PropertyChanged` event. ```csharp [Observable] public class Animal { public string Species { get; set; } } public class Dog : Animal { public string Breed { get; set; } } ``` ### Resources * Reference documentation: [Applying aspects to derived types](https://doc.metalama.net/conceptual/aspects/aspect-inheritance). ## Cross-project fabrics A `TransitiveProjectFabric` is a fabric that executes in any _referencing_ project. For example, you can create a logging library that automatically adds logging to the methods of all projects referencing this library, whether through a project or package reference. You can also share fabrics across multiple projects, for instance, to configure several solutions at once. ### Example In the following example, the `Fabric` class adds an exception handler to all public methods of public types in every project that references the current project, rather than in the current project itself. This works even if the project is compiled and referenced as a library or package. ```csharp internal class Fabric : TransitiveProjectFabric { public override void AmendProject(IProjectAmender amender) => amender .SelectTypes() .Where( t => t.Accessibility == Accessibility.Public ) .SelectMany( type => type.Methods ) .Where( m => m.Accessibility == Accessibility.Public ) .AddAspectIfEligible(); } ``` ### Resources * Reference documentation: [Adding aspects to multiple projects](https://doc.metalama.net/conceptual/using/amending-many-projects). --- # Deterministic Aspect Composition > When several aspects apply to the same declaration, Metalama composes them in a deterministic order you control, so stacking logging, caching, and retry on one method is predictable rather than fragile. Canonical: https://postsharp.net/metalama/features/aspect-composition.html Real methods often carry more than one concern. A service call might need logging, caching, and retry at once. Metalama lets several aspects apply to the same declaration and composes them in a deterministic order that you control, so combining aspects is predictable instead of order-dependent guesswork. ## The matryoshka model Metalama wraps aspects _around_ your code like nested dolls: your source is the innermost doll, and each aspect adds a layer around it. At run time, execution starts with the outermost layer and works inward, so your original code runs last. This composition is why an aspect can wrap another aspect's output (a caching layer can sit outside a logging layer, which sits outside your method) without any aspect knowing about the others. ## You control the order By default, aspects execute in alphabetical order: deterministic, but not necessarily what you want. To set the order explicitly, an aspect library declares it once with an assembly-level attribute: ```cs using Metalama.Framework.Aspects; [assembly: AspectOrder( AspectOrderDirection.RunTime, typeof(RetryAttribute), typeof(CacheAttribute), typeof(LogAttribute))] ``` Here the retry runs outermost, then caching, then logging, then the original method. Order is defined as a set of pairwise relationships that Metalama merges into one global order for the project, so libraries that don't know about each other can still be combined predictably. ## Whose job it is Defining the execution order is primarily the responsibility of the aspect library author, not the developer applying the aspects: a well-built library sets a sensible order with the aspects it references, so users get correct composition by default. When you combine two unrelated libraries, you declare the order between them once, in your own project. --- # Ready-to-Use Aspect Libraries > Metalama offers open-source, professionally built aspect libraries for caching, code contracts, memoization, observability, and WPF integration. Canonical: https://postsharp.net/metalama/features/aspect-libraries.html Metalama comes with a set of professionally built aspects, ready to be used in your projects. ## Features * **Open source.** Released under the same MIT license as the Metalama Framework. * **Professionally built.** Crafted by the Metalama core team. * **Continuously tested.** Integrated into Metalama's CI/CD process. * **Customizable.** Designed for extensibility and adaptability. * **Professionally supported.** Covered by Metalama Professional and Metalama Enterprise support. ## Libraries
### Caching Cache method results with simple custom attributes. [Learn more](https://postsharp.net/metalama/applications/caching)
### Code Contracts Check preconditions, postconditions, and invariants. Validate fields, properties, parameters, and return values. [Learn more](https://postsharp.net/metalama/applications/contracts)
### Memoization Add high-performance, low-overhead caching to get-only properties and parameterless methods. [Learn more](https://postsharp.net/metalama/applications/memoization)
### Observability Implement `INotifyPropertyChanged` without any boilerplate code. Never miss a notification again. [Learn more](https://postsharp.net/metalama/applications/inotifypropertychanged)
### Dependency Properties and Commands Transform C# automatic properties into WPF dependency properties and methods into WPF commands without redundant code. [Learn more](https://postsharp.net/metalama/applications/dependency-property)
## Resources * Source code on [GitHub](https://github.com/metalama/Metalama/tree/HEAD/Metalama.Patterns) * [Metalama Marketplace](https://postsharp.net/metalama/marketplace) indexes all aspect libraries and extensions --- # Code Fix Toolkit > Metalama's Code Fix Toolkit enhances productivity by automating code fixes, improving API discoverability, and reducing developer errors. Canonical: https://postsharp.net/metalama/features/code-fixes.html With Metalama, coding architects and senior developers can create custom code fixes and refactorings tailored to your business domain. These can be surfaced through warnings or the IDE’s lightbulb/screwdriver menu, improving team productivity at scale. ## Benefits * **Accelerate development.** Developers can fix issues with a single click instead of manually figuring out how to resolve a warning or apply a pattern. * **Improve API discoverability.** Make it easier for new developers to discover less common members of your API. * **Reduce load on senior developers.** Frequent questions like “how do I fix this?” or “what’s the right pattern here?” are answered automatically with working, production-grade code. * **Support junior developers.** Junior developers get guardrails and guidance without constant handholding, reducing errors and building confidence. * **Reduce errors.** Automated fixes reduce the chance of human error when applying repetitive or complex changes. ## Features * Attach a code fix to any warning or error reported by your aspects or fabrics. * Suggest a code fix without reporting warnings or errors (also called code refactorings). * Use the full code generation framework for code fixes. * Look and feel like native code fixes. * Work in any IDE. ## Example Suppose we built a `[ToString]` aspect, which generates the `ToString()` method from all properties. We defined an attribute `[NotToString]` to exclude a property from the generated `ToString()` method. To improve the discoverability of the `[NotToString]` attribute and avoid endless "how do I fix this" questions, we add the following code to the aspect: ```cs foreach ( var property in GetIncludedProperties( builder.Target ) ) { builder.Diagnostics.Suggest( CodeFixFactory.AddAttribute( property, typeof(NotToStringAttribute), "Exclude from [ToString]" ), property ); } ``` ## Resources * Reference documentation: - [Exposing an aspect as a live template](https://doc.metalama.net/conceptual/aspects/ide/live-template) - [Offering code fixes and refactorings](https://doc.metalama.net/conceptual/aspects/ide/code-fixes) * Examples: - [ToString: adding code fixes and refactorings](https://doc.metalama.net/examples/tostring/tostring-2) --- # Code Generation for C# > This article presents different code generation techniques for C# and compares them to Metalama. Canonical: https://postsharp.net/metalama/features/code-generation.html Why write repetitive code by hand when you can generate it automatically? C# lets you name a method or a class, but it has no keyword for a recurring _pattern_, so the boilerplate must be written by hand. That is the gap Metalama fills. Metalama is the most capable code generator for .NET, thanks to its unique C#-to-C# template technology. ## Benefits Four reasons to eliminate repetitive code with a code generator: * **Boost development productivity.** Using Metalama can reduce hand-written code by 10–50%, enabling developers to focus on meaningful tasks. Architects and senior developers act as productivity _multipliers_ by minimizing repetitive and error-prone work for the rest of the team. * **Reduce code complexity.** Separating technical details from business logic results in cleaner, more readable code. New team members can contribute effectively without being overwhelmed by low-level technical details. * **Improve reliability.** Fewer lines of code mean fewer defects. Lower complexity also contributes. Additionally, Metalama simplifies the implementation of resilience features, further improving your app's reliability. * **Reduce maintenance costs.** Metalama's ultimate advantage lies in reducing maintenance costs and extending the lifespan of the codebase. Since maintenance accounts for 55%–95% of a software system's costs, keeping complexity low is crucial for post-release team productivity. ## Features Metalama is the only framework for .NET that offers all of the following: | Feature | Description | |---------|-------------| | [C#-to-C# Templates](https://postsharp.net/metalama/features/templates) | Classic code generators are essentially text generators and don’t provide syntax highlighting, code completion, or error checking for generated code. Metalama is a true, strongly-typed object-oriented code generator featuring T#, a unique C#-to-C# template language. | | [Enhance Hand-Written Code](https://postsharp.net/metalama/features/override) | Metalama is the only Roslyn-based tool that lets you add behaviors to existing hand-written code. Other C# generators let you _add new classes_ to a project or _extend partial classes_, but they don't allow injecting new behaviors into existing methods, properties, or fields. | | [Reference Generated Code From Source Code](https://postsharp.net/metalama/features/reference-from-source-code) | With Metalama, your source code can reference generated code as if it were itself source code. This is a significant advancement over previous MSIL-based generation, where code additions were not visible from source code. | | [Dependency Injection Support](https://postsharp.net/metalama/features/dependency-injection) | You can use dependency injection concepts in your aspects to make both the aspects and the target code more testable. | | [Flexible Code Selection Mechanisms](https://postsharp.net/metalama/features/applying) | Metalama offers several mechanisms to add aspects (which encapsulate code generation and validation) to target code: custom attributes for selective application, and fabrics for a bulk, programmatic approach. Also, explore cross-project options. | --- # Code Query API > Metalama's Code Query API enables querying code like a database, facilitating diagnostics, interactive investigations, and architecture validation in .NET applications. Canonical: https://postsharp.net/metalama/features/code-query.html Query your code as if it's a database using the `Metalama.Framework.Workspaces` package. Whether you're running interactive queries in LINQPad or hosting them in any .NET application, it's all possible. Although Metalama is primarily designed to run within the compiler or IDE process, you can also use its code API from any .NET application. ## Use cases - **Diagnose the Metalama compilation process.** Inspect aspects and see their effects on source code. - **Interactively investigate a codebase.** Run queries in LINQPad and search for types or members based on any criteria. - **Validate architecture.** Spot anomalies in your architecture by running code queries and reporting errors from your CI/CD pipeline. ## Features - **LINQPad driver.** Execute code queries interactively from LINQPad. - **Hostable in any application.** After all, it's just a .NET API. - **Open projects and solutions.** Open `csproj` or `sln` files. - **Query code references and dependencies.** Access the dependency graph through the API. ## Resources * Reference documentation: - [Querying code with or without LINQPad](https://doc.metalama.net/conceptual/introspection/linqpad) - [Introspection API](https://doc.metalama.net/api/introspection-api) --- # Code Validation > Metalama aspects validate code, ensuring assumptions are met, providing error messages, and defining eligibility. Canonical: https://postsharp.net/metalama/features/code-validation.html When enhancing a class or method, it's crucial to validate that the target code meets your assumptions. If it doesn't, a clear and appropriate error message should be reported. In Metalama, aspects can both generate code and _validate_ it. You can even create aspects that serve purely as validators without any code generation logic. ## Benefits - **Least astonishment.** As an aspect author, it's your responsibility to ensure users of your aspects receive clear and meaningful error messages. - **Fail fast, early.** Avoid exceptions in your aspect logic or confusing C# errors due to invalid generated code. - **Improved developer experience.** Provide real-time feedback in the IDE, helping developers identify and fix issues as they write code. - **Enforce coding standards.** Ensure that code adheres to specific rules and guidelines, improving consistency across the codebase. - **Reduce debugging time.** Catch issues early during development rather than at runtime, saving time and effort. - **Architecture validation.** Ensure that code adheres to [architectural rules](https://postsharp.net/metalama/applications/architecture-verification) and constraints, preventing violations that could lead to long-term maintenance issues. ## Features - **Warnings and errors reporting.** Your aspects or fabrics can report custom errors, warnings, or messages. - **Warnings suppress.** Your aspects or fabrics can suppress warnings reported by the compiler or other analyzers. - **Aspect eligibility.** Your aspects can specify which declarations the aspect can be applied to. If eligibility conditions are violated, an error is reported, and the aspect is not applied. Additionally, the refactoring menu in the IDE will only suggest adding eligible aspects. - **Metrics.** You can query code metrics such as the number of nodes or statements in a method and define custom metrics. - **Reference validation.** Find who in your solution is using your code and enforce your design intent. All features work both in real time as you type and during the build process. Metalama lets you author custom code validators with a simple, high-level API and run them in real time in the IDE. Competing approaches force a trade-off: real-time, or customizable, or simple, but rarely all three. ## Example The following aspect makes two assumptions about the method it is applied to: - The method must be non-static. This is an eligibility condition because the aspect should not appear in the lightbulb menu for static methods. - The containing class must have a field named `_logger`. If this condition is not met, an error will be reported. ```cs internal class LogAttribute : OverrideMethodAspect { private static readonly DiagnosticDefinition _error = new( "MY001", Severity.Error, "The type {0} must have a field named '_logger'." ); public override void BuildEligibility( IEligibilityBuilder builder ) { base.BuildEligibility( builder ); // Ensure the target method is non-static; otherwise, do not // suggest the aspect in the lightbulb menu. builder.MustNotBeStatic(); } public override void BuildAspect( IAspectBuilder builder ) { base.BuildAspect( builder ); if ( !builder.Target.DeclaringType.Fields.OfName( "_logger" ).Any() ) { // Report an error if the '_logger' field is missing. builder.Diagnostics.Report( _error.WithArguments( builder.Target.DeclaringType ) ); } } public override dynamic? OverrideMethod() { meta.This._logger.WriteLine( $"Executing {meta.Target.Method}." ); return meta.Proceed(); } } ``` ## Resources * Reference documentation: - [Reporting and suppressing diagnostics](https://doc.metalama.net/conceptual/aspects/diagnostics) - [Defining the aspect eligibility](https://doc.metalama.net/conceptual/aspects/eligibility) --- # Core Features > Metalama offers features like code generation, verification, real-time feedback, architecture validation, and a code fix toolkit for C#. Canonical: https://postsharp.net/metalama/features/core.html | Feature | Description | |---------|-------------| | [Code Generation](https://postsharp.net/metalama/features/code-generation) | Metalama is the simplest and most capable code generation framework for C#. Inspired by aspect-oriented programming, it's the only Roslyn-based approach that integrates generated code with handwritten code, making it possible to implement most code patterns. | | [Code Verification](https://postsharp.net/metalama/features/code-validation) | Metalama makes it easy to validate handwritten or AI-generated code in real time, reporting errors and warnings when rules are violated. With the architecture validation package, you can also verify dependencies between classes and namespaces. | | [Immediate Editor Feedback](https://postsharp.net/metalama/features/design-time-feedback) | Metalama provides real-time feedback as you type, eliminating the need to constantly rebuild to refresh your IDE. Unlike MSIL-based approaches, and thanks to its integration with Roslyn source generators, Metalama allows you to reference generated declarations from handwritten code. | | [Architecture Validation](https://postsharp.net/metalama/applications/architecture-verification) | Validate code usages, references, and dependencies, and enforce your architecture straight from the IDE with Architecture As Code. | | [Code Fix Toolkit](https://postsharp.net/metalama/features/code-fixes) | Suggest custom code fixes for any errors or warnings reported by your code verification logic. | --- # Transformed Code Debugging > Metalama's `LamaDebug` configuration enables debugging of transformed code, allowing breakpoint setting and inspection in any IDE. Canonical: https://postsharp.net/metalama/features/debugging.html Metalama offers step-into debugging of transformed code, with a switch between the _source_ view and the _transformed_ view. Build with the `LamaDebug` configuration to step through the generated code line by line. When you build with the `LamaDebug` configuration, the transformed code is stored under the `obj/LamaDebug/metalama` directory. This makes it easy to inspect the generated code and set breakpoints directly in it. ## Benefits * Focus on the abstraction level relevant to your current task. * Works with all IDEs. * Produces readable, well-formatted code. * Allows you to set breakpoints in the generated code. --- # Dependency Injection > Metalama integrates dependency injection with aspect-oriented programming, enhancing testability and supporting multiple frameworks like `Microsoft.Extensions.DependencyInjection`. Canonical: https://postsharp.net/metalama/features/dependency-injection.html Metalama offers built-in support for dependency injection. Aspects can introduce dependencies, which are automatically injected into the class where the aspect is applied. Many frameworks are supported. There's a common misconception that aspect-oriented programming and dependency injection are competing paradigms. In reality, they complement each other. While these concepts were formalized in the early 2000s and weren't initially designed to work together, Metalama, redesigned in the 2020s, integrates both ideas. Each tool has its own job. Use dependency injection for what it does best (composing the application by wiring services together at their interface boundaries), and use aspects for the cross-cutting behavior that lives _inside_ those services. That way you don't twist dependency injection into solving problems it was never designed for, such as adding logging or caching to methods that sit behind no interface at all. ## Benefits * **Enhanced testability.** One of the primary benefits of dependency injection is improved testability. It allows you to provide different service implementations for testing and production environments. Unlike other aspect frameworks, Metalama fully supports this approach, enabling you to make your code as testable as needed. * **Support for multiple DI frameworks.** Metalama natively supports the `Microsoft.Extensions.DependencyInjection` namespace and the _service locator_ pattern. Additionally, you can implement support for other frameworks. Thanks to an abstraction layer, your aspects don't need to be tightly coupled to a specific dependency injection framework. ## Example Suppose our source code looks like this. We are adding a `[Log]` attribute to the `ExecuteAsync` method. ```csharp class Worker : BackgroundService { [Log] protected override Task ExecuteAsync( CancellationToken stoppingToken ) { Console.WriteLine( "Hello, world." ); return Task.CompletedTask; } } ``` The `Worker` class would be transformed into the following code: ```csharp class Worker : BackgroundService { ILogger _logger; public Worker( ILogger logger ) { this._logger = logger; } [Log] protected override Task ExecuteAsync( CancellationToken stoppingToken ) { this._logger.LogDebug( "Executing Worker.ExecuteAsync" ); Console.WriteLine( "Hello, world." ); return Task.CompletedTask; } } ``` ## Resources * Reference documentation: [Injecting dependencies into aspects](https://doc.metalama.net/conceptual/aspects/dependency-injection) * Commented example: [Logging with DI](https://doc.metalama.net/examples/log/log-4) --- # Immediate Editor Feedback > Metalama provides immediate error notifications and real-time access to code changes within the IDE without recompilation. Canonical: https://postsharp.net/metalama/features/design-time-feedback.html Metalama operates both within the compiler process and the IDE. Your aspects are triggered whenever a relevant change is detected in the code. This means you don't have to build your project to see warnings and errors or to reference generated artifacts in your source code. ## Benefits
  • Diagnostics

    Instant diagnostics: Get immediate error and warning notifications directly in the editor, thanks to aspects.

  • Intellisense

    Intellisense: Enjoy real-time access to aspect-introduced code, eliminating the need for recompilation.

--- # Divorcing from Metalama > Metalama provides tools and procedures for removing its dependency, allowing projects to revert to the standard C# compiler. Canonical: https://postsharp.net/metalama/features/divorce.html We understand the risks you're taking by depending on Metalama, so we've made it as simple as possible to remove it from your projects. We provide a [procedure](https://doc.metalama.net/conceptual/divorcing) and tools to write the generated code back into your source code repository. You can then use any `git` tool to review the changes and commit the generated code back into your source repository. Metalama Divorce is not designed for regular execution. Like any divorce, breaking up from Metalama can be challenging and might take anywhere from hours to days, depending on the size of your projects. We've aimed to make the process as smooth as _possible_. ## Benefits * No vendor lock-in. You can revert to the standard C# compiler at any time. ## Resources * Reference documentation: [Divorcing from Metalama](https://doc.metalama.net/conceptual/divorcing) --- # Extensibility Features > Metalama offers extensibility through Roslyn SDK integration, a Code Query API, and easy removal via the "divorce" feature. Canonical: https://postsharp.net/metalama/features/extensibility.html We hate lock-ins just as much as you do. You can use the underlying Roslyn SDK or integrate Metalama from any process. We have even made it possible to remove Metalama from your projects when necessary. | Feature | Description | |----------|----------| | [Roslyn Extensibility](https://postsharp.net/metalama/features/roslyn) | Overcome Metalama limitations by directly accessing the Roslyn API. Analyze method bodies or run custom aspect weavers. | | [Code Query API](https://postsharp.net/metalama/features/code-query) | Query your code like a database and explore code references using LINQPad. Access the Metalama object model to diagnose how aspects and fabrics affect your code. | | [Divorce](https://postsharp.net/metalama/features/divorce) | Metalama makes it easy to part ways when needed. Run the `metalama divorce` command to commit the generated code into your source code and revert to using Microsoft's plain compiler. | --- # Enhance Hand-Written Code > Metalama adds behaviors to existing C# code, enabling features like logging and exception handling without altering the original methods in your source. Canonical: https://postsharp.net/metalama/features/override.html Metalama is the only Roslyn-based C# generator that lets you add behaviors to existing hand-written code. While other source generators allow you to add new classes to a project or extend partial classes, they don't let you inject new behaviors into existing methods, properties, or fields. ## Benefits * Features like instrumentation (logging, metrics), exception handling (retry, throttling), observability (`INotifyPropertyChanged`), and thread synchronization (locking) all require modifying existing code; adding new classes is not enough. * It's transparent. There is no need for the `partial` keyword. ## Example Suppose you have this source code: ```cs public class HatShop { public void PlaceOrder() { Console.WriteLine( "Ordering a hat." ); } } ``` We want to measure the number of executions of all public methods in our project, so we add this fabric: ```csharp internal class Fabric : ProjectFabric { public override void AmendProject(IProjectAmender amender) => amender .SelectTypes() .Where(type => type.Accessibility == Accessibility.Public) .SelectMany(type => type.Methods) .Where(method => method.Accessibility == Accessibility.Public) .AddAspectIfEligible(); } ``` The `[MeasureExecutionCount]` attribute will transform the `HatShop` class into this: ```csharp public class HatShop { // This field is added by the aspect. private HatShopMetrics _hatShopMetrics; // This constructor is added by the aspect. It pulls the dependency. public HatShop(HatShopMetrics hatShopMetrics = null) { this._hatShopMetrics = hatShopMetrics; } public void PlaceOrder() { // This line of code is added by the aspect. this._hatShopMetrics?.PlaceOrderExecutionCount.Add(1); // This is the original source code. Console.WriteLine("Ordering a hat."); } } ``` As you can see, it adds new members like the `HatShopMetrics` class and field, and it also injects logic into the `PlaceOrder` method. ## Resources * Reference documentation: [Creating simple aspects](https://doc.metalama.net/conceptual/aspects/simple-aspects) --- # Productivity Features > Metalama offers productivity features like ready-to-use aspect libraries, Visual Studio tooling, test frameworks, and transformed code debugging. Canonical: https://postsharp.net/metalama/features/productivity.html Metalama ships aspect libraries, Visual Studio tooling, test frameworks, and transformed-code debugging. By the PostSharp team: meta-programming in .NET since 2004. | Feature | Description | |----------|----------| | [Ready-to-Use Aspect Libraries](https://postsharp.net/metalama/features/aspect-libraries) | Pull professionally built and tested aspects from NuGet instead of writing your own. | | [Visual Studio Tooling](https://postsharp.net/metalama/features/tooling) | See how your code is affected by aspects directly in Visual Studio with Code Lens integration, Aspect Diff, and Aspect Explorer. Enjoy syntax highlighting for T#, Metalama's C#-to-C# template language. | | [Test Frameworks](https://postsharp.net/metalama/features/testing) | Test code generation patterns and the reporting of warnings and errors. | | [Transformed Code Debugging](https://postsharp.net/metalama/features/debugging) | Choose whether to step into the transformed code or stick to the source code during debugging. You can see exactly which code is executed. | --- # Reference Generated Code From Source Code > Metalama allows source code to reference generated code, enabling integration and IntelliSense support, enhancing code completion and design-time verification. Canonical: https://postsharp.net/metalama/features/reference-from-source-code.html With Metalama, your source code can reference generated code as if it were part of the original source. This marks a significant step forward from previous MSIL-based generation methods, where code additions remained invisible to the source code. ## Benefits * **Idiomatically C#.** No need for cumbersome constructs like in MSIL-based approaches. * **IntelliSense.** Code completion works with generated code. ## Example Consider a scenario where an aspect implements the Memento pattern for the `Fish` class. In this case, the source code will "recognize" that the class implements the `IMementoable` interface, allowing you to invoke the generated `SaveToMemento` and `RestoreMemento` methods. IntelliSense and design-time code verification will function as expected. Suppose we have the following class in the source code: ```cs [Memento] public sealed partial class Fish { public string? Name { get; set; } public string? Species { get; set; } public DateTime DateAdded { get; set; } } ``` The `[Memento]` aspect automatically implements the `IMementoable` interface, defined as follows: ```cs public interface IMementoable { IMemento SaveToMemento(); void RestoreMemento( IMemento memento ); } ``` Another part of the source can use the `IMementoable` interface and its methods as if they were defined directly in the source code: ```cs var fish = new Fish() { Name = "Hannibal", Species = "Tilapia" }; // Save by using the generated SaveToMemento method. var memento = fish.SaveToMemento(); // Change fish.Name = "Shark"; // Undo change by using the generated RestoreMemento method. fish.RestoreMemento(memento); ``` --- # Extensible with Roslyn > Metalama, built on Roslyn, allows advanced analysis and code transformations using the `Metalama.Framework.Sdk` package for enhanced functionality. Canonical: https://postsharp.net/metalama/features/roslyn.html Metalama is a simplified and productive API built on top of Roslyn. When the need arises, you can use the `Metalama.Framework.Sdk` package to access the low-level API and overcome any limitations of Metalama. Disclaimer: this breaks the warranty. You can use `Metalama.Framework.Sdk` in the following scenarios: 1. **Analyze method bodies.** Metalama's object model does not allow you to analyze method bodies, expressions, or field/property initializers. To perform advanced analysis, such as analyzing the property dependency graph to implement the `INotifyPropertyChanged` interface, you can use Roslyn. 2. **Implement arbitrary code transformations.** You can implement aspects at Roslyn's syntax level by authoring custom aspect weavers. However, this approach is not generally recommended due to its high complexity and performance overhead. 3. **Develop custom metrics.** ## Resources * Reference documentation: [Extending Metalama](https://doc.metalama.net/conceptual/sdk) --- # C#-to-C# Templates > Metalama's T# offers a C#-to-C# template language with syntax highlighting, code completion, and no runtime overhead for code generation. Canonical: https://postsharp.net/metalama/features/templates.html Classic code generators are just _text_ generators. They don't offer syntax highlighting, code completion, or error checking for generated code. Metalama is a true, strongly-typed _object-oriented code generator_ that features T#, a unique C#-to-C# template language. T# templates are pure C# methods and expressions, 100% compatible with any C# editor. They differ from regular C# methods solely in how they are compiled. With T#, you can blend generated code with hand-written code. Benefits of T# include: - **Full Intellisense support.** Enjoy syntax highlighting, code completion, member lists, and error detection. - **100% C# compatible.** Use any C# editor of your choice. - **High performance.** Generate high-performance code without any runtime overhead. - **No need to learn MSIL.** It's just the C# you already know. - **Completely debuggable.** Step into the code you generate. [Learn more](https://postsharp.net/metalama/debugging). - Additional syntax highlighting is available as a premium feature via Visual Studio Tools for Metalama. ## Example Consider this aspect: ```csharp public class RetryAttribute : OverrideMethodAspect { public override dynamic? OverrideMethod() { for ( var i = 0;; i++ ) { try { return meta.Proceed(); } catch ( Exception e ) when ( i < 3 ) { Console.WriteLine( $"{meta.Target.Method} failed: {e.Message}" ); Thread.Sleep( 100 ); } } } } ``` The `OverrideMethod` is a T# template: - `meta.Proceed()` is replaced by the implementation of the method being overridden. - `meta.Target.Method` is the object model of the method being overridden. Here, we are calling the `ToString` method. Now let's apply this template to a method: ```csharp [Retry] public async Task GetExchangeRate() { using var client = new HttpClient(); var url = $"https://api.example.com/exchange?base=CZK&target=USD"; var response = await client.GetAsync(url); response.EnsureSuccessStatusCode(); var responseString = await response.Content.ReadAsStringAsync(); return decimal.Parse(responseString); } ``` During compilation, Metalama will apply the `[Retry]` template to the `GetExchangeRate` method and generate the following code: ```csharp [Retry] public async Task GetExchangeRate() { for ( var i = 0;; i++ ) { try { using var client = new HttpClient(); var url = $"https://api.example.com/exchange?base=CZK&target=USD"; var response = await client.GetAsync(url); response.EnsureSuccessStatusCode(); var responseString = await response.Content.ReadAsStringAsync(); return decimal.Parse(responseString); } catch ( Exception e ) when ( i < 3 ) { Console.WriteLine( $"CurrencyService.GetExchangeRate() failed: {e.Message}" ); Thread.Sleep( 100 ); } } } ``` ## How does it work? T# templates differentiate between *compile-time* and *run-time* code execution. Since there are no tags like `{% %}` or `<% %>` to distinguish compile-time C# from run-time C#, Metalama relies on inference rules to determine which statements and expressions are compile-time and which ones are run-time. By default, all APIs are considered run-time. Compile-time APIs (like the `meta` class) must be tagged with the `[CompileTime]` custom attribute. When Metalama encounters a compile-time API call in your template, it uses complex inference rules to determine which parts of the template should execute at compile-time. For example, in the snippet below, `meta.Target` is tagged as compile-time. By inference, `meta.Target.Method.Parameters` is compile-time, making the `parameters` variable and the `foreach` loop compile-time as well. In `$"{p.Name} = '{p.Value}'"`, `p.Name` is compile-time, but `p.Value` is a compile-time expression returning a run-time expression. ```csharp public override dynamic? OverrideMethod() { var parameters = meta.Target.Method.Parameters; foreach ( var p in parameters ) { Console.WriteLine( $"{p.Name} = '{p.Value}'"); } return meta.Proceed(); } ``` Most of the `Metalama.Framework` namespace is compile-time. You can extend T# with your own classes and methods by tagging them with the `[CompileTime]` or `[RunTimeOrCompileTime]` attributes. ## Features - Compile-time local variables, `foreach`, `if`, `switch`. - Access unknown members through C# `dynamic` typing. - Template parameters. - Call a template from another template (including virtual calls). - Complete API to reflect on the code being compiled. - APIs to dynamically build interpolated strings, `switch` statements. - Serialization from compile-time objects to run-time expressions. --- # Test Frameworks > Metalama provides testing libraries for code generation and diagnostics, supporting xUnit and integrating with diff tools for efficient testing. Canonical: https://postsharp.net/metalama/features/testing.html As the author of an aspect, it's your responsibility to thoroughly test both the code generation and code validation logic under various conditions. Metalama's testing frameworks let you test both code generation patterns and the reporting of warnings and errors. Metalama includes two testing libraries: - **`Metalama.Testing.AspectTesting`.** Enables you to test code generation (including templates) and diagnostic reporting logic. Each file in the test project is compiled into a standalone project, and the aspects are executed. The aspect outputs, which include the transformed code and diagnostics, are compared to their expected values. You can use your favorite diff tool to compare, reject, or accept changes. - **`Metalama.Testing.UnitTesting`.** Allows you to unit-test compile-time code (typically queries on the code model) without executing the aspects themselves. ## Benefits - **Test code generation patterns.** Compare the actual generated code with the expected one. - **Test warnings and errors.** Diagnostics are prepended to the output, making it easy to check if the validation went as expected. - **Isolate tests in autonomous mini-projects.** Debugging aspects in production projects is cumbersome because all aspect instances are executed concurrently. It can be difficult to step into the execution of a specific aspect instance. Test projects allow you to run and debug a single aspect instance at a time. ## Features - **Compatible with xUnit.** Works with all test runners that support xUnit, including Rider and Resharper. - **Test cross-project behaviors.** Create tests that involve several projects without needing to create any scaffolding. - **Integrates with diff viewers.** Uses [DiffEngine](https://github.com/VerifyTests/DiffEngine) to open your favorite diff tool when a test output does not meet expectations. - **Bulk accept.** Includes a command-line tool to apply changes in bulk when you prefer to use `git` to track acceptance of test results. ## Resources - **Reference documentation.** [Testing the aspect's code generation and error reporting](https://doc.metalama.net/conceptual/aspects/testing/aspect-testing). --- # Visual Studio Tools for Metalama > Visual Studio Tools for Metalama enhance productivity with CodeLens, Aspect Diff, and Aspect Explorer. Canonical: https://postsharp.net/metalama/features/tooling.html See how Metalama aspects affect your code with CodeLens integration, Aspect Diff, and Aspect Explorer. Write aspects in T#, our C#-to-C# template language, with syntax highlighting. Visual Studio Tools for Metalama require either a free Metalama Community license or a Metalama Professional license. ## Features
  • CodeLens Integration

    CodeLens: Identify applied aspects within the editor, even when they are not evident from the source code.

  • Diff Preview

    Metalama Diff: Compare source and transformed code directly within Visual Studio.

  • Syntax Highlighting

    Syntax Highlighting: Distinguish meta-code with T# syntax highlighting.

  • Aspect Explorer

    Aspect Explorer: View all aspects in the solution and see how they affect the source code.

## Resources * Download from [Visual Studio Gallery](https://marketplace.visualstudio.com/items?itemName=PostSharpTechnologies.PostSharp) --- # Welcome to Metalama Tools for Visual Studio! Canonical: https://postsharp.net/metalama/welcome-vsx.html Metalama generates the boilerplate you'd otherwise hand-write, and validates your code as you type. Metalama is PostSharp's open-source patterns & architecture toolkit for C#. Our motto: _If it's repetitive, it can be automated._ Say goodbye to tedious tasks and cumbersome boilerplate. Welcome to clean, maintainable, and DRY code. ## Free Email Course & Newsletter Subscribe for release news and monthly status updates: ## Getting Started with Metalama Get started: - **[Using Metalama](https://doc.metalama.net/conceptual/using):** Start here if, like most developers, you will use Metalama but won't author your own aspects. - **[Building Aspects](https://doc.metalama.net/conceptual/getting-started):** Ready to build your own aspects? This is your launchpad. - **[Metalama Marketplace](https://postsharp.net/metalama/marketplace):** Pre-built aspect libraries to save time. - **[Use Cases](https://postsharp.net/metalama/applications):** Learn when Metalama is the right tool for the job (and when it isn't). --- # Welcome to Metalama! Canonical: https://postsharp.net/metalama/welcome.html Metalama is PostSharp's open-source patterns & architecture toolkit for C#. Define your team's patterns once: the compiler writes the repetitive parts at build time and enforces your rules as you type. Our motto: _If it's repetitive, it can be automated._ Say goodbye to boilerplate, and hello to clean, maintainable, DRY code. Metalama collects usage data to help us improve your experience. This data is securely gathered by PostSharp Technologies and handled in accordance with their [privacy policy](https://www.postsharp.net/company/legal/privacy-policy). If you prefer not to share this data, you can opt out: set the `METALAMA_TELEMETRY_OPT_OUT` environment variable to `true` in your shell, by adding a properly configured `metalama.json` file to your repo root, or use the `metalama telemetry` CLI tool. For detailed instructions, visit our [telemetry documentation](https://doc.metalama.net/conceptual/configuration/telemetry). ## Free Email Course & Newsletter Subscribe for release news and a monthly status report: ## Install Visual Studio Tools for Metalama The **Visual Studio Tools for Metalama** show what aspects do to your code: - **CodeLens Integration** Identify applied aspects directly in the editor, even when they aren't visible in the source code. - **Metalama Diff** Compare source and transformed code in Visual Studio to see the impact of your aspects. - **Syntax Highlighting** Distinguish meta-code with T# syntax highlighting. - **Aspect Explorer** Explore all aspects in your solution and see how they influence the source code, providing a comprehensive view of your project. These tools are available as a premium feature and are free for individuals, non-commercial use, open-source development, and companies with up to 3 developers. ## Getting Started with Metalama Get started: - **[Using Metalama](https://doc.metalama.net/conceptual/using):** Start here if, like most developers, you will use Metalama but won't author your own aspects. - **[Building Aspects](https://doc.metalama.net/conceptual/getting-started):** Ready to build your own aspects? This is your launchpad. - **[Metalama Marketplace](https://postsharp.net/metalama/marketplace):** Pre-built aspect libraries to save time. - **[Use Cases](https://postsharp.net/metalama/applications):** Learn when Metalama is the right tool for the job (and when it isn't). --- ## Section: Pricing and licensing # Pricing FAQ > The most frequently asked questions about PostSharp and Metalama pricing and licensing: license types, free licenses, discounts, upgrades, renewals and ordering. Canonical: https://postsharp.net/pricing/faq Everything we are asked most often about licensing, discounts, renewals and ordering. Still stuck? [Contact us](https://postsharp.net/contact). --- ## Section: Support and policies # Compliance > Our security, privacy and legal policies for PostSharp, Metalama and the postsharp.net website. Canonical: https://postsharp.net/compliance/ ## Overview This section gathers the policies that govern the security of our products, how we handle your personal data, and the legal terms under which we provide our software, content and services. | Page | Description | |------|-------------| | [Security Policy](https://postsharp.net/compliance/security) | Our trust boundary, how we assess risk, and how to report a vulnerability. | | [Privacy Policy](https://postsharp.net/compliance/privacy) | How we collect, use and protect your personal data. | | [Terms of Use](https://postsharp.net/compliance/terms-of-use) | Your obligations when using the postsharp.net website. | | [License Agreements](https://downloads.postsharp.net/legal) | The License and Support Services Agreement (LSSA) and other legal agreements. | --- # Privacy Policy > How we collect, use and protect your personal data. Canonical: https://postsharp.net/compliance/privacy.html This Privacy Policy explains how SharpCrafters s.r.o. collects and uses personal data in connection with the website **postsharp.net** (the "Website"), the customer portal, and the **PostSharp** and **Metalama** software products (the "Software"). It covers visitors to the Website, people who hold or use an account, and developers who use the Software. This Privacy Policy pertains to the latest versions of our products. Earlier versions may collect or transmit data differently. Where this Privacy Policy conflicts with the License and Support Services Agreement (the "[LSSA](https://downloads.postsharp.net/legal)") or another agreement between you and us regarding a specific product or service, that agreement governs for the matters it covers. ### 1. Who is responsible (controller) SharpCrafters s.r.o., registered office at Praha 5, nám. 14. října 1307/2, Postal Code 150 00, the Czech Republic, ID 28953690, registered in the Commercial Register maintained by the Municipal Court in Prague, Section C, File 155506, doing business as "PostSharp Technologies" ("we", "us", "our"), is the controller of the personal data described in this Privacy Policy. For privacy questions or to exercise your rights, contact privacy@postsharp.net. Our data protection contact is dpo@postsharp.net. ### 2. The personal data we collect, and why #### 2.1 Website visitors When you browse the Website we collect, through analytics and server logs, technical data such as pages viewed, referring page, approximate location, device and browser type, and IP address. We use **Matomo** analytics, provided by Matomo Cloud (InnoCraft) and served through our analytics.postsharp.net subdomain, to understand and improve how the Website is used. Our analytics are configured to minimize the personal data involved: the cookies are set as first-party cookies on our own domain; we anonymize IP addresses; the visitor identifier is a random value that we rotate every week, and reduce to session-only if your browser sends a "Do Not Track" signal; we strip identifiers such as license keys and newsletter-subscriber ids from the URLs and page titles we record; and we disable browser-feature fingerprinting. **Cloudflare** operates as the reverse proxy and content-delivery network in front of our Website: all traffic to the Website passes through Cloudflare's network, which processes technical request data (including your IP address and request headers) for essentially every visitor. We also use Cloudflare for security and anti-bot protection (including an invisible Cloudflare Turnstile / Bot Management check). We use **Google reCAPTCHA** to protect certain forms and sign-in against automated abuse, which sends device and interaction signals (including your IP address) to Google. We make a reasonable, risk-based effort to limit the personal data we process. In particular, we do **not** use third-party advertising networks or cross-site tracking technologies, and we do not use cookies for advertising – we removed Google Analytics for this reason. The third parties named in this Policy provide security, anti-bot, performance, monitoring, payment, or communication functions, not advertising. #### 2.2 Forms you submit When you request a quote, contact us, request support, or download the Software, we collect the information you provide, which may include your first and last name, company name, email address, country, job title, and (optionally) telephone number, together with the content of your request. **Public issue tracker.** If you interact with us through our public issue tracker on GitHub, that platform is operated by GitHub under its own privacy policy. Anything you post there is publicly visible, so please do not include sensitive personal data or license keys. #### 2.3 Account holders and the Customer Portal If you have an account, we process the data needed to manage your relationship with us, including: company name and billing/shipping address, tax/VAT number, account contacts (name, email, role) that you or your administrators create, your subscriptions and entitlements (including license keys, products, quantities and dates), and your invoices and order history. Account administrators can create, edit, and archive contacts and can manage source-code (Git) access credentials on behalf of the account; we keep an audit record of those administrative actions, which includes the email address of the person performing them. **Signing in.** The portal uses one-time "magic links" sent to your registered email address; we do not use passwords. To operate this securely we briefly store a hashed token and a short verification code, and we log sign-in attempts (success, expiry, reuse, invalid code) to prevent abuse and apply rate limits. The PostSharp and Metalama software collect data in the ways described below. The mechanism is the same in both products except where noted. #### 2.4 License audit Both PostSharp and Metalama include a license-audit mechanism that works in much the same way, except for the use of our analytics provider, noted below. Under the terms of licenses subject to it (LSSA §5.7), the software sends a **daily usage report** that lets us count the number of distinct daily users — the metric on which these licenses are priced. Each report contains the license key, the license type, the product edition, build date and version, the date of use, a one-way hash of your operating-system user name, and a one-way hash of a machine identifier that rotates monthly. These reports are sent to our own servers (bits.postsharp.net), where the full license key is used to verify license compliance and count distinct users. For Metalama, a copy containing only the pseudonymous identifiers — never the license key, in any form — is additionally sent, through an independent channel, to our analytics provider Matomo Cloud (InnoCraft) for aggregate statistics. PostSharp sends its reports only to our own servers, never to Matomo. License audit does not transmit a usage description or your source code. We do not collect your IP address as a data field; as with any network request, your IP is visible at the point of contact, where it is anonymized, and we do not store it. #### 2.5 Telemetry Telemetry (also called the Customer Experience Improvement Program) collects technical and diagnostic data to help us improve the products: computer and operating-system configuration, feature usage, performance and reliability data, project metrics, and redacted error reports. Reports carry randomly generated, pseudonymous identifiers that rotate monthly, and we use a **separate salt for each destination**, so that telemetry data cannot be linked to license-audit data. We also use different salts between our own servers and Matomo. In **PostSharp**, telemetry is **off by default**: you are asked, it is available on Windows only, and it runs only if you turn it on. In **Metalama**, telemetry is **on by default** and can be [turned off](https://doc.metalama.net/conceptual/configuration/telemetry) at device level, user level, or repository level. We do not collect your source or compiled code. Telemetry is sent to our own servers over an encrypted channel. For Metalama, it is additionally sent, through an independent channel, to Matomo Cloud (InnoCraft); PostSharp telemetry is not sent to Matomo. #### 2.6 Exception and performance reports Unless you opt in for automatic sending, you can review error reports before they are uploaded to our servers. Error reports are minimized before upload: we omit the exception message and any exception data, and we remove identifying data such as file paths, type names and namespaces, and recognizable secrets. We do not store your IP address with the reports sent to our servers. On Matomo, IP addresses are truncated. #### 2.7 News feed PostSharp and Metalama periodically fetch a product news feed from our servers to display announcements. We do not collect any data through this feature; as with any web request, your IP address is visible to our server at the point of contact, but we do not use it to build a profile and we do not store it. The news feed can be turned off using a user setting. If telemetry is disabled at machine- or repository-level, the news feed is also disabled. #### 2.8 Purchases Online purchases are processed by our payment provider, **PayPro Global** (located in Canada), which acts as the merchant of record. When you place an order, we transfer the order and customer data you provided (such as name, company, billing address, and the products purchased) to PayPro to complete the purchase; PayPro collects your payment details directly and returns the completed order, including billing data, to us so we can fulfil it and issue invoices. **We do not receive or store your payment card details** – card payments are handled entirely by PayPro under its own privacy terms. Purchases can also be made by bank transfer. Our PDF quotes and invoices are generated from HTML by **EuroPDF**, a document-rendering service operated by DIE ANTWORT and hosted in Germany. The content we send it (which includes your name, company, and billing details) is deleted immediately after the PDF is produced. #### 2.9 Communications and marketing We use your email address to send transactional and service messages relating to your account, orders, and the Software. If you subscribe to our newsletter, we process your email address (and any details you provide) for that purpose; you can unsubscribe at any time. You can subscribe to the newsletter from the Website. Our newsletter is operated using a third-party email service, Kit (operated by ConvertKit, LLC). ### 3. Legal bases We rely on the following legal bases under the GDPR: **performance of a contract** (managing your account, subscriptions, orders, invoices, support, the portal, and Automatic License Auditing, which is a condition of the licenses subject to it under the LSSA); **legitimate interests** (securing and improving the Website and Software, Website and application analytics, and fraud and abuse prevention – balanced against your interests); **consent** (the newsletter, and PostSharp telemetry, which you can withdraw at any time); and **compliance with legal obligations** (for example, retaining invoices under tax law). ### 4. Cookies and similar technologies We use first-party cookies for analytics and for security / anti-bot protection. We do **not** use third-party advertising or cross-site tracking cookies. Security and anti-bot components from Cloudflare, and Google reCAPTCHA, may set or read their own cookies and receive your IP address as part of providing those functions. - **analytics_visitor_id** (Matomo, first-party) – a random visitor identifier; rotated weekly (expires the following Monday), or session-only if "Do Not Track" is enabled. - **human_verified** (first-party) – records that an anti-bot check was passed so it need not be repeated; up to 365 days. - **cf_clearance** (Cloudflare) – set when a security / anti-bot challenge is passed, so it is not repeated; short-lived. You can block or delete cookies in your browser; some Website features may then work less well. We do not rely on the browser "Do Not Track" signal as it is not consistently supported. ### 5. Children The Website and Software are intended for professional and business use and are not directed to children. We do not knowingly collect personal data from children. ### 6. Recipients and processors We do not sell your personal data. We share it only as needed to operate our business, with the categories of recipients below, and where required by law (for example, to comply with a court order or to protect our rights). We may disclose your name, company affiliation, email address, and telephone number to our authorized reseller for your region. In a merger, acquisition, or sale of the business, our successor will be bound by this Privacy Policy. - **Microsoft** – Azure (hosting and infrastructure), Microsoft 365 / Office 365 (business email and document storage and collaboration, which may contain your correspondence and details), and Dynamics 365 (CRM – customer and order records). Hosted in the EU. - **InnoCraft / Matomo Cloud** – provides our Website analytics (via analytics.postsharp.net) and receives pseudonymous license-audit and telemetry beacons from the Metalama software (postsharp.matomo.cloud), hosted in the EU. These beacons do not include the license key, in any form. Metalama telemetry is also sent, through an independent channel, to our own bits.postsharp.net. PostSharp license-audit and telemetry go only to our own servers (licensing.postsharp.net, bits.postsharp.net), not to Matomo. - **Cloudflare** – reverse proxy / CDN in front of the Website: all traffic passes through it, so it processes technical request data (IP address, headers) for essentially all visitors; also security / anti-bot protection (Bot Management, Turnstile). United States; see Section 7. - **Google** – reCAPTCHA (bot protection on forms and sign-in). United States; see Section 7. - **PayPro Global** – online payment processing / merchant of record. Canada; see Section 7. - **Amazon Web Services** – transactional email delivery (SES). Hosted in the EU. - **EuroPDF / DIE ANTWORT** – renders our PDF quotes and invoices from HTML; the document content is deleted immediately after rendering. Hosted in Germany (EU). - **Kit (ConvertKit)** – newsletter delivery, if you subscribe. United States; see Section 7. - **Our authorized resellers** – for customers who purchase through a reseller, and for regional reseller referrals. ### 7. International transfers Where we transfer personal data outside the European Economic Area, we rely on an appropriate safeguard under Chapter V of the GDPR – Standard Contractual Clauses, the EU–US Data Privacy Framework, or an adequacy decision, as applicable to each recipient. Recipients that process data **outside the EEA**: - **Cloudflare** (United States) – reverse proxy / CDN, security / anti-bot; under Cloudflare's standard data protection terms, relying on Standard Contractual Clauses / the EU–US Data Privacy Framework. - **Google** (United States) – reCAPTCHA; under Google's data protection terms, relying on Standard Contractual Clauses / the EU–US Data Privacy Framework. - **Kit / ConvertKit** (United States) – newsletter delivery; under Kit's standard data protection terms, relying on Standard Contractual Clauses. - **PayPro Global** (Canada) – payment merchant of record; Canada benefits from an EU adequacy decision, and the transfer is also necessary to perform the purchase you requested. Recipients and infrastructure **within the EU** (no international transfer): - **Microsoft** – Azure, Microsoft 365, and Dynamics 365 (EU). - **Amazon Web Services** – SES transactional email (EU). - **EuroPDF / DIE ANTWORT** – PDF rendering of quotes and invoices (Germany, EU). - **Matomo Cloud / InnoCraft** – Website analytics and Metalama beacons (EU; InnoCraft, as a New Zealand entity, also benefits from an EU adequacy decision). - **Our own servers** – licensing.postsharp.net and bits.postsharp.net (EU). ### 8. Retention We keep personal data only for as long as necessary for the purposes described in this Privacy Policy and to meet our legal obligations: - Invoices and accounting records – 10 years, as required by Czech accounting and tax law. - Customer identity, address, and contracts – 10 years after the last invoice, as part of the accounting record. - Orders, license keys, and contact records – 3 years after the last order. - Sign-in logs (kept for security and abuse detection) – 3 months. Magic-link tokens and verification codes expire within minutes. - Telemetry and license-audit data – 24 months for raw data; aggregated, anonymized data may be kept indefinitely. - Website analytics (Matomo) – 730 days for raw data; aggregated data may be kept indefinitely. When personal data is no longer needed, we delete or anonymize it. ### 9. Your rights Subject to applicable law, you have the right to access, correct, update, or delete your personal data, to restrict or object to certain processing, to data portability, and to withdraw consent where processing is based on consent. You can manage much of your account data by signing in to the customer portal, or contact privacy@postsharp.net and we will respond within the time required by law. You also have the right to lodge a complaint with a supervisory authority; in the Czech Republic this is the Office for Personal Data Protection (Úřad pro ochranu osobních údajů). ### 10. Security We take reasonable technical and organizational measures to protect personal data against unauthorized access, use, or disclosure, including access controls and encryption in transit. Access to systems holding personal data is limited to authorized personnel and contractors. No method of transmission or storage is completely secure, and we cannot guarantee absolute security. For more detail on how we handle security issues in the Software, see our [Security policy](https://postsharp.net/compliance/security). ### 11. Changes to this Privacy Policy We may update this Privacy Policy from time to time. The current version, with its effective date, is always available on this page. Minor or clarifying changes take effect when posted. For material changes – such as new purposes, new categories of recipients, or a change of legal basis – we will give reasonable notice by appropriate means, which may include a notice on the Website or in the customer portal; where a change requires your consent, we will obtain it before that change applies to you. This Privacy Policy is governed by the laws of the Czech Republic. ### 12. Related documentation - [Security policy](https://postsharp.net/compliance/security) – how we receive, prioritize, and disclose security issues in the Software. - [Metalama telemetry configuration](https://doc.metalama.net/conceptual/configuration/telemetry) – what Metalama telemetry collects and how to turn it off, including per-repository opt-out. --- # Security policy > Our trust boundary, how we assess risk, and how to report a vulnerability. Canonical: https://postsharp.net/compliance/security.html This document outlines the risks associated with using PostSharp or Metalama and describes our policies for addressing security vulnerabilities. ### Trust boundary Like the .NET SDK itself, PostSharp and Metalama execute your source code and your NuGet dependencies at build time, and they assume that both are trusted. Vetting what enters your build is outside the scope of these products. This document describes the security of the products themselves, not of the code they compile. ### Network access PostSharp and Metalama are compilers and/or IDE plug-ins that run locally on development workstations or build agents. They open no network port and cannot be reached over the network from another machine. They do use local inter-process channels which are reachable by other processes on the same machine. The products themselves make outbound connections only to our own servers (under postsharp.net and metalama.net) for license registration and auditing, for telemetry, and to fetch the product news feed. As part of the build, the .NET SDK also restores our NuGet packages from your configured package sources, which makes its own outbound connections like any other dependency. Please refer to our [privacy policy](https://postsharp.net/compliance/privacy) for details about the data we collect. ### Sensitive data Your source code and compiled code never leave your workstation or build agent; we do not collect or process them. We do process a limited amount of personal data: - **License metering and auditing** verify license compliance. This feature sends the full license key, a one-way hash of your operating-system user name, a one-way hash of a machine identifier (rotated monthly), and the product edition, version, and license type. Metalama's license auditing sends the full license key, the license type, the product version and date of use, and pseudonymous identifiers of the device and user. - **Telemetry**, when enabled, collects technical configuration, feature usage, project metrics, and redacted error reports, tagged with a randomly generated pseudonymous machine identifier, different from the one used for license auditing (also rotated monthly). Both PostSharp and Metalama send this data to our servers. Additionally, Metalama's telemetry implementation sends pseudonymized data to Matomo Cloud (InnoCraft). Your license key or its hash is never sent to Matomo. We do not store your IP address with this data. ### Risk assessment and mitigation Because our products open no network port, vulnerabilities in Metalama, PostSharp, or their dependencies are very unlikely to be exploitable remotely. The realistic attack surface is local: an attacker who already runs code as a different user on the same machine (which mainly matters on shared or multi-user build agents), or one positioned between your machine and our servers (a man-in-the-middle). Beyond your source code, our products also read a few other inputs: the product news feed, content feeds, and the design-time channel between the analyzer and the IDE. We have hardened the parsing of these inputs to prevent a compromise of our servers, or a malicious local process, from turning them into code execution on your machine. As with any such hardening, this reduces the risk but cannot eliminate it entirely. The most critical risk associated with our products is a **supply chain attack**, where malicious code could be injected into our products not to harm our own company, but our customers. Such an attack could target either our organization or one of our dependencies. To mitigate this risk, we implement the following measures: - **Dependency audits**: We regularly audit our dependencies using automated tools such as NuGet Package Vulnerability Scanner and GitHub Dependabot to identify known vulnerabilities. Any flagged issues are promptly assessed and addressed if necessary. Most vulnerabilities in dependencies require network exposure or untrusted input to be exploited. Our products expose no network service, and the limited external data they do read (the news and content feeds, and the IDE channel) is parsed defensively. Consequently, very few vulnerabilities in dependencies require action on our part. - **AI-based security audits**: We periodically use an AI model to audit the entire codebase for security and privacy weaknesses. Every finding and proposed fix is reviewed, corrected, and tested by a human before release. - **Hardware security keys**: All accounts sign in with hardware security keys (FIDO2) on every device or online service; passwords or software alone do not grant access. - **Privileged access workstations**: We treat every developer machine as potentially compromised, so access to our CI/CD pipeline and production environments comes only from dedicated, hardened workstations. Each sign-in requires a physical touch on a security key, which no software on a compromised machine can supply on its own. - **Binary signing**: We sign our binaries using an Authenticode key stored on a secure and isolated device, separate from build agents and development machines. Before signing, we scan the binaries for known malware. - **Source availability**: Metalama is open source. PostSharp is available as source code to enterprise customers under a source-available license, going back to version 1.0. In both cases, customers can audit the source code and build the product using their own infrastructure. ### Supported versions How we apply a security fix depends on the [servicing phase](https://www.postsharp.net/support/policies/versions) of your version: - **Current** and **Long-Term Support (LTS)** versions: we fix all reported vulnerabilities. - **Extended Support** versions: we proactively fix only serious vulnerabilities, those significant enough to warrant a published security advisory (typically High severity or above), and address lower-severity issues on request. Versions that have reached the end of their support window do not receive security fixes. Please ensure that you are using a supported version. The list of supported versions is maintained in the [Version and Support Policy](https://www.postsharp.net/support/policies/versions). ### Reporting a vulnerability Report vulnerabilities privately through GitHub Security Advisories, using the **Report a vulnerability** button on the Security tab of the relevant repository: - [Metalama](https://github.com/metalama/Metalama/security/advisories) - [PostSharp](https://github.com/postsharp/PostSharp.Public/security/advisories) - [Visual Studio Tools for PostSharp and Metalama](https://github.com/metalama/Metalama.Vsx.Public/security/advisories) This keeps the report private until a fix is available. The same contacts are published in machine-readable form at [/.well-known/security.txt](https://postsharp.net/.well-known/security.txt), per [RFC 9116](https://www.rfc-editor.org/rfc/rfc9116). We aim to acknowledge vulnerability reports within 24 hours and provide a resolution or mitigation plan within three days, depending on the severity of the issue. If you do not receive a prompt response, or in case of emergency, please [contact us by phone](https://postsharp.net/contact). --- # Terms of Use > Your obligations when using the postsharp.net website, and the terms under which we provide the software, content and services available on it. Canonical: https://postsharp.net/compliance/terms-of-use.html The following Terms of Use outline your obligations when using the website [https://postsharp.net](https://postsharp.net). We also recommend reviewing our [Privacy Policy](https://postsharp.net/compliance/privacy), which details our mutual rights and obligations, as well as our practices for handling any personal information that you may provide to us. ## 1. Acceptance of Terms of Use The web pages available under the domain **postsharp.net** and all linked pages (the “Website”), are owned and operated by SharpCrafters s.r.o., with its registered office at Praha 5, nám. 14. října 1307/2, Postal Code 150 00, the Czech Republic, ID 28953690, registered in the Commercial Register maintained by the Municipal Court in Prague, Section C, File 155506 (the "SharpCrafters") and is accessed by you under the Terms of Use described below (the "Terms of Use"). By (i) using this Website in any way, including using, transmitting, downloading or uploading the Software, Content or Services (as these terms are defined below) made available or enabled via the Website by SharpCrafters, or (ii) using the web forum contained on the Website which includes responses to generic questions from SharpCrafters's engineers and from the community, or (iii) merely browsing the Website, you agree to become bound by these Terms of Use. “Content” means any materials displayed, transmitted, or performed on the Website or through the Software (including, but not limited to text, user comments, messages, information, data, graphics, news articles, photographs, images, illustrations, software). “Software” means any software made and/or published by SharpCrafters, including, but not limited to, the program known as PostSharp. The general specification of the then-current version of the software is contained on the Website. “Services” means the support services provided by the SharpCrafters based on a separate agreement. ## 2. Use of the Software, Content, and Services Any Software that is made available via the Website is the property of SharpCrafters (or third parties, our partners). Use of such Software is governed by the terms of the license agreement relating to the Software. You shall not use, download, or install any Software unless you agree to the terms of such a license agreement. Unless the license agreement expressly allows otherwise, any copying or redistribution of the Software is prohibited, including any copying or redistribution of the Software to any other server or location. Certain Content and/or Services may also be subject to a subscription or other agreement. If there is any conflict between the Terms and such respective agreement, the respective agreement shall take precedence in relation to that Software, Content, or Services. SharpCrafters make some Software, Content, or Services available only if you have paid a fee and/or have provided SharpCrafters with certain registration information (collectively, the "Account Information"). You agree that your Account Information will always be complete, accurate, and up-to-date. It is your responsibility to keep your Account Information confidential at all times and you are solely responsible for all activity that occurs to your Account Information when you are logged in to your account and also for all activities done on the Website (e.g. forum posts) using your account. If you become aware of any unauthorized use of your account or Account Information, or any other breach of security, you agree to notify SharpCrafters immediately. You may not use another person's Account Information. SharpCrafters may require that you change your Account Information or certain parts of your Account Information provided such information is in violation of these Terms of Use. Failure to do so shall constitute a breach of the Terms of Use, which may result in immediate termination of your account. SharpCrafters reserves the right to refuse registration at its discretion. You hereby acknowledge that for correct use of the Website, it may sometimes be necessary to store some data on your system, otherwise, the Website may not function correctly. You are solely responsible for applying the appropriate settings of your browser and system. SharpCrafters may change, suspend, or discontinue the availability of the Software, Content, or Services for any reason, at any time. SharpCrafters may also impose limits on certain Content or Services or restrict your access to parts or all of the Website without notice or liability. You agree to use the web only for purposes that are permitted by these Terms of Use and any applicable law, regulation, or generally accepted practices or guidelines in any applicable jurisdiction. ## 3. User Conduct As a condition of use, you are obliged not to use, or encourage or permit others to use, the Website for any purpose that is unlawful or prohibited by these Terms of Use, or any other purpose not reasonably intended by SharpCrafters, including, without limitation: 1. Abuse, harass, threaten, impersonate or intimidate other Website users; 2. Post or transmit, or cause to be posted or transmitted, any content that is infringing, libelous, defamatory, obscene, pornographic, abusive, offensive, profane, or otherwise violates any law or right of any third party; 3. Use for any illegal or unauthorized purpose. If you are an international user, you agree to comply with all local laws regarding online conduct and acceptable content; 4. Post or transmit, or cause to be posted or transmitted, any communication or solicitation designed or intended to obtain password, account, or private information from any Website user; 5. Create or submit unwanted email ("Spam") to any other Website users or any URL; 6. Violate any laws in your jurisdiction (including but not limited to copyright laws); 7. Submit stories or comments linking to affiliate programs, multi-level marketing schemes, sites/blogs repurposing existing stories (source hops), or off-topic content; 8. Advertise to, or solicit, any user to buy or sell any products or services; 9. Attempt to impersonate another user or person; 10. Engage in any chain letters, contests, junk email, pyramid schemes, spamming, surveys or other duplicative or unsolicited messages (commercial or otherwise); 11. Use any SharpCrafters domain name as a pseudonymous return email address; 12. Make available any material that contains software viruses or any other computer code, files or programs designed to interrupt, destroy or limit the functionality of any computer software, hardware or telecommunications equipment; 13. Access or use the Website in any manner that could damage, disable, overburden or impair any SharpCrafters server or the networks connected to any SharpCrafters server; 14. Use any data mining, robots, or similar data gathering and extraction methods in connection with the Website. SharpCrafters may remove any Content at any time for any reason (including, but not limited to, upon receipt of claims or allegations from third parties or authorities relating to such content), or for no reason at all. You are solely responsible for your interactions with other users of the Website. SharpCrafters reserves the right, but has no obligation, to monitor disputes between you and other users. ## 4. Intellectual Property Rights The trademarks, logos, and service marks displayed on the Website (the "Marks") are the property of SharpCrafters or third parties and are protected by copyright, trademark, trade dress, patent, trade secret, unfair competition, and other intellectual and proprietary rights (the "Intellectual Property Rights"). You are not permitted to use the Marks without the prior consent of SharpCrafters or the third party that may own the Marks, except for intelligence purposes and recommendation that is not likely to cause confusion. In particular, at no time are you entitled to alter or modify the Marks in any manner. SharpCrafters's Marks may be used only in connection with the Software. By submitting content to the Website (such as: by asking or answering on the forum, submitting a suggestion, or posting or commenting on an issue), you automatically grant to SharpCrafters an unlimited and royalty-free right to store, reproduce, alter, cite, and utilize such content for all purposes permitted by law, including the right to grant the sub-license in the same extent (and any other right we may need). ## 5. Links The Website may include, or third parties may provide, links to other sites or resources. Because SharpCrafters has no control over such sites and resources, you acknowledge and agree that SharpCrafters is not responsible for the availability of such external sites or resources, and does not endorse and is not responsible or liable for any content, advertising, products or other materials on or available from such sites or resources. You further acknowledge and agree that SharpCrafters shall not be responsible or liable, directly or indirectly, for any damage or loss caused or alleged to be caused by or in connection with the use of or reliance on any such content, goods, or services available on or through any such site or resource. ## 6. International Access This Website can be accessed from countries around the world and may contain references to services and content that are not available in your country. These references do not imply that SharpCrafters intends to announce such services or content in your country. This Website is controlled, operated, and administered by SharpCrafters from its offices in the Czech Republic. SharpCrafters make no representation that the Website, services, or content are appropriate or available for use at other locations outside the Czech Republic, and access to the Website from jurisdictions where the Website, services, or content are illegal is prohibited. SharpCrafters reserve the right to block access to the Website, services, or content by certain international users. If you access the Website from a location outside the Czech Republic, you are responsible for compliance with all local laws. ## 7. Indemnity You will indemnify and hold harmless SharpCrafters, its parents, subsidiaries, affiliates, customers, vendors, officers, and employees from any liability, damage or cost (including reasonable attorneys. fees and cost) from any claim or demand made by you or any third party due to or arising out of your access to the Website, use of the content or services, violation of the Terms of Use by you, or the infringement by you, or any third party using your Account Information, of any intellectual property or other right of any person or entity. ## 8. Modifications of the Terms of Use SharpCrafters reserves the right, at its sole discretion, to modify or replace the Terms of Use at any time. If the alterations constitute a material change to the Terms of Use, SharpCrafters may notify you by posting an announcement on the Website. What constitutes a "material change" will be determined at SharpCrafters's sole discretion, in good faith and using common sense and reasonable judgment. Regardless of the above, you shall be solely responsible for reviewing and becoming familiar with any such modifications. Use of the Website, Content, or Services by you following such a modification of Terms of Use constitutes your acceptance of the terms and conditions of the Terms of Use as modified. ## 9. Termination SharpCrafters may terminate or suspend your account immediately, without prior notice or liability, if you breach any of the terms or conditions of the Terms of Use. All provisions of the Terms of Use which by their nature should survive termination shall survive termination, including, without limitation, ownership provisions, warranty disclaimers, indemnity, and limitations of liability. ## 10. Miscellaneous No agency, partnership, joint venture, or employment is created as a result of the Terms of Use and you do not have any authority of any kind to bind SharpCrafters in any respect whatsoever. The failure of either party to exercise in any respect any right provided for herein shall not be deemed a waiver of any further rights hereunder. SharpCrafters shall not be liable for any failure to perform its obligations hereunder where such failure results from any cause beyond SharpCrafters's reasonable control, including, without limitation, mechanical, electronic or communications failure. If any provision of the Terms of Use is found to be unenforceable or invalid, that provision shall be limited or eliminated to the minimum extent necessary so that the Terms of Use shall otherwise remain in full force and effect and enforceable. The Terms of Use is not assignable, transferable or sublicensable by you except with SharpCrafters's prior written consent. SharpCrafters may transfer, assign or delegate the Terms of Use and its rights and obligations without consent. The Terms of Use shall be governed by and construed in accordance with the laws of the Czech Republic. --- # Get Support > How to get help with PostSharp and Metalama: commercial support by email, public issue trackers on GitHub, release histories, and documentation. Canonical: https://postsharp.net/support Whether you hold a commercial subscription or use the open-source editions, here is how to reach us and find what you need. ## Commercial support If you have a commercial subscription, email us at **[support@postsharp.net](mailto:support@postsharp.net)**. Response times depend on your product and edition; see our [service levels](https://postsharp.net/support/policies/sla). ## Resources Documentation, public issue trackers, release notes, and security advisories for each product. Our issue trackers are on GitHub, and you do not need a subscription to open an issue or report a vulnerability. The documentation links land on the getting-started guide; the rest of the reference is a click away. | Product | Documentation | Issues | Releases | Security | |---------|---------------|--------|----------|----------| | **Metalama** | [Documentation](https://doc.metalama.net/conceptual/getting-started) | [Report an issue](https://github.com/metalama/Metalama/issues) | [Release history](https://github.com/metalama/Metalama/releases) | [Advisories](https://github.com/metalama/Metalama/security/advisories) | | **PostSharp** | [Documentation](https://doc.postsharp.net/deploying-postsharp) | [Report an issue](https://github.com/postsharp/PostSharp.Public/issues) | [Release history](https://github.com/postsharp/PostSharp.Public/releases) | [Advisories](https://github.com/postsharp/PostSharp.Public/security/advisories) | | **Visual Studio Tooling** | — | [Report an issue](https://github.com/metalama/Metalama.Vsx.Public/issues) | [Release history](https://github.com/metalama/Metalama.Vsx.Public/releases) | [Advisories](https://github.com/metalama/Metalama.Vsx.Public/security/advisories) | Release notes ship with every build. You can also follow them on our [briefs feed](https://postsharp.net/briefs). ## Discord You can also find the team on our [Discord server](https://postsharp.net/discord) to ask questions and reach us directly. Private discussions are reserved for enterprise customers. ## Policies Versioning and servicing phases, supported platforms, service levels, and our security and privacy policies are documented under [support policies](https://postsharp.net/support/policies). --- # Support Policies > The principles and guidelines that govern how we maintain our products and deliver support services. Canonical: https://postsharp.net/support/policies/ ## Overview This section outlines the principles and guidelines that govern how we maintain our products and deliver support services. > **Note** > > These policies are intended as guidelines and are implemented on a "commercially reasonable effort" basis. > We reserve the right to modify these policies, or to make exceptions, at any time without prior notice. The following pages provide detailed information about our support policies: | Page | Description | |-------------------------------|-----------------------------------------------------------------------------| | [Scope](https://postsharp.net/support/policies/scope) | Defines the scope and limitations of our support services. | | [Supported Platforms](https://postsharp.net/support/policies/platforms) | Explains the criteria for determining which versions are supported. | | [Supported Components](https://postsharp.net/support/policies/components) | Describes how support and maintenance services apply to various packages and identifies projects for which we assume no responsibility. | | [Version and Support Policy](https://postsharp.net/support/policies/versions) | Which versions are supported today, servicing phases, and long-term support. | | [Service Levels](https://postsharp.net/support/policies/sla) | Details the benefits included in different service levels and their alignment with purchasable products. | | [Bug Prioritization](https://postsharp.net/support/policies/prioritization) | Explains how we assign bug severities and when we resolve them. | --- # Component Support Status > Describes how support and maintenance services apply to our packages, and which repositories we take no responsibility for. Canonical: https://postsharp.net/support/policies/components.html ## Support status of different components Our products consist of several components, maintained in different source repositories, for which we assume varying levels of responsibility and, therefore, provide different levels of support. ## Core components We assume maintenance responsibility and provide full support to enterprise customers for the following repositories and packages: | Repository | Namespaces | |----|----| | [Metalama](https://github.com/metalama/Metalama) and [Metalama.Premium](https://github.com/metalama/Metalama.Premium) 💎 | `Metalama.Framework`, `Metalama.Testing`, `Metalama.Extensions`, `Metalama.Patterns`, `Metalama.LinqPad`, `Flashtrace` | | [Metalama.Compiler](https://github.com/metalama/Metalama.Compiler)[^1] | `Metalama.Compiler` | | PostSharp 🔑 | `PostSharp` (exclusive of any sub-namespace not mentioned here), `PostSharp.Pattern`, `PostSharp.Compiler`, `PostSharp.Sdk` | | Visual Studio Tools for Metalama and PostSharp 🔑 | N/A | In this table, 💎 means private repositories with possible customer access, and 🔑 means private repositories with no customer access. ## Documentation We assume responsibility for the accuracy of the documentation and samples. However, sample code is not intended for use in production, as many details are intentionally omitted for the sake of clarity. | Repository | Namespaces | |----|----| | [Metalama.Samples](https://github.com/metalama/Metalama.Samples) | `Metalama.Samples` | | [Metalama.Documentation](https://github.com/metalama/Metalama.Documentation) | | | PostSharp Documentation | | ## Unsupported repositories We do not assume responsibility nor provide support for the following repositories, or for any other project or repository not listed elsewhere on this page: | Repository | Namespaces | |----|----| | [Metalama.Community](https://github.com/postsharp/Metalama.Community) | `Metalama.Community` | | [PostSharp.Engineering](https://github.com/postsharp/PostSharp.Engineering) | `PostSharp.Engineering` | --- [^1]: We do not assume responsibility for bugs that can be reproduced using the standard C# compiler (Roslyn), from which `Metalama.Compiler` is forked. --- # Supported Platforms and Frameworks > Explains the criteria for determining which platform and framework versions are supported. Canonical: https://postsharp.net/support/policies/platforms.html ## Supported Platforms and Frameworks In this section, we use the term *platform* to refer to: - .NET Framework, .NET Core, and .NET SDK, - Visual Studio, - Visual Studio Code, - Rider. This section covers only policies. For a list of supported platforms, please see the Requirements and Compatibility page in the [PostSharp](https://doc.postsharp.net/requirements) or [Metalama](https://doc.metalama.net/conceptual/requirements) documentation. ### What we mean by *supported platform* By *supported* (or *officially supported*) platform, we mean a platform for which we have spent or will spend reasonable efforts to: - test our product with the platform before or shortly after they are released, - fix issues that stem from the use of the platform, and - provide support services. It frequently happens, in practice, that PostSharp or Metalama will work with a non-supported platform version. However, if an issue arises, we will first ask you to upgrade or downgrade to a supported platform version. ### Support of platform versions by their manufacturer Only platform versions that are currently supported by their manufacturer are supported by our products. If the manufacturer differentiates between Mainstream Support and Extended Support, we will only supports platforms under Mainstream Support. Check the support policies of the platform you rely on. The support lifecycle is probably shorter than you would expect! Check the following documents: - [Visual Studio Servicing Policies](https://docs.microsoft.com/en-us/visualstudio/productinfo/vs-servicing-vs) - [Windows Lifecycle FAQ](https://learn.microsoft.com/en-us/lifecycle/faq/windows) - [.NET Framework Lifecycle FAQ](https://learn.microsoft.com/en-GB/lifecycle/faq/dotnet-framework) - [.NET Core Lifecycle FAQ](https://learn.microsoft.com/en-US/lifecycle/faq/dotnet-core) - [.NET Support and Distribution Policies](https://github.com/dotnet/core/blob/main/support.md) > **Note** > > JetBrains does not publish any lifecycle policy for their products. Therefore, we only support their latest build. ### Support of platform versions by our products #### Preview builds Pre-release platform builds are never officially supported, even when they get a production license from their publisher. #### Patch builds Only the latest Patch Build of a supported platform version is officially supported. Operating systems must be updated with all Important Updates to qualify for support. Before providing support services to you (such as diagnosing or fixing a bug), we ask you to update PostSharp and all platforms to the latest Patch Build. #### Platform retirement policy When we release a new version, we remove support for platforms, frameworks, compilers, and other tools that are no longer in, or will soon fall out of, mainstream support from Microsoft. We may also remove support for a version of a platform if we consider that too many versions of the platform would otherwise be supported, taking usage statistics into account. Customers of these retired platforms may still use a supported version of PostSharp for some time but may not upgrade to a new version. We reserve the right not to implement support for all new platforms or platform versions introduced by Microsoft or others. --- # Bug Prioritization > Explains how we assign bug severities and when we resolve them. Canonical: https://postsharp.net/support/policies/prioritization.html ## Bug Prioritization We assign priorities to bug fixes based on the following rules: | Severity | Description | Scheduling | |-----------|-----------------------------------------------------------------------------|---------------------| | Cosmetic | The product works as intended, but its UI or output contains typos or formatting issues. | Backlog | | Annoying | A specific feature does not function as intended, but the use case has never worked before. | Backlog | | High | The entire product is unusable, or there is a regression in an important feature, but downgrading is possible. | Next business day | | Critical | A large number of developers are blocked, and there is no workaround, not even rolling back to a previous build. | Immediate attention | _Backlog_ typically means that fixing these bugs will be considered when the team is not overloaded, typically when we are not focused on a feature-oriented release. > **Note** > > Enterprise customers can escalate issues from Annoying to High, after discussion with the core team. To escalate a GitHub issue, please [open a private support ticket](https://postsharp.net/support). --- # Scope & Limitations of Support Services > Defines the scope and limitations of our support services. Canonical: https://postsharp.net/support/policies/scope.html ## Scope We will: - Answer your questions related to the use of [supported components](https://postsharp.net/support/policies/components) of PostSharp and Metalama. - Help you find workarounds for issues you might encounter. - Make economically reasonable efforts to fix blocking issues. Unless otherwise specified in the [Entitlement Page](https://store.postsharp.net/account), we do not provide support for: - Issues that can be resolved by: - Using a newer build of the same version. - Upgrading to a newer version, if fixing the issue requires significant changes to the older version (at our discretion). - Using or modifying the software in ways not intended by the documentation or the license agreement. - Running the software on an [unsupported platform](https://postsharp.net/support/policies/platforms). - Problems caused by third-party products or technologies, except for [supported platforms](https://postsharp.net/support/policies/platforms) and tools, unless the issue is due to a defect in the supported platform. - Using low-level APIs of the software, such as libraries starting with: - `PostSharp.Compiler` - `Metalama.Compiler` - `Metalama.Framework.Sdk` - `Microsoft.CodeAnalysis` These APIs often require advanced knowledge of Microsoft Intermediate Language (MSIL) or the .NET Compiler SDK (Roslyn). - Using binaries other than those provided by us. - Excessive support requests, defined as more than one hour of support time for every $500 spent in the last 12 months, excluding time spent fixing bugs identified by us. - Submitting support requests without involving senior or qualified software developers. Since we do not have a level-1 support team, all requests are handled directly by senior developers, and we expect the same level of expertise from the requesters. - Repeatedly submitting support requests without first reviewing the FAQ, documentation, or samples on our support site. - Attempting to build the software from source code unless you have purchased a subscription that includes source code access. > **Note** > > You can use [advisory hours](https://postsharp.net/support/policies/sla), if eligible, for questions that fall out of the support scope. --- # Service Levels > Details the benefits included in each service level and how they map to purchasable products. Canonical: https://postsharp.net/support/policies/sla.html ## Service Levels ### Service map The following table summarizes the benefits and maps them to the respective product tiers: | Benefit | Community | Basic | Professional[^1] | E1 | E2 | E3 | E4 | |--------------------------------------|-----------|-------|-------------------|--------|--------|------|----------| | Public issues on GitHub | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | Private support tickets | | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | Extended Support builds | | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | Technical contacts | | 1 | 1 | 1 | 2 | 5 | 10 | | Ticket response time (business days) | | 5 | 3 | 2 | 1 | 1 | 1 | | LTS builds | | | ✔ | ✔ | ✔ | ✔ | ✔ | | Escalate issues | | | | ✔ | ✔ | ✔ | ✔ | | Full source code access | | | | ✔ | ✔ | ✔ | ✔ | | Advisory calls (hours per year) | | | | 2 | 5 | 12 | 24 | | Open-source sponsorship tier | | | | Bronze | Silver | Gold | Platinum | | Security/GDPR/vendor forms | | | | | ✔ | ✔ | ✔ | | Private Slack/Teams channel | | | | | ✔ | ✔ | ✔ | | Video calls | | | | | ✔ | ✔ | ✔ | | Skip-the-line bug fix releases | | | | | ✔ | ✔ | ✔ | | License audit waiver | | | | | ✔ | ✔ | ✔ | | Remote debugging | | | | | | ✔ | ✔ | | EULA amendments | | | | | | ✔ | ✔ | | Regression shield | | | | | | ✔ | ✔ | [^1]: The "Professional" support level applies to subscriptions acquired or renewed before May 2nd, 2025, and is no longer available for new or renewed subscriptions. ### Description #### Extended and long-term support builds We provide continued maintenance for older versions of our products, even after newer versions are released. This ensures you can keep using a version that meets your needs without being forced to upgrade and risk potential breaking changes, while still benefiting from critical bug fixes and security updates. - **Extended support builds** are available for up to six months after the release of the next version. - **Long-term support (LTS) versions** are maintained for two years, provided the underlying .NET SDK or Visual Studio versions remain supported by Microsoft. Longer support terms may be negotiated on a case-by-case basis. For more details, see the [Version and Support Policy](https://postsharp.net/support/policies/versions). > **Note** > > Starting with PostSharp and Metalama 2025.1, access to extended and LTS builds is reserved for commercial and enterprise customers. However, access to these builds for previous versions remains unrestricted. #### Full source code access We will grant access to the source code of premium packages through a mirror of our private Git repository, with rights to edit and build: - `PostSharp`: the whole PostSharp product. - `Metalama.Premium` for premium features. - `Metalama.ExtendedSupport`, a fork of the `Metalama` repository with an extended maintenance term. > **Warning** > > The source code of Visual Studio Tools for PostSharp and Metalama is not yet available. #### Escalate issues At your request, we will increase the [prioritization](https://postsharp.net/support/policies/prioritization) of bug fixes and small feature requests. #### Ticket response time We guarantee that a senior engineer will review support requests within the specified number of business days, depending on your support level. #### Advisory calls We provide direct access to the founder and core developers for consulting or code reviews beyond the usual support scope. The number of entitled hours varies by support level. This benefit is subject to availability, as some periods of the year may be too busy to accommodate long advisory calls. #### Open-source sponsorship At your request, we will publicly recognize you as a sponsor of the Metalama open-source projects on GitHub and our website. #### Private Slack/Teams channel We will provide access to a private Slack or Teams channel for support and direct communication with our core team. #### Video calls We will offer remote assistance through video calls with senior developers and architects. #### Remote debugging We will debug our products directly within your environment, eliminating the need for you to isolate complex issues in a standalone project or transfer confidential intellectual property. #### Skip-the-line bug fix releases We will deliver immediate bug fix releases instead of adhering to the normal release cycle. #### Technical contacts We will allow designated contacts to access support services, including: - Submitting support tickets via our portal or email. - Contacting us via Slack or Teams (if applicable). You can update the contact list twice a year. #### EULA amendments We will collaborate with your legal team to discuss potential amendments to the End User License Agreement, potentially increasing our professional responsibility. #### Security/GDPR/vendor forms We will assist in completing various security, GDPR, and vendor forms. #### Regression shield At your request, we will integrate your test suite into Metalama's CI/CD pipeline to catch regressions before release. #### License Audit Waiver At your request, we will provide license keys that prevent our products from performing audits of your usage. This ensures that no traffic is sent from our products to our servers ("call home"), offering complete privacy and compliance with your internal policies. ### Product Map Here is how our products map to the service levels for two versions of the Service and Support License Agreement (SSLA): - _Previous_ means the version for subscriptions purchased or renewed before May 2nd, 2025 (2023-Q2). - _Current_ means the latest version, typically for purchases after May 2nd, 2025. #### Previous SSLA (2023-Q2) | Product | Professional | E1 | E2 | E3 | E4 | |-----------------------|--------------|------------|------------|-------------|-------------| | PostSharp Framework | ✔ | ≥ 15 users | ≥ 25 users | ≥ 50 users | ≥ 100 users | | PostSharp Ultimate | ✔ | ≥ 10 users | ≥ 15 users | ≥ 30 users | ≥ 75 users | | Metalama Starter | ✔ | | | | | | Metalama Professional | ✔ | ≥ 20 users | ≥ 50 users | ≥ 200 users | | | Metalama Ultimate | ✔ | ≥ 10 users | ≥ 25 users | ≥ 90 users | | #### Current SSLA | Product | Basic | E1 | E2 | E3 | E4 | |------------------------|-------|------------|------------|------------|-------------| | PostSharp Framework | ✔ | ≥ 15 users | ≥ 25 users | ≥ 50 users | ≥ 100 users | | PostSharp Ultimate | ✔ | ≥ 10 users | ≥ 15 users | ≥ 30 users | ≥ 75 users | | Metalama Professional | ✔ | | | | | | Metalama Enterprise E1 | | ✔ | | | | | Metalama Enterprise E2 | | | ✔ | | | | Metalama Enterprise E3 | | | | ✔ | | | Metalama Enterprise E4 | | | | | ✔ | ### Remediation If we fail to meet our SLA commitments, customers with a support level from E1 to E4 will be eligible for a one-month subscription credit for each month in which a commitment was not met. This credit must be explicitly requested by the customer at the time of subscription renewal. --- # Version and Support Policy > Which versions of PostSharp and Metalama are supported today, how long each release is serviced, and our long-term support (LTS) commitment. Canonical: https://postsharp.net/support/policies/versions.html PostSharp and Metalama both support the current versions of .NET, C#, and Visual Studio, with a new long-term support (LTS) release about every two years. The table below shows what is supported today; the rest of this page covers our versioning scheme, servicing phases, and breaking-change policy. ## Supported versions | Version | GA Date | Servicing Phase | End of Extended or Long Term Support | Supported .NET / VS Versions| Details | |-------------------|-------------------|-----------------|--|----------------------------------------------------| | Metalama 2026.1 LTS | June 2026 | Current | 2 years after the next Metalama LTS is released (i.e., presumably, January 2030) | .NET 10.0 LTS, .NET 9.0, .NET 8.0 LTS, .NET Framework 4.7.2, Visual Studio 2022 (17.14), Visual Studio 2026 (18.0) | [Release Notes](https://doc.metalama.net/conceptual/release-notes/release-notes-2026-1) | | Metalama 2026.0 | January 2026 | Extended | December 2026 | .NET 10.0 LTS, .NET 9.0, .NET 8.0 LTS, .NET Framework 4.7.2, Visual Studio 2022 (17.12), Visual Studio 2026 (18.0) | [Release Notes](https://doc.metalama.net/conceptual/release-notes/release-notes-2026-0) | | PostSharp 2026.0 LTS | January 2026 | Current | 2 years after the next PostSharp LTS is released (i.e., presumably, January 2030) | .NET 10.0 LTS, .NET 9.0, .NET 8.0 LTS, .NET 6.0 LTS, .NET Framework 4.7.2, Visual Studio 2022 (17.12), Visual Studio 2026 (18.0) | [Release Notes](https://postsharp.net/blog/postsharp-2026-0-ga) | | PostSharp 2024.0 LTS | January 2024 | LTS | January 2028 | .NET 8.0 LTS, .NET 6.0 LTS, .NET Framework 4.7.2, Visual Studio 2019 (16.11), Visual Studio 2022 (17.4) | [Release Notes](https://postsharp.net/blog/postsharp-2024-0-ga) | > **Note** > > Metalama 2025.0 Extended Support builds, PostSharp 2025.0 Extended Support builds and PostSharp 2024.0 LTS builds will be available to anyone regardless of eligible support level. Versions that are no longer listed above have reached the end of their servicing life and no longer receive fixes. For what each servicing phase means and how long it lasts, see [servicing phases](#servicing-phases) below. ## Versioning and servicing phases ### Versioning scheme PostSharp and Metalama both follow the versioning scheme `YYYY.N.B[-M]`, where: - `YYYY` represents the current or next year, - `N` signifies the version number within this year, - `B` stands for the build number within the minor version, - `M` is an optional suffix and denotes the release maturity level: - **Preview** releases are intermediate public builds that are not yet feature-complete and may still be subject to breaking changes. - **RC** releases meet all quality standards for stable releases, except that they have not been tested in the wild. - **Stable (Generally Available/GA)** releases have no suffix. They meet all quality standards, and the feature freeze applies. At this moment, the releases appear in the stable channels on our website, the Visual Studio Marketplace, and the NuGet Gallery, and they start to be widely downloaded. We don't differentiate between a major and a minor version. We only distinguish *versions* (`YYYY.N`) and *builds* (`YYYY.N.B`). > **Warning** > > It typically takes 4 to 6 weeks _after_ a new `YYYY.N` version is generally available before most bugs surface. Large teams working on tight deadlines are advised to wait for 8 weeks before updating to a new version. ### Quality standards We take the term *release candidate* seriously: an RC meets the same quality bar as a stable release, the only difference being that it has been less tested in the wild. Before we tag a release as RC quality, the following criteria must be fulfilled for all features: 1. Features are fully implemented. 2. Features are reasonably tested, including error conditions. 3. Features are documented, with both conceptual and procedural documentation. 4. Features have been tested on physical devices. 5. All public APIs have undergone extensive critical review. 6. Code analysis warnings have been addressed for public APIs. 7. Integration with new and old features has been tested. 8. All bugs with a higher priority than those marked *later* have been fixed. ### Servicing phases Each version goes through several servicing phases, which affect their eligibility and licensing terms. The servicing phases available to you are specified on the [Entitlement Page](https://store.postsharp.net/account). | Phase | Duration | License | Support Level | |------------------|-------------------------------------------------------------|---------|-------------| | Preview | Until the version is generally available. | Default[^1] | Community | | Stable / Current | Until a new version is generally available. | Default[^1] | Community | | Extended Support | Until 6 months after the next version is generally available. | Proprietary | Standard | | Long-Term Support | Two years after a subsequent release has been promoted to LTS or when the underlying platform version is no longer supported, whichever is sooner. | Proprietary | Enterprise | [^1]: The default license is open-source for Metalama (except for premium packages) and proprietary for PostSharp. To be eligible for technical support, you must use the latest build of any version available to you, except if a regression in the latest build prevents you from updating. ### Long-term support (LTS) versions LTS versions are designed to be used with the underlying LTS versions of the .NET SDK and Visual Studio platforms to ensure that the whole technology stack is as stable as possible. Typically, we release an LTS version every second year, matching the pace of Microsoft's platforms. ## Breaking changes * **Within a version.** We do our best to avoid introducing any breaking changes during the stable, extended support, or long-term support servicing phases. If we do introduce a breaking change nevertheless, this will be considered a high-priority bug. * **Between versions.** We generally avoid introducing high-impact breaking changes between `YYYY.N` versions. However, we might occasionally introduce breaking changes in less-often used APIs _between_ versions. These changes are documented. > **Note** > > To avoid breaking changes, consider staying on an LTS version. ## Using different versions side-by-side A project can have references (direct and indirect) to several PostSharp or Metalama packages of different builds within the same version. Side-by-side compatibility is provided on an "economically reasonable effort" basis. We perform structural tests of backward compatibility (comparison of public APIs, shared internals, and serialization details), but not behavioral tests such as unit tests. We may ask customers to upgrade all their packages to the same patch release, as there may be no other economically reasonable solution to some issues. --- ## Section: Company # Use Cases > Practical .NET engineering articles: patterns, logging, caching, threading and architecture, from the team behind PostSharp and Metalama. Canonical: https://postsharp.net/blog/use-cases Practical articles on the timeless craft of .NET engineering: patterns, logging, caching, threading and architecture. Less about the newest C# feature, more about code you will still be glad to read in a decade. --- # Contact Us > How to reach PostSharp Technologies: general contact, technical support, online orders, bank payment details and our registered office in Prague. Canonical: https://postsharp.net/contact ## General contact - Call us at **+420 246 052 306** (8:30 AM – 7:30 PM CET/CEST). - Email us at [hello@postsharp.net](mailto:hello@postsharp.net). - File a [technical support request](https://postsharp.net/support). ## Online orders Our online shop is managed by PayPro Global, who acts as a reseller between you and us. Please contact them for any question related to online orders and invoices: [payproglobal.com/customer-support](https://payproglobal.com/customer-support). For subscriptions, license keys and invoices you already own, use the [customer portal](https://store.postsharp.net/account). ## Bank payment details | | | |---|---| | **Company name** | SharpCrafters s.r.o. | | **Bank name** | Revolut Bank UAB | | **Bank address** | Konstitucijos ave. 21B, Vilnius, LT-08130, Lithuania | | **IBAN** | LT203250098394515068 | | **SWIFT/BIC** | REVOLT21 | | **Intermediary BIC** | CHASGB2L for EUR payments · CHASDEFX for USD payments | ## Registered office Kindly use this address for all legal correspondence.
SharpCrafters s.r.o.
Náměstí 14. října 1307/2
150 00 Prague 5
Czech Republic
Business ID (IČO): 28953690 · VAT number (DIČ): CZ28953690 · DUNS: 367081377 --- # Discord > Reach the PostSharp and Metalama team on Discord. Canonical: https://postsharp.net/discord You can find the PostSharp and Metalama team on our Discord server. Join to ask questions and reach us directly. Private discussions are reserved for enterprise customers.

Join the Discord server

--- ## Section: Blog # Security and Privacy Update for PostSharp and Metalama > A coordinated security and privacy hardening update across every supported branch of PostSharp and Metalama, closing a June 2026 review: three published GitHub Security Advisories (all rated High, all requiring local access), plus additional man-in-the-middle fixes (none remotely exploitable), along with hardened telemetry and data minimization, and toolchain hardening for shared build agents. Published: 2026-06-30 · Author: Gaël Fraiteur · Canonical: https://postsharp.net/blog/security-privacy-hardening-2026-06 Today we are shipping a coordinated security and privacy update across every supported branch of PostSharp and Metalama. It covers the build toolchain, the design time services, telemetry, and the Visual Studio Tools. It does not change the public API or how you write aspects, and none of it reaches your compiled application: every issue is in tooling that runs on developer and build machines, so the applications you have already built and shipped are not affected. **In short:** All fixes are already shipped on every supported branch. The most serious issues (three High-severity advisories) require an attacker to already have a local account on your machine, so they mainly affect shared or multi-user build agents. Upgrade when convenient; prioritize it if you build on shared agents. Nothing else to do. ## Scope and attack vectors We published GitHub Security Advisories for the most severe findings, all rated High. They require the attacker to already have local access to the same machine, under a different user account, which mainly matters on shared or multi-user build agents. - **PostSharp**: - Local code execution, information disclosure, and denial of service in the build toolchain on shared machines ([GHSA-gpwg-4q4j-792q](https://github.com/postsharp/PostSharp.Public/security/advisories/GHSA-gpwg-4q4j-792q)) - **Metalama**: - Unsafe deserialization over the Visual Studio named pipe enables local code execution ([GHSA-h26j-4vp7-x9w2](https://github.com/metalama/Metalama/security/advisories/GHSA-h26j-4vp7-x9w2)) - Local code execution via DLL planting in world-writable temp directories on Linux/macOS ([GHSA-m6wq-39m7-9xpp](https://github.com/metalama/Metalama/security/advisories/GHSA-m6wq-39m7-9xpp)) We found and fixed other vulnerabilities that required the attacker to sit between your device and our `postsharp.net` or `metalama.net` servers as a man-in-the-middle. See the GitHub release notes in the _What changed_ section below. None of these vulnerabilities could be exploited by a remote attacker against a normal developer or CI setup. One assumption is unchanged: like the .NET SDK itself, PostSharp and Metalama assume your source code and NuGet dependencies are trusted, and they execute that code at build time. This review hardened everything around that boundary, but it does not sandbox hostile source or malicious packages. Vetting what enters your build remains your job. There is no known exploitation, and no customer needs to do anything beyond upgrading when convenient. ## Why now, and not sooner No attacker or customer reported these issues. We found and disclosed them ourselves, and published the advisories with the fixes already shipped on every supported branch. Why now? Because the new generation of AI models makes it both possible and urgent. Auditing a 20-year-old living codebase used to mean weeks of work and skills we didn't have in-house. It did not seem necessary for a niche tool that processes trusted local inputs without network access. However, with new AI tools, an in-depth security analysis takes a couple of hours, both for the publisher and the attacker. We used Claude Opus 4.8 for the review, reviewed every finding, supervised and refined the fixes, and tested them on real VMs before release. The whole pass still took weeks; without the model it would not have happened. ## Run the analysis yourself We believe that security stems from transparency. Ultimately, it does not matter _who_ wrote the code you execute. What matters is the code itself. Metalama is open source: clone it from [GitHub](https://github.com/metalama/Metalama) and point your own tools at it. PostSharp's source is available to enterprise customers at no additional charge under a source-available license, going back to version 1.0; see [PostSharp Is Now Source Available](https://postsharp.net/blog/postsharp-source-available). If you run your own analysis, report any defects privately through GitHub Security Advisories ([Metalama](https://github.com/metalama/Metalama/security/advisories), [PostSharp](https://github.com/postsharp/PostSharp.Public/security/advisories)) and we will handle them the way we handled this review. Our [security policy](https://www.postsharp.net/compliance/security) describes how we receive, prioritize, and disclose vulnerabilities. ## What changed Across all these releases, the work falls into a few themes: - **Safer behavior on shared machines.** Caches, temp files, and local servers now stay scoped to the current user, so one user cannot interfere with or read another's build. - **Telemetry hardened to minimize what leaves your machine.** Crash reports now omit exception messages and data, and do a more thorough job scrubbing both secrets and confidential identifiers, such as a company name embedded in a namespace, so a stack trace can't carry a token, a connection string, or your internal names off your machine. We rotate device IDs monthly and keep separate salts for our own diagnostics and our third-party analytics, so the two are much harder to correlate. For exactly what we collect and how to turn it off, see our [privacy policy](https://www.postsharp.net/company/legal/privacy-policy). - **Removed features that collected data.** We took out the in-product extras that came with a data cost: the Learning Hub tool window in Visual Studio, the Areas of Interest selection, and newsletter and email registration. We also removed the telemetry code for the long-defunct per-usage licensing model, which used to upload reversibly-hashed type names. - **Defense in depth against our own servers.** Some data comes from postsharp.net or metalama.net, such as the news feed and content feeds. We hardened the parsers and serializers that handle it, so a compromise of our servers could not turn that data into code execution on your machine. The LTS and latest branches get the full set of changes. The two Metalama advisory fixes were also backported to the other supported versions, 2025.1.18 and 2026.0.25, without the broader privacy and telemetry rework. See the [version status page](https://www.postsharp.net/support/policies/versions) for what is still serviced. The per-release notes have the full technical detail: - **PostSharp**: - [2026.0.13](https://github.com/postsharp/PostSharp.Public/releases/tag/v2026.0.13) (Current) - [2024.0.23](https://github.com/postsharp/PostSharp.Public/releases/tag/v2024.0.23) (LTS) - **Metalama**: - [2026.1.19](https://github.com/metalama/Metalama/releases/tag/release/2026.1.19) (Current) - [2026.0.25](https://github.com/metalama/Metalama/releases/tag/release/2026.0.25) (Extended Support: advisory fixes only) - [2025.1.18](https://github.com/metalama/Metalama/releases/tag/release/2025.1.18) (Extended Support: advisory fixes only) - **Visual Studio Tools for PostSharp and Metalama**: - [2026.1.5](https://github.com/metalama/Metalama.Vsx.Public/releases/tag/release/2026.1.5) All packages are on [NuGet.org](https://www.nuget.org/packages/Metalama.Framework), and the extension is on the [Visual Studio Marketplace](https://marketplace.visualstudio.com/items?itemName=PostSharpTechnologies.PostSharp). ## Updated policies As part of this review we updated our [privacy policy](https://www.postsharp.net/company/legal/privacy-policy) to describe exactly what we collect, and our [security policy](https://www.postsharp.net/compliance/security) to reflect the current threat model and how to report a vulnerability. ## How to upgrade For most users this is a drop-in upgrade: bump the NuGet versions and update the extension. No API changes, nothing to reconfigure. Open-source and free-edition users can upgrade freely; for commercial editions, upgrading requires an active maintenance subscription. If you build on shared or multi-user agents, prioritize it. ## What's next The next Metalama and PostSharp feature releases will focus on .NET 11 support, with work starting in late August. Found an issue or have a feature request? Let us know on [GitHub](https://github.com/metalama/Metalama/issues). Happy meta-programming! -gael --- # Metalama 2026.1 Generally Available: First LTS Release, Complete C# 14 Support, and a Redesigned Redis Backend > Announcing the general availability of Metalama 2026.1, our first long-term support release: complete C# 14 support in aspects, new initialization and parameter-introduction advice, a redesigned Redis caching backend, fewer third-party dependencies, and over 100 bug fixes. Published: 2026-06-03 · Author: Gaël Fraiteur · Canonical: https://postsharp.net/blog/metalama-2026-1-ga Metalama 2026.1 is now generally available, and it's our first **long-term support (LTS)** release. This is a consolidation release: we finished the C# 14 work we started in 2026.0, extended the advising API, redesigned the Redis caching backend, cut several third-party dependencies, and closed most of the bug backlog. The packages are on [NuGet.org](https://www.nuget.org/packages/Metalama.Framework), and the Visual Studio extension is on the [Visual Studio Marketplace](https://marketplace.visualstudio.com/items?itemName=PostSharpTechnologies.PostSharp). For the complete list of changes, see the [release notes](https://doc.metalama.net/conceptual/release-notes/release-notes-2026-1). ## Complete C# 14 support in aspects In 2026.0 we supported all C# 14 features in your own code but left a few gaps in aspects. Those gaps are now closed: - **Introducing extension blocks.** The new [IntroduceExtensionBlock](https://doc.metalama.net/api/metalama-framework-aspects-adviserextensions-introduceextensionblock) advice lets aspects add extension blocks to static classes. See [Introducing types](https://doc.metalama.net/conceptual/aspects/advising/introducing-types). - **Contracts on extension-block receiver parameters.** You can now add a contract to the receiver parameter of an extension block, and it propagates across the extension members. - **Introducing user-defined compound assignment operators.** Operators such as `+=`, `-=`, and `*=` can now be introduced through [IntroduceMethod](https://doc.metalama.net/conceptual/aspects/advising/introducing-members). - **Run-time invoker interfaces for extension members.** - **Introducing parameters into partial constructors.** Two C# 14 constructs remain unsupported *inside templates*: null-conditional assignments (`x?.P = v`) and the `field` keyword. Rather than fail confusingly, Metalama now detects them and reports a clear diagnostic (`LAMA0101`). ## Initialization advice We've extended the [initialization advice](https://doc.metalama.net/conceptual/aspects/advising/initializers) with two new initializer kinds and better support for records: - **`AfterLastInstanceConstructor`** injects code into a `protected virtual OnConstructed` method, so it runs once after the most-derived constructor completes. - **`AfterObjectInitializer`** runs after the constructor and any object initializer or `with` expression have completed. Metalama makes the target implement `IInitializable` and rewrites the call sites accordingly, so you can validate or compute derived state once every property has been set. - **`BeforeInstanceConstructor`** now works on positional records with primary constructors. - **[Constructor parameter introduction](https://doc.metalama.net/conceptual/aspects/advising/introducing-constructor-parameters)** can now generate forwarding overloads, so you can add a parameter without breaking binary compatibility. ## Dependency injection [IntroduceParameterAndPull](https://doc.metalama.net/api/metalama-framework-advising-pullstrategy-introduceparameterandpull) now accepts a `reuseExistingParameterOfCompatibleType` argument. When a chained constructor already accepts a parameter of the same or a more specific type, the existing parameter is forwarded instead of duplicated. The default .NET DI adapter relies on this: an aspect that pulls an `ILogger` no longer adds a second `ILogger` parameter when the target constructor already has one. See [Injecting dependencies into aspects](https://doc.metalama.net/conceptual/aspects/dependency-injection). ## Redesigned Redis caching backend [Metalama.Patterns.Caching.Backends.Redis](https://doc.metalama.net/patterns/caching/redis) received a major overhaul, porting the work we did for PostSharp 2025.1 (see [Breaking Update of PostSharp Caching adapter for Redis in 2025.1](https://postsharp.net/blog/redis-caching-update)): - **New data schema.** Reading a cache item is now a single, observationally-consistent operation, and cache keys are versioned. - **Cluster support.** Hash tags keep related keys on the same shard, and you can configure `ReadCommandFlags` and `WriteCommandFlags` for multi-node setups. - **Retry and exception-handling policies.** The new `IRetryPolicy` and `IExceptionHandlingPolicy` interfaces give you exponential backoff and centralized error handling. - **Key compression.** Keys longer than a configurable threshold (128 characters by default) are hashed automatically with `XxHash64` or `XxHash128`. - **Overload detection.** The `IsBackgroundTaskQueueOverloaded` property lets you back off under pressure, and the dependency garbage collector pauses itself when the queue is saturated. If you use the Redis backend, note that the schema change requires purging the cache (`FLUSHDB`) on upgrade. See the breaking changes below. ## Fewer third-party dependencies We reduced our dependency surface to improve supply-chain safety: - `System.Text.Json` replaces `Newtonsoft.Json`. - `System.IO.Hashing` replaces `K4os.Hash` (the xxHash implementation now comes from the .NET runtime). - Diff-tool integration moved to the optional `Metalama.Extensions.DiffEngine` package, and HTML test output moved to `Metalama.Extensions.HtmlWriter` (both used in [aspect snapshot testing](https://doc.metalama.net/conceptual/aspects/testing/snapshot-testing)). - We no longer use MD5 for non-cryptographic hashing. ## Performance improvements This release improves both build-time and design-time performance. At build time, configuration files now use `System.Text.Json` source generators, polymorphic pattern matching in the code model was restructured to match on `DeclarationKind` or `TypeKind`, and the WPF temporary compilation phase (`MarkupCompilePass1`) runs a reduced pipeline that emits member signatures only and skips the linker step. At design time, the protocol between the analyzer and the design-time services moved from JSON to MessagePack. ## Bug fixes We fixed over 100 bugs in this release, spanning the template engine, linker, code model, design-time services, dependency injection, and diagnostics. Metalama 2026.1 is the first release ever to ship with (almost) zero known bugs. ## Breaking changes A few changes may affect existing code: - `AddInitializer` ordering now respects `AspectOrderDirection.RunTime`. - `IntroduceParameter` on a record primary constructor no longer materializes the parameter by default; opt in with `materializeOnRecord`. - `IConstructor.InitializerKind` returns `Base` (instead of `None`) for an implicit `base()` call. - `IEvent.RaiseMethod` returns `null` for non-raisable events. Use `IEventInvoker.CanRaise`. - The test base classes `AspectTestClass`, `DefaultAspectTestClass`, `CurrentDirectoryAttribute`, and `CurrentProjectAttribute` were removed. - For the Redis backend: the schema redesign requires a cache purge, `ExceptionHandlingCachingBackendEnhancer` was removed in favor of the `ExceptionHandlingPolicy` property, and `TransactionMaxRetries` and `CacheCleanupOptions.Sequential` are obsolete. See the [release notes](https://doc.metalama.net/conceptual/release-notes/release-notes-2026-1#breaking-changes) for the full list. ## What's next As our first LTS release, 2026.1 is the version to standardize on if you want stability. The next feature release is expected to be 2027.0, with support for .NET 11. Work will start in late August, and we don't expect any larger work on Metalama in the meantime. Found an issue or have a feature request? Let us know on [GitHub](https://github.com/metalama/Metalama/issues). Happy meta-programming! -gael --- # PostSharp 2026.0 Generally Available: Support for .NET 10, C# 14 and Extension Blocks > PostSharp 2026.0 is now generally available. It includes support for .NET 10, C# 14, and the new extension blocks syntax. The minimum supported frameworks have also been updated. Published: 2026-01-07 · Author: Gaël Fraiteur · Canonical: https://postsharp.net/blog/postsharp-2026-0-ga Alongside [Metalama 2026.0](https://postsharp.net/blog/metalama-2026-0-ga), we're announcing the general availability of PostSharp 2026.0. This release brings full support for .NET 10 and C# 14, including proper handling of the new extension blocks syntax introduced in C# 14. ## .NET 10 and C# 14 We've updated and tested PostSharp to fully support the .NET 10 SDK and C# 14 across all packages. You can now confidently use PostSharp with the latest .NET stack. ## Extension Blocks Support C# 14 introduces extension blocks as an alternative syntax for defining extension methods. This new language feature required careful consideration in PostSharp to ensure predictable behavior. By default, multicast attributes now skip extension block members. This prevents aspects from being unintentionally applied to compiler-generated metadata associated with extension blocks. For scenarios where you do want to target extension block members, we've added the `AllowExtensionBlockMembers` property to both `MulticastAttribute` and `MulticastAttributeUsageAttribute`. Setting this property to `true` enables your aspects to target extension block members explicitly. Additionally, developers using `IAspectProvider` or `IAdviceProvider` interfaces can use the new `ReflectionHelper.IsExtensionBlockMetadata` utility method to identify compiler-generated metadata associated with extension blocks. ## Updated Framework Baselines We've raised the minimum supported frameworks to reflect the current .NET ecosystem: * **.NET Standard 2.0** (minimum) * **.NET Framework 4.7.0** (minimum) * **.NET 6.0** (minimum for .NET Core-based projects) All pre-2017 target frameworks and obsolete .NET Core versions that have lost mainstream support have been removed. Please consult the [breaking changes documentation](https://doc.postsharp.net/introduction/whatsnew/whats-new-20260) for complete migration guidance. ## Conclusion PostSharp 2026.0 continues our commitment to keeping PostSharp compatible with the latest .NET releases. While our new feature development is focused on [Metalama](https://postsharp.net/blog/metalama-2026-0-ga), we remain dedicated to ensuring that PostSharp users can stay current with the evolving .NET platform. Happy meta-programming with PostSharp! --- # Metalama 2026.0 Generally Available: Full Support for C# 14 and .NET 10 > Announcing the general availability of Metalama 2026.0: full support for C# 14 extension blocks, .NET 10, and Visual Studio 2026; event handler overriding; first-class tuple support; Claude Code skill for AI-assisted development. Published: 2026-01-05 · Author: Gaël Fraiteur · Canonical: https://postsharp.net/blog/metalama-2026-0-ga Metalama 2026.0 is now generally available! This major release brings full support for C# 14 (the most significant evolution of the C# language in years), along with .NET 10 SDK and Visual Studio 2026 compatibility. We're also closing one of the last feature gaps with PostSharp: the ability to override event handler invocations. And you're getting first-class support for tuple types. Ready to try it? The packages are on [NuGet.org](https://www.nuget.org/packages/Metalama.Framework), and the Visual Studio extension is available from the [Visual Studio Marketplace](https://marketplace.visualstudio.com/items?itemName=PostSharpTechnologies.PostSharp). For the complete list of changes, see the [release notes](https://doc.metalama.net/conceptual/release-notes/release-notes-2026-0). For even more detail, see the change notes for the releases [2026.0.11](https://github.com/metalama/Metalama/releases/tag/release%2F2026.0.11), [2026.0.10-rc](https://github.com/metalama/Metalama/releases/tag/release%2F2026.0.10-rc), [2026.0.9-rc](https://github.com/metalama/Metalama/releases/tag/release%2F2026.0.9-rc), [2026.0.8-rc](https://github.com/metalama/Metalama/releases/tag/release%2F2026.0.8-rc), [2026.0.7-rc](https://github.com/metalama/Metalama/releases/tag/release%2F2026.0.7-rc), [2026.0.6-rc](https://github.com/metalama/Metalama/releases/tag/release%2F2026.0.6-rc), [2026.0.5-preview](https://github.com/metalama/Metalama/releases/tag/release%2F2026.0.5-preview), [2026.0.4-preview](https://github.com/metalama/Metalama/releases/tag/release%2F2026.0.4-preview), [2026.0.3-preview](https://github.com/metalama/Metalama/releases/tag/release%2F2026.0.3-preview), [2026.0.2-preview](https://github.com/metalama/Metalama/releases/tag/release%2F2026.0.2-preview), [2026.0.1-preview](https://github.com/metalama/Metalama/releases/tag/release%2F2026.0.1-preview). ## C# 14 support C# 14 introduces several language features, and Metalama 2026.0 supports all of them _in user code_: - Extension blocks (see below) - Null-conditional assignments - `field` keyword in properties - Partial constructors and events - Compound assignment operators - Simple lambda parameters with modifiers However, some features are **not yet supported in aspects**: - Null-conditional assignments in templates - The `field` keyword in templates - Generating run-time code for extension members via invoker interfaces - Adding contracts to the receiver parameter of extension blocks - Introducing new compound assignment operators - Introducing parameters into partial constructors - Introducing new extension blocks ## Extension blocks Extension blocks are the headline feature of C# 14, letting you extend any type with new members. Metalama supports them: - Extension blocks are modeled via the `IExtensionBlock` interface, which implements `INamedType` (`TypeKind = Extension`). - They appear under `INamedType.ExtensionBlocks`. - You can override extension members using the advising API or apply aspects via custom attributes. - You can introduce new members into existing extension blocks. - Introducing _new_ extension blocks is not yet supported. Here's how to add logging to all extension methods in your project using a fabric: ```csharp public class Fabric : ProjectFabric { public override void AmendProject( IProjectAmender amender ) { amender .SelectTypes() .SelectMany( t => t.ExtensionBlocks ) .SelectMany( e => e.Methods ) .AddAspectIfEligible(); } } ``` ## Event handler overriding You can now override event handler invocations, enabling patterns like "safe events" where each handler is isolated by exception handling. The `OverrideInvoke` method in `OverrideEventAspect` intercepts individual handler invocations. Typical use cases: - Safe events (exception isolation) - Background or asynchronous event dispatch - Event invocation logging Here's an aspect that catches exceptions and removes faulty handlers: ```csharp public class SafeEventAttribute : OverrideEventAspect { public override dynamic? OverrideInvoke( dynamic? handler ) { try { return meta.Proceed(); } catch ( Exception e ) { Console.WriteLine( e ); meta.Target.Event.Remove( handler ); throw; } } } ``` Apply it to any event: ```csharp public class Camera { [SafeEvent] public event EventHandler? FocusChanged; private void OnFocusChanged() { this.FocusChanged?.Invoke( this, EventArgs.Empty ); } } ``` For details, see [Overriding events](https://doc.metalama.net/conceptual/aspects/advising/overriding-events). ## First-class tuple support Previously, Metalama treated tuples as plain `INamedType` objects, with no access to element names or types. The new `ITupleType` interface changes that, making scenarios like packing method arguments for interceptors much cleaner: ```csharp // Create a tuple type from method parameters. var tupleType = TypeFactory.CreateTupleType( method.Parameters ); // Create a tuple instance. var tupleExpression = tupleType.CreateCreateInstanceExpression( meta.Target.Parameters ); // Pass it to an observer. _myObserver.OnMethodCalled( method.DeclaringType.FullName, method.Name, tupleExpression.Value ); ``` ## Metrics enhancements - **LinesOfCode metric**: Three measurements (Logical, NonBlank, and Total). - **Workspaces API**: Now officially supports metrics consumption. ## Visual Studio tooling improvements We've refactored several background components of our VSIX. It's now more stable, consumes less CPU, and better utilizes your cores. ## Claude Code skill for AI-assisted development We're introducing a Claude Code skill that gives Anthropic's AI assistant Metalama knowledge: - Conceptual documentation - API references - Sample code and pattern libraries Whether you're new to Metalama or tackling a complex scenario, Claude can now provide contextually relevant guidance for aspect development, templates, fabrics, and the code model. The skill is available on the [Metalama.AI.Skills marketplace](https://github.com/metalama/Metalama.AI.Skills). For setup instructions, see [Configuring Claude Code](https://doc.metalama.net/conceptual/using/claude-code). ## Documentation updates We've made these improvements: - New: [Working with types](https://doc.metalama.net/conceptual/aspects/type-system) - Improved: [Writing T# templates](https://doc.metalama.net/conceptual/aspects/templates) - Updated: [Injecting dependencies into aspects](https://doc.metalama.net/conceptual/aspects/dependency-injection) (now covers cross-project DI) - Updated: [Overriding events](https://doc.metalama.net/conceptual/aspects/advising/overriding-events) - New: [Consuming code metrics](https://doc.metalama.net/conceptual/introspection/metrics) and [Custom metrics](https://doc.metalama.net/conceptual/sdk/custom-metrics) - Enhanced API docs with integrated conceptual content ## Breaking changes - .NET 6 SDK is no longer supported. - Several API and behavioral changes. See the [release notes](https://doc.metalama.net/conceptual/release-notes/release-notes-2026-0) for details. ## What's next With 2026.0 shipped, we'll focus on extending C# 14 support in aspects. Found an issue or have a feature request? Let us know on [GitHub](https://github.com/metalama/Metalama/issues). Happy meta-programming! -gael --- # Metalama November 2025 Update: Release Candidate for Metalama 2026.0 > Metalama 2026.0 is now a Release Candidate with .NET 10 and C# 14 support. GA on January 5th, 2026. Published: 2025-11-30 · Author: Gaël Fraiteur · Canonical: https://postsharp.net/blog/status-2025-11 Metalama 2026.0 has reached Release Candidate status! All planned features are implemented, our test suite is green, and the release is stable enough for production evaluation. We're now gathering community feedback ahead of our GA release on January 5th, 2026. Ready to try it? The RC packages are available on [NuGet.org](https://www.nuget.org/packages/Metalama.Framework/2026.0.6-rc), and the Visual Studio extension can be downloaded from the [Metalama 2026.0 downloads page](https://www.postsharp.net/downloads/metalama/metalama-2026.0). This article covers what we shipped this month. For the complete list of changes in Metalama 2026.0, see the [release notes](https://doc.metalama.net/preview/conceptual/release-notes/release-notes-2026-0). For the full list of fixes and enhancements, see the changelogs: - [2026.0.4-preview](https://github.com/metalama/Metalama/releases/tag/release%2F2026.0.4-preview) (November 8). - [2026.0.5-preview](https://github.com/metalama/Metalama/releases/tag/release%2F2026.0.5-preview) (November 12). - [2026.0.6-rc](https://github.com/metalama/Metalama/releases/tag/release%2F2026.0.6-rc) (November 28). ## C# 14 Extension Members This month, we completed support for C# 14 extension blocks. You can now advise extension members and introduce members into existing extension blocks. Extension blocks are the "extend everything" feature in C# 14. Here's how Metalama models them: - Extension blocks use the `IExtensionBlock` interface, which implements `INamedType` (`TypeKind = Extension`) so you can discover members the usual `Fields`, `Properties`, `Methods`, ... collections. - They appear under `INamedType.ExtensionBlocks`. - Extension methods and property accessors are also listed in `INamedType.Methods` as implicitly-implemented methods. - You can override extension members using the advising API or add aspects using custom attributes. - You can also add new extension members to existing extension blocks. - Introducing new extension blocks won't be supported in 2026.0. ## Other Improvements **Single-file applications.** Metalama aspects now work in single-file `dotnet run` applications: arguably a niche scenario. **Toast notifications.** Product briefs and news now appear via toast notifications on Windows. You can disable or configure this feature through the UI or CLI tool. ## Visual Studio Tools improved stability and performance During the last two weeks, we focused heavily on Visual Studio extension stability, and uncovered more issues than expected. We owe you an apology. Most of these bugs were already present in the 2025.1 release, though some may have been introduced by a Visual Studio 17.x minor update (we don't repeat our full manual test suite after each VS release, so we can't be certain). We discovered them through exhaustive testing on clean virtual machines as part of our RC validation process. This level of testing clearly wasn't done properly for 2025.1. To ensure quality meets our standards, we chose to delay the release by one month, missing our target to reach GA before the Christmas break. It was the right call. The good news: the Visual Studio extension is now significantly more stable, and we improved performance along the way. You can download it from the [Metalama 2026.0 downloads page](https://www.postsharp.net/downloads/metalama/metalama-2026.0). ## Known Issues A couple of items to address before GA: - There's a known issue ([#149](https://github.com/metalama/Metalama.Compiler/issues/149)) with the `apt` package on Linux. We're working on a fix. - The next RC will update to Roslyn 5.0.0 GA ([#1212](https://github.com/metalama/Metalama/issues/1212)). ## What's Next With full .NET 10 SDK and C# 14 support (except for by-design limitations documented in the [release notes](https://doc.metalama.net/preview/conceptual/release-notes/release-notes-2026-0)), and a significantly more stable Visual Studio extension, we're on track for GA on January 5th, 2026. If you haven't tried the RC yet, now is the time. Test it with your projects and let us know what you find. Your feedback helps us ship a stable release. Happy meta-programming! -gael --- # Metalama October 2025 Update: Full Steam Ahead Toward 2026.0 > Metalama 2026.0 is almost ready, with support for .NET 10 SDK and C# 14. Published: 2025-10-30 · Author: Gaël Fraiteur · Canonical: https://postsharp.net/blog/status-2025-10 October is traditionally our busiest month. Microsoft ships the RCs of the .NET SDK, C# and Visual Studio, and we sprint to make Metalama ready. This year is no exception: we’ve made solid progress, and we’re confident we’ll be ready on time for the GA wave. Below is a recap of what we shipped and what’s coming. ## Metalama 2025.2: skipped for simplicity We’ve decided not to ship Metalama 2025.2 as a standalone release. All its features, including the ability to override event handler invocations, are already merged into Metalama 2026.0. Why? The only real benefit of releasing 2025.2 would be making event-invocation advising available a few weeks earlier, but supporting two versions in parallel would slow us down. ## Metalama 2026.0: almost there We are now almost code-complete with Metalama 2026.0, and have released several previews: - [2026.0.3-preview changelog](https://github.com/metalama/Metalama/releases/tag/release%2F2026.0.3-preview) - [2026.0.2-preview changelog](https://github.com/metalama/Metalama/releases/tag/release%2F2026.0.2-preview) - [2026.0.1-preview changelog](https://github.com/metalama/Metalama/releases/tag/release%2F2026.0.1-preview) ### .NET 10 SDK and C# 14 We’re nearly done with support for the .NET 10 SDK and C# 14. Want to see where things stand? Check out these resources: - [General status board](https://github.com/orgs/metalama/projects/1/views/2?sliceBy%5Bvalue%5D=2026.0) highlighting the leftovers for the 2026.0 version. - [C# 14 status board](https://github.com/orgs/metalama/projects/1/views/14) showing our progress with C# 14 features. Out of scope for Metalama 2026.0: - Using null-conditional assignments in templates. [#1109](https://github.com/metalama/Metalama/issues/1109) - Using the `field` keyword in templates. [#1114](https://github.com/metalama/Metalama/issues/1114) - Introducing new extension blocks. [#1159](https://github.com/metalama/Metalama/issues/1159) - ~~Introducing extension members into existing extension blocks. [#1160](https://github.com/metalama/Metalama/issues/1160)~~ UPDATE: implemented in 2026.0.4-preview. **Fun fact**: we noticed that compiling our [NopCommerce snapshot](https://github.com/metalama/Metalama.Tests.NopCommerce) with C# 14 breaks LINQ-to-SQL because of implicit conversions of arrays to `ReadOnlySpan`. We've had to pin this solution to C# 13 to work around this issue. ### Extension blocks “Extend everything” is the headline feature of C# 14, and we’re making sure Metalama supports it out of the box, including overriding extension members. Here’s how we’re modeling extension blocks: - Extension blocks use the `IExtensionBlock` interface (`TypeKind = Extension`). It derives from `INamedType`, and adds two properties: `ReceiverParameter` and `ReceiverType`. Although extension blocks derive from `INamedType`, you can’t always treat them like regular ones. Yes, this technically bends the Liskov Substitution Principle, but splitting the hierarchy would make the API noisier for little practical benefit. For extension members (except classic extension methods), `IMember.DeclaringType` points to the `IExtensionBlock`. - Extension blocks show up under `INamedType.ExtensionBlocks`, not as nested types (`INamedType.Types`). - Extension methods and property accessors are listed in `INamedType.Methods` as implicitly-implemented methods. They exist in IL and are addressable in C#, but you can’t override them with Metalama. This approach feels natural for C# developers and closely matches Roslyn’s model. ### First-class support for named tuples Previously, Metalama treated tuples as plain `INamedType` objects, without access to element types or names. This made scenarios like packing method arguments for interceptors (think caching) awkward and inefficient. Now, with the new `ITupleType` interface, you get direct access to element names and types. Create named tuples with `TypeFactory.CreateTupleType`, and instantiate them with `ITupleType.CreateCreateInstanceExpression`. Our implementation even supports tuples with zero or one element. For two or more, you get the nice tuple syntax; for degenerate cases, Metalama falls back to `ValueTuple.Create(...)`. When you use `ITupleType`, you don’t have to worry about these details yourself. Example: Want to advise multiple methods and pass all arguments to an observer? Here’s how: ```csharp // Create the tuple type. var tupleType = TypeFactory.CreateTupleType(method.Parameters); // Create the tuple instance. var tupleExpression = tupleType.CreateCreateInstanceExpression(meta.Target.Parameters); // Invoke the interceptor. _myObserver.OnMethodCalled(method.DeclaringType.FullName, method.Name, tupleExpression.Value); ``` Transformed code: ```csharp // Two or more arguments: tuple syntax public IEnumerable FindHats(decimal size, string category) { _myObserver.OnMethodCalled("HatStore", "FindHats", (size, category)); // ... } // One argument: ValueTuple.Create public bool IsHatAvailable(long hatId) { _myObserver.OnMethodCalled("HatStore", "IsHatAvailable", ValueTuple.Create(hatId)); // ... } // Zero arguments public bool IsStoreOpen() { _myObserver.OnMethodCalled("HatStore", "IsStoreOpen", ValueTuple.Create()); // ... } ``` **Breaking Change:** `INamedType.TypeKind` now returns `Tuple` instead of `NamedType` for tuples. ### Advising event invocations Overriding event handler invocation logic, originally planned for 2025.2, is now part of 2026.0. For details, see the [September 2025 Status Update](https://postsharp.net/blog/status-2025-09#metalama-20252-rc-event-invocation-overrides). ### Other improvements - Introducing user-defined `checked` operators. - Dependency injection: pull constructor parameters across projects. - API cleanup (some breaking changes, `[Obsolete]` warnings). ## Metalama 2025.1: now supports Visual Studio 2026 Last month, we added limited .NET 10 SDK support to Metalama 2025.1. As of October 7, Visual Studio 2026 is also supported. Just make sure to update both the NuGet packages and the VSIX extension. ## Roadmap: finalizing 2026.0 We’re wrapping up the last features for full C# 14 support in Metalama 2026.0. Next up: stabilization, code cleanup, manual testing, and performance validation. We’re aiming for a release candidate (RC) on November 7, barring any surprises during testing. The final GA release will follow soon after Microsoft ships the GA versions of the .NET SDK and related packages. ## Conclusion In October, we made good progress on Metalama 2026.0, whose main focus is to support the .NET 10 SDK, C# 14, and Visual Studio 2026. We’re almost code complete, and we’ll spend most of November stabilizing the release. Happy meta-programming! -gael --- # Metalama Status Update, September 2025 > New in September 2025 with Metalama: overriding event invocations with 2025.2 RC, limited .NET 10 SDK support added to 2025.1, WIP with full .NET 10 support in 2026.0. Dockerization of our CI infrastructure. Published: 2025-10-01 · Author: Gaël Fraiteur · Canonical: https://postsharp.net/blog/status-2025-09 Welcome to Metalama’s September 2025 status update. After a relaxed summer, we’re back in full swing, delivering .NET 10 SDK support for Metalama and closing a key gap versus PostSharp: the ability to override event handler invocations. ## Our first community contributor Giuseppe Marazzi Let me start with the announcement that brings me the greatest joy. This past summer, Metalama received its first contribution from the open-source community. [Giuseppe Marazzi](https://github.com/beppemarazzi) submitted [two PRs](https://github.com/metalama/Metalama/pulls?q=author%3Abeppemarazzi++is%3Apr&filterScopeGroup=true) fixing two small and hard-to-find bugs. A warm thanks to Giuseppe! While Metalama may look like a complex codebase at first glance, Giuseppe's contribution proves that anyone can make a meaningful impact. Don't let the technical depth intimidate you: every contribution counts, however small. Our [contributor's guide](https://doc.metalama.net/contributing) shows you where to start. ## .NET SDK 10 readiness As autumn arrives, Microsoft is finalizing the new .NET stack, and, like all .NET extenders, we’re working hard to make sure you can use the new features as soon as possible. We’ve split the work into two releases: - Metalama 2025.1 received quick fixes to keep working with the .NET 10 SDK, with limitations; - Metalama 2026.0 will be built natively for the .NET 10 SDK. ### Metalama 2025.1: limited .NET SDK 10 support With the .NET SDK 10 already in RC, we didn’t want you to wait for full integration, so we made small changes to ensure compatibility with the new .NET SDK. This build is Metalama 2025.1.14, released this week. From our [platform integration matrix tests](https://github.com/metalama/Metalama.Tests.DotNetSdk/actions/workflows/test.yml), most .NET workloads are working. Keep in mind that Metalama 2025.1 is still based on Roslyn 4.14, while the .NET SDK 10 includes Roslyn 5.0. This has some implications: - Analyzers and source generators compiled against Roslyn 5.0 will not work. When possible, we replace them with an older Roslyn 4.14 version. This often works, but not always. - Specifically, Blazor projects (all flavors) are not supported yet because of an incompatibility in their source generators. - You cannot use C# 14 features in your Metalama projects. ### Metalama 2026.0 in progress The goal of Metalama 2026.0 is to bring full native support for the .NET 10 SDK and C# 14: native, not hacks. C# 14 brings a lot of new features, and we won’t support all of them in every scenario in November, when the new .NET is expected to become generally available. Our primary goal with Metalama 2026.0 is to let you safely use any new C# feature in your runtime code, but not necessarily in aspect templates. Support for new language features in templates may come in later versions. This aligns with our design philosophy: Metalama should be able to compile any valid C# code, but it’s okay to limit features available in aspects. We’re using GitHub to track our progress. You can use [this meta-issue](https://github.com/metalama/Metalama/issues/1045) or [this dashboard](https://github.com/orgs/metalama/projects/1/views/2?sliceBy%5Bvalue%5D=2026.0) to check the status. We plan to have an RC ready in November, when Microsoft is expected to release the new stack. If you’re eager for a preview, look for one next week. Or, if you’re more patient, compile the develop/2026.0 branch of [Metalama](https://github.com/metalama/Metalama/tree/develop/2026.0) and [Metalama.Compiler](https://github.com/metalama/Metalama.Compiler/tree/develop/2026.0) repos (merged from Roslyn tag `NET-SDK-10.0.100-preview.7.25380.108` as of today). ## Visual Studio 2026 readiness We ran some initial tests of our Metalama Tools for Visual Studio with Visual Studio 2026. They failed, subtly but miserably. It seems those incompatibilities can be addressed with fairly small bug fixes. If that proves to be true, we’ll update our tooling 2025.1 to support Visual Studio 2026, and there will be no need to wait for Metalama 2026.0. ## Metalama 2025.2 RC: Event invocation overrides We have just released the first (and hopefully final) RC of Metalama 2025.2. Since the beginning, Metalama has allowed overriding the _add_ and _remove_ accessors of events, which were arguably of limited utility. This version adds the ability to override the _invocation_ of event handlers. The OverrideInvoke method of the OverrideEventAspect class lets you intercept the invocation of an individual event handler. Typical use cases are: - safe events (exception handling) - background/asynchronous events - event invocation logging Let's illustrate this with an example. This aspect catches exceptions in event handlers; if any exception is caught, it removes it from the event invocation list. ```csharp public class SafeEventAttribute : OverrideEventAspect { // If there are many event handlers, this method will be called several times. public override dynamic? OverrideInvoke( dynamic? handler ) { try { return meta.Proceed(); } catch ( Exception e ) { Console.WriteLine( e ); // First time shame on you, second time shame on me. meta.Target.Event.Remove( handler ); throw; } } } ``` We can now add this to any event: ```csharp public class Camera { [SafeEvent] public event EventHandler? FocusChanged; private void OnFocusChanged() { this.FocusChanged?.Invoke( this, EventArgs.Empty ); } } ``` As you might expect, it took us a while to implement. It's far from simple. Here’s how Metalama transforms your code: ```csharp public class Camera { // The field backing the event. private event EventHandler? _focusChanged; // A broker sitting between the event API and the backing field. private volatile ActionEventBroker? _focusChangedBroker; // Binds the broker to the event implementation and the advice. private static readonly ActionEventBrokerCallbacks FocusChangedBrokerCallbacks_0 = new ( static (handler, me, args) => ((Camera)me).FocusChanged_Invoke_SafeEvent(handler, args), static b => (sender, e) => b.Invoke((sender, e)), static (handler, me) => ((Camera)me).FocusChanged_SafeEvent += handler, static (handler, me) => ((Camera)me).FocusChanged_SafeEvent -= handler ); // The public event with the new implementation. [SafeEvent] public event EventHandler? FocusChanged { add { ActionEventBroker.EnsureInitialized(ref this._focusChangedBroker, this, FocusChangedBrokerCallbacks_0); this._focusChangedBroker.AddHandler(value); } remove { this._focusChangedBroker?.RemoveHandler(value); } } // The original event implementation. private event EventHandler? FocusChanged_SafeEvent { add { this._focusChanged += value; } remove { this._focusChanged -= value; } } // Your advice intercepting the handler invocation. private void FocusChanged_Invoke_SafeEvent(EventHandler? handler, (object? sender, EventArgs e) args) { try { handler.Invoke(args.sender, args.e); return; } catch (Exception e) { Console.WriteLine(e); _focusChanged -= handler; return; } } private void OnFocusChanged() { this._focusChanged?.Invoke(this, EventArgs.Empty); } } ``` It's a bit lengthy, but aside from a touch of syntactic sugar, the event broker is the most reliable pattern, because we can't predict or analyze how an event is raised. This approach is oblivious to the event's implementation and to how it gets raised. You can find more details on this [documentation page](https://doc.metalama.net/preview/conceptual/aspects/advising/overriding-events). Subscribe to our [briefs](https://postsharp.net/metalama/briefs) to receive updates about ongoing work on Metalama 2026.0 before the next monthly update. ## Dockerization of our build infrastructure As a side note, the entire Metalama build infrastructure now runs on Docker, requiring only lean build agents. That means you can build Metalama in the exact environment we use. All you need is a Windows 11 or Windows Server 2025 machine with Docker installed and Windows containers enabled, then run: ```powershell .\DockerBuild.ps1 build ``` ## Summary On the feature side, the `OverrideEventAspect.OverrideInvoke` advice closes one of Metalama's remaining gaps with PostSharp. On the platform front, we've released the first bits of .NET 10 support and we expect to deliver more soon. In terms of infrastructure, Dockerizing our build workflow makes Metalama easy to reproduce across agents and helps reduce the cost and complexity of our setup. Happy meta-programming with Metalama! --- # Security Notice: Data Leak Affecting a Limited Number of Accounts > Published: 2025-09-07 · Author: Gaël Fraiteur · Canonical: https://postsharp.net/blog/2025-09-09-security-notice We regret to inform you of a data leak that occurred on the **PostSharp Customer Portal** between **Wednesday, September 3rd, and Tuesday, September 9th**. ### Who is affected? A total of **38 customer accounts** were impacted. If your account was affected, you will: 1. Receive a personal notification email within the next 24 hours. 2. Notice that you can no longer sign in to the portal using your license keys. ### What data was leaked? Only the content of the page of the portal was exposed, including: * Company name and billing address (or customer name and address for natural persons) * All subscription dates, number of users, licensed products, and support level * All license keys for PostSharp and Metalama ### What was *not* leaked? * Contact lists or email addresses * Prices, invoices, or quotes * Support tickets * Source code (never uploaded to our servers) ### Who accessed the data? At most, **one other PostSharp customer** may have received your data in error. ### How did this happen? A **caching error** occurred in the daily job that sends subscription renewal notifications, causing account data to be sent to the wrong recipient. The first customer of the batch received all emails. ### Timeline of events * **Wed 9/3, 11:00 AM** – First incorrect email sent. * **Mon 9/8, 8:00 PM** – Data leak reported by the receiving customer. * **Tue 9/9, 2:00 PM** – Report read and incident confirmed. * **Tue 9/9, 4:30 PM** – Credentials invalidated and issue remediated. ### How we fixed it? * All past **magic URLs** were invalidated by rotating the salt for each affected account. * License keys exposed in the leak were **disabled for authentication** to the portal. * The caching bug was fixed. ### Do you need to take action? * **No action is required**. Your license keys will continue to work normally with PostSharp and Metalama products. * If you prefer, we can issue **new license keys** on request, to ensure that any leaked key cannot be used by another party. ### How to log in now? If your license keys no longer work for portal login: * Use **email authentication** (one-time PIN or magic link) for one of your account contacts. * If you do not know your contact, please write to **[hello@postsharp.net](mailto:hello@postsharp.net)** from your work email. We take full responsibility for this incident and sincerely apologize for the error. We are committed to ensuring it does not happen again. --- # Breaking Update of PostSharp Caching adapter for Redis in 2025.1 > PostSharp 2025.1 introduces a breaking update to the Redis caching backend that fixes critical bugs in master/replica setups but requires cache purging during update. Published: 2025-09-01 · Author: Gaël Fraiteur · Canonical: https://postsharp.net/blog/redis-caching-update We have released a refactored version of `PostSharp.Patterns.Caching.Backends.Redis` in PostSharp 2025.1 to address a reliability issue that could surface in multi-node Redis deployments (master/replica or cluster) when cache dependencies are enabled under sustained load. The fix required a redesign of the internal data schema, which is why this is a breaking change (one-time cache purge). Outside of that purge, the upgrade is straightforward and brings measurable gains in performance, consistency, and resilience. Impact at a glance: - If you run multi-node Redis with dependencies, the update removes a potential failure mode. - You only need a one-time cache purge, and you’ll still benefit from performance and diagnostics improvements. - There is no risk of user data loss; only old cached entries stay in the database until purged. - No impact for the other backends. ## Who is affected? - **Affected by the bug:** - Applications that use `PostSharp.Patterns.Caching.Backends.Redis` with support for dependencies in a Redis master/replica or cluster setup. - **Affected by the refactoring:** - All applications that use `PostSharp.Patterns.Caching.Backends.Redis`, with or without support for dependencies, are affected by the redesign of the data schema. You must purge the cache after the update, and there is no other impact. All versions prior to 2025.1.7 are affected. Customers using `Metalama.Patterns.Caching.Backends.Redis` are affected by this issue too, and the solution has not yet been ported to Metalama. Please contact us if it matters to you. ## What was wrong? The previous design of the `RedisCachingBackend` relied on a retry loop in `GetItem` operations to retrieve a consistent snapshot of the cache item, which includes both the cache value and its dependencies, stored in separate Redis keys. Under heavy load in a master/replica setup, this loop could exhaust its iterations and fail. The consistent snapshot of value and dependencies was required because recursive dependencies (when a cached method calls another already cached method) were flattened in the `SetItem` operation, so the `RemoveItem` and `InvalidateItem` operations did not need to operate recursively. This was a poor design choice. The previous component was only tested in a single-node Redis topology. We believe the root cause was a lack of testing in multi-node topologies under high load. ## How was this fixed? We redesigned the data schema so `GetItem` never needs to iterate: - Dependencies are no longer flattened. The `RemoveItem` and `InvalidateItem` operations now work recursively. - Cache keys (a single logical cache item requires several Redis keys) are now versioned, so we don't need loops to ensure read consistency. ## What else has been improved? Since a major refactoring was required to implement the new data schema, we went the extra mile and made the component more resilient in high-load production environments: - **Performance improvements** due to the new design of the Redis data schema that is always observationally consistent for read operations. - **Key compression (hashing).** Enable this feature by assigning the `CachingServices.DefaultKeyBuilder` property. - **More resilient** thanks to new behaviors controlled by the following properties on the `RedisCachingBackendConfiguration` object: - **Exception handling:** `RedisCachingBackendConfiguration.ExceptionHandlingPolicy`. - **Retry policies:** `RedisCachingBackendConfiguration.BackgroundRecoveryRetryPolicy`, `RedisCachingBackendConfiguration.BackgroundTasksRetryPolicy`, `RedisCachingBackendConfiguration.TransactionRetryPolicy`. - **Concurrency control:** `BackgroundTasksMaxConcurrency` and `InvalidationMaxConcurrency`. - **Node selection:** `ReadCommandFlags` and `WriteCommandFlags`, defaulting to `PreferReplica` and `PreferMaster`, respectively. - **Support for sharding clusters,** with keys containing taghashes. Tested on a 3+3 cluster. - **Support for dependencies** with a very large number of dependent cache items. - **GC process:** - Detection of overloads and automatic temporary disabling. - Periodic background cleanups to handle situations that were not detected by processing keyspace notifications in real time. - **Keys contain the PostSharp Caching schema version** for safe updates. - **Fixed bugs** with sliding expiration. - **Improved [documentation](https://doc.postsharp.net/caching/backends/caching-redis).** ## What changes are breaking? - The data schema has changed, so you must purge the old version's cache; otherwise it will contain permanently unreachable data (garbage) forever. - The `CacheValue.Dependencies` and `CacheItem.Dependencies` properties now contain first-level dependencies rather than all recursive dependencies. These objects are largely implementation details and do not surface to the aspect-oriented API of PostSharp Caching. ## How to update? 1. Update all PostSharp packages to `2025.1.8` and deploy your application. 2. Purge the cache using Redis's `FLUSHDB` command after your old application has been undeployed. If you ignore this post and only update the package, the Redis database will contain the data of the old version forever, since the dependency-management keys are set to never expire. ## Read more For details about the refactored PostSharp Caching for Redis, see our [documentation](https://doc.postsharp.net/caching/backends/caching-redis). ## Conclusion PostSharp Caching for Redis now runs on a more robust, versioned schema that removes a reliability edge case and improves performance and consistency across single-node, replicated, and sharded clusters when dependency support is enabled. The component has been overhauled for better observability and resilience. The only required action is a one-time cache purge after upgrading to 2025.1.8 or later. If you rely on the Metalama Redis backend, let us know so we can prioritize porting the same improvements. Thanks to customers who surfaced the scenario early—enjoy the improved backend. --- # PostSharp Is Now Source Available > PostSharp source code is now available to enterprise customers under a source-available license, enabling security audits, custom builds, troubleshooting, and business continuity. Published: 2025-08-07 · Author: Gaël Fraiteur · Canonical: https://postsharp.net/blog/postsharp-source-available This spring we open-sourced Metalama. Continuing that push for transparency and dependability, PostSharp's source code is now available to enterprise customers at no extra cost. You can access a read-only mirror of our primary Git repository, including every version back to 1.0 (2009). It's an exact copy of the repo we use to build the product — down to the commas. We also provide a supported, Docker-based build for versions 2024.0, 2025.0, and 2025.1. Those versions are no longer obfuscated in our public builds. The code is released under a permissive, proprietary source-available license. You can modify, build, and distribute the software, provided users of the modified code hold a valid PostSharp license. We are releasing our source code with the following use cases in mind: - Troubleshooting - Supply chain security - Supply chain resilience In an era of tightening regulations (like the EU's Cyber Resilience Act) and increased focus on software supply chain security, transparency isn't optional — it's necessary. ## Who qualifies To access the source, you need an Enterprise E1 support plan or higher, which is included in: - a 10-user subscription of PostSharp Ultimate, or - a 15-user subscription of PostSharp Framework. There's no additional cost — it's included in your subscription. If you have fewer users but need source access, add more users to your subscription. See our [support policies](https://www.postsharp.net/support/policies/sla) for a description of our support levels and their eligibility. Before getting access to the repo, you must sign a Non-Disclosure & Non-Competing Agreement. ## Use case 1. Troubleshooting You now have full visibility into the source to troubleshoot issues. We've disabled obfuscation in all maintained versions. When decompilation isn't sufficient, you can read the original source. Note: SourceLink is not available yet. ## Use case 2. Supply chain security Supply chain security isn't just about trusting suppliers — it's about trusting the code you run. With full access to our source, you can run your own analysis tools and assess the code's quality. Auditing the source alone isn't enough. You need to guarantee that the binaries you execute were built from that audited source. That means you should be able to build the code yourself. To make that practical, we provide a Docker-based build that requires only Windows containers. Once you've built the packages, you have two theoretical options to verify the chain of custody: 1. Use the packages you built yourself instead of sourcing them from `nuget.org`, or 2. Compare your packages with those sourced publicly. In practice, our public binaries won't be bitwise identical to what you build because our public builds are signed with Authenticode using a timestamp and a private certificate we cannot share. For that reason, only the first option — using your own built packages — is available to guarantee the chain of custody. You can use [Package Source Mapping](https://learn.microsoft.com/en-us/nuget/consume-packages/package-source-mapping) (a NuGet client option) to ensure packages are sourced from your private builds rather than PostSharp's public ones. ## Use case 3. Supply chain resilience Companies rely on PostSharp for mission-critical applications and need contingency plans for scenarios such as product end-of-life, company closure, or temporary inability of the supplier to deliver fixes or platform updates. By giving enterprise customers full source access, we enable practical contingency plans. You have the legal right and technical capability to: - Fork and maintain your own version of PostSharp if our support becomes unavailable - Apply critical security patches without waiting for official updates - Maintain older versions beyond their official support lifecycle - Ensure business continuity even in worst-case scenarios Section 9 of the [license agreement](https://www.postsharp.net/downloads/legal/PostSharp%20License%20and%20Support%20Services%20Agreement%202025-Q2.docx) provides the legal framework for these business continuity scenarios, so you can be confident your critical systems won't be held hostage by vendor dependencies. ## How does it work? ### 1. Getting access 1. Sign in to the [customer portal](https://www.postsharp.net/account). 2. Check your support entitlements. You must have an Enterprise support plan (E1 or higher). 3. Go to the [source access](https://www.postsharp.net/account/source-access) page. 4. Sign the Non-Disclosure/Non-Competing Agreement online (or offline). 5. Allow one business day for review and account update. 6. Return to the [source access](https://www.postsharp.net/account/source-access) page and create Git users and passwords. 7. Clone the repositories using Git. 8. Check out one of the support release branches: `release/2024.0`, `release/2025.0`, or `release/2025.1`. ### 2. Building packages See `README.md` in the cloned repo for details about requirements and build instructions. 1. You need a Windows 11 machine with Docker Desktop configured for Windows containers. 2. Open a PowerShell prompt and execute `./Build-Docker.ps1`. 3. Wait — initial image creation can take over an hour on a high-end notebook; subsequent builds may take around 30 minutes. ## Getting support We're here to help. Enterprise customers can email support@postsharp.net for help with source access, builds, or questions about the source-available license. Customers with Enterprise E2 support plans or higher can also reach us on Microsoft Teams or Slack for real-time collaboration and faster resolution of complex issues. ## Limitations - The source code of Visual Studio Tools for Metalama and PostSharp is currently not available. - Our obligation is limited to a successful Docker build from the unmodified source: - Although the repo includes CI/CD scripts, running those tests or configuring CI/CD infrastructure is not supported. - We do not provide support for modified source code. - We only support building release branches for currently maintained versions. ## Conclusion Publishing PostSharp's source is the final step in our 2025 transparency and dependability initiative, following Metalama's earlier move to open source. Eligible customers now have full access to the source, improving supplier security and resilience. If you want access, visit the [PostSharp Customer Portal](https://www.postsharp.net/account) to verify your eligibility, complete the NDA/NCA process, and gain repository access. --- # Metalama Status Update, Mid Summer 2025 > So far for summer 2025 in Metalama: bug fixes, migrating project management. WIP with event invocation overrides, plans for 2026.0. Published: 2025-08-07 · Author: Gaël Fraiteur · Canonical: https://postsharp.net/blog/status-2025-08 It's been over two months since our last status update. After a busy spring that peaked with the open-sourcing of Metalama, summer has been a bit quieter on the development side. Our focus shifted to releasing the source code of both PostSharp and Metalama's premium packages under a source-available license, while fitting in some vacation time. But don't worry, the open-source Metalama hasn't been neglected. We've been moving our bug tracker to GitHub, squashing bugs in 2025.1, and tackling one of the last feature gaps between PostSharp and Metalama: the ability to override event invocations. ## Metalama 2025.1 - Maintenance Since the last status update, we've released three maintenance builds, focusing solely on bug fixes: [2025.1.12](https://github.com/metalama/Metalama/releases/tag/release%2F2025.1.12), [2025.1.11](https://github.com/metalama/Metalama/releases/tag/release%2F2025.1.11), and [2025.1.10](https://github.com/metalama/Metalama/releases/tag/release%2F2025.1.10). In general, we now consider 2025.1 to be stable. ## Metalama 2025.2 - WIP The focus of Metalama 2025.2 is a single new feature: the ability to [intercept the _invocation_ of events](https://github.com/metalama/Metalama/issues/549). This addresses one of the last gaps with PostSharp: the main remaining one is the ability to advise the `await` operator. The first preview is planned for late August or early September, with an RC in late September and GA in late October. We aim to make it possible to write aspects like this: ```csharp public class SafeEventAttribute : OverrideEventAspect { public override dynamic? OverrideEvent( dynamic? handler ) { try { return meta.Proceed(); } catch ( Exception e ) { Console.WriteLine( e ); // First time shame on you, second time shame on me. meta.Target.Event.Remove( handler ); throw; } } } ``` ## Metalama 2026.0 - Roadmap Metalama 2026.0 will add support for .NET 10 and C# 14. Our goal this year is to release the GA days after Microsoft's own GA, expected in mid-November. Previously, our tradition was to release in the early days of the new year, but this time we'll aim to be ready sooner with platform support, ensuring the release isn't bogged down with other features. We want to enable Metalama users to use new C# features in _normal_ code. However, support for new language features _in aspect code_, such as _generation_ of extension members, will be delayed to another version. This should ensure we're ready with the GA in November. ## Equality comparison sample We've published a new comprehensive example: implementing equality comparison, i.e., `IEquatable` and its companions. Metalama can now implement the complete pattern. The ultimate goal is to be able to write code like this: ```csharp public partial class Entity { [StringEqualityMember( StringComparison.InvariantCultureIgnoreCase )] public required string EntityType { get; init; } [EqualityMember] public int Id { get; init; } public Guid ObjectId { get; } = Guid.NewGuid(); } ``` See the [Equality Comparison Example](https://doc.metalama.net/examples/comparison). ## Project management on GitHub Being open source means more than making the source code public. It's also about collaboration and transparency, including in product and project management. This summer, we have moved our project management to [a public GitHub project](https://github.com/orgs/metalama/projects/1), and migrated our complete backlog. You might be surprised by the [number of bugs](https://github.com/orgs/metalama/projects/1/views/1?filterQuery=status%3ABacklog%2CPlanned%2C+type%3ABug) now. Most of them have been discovered internally and never reported by the community. We try to fix community- and customer-reported bugs as soon as possible, but delay internally-discovered bugs until we are done with more important features. ## Source access for proprietary components As you know, Metalama is 85% open-source, with premium components available for enterprise customers who need advanced features and commercial support. We're happy to announce that the source code of premium packages is now available, [together with PostSharp's source code](https://postsharp.net/blog/postsharp-source-available), to enterprise customers under a relatively permissive proprietary source-available license, allowing you to modify, build, and distribute the software for various purposes. The principal scenarios are troubleshooting, supply chain security, and supply chain resilience. Here's the process: 1. Sign in to the [customer portal](https://www.postsharp.net/account). 2. Check your support entitlements. You must have an Enterprise support plan (E1 or higher). If you're a PostSharp customer, you might have it without knowing it. 3. Go to the [source access](https://www.postsharp.net/account/source-access) page. 4. Sign the Non-Disclosure/Non-Competing Agreement [online](https://app.hellosign.com/s/BNWR7aMh) (or [offline](https://www.postsharp.net/downloads/legal/Source%20Access%20NDA-NCA.docx)). 5. Allow for one business day to review the signature and update your account. 6. Return to the [source access](https://www.postsharp.net/account/source-access) page and create Git users and passwords. 7. Clone the `Metalama.Premium` repository using Git. For details, see [PostSharp Is Now Source Available](https://postsharp.net/blog/postsharp-source-available) on PostSharp's blog. ## Summary Even though summer has been quieter for Metalama, we've made significant progress with moving the project management to GitHub, fixing bugs, keeping up with Roslyn, and adding small features. Down the road, the upcoming features in 2025.2 are set to further close the gap with PostSharp, and 2026.0 will be compatible with .NET 10 and C# 14. The new source access policy for enterprise customers also marks a significant step in our journey towards greater transparency. Happy meta-programming! --- P.S. If you're looking for more frequent updates than our monthly (or less) newsletter provides, we regularly share quick announcements and technical tidbits on [Metalama Briefs](https://postsharp.net/metalama/briefs). Subscribe to this [RSS feed](https://postsharp.net/briefs.xml) with your favorite newsreader, which won't manipulate your timeline like social media algorithms do. --- # Metalama Status Update, May 2025 > May 2025: Metalama's first open-source release, 2025.1, plus post-release updates, the roadmap, and the transition to open-source practices. Published: 2025-05-30 · Author: Gaël Fraiteur · Canonical: https://postsharp.net/blog/status-2025-05 At the start of this month, we open-sourced our codebase, one of the most significant contributions to the .NET community this year. Throughout May, we focused on stabilizing our first open-source release, improving compliance with supply chain security and transparency best practices, and participating in the Open Source Founder Summit in Paris. ## Open Sourcing Metalama The biggest news this month: we've released Metalama 2025.1, our first open-source version! This is big news for us and for the entire .NET ecosystem. While many open-source projects have announced their commercial transition this year, we're going against the tide and have released 85% of Metalama's codebase under the MIT license. We believe that vendor-led open-source projects are the most viable option for development tools, components, and libraries (except for IDEs and UI components, which have an established commercial market). As a result, the entire code generation and aspect-oriented programming feature is now open source, while IDE tooling, architecture verification, and some other extensions remain proprietary. Curious about why we made this decision, what it means for you, and what’s next? Read the full story in my blog post, [Metalama Is Now Open Source](https://postsharp.net/blog/metalama-open-source), along with my thoughts about the delicate balance between community-driven development and commercial sustainability. If you haven't already, please [give us a star](https://github.com/metalama/Metalama) on GitHub to show your support! ## Metalama 2025.1: Post-Release Updates Since the general availability (GA) release of Metalama 2025.1, we’ve been busy listening to your feedback and rolling out fixes and improvements. Thank you to everyone who reported issues and shared suggestions! Here’s a quick look at recent releases: - [2025.1.6](https://github.com/metalama/Metalama/releases/tag/release%2F2025.1.6): Bug fixes and minor telemetry improvements. - [2025.1.7](https://github.com/metalama/Metalama/releases/tag/release%2F2025.1.7): Major update: merged with Roslyn 4.14 for improved compatibility. - [2025.1.8](https://github.com/metalama/Metalama/releases/tag/release%2F2025.1.8): Critical licensing bug fix, made `[Observable]` applicable to interfaces, and further telemetry improvements. - [2025.1.9](https://github.com/metalama/Metalama/releases/tag/release%2F2025.1.9): Critical Visual Studio Extension (VSX) bug fix. For the full changelog and all release notes, check out the [GitHub Releases page](https://github.com/metalama/Metalama/releases). The Roslyn merge in 2025.1.7 is especially important: it keeps Metalama aligned with the latest C# language features and tooling. ## Open Source Security Foundation Best Practices As part of many critical supply chains, we're taking security and best practices seriously. Our pipelines are now compliant with OpenSSF best practices. [![OpenSSF Best Practices](https://www.bestpractices.dev/projects/10558/badge)](https://www.bestpractices.dev/projects/10558) [![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/metalama/Metalama/badge)](https://scorecard.dev/viewer/?uri=github.com/metalama/Metalama) ## Open Source Founder Summit in Paris ![Open Source Founder Summit](https://postsharp.net/assets/images/2025/2025-05-status/osfs-1315689cfd.jpeg) This month, I had the pleasure of attending the [Open Source Founder Summit](https://05f5.com/) in Paris. It was inspiring to connect with fellow open-source leaders and dive deep into the realities of open-source business models, governance, and sustainability. I’ve captured my reflections and key takeaways in a dedicated LinkedIn article: [Attending the Open Source Founder Summit in Paris](https://www.linkedin.com/pulse/attending-open-source-founder-summit-paris-gael-fraiteur-ktrvf). I share candid thoughts on open-source foundations, why I believe the .NET open-source community lags behind others, and why I don't think this will change anytime soon. If you’re curious about my perspective, I invite you to give it a read! ## Closing the Metalama Community Slack Workspace As part of our transition to open source, we've closed the Metalama community Slack workspace, which previously served as a channel for brief news and technical support. Going forward, real-time conversations with our core team on Slack or Teams will be reserved for enterprise customers. For the wider community, we encourage you to use [GitHub Discussions](https://github.com/orgs/metalama/discussions) for questions, ideas, and general conversation, or [GitHub Issues](https://github.com/metalama/Metalama/issues) for bug reports and feature requests. While we do monitor these channels, please note that our response times may be slower than before. ## Metalama Briefs: Stay Tuned To stay informed about the latest news, releases, and quick updates, visit our [briefs page](https://postsharp.net/metalama/briefs) or subscribe to the [RSS feed](https://postsharp.net/briefs.xml). This is the most reliable way to receive announcements about new builds and community updates, without worrying about social media algorithms filtering them out of your timeline. ## Roadmap After a busy and successful spring, we're looking forward to slowing down during the summer, focusing on filling some remaining gaps between Metalama and PostSharp. In autumn, we plan to work on .NET 10 and C# 14 compatibility. We've updated the [roadmap](https://github.com/metalama/Metalama/milestones) to provide more clarity on upcoming features and releases: ### June 2025 - Stabilize 2025.1. - Migrate the issue database and project management to GitHub. - Release PostSharp's source code under our proprietary source-available license. ### Summer 2025 - **Metalama 2025.2: Filling gaps with PostSharp**: - Support for overriding event invocation. - If time permits: interception of `await` for enhanced asynchronous programming capabilities. ### Autumn 2025 - **Metalama 2026.0: Compatibility with .NET 10 and C# 14**: - Ensuring full support for the latest language features and runtime improvements. - Additional enhancements to streamline meta-programming workflows. ## Conclusion Metalama is now open source, and honestly, there’s no excuse left for writing repetitive code in .NET. Open source is about embracing transparency, security, and evolving how we do business. It means far more than throwing code over the wall. We’re still ironing out some details, but we’re committed to building a project that’s strong, reliable, and here to stay. Thanks for being part of this. Now go write some aspects. Happy meta-programming! --- # Announcing Metalama 2025.1: Going Open Source! > This post summarizes the licensing and pricing changes in Metalama 2025.1 Published: 2025-05-02 · Author: Gaël Fraiteur · Canonical: https://postsharp.net/blog/metalama-2025-1 Today, we're excited to announce that Metalama 2025.1, our first open-source release, is now generally available! With this milestone, Metalama gets its own dedicated website at . Be sure to check out the [announcement](https://postsharp.net/blog/metalama-open-source) to learn more about this transition and why it is so significant. This post covers the details of the changes in licensing, pricing, and support, and what they mean for our customers. Our goal is to make Metalama more accessible while continuing to provide productivity features and robust support for those who choose our premium plans. Our objective with this move is to make our tools more accessible while offering enhanced support and services to customers who opt for higher-tier commercial plans, ensuring a balanced and sustainable approach for all users. > This article is part of a 3-post series announcing the launch of our 2025.1 versions: > - [Metalama Is Now Open Source – Adding Full Meta-Programming Capabilities to C#](https://postsharp.net/blog/metalama-open-source): an introduction to Metalama and our open-source vision. > - **Announcing Metalama 2025.1: Going Open Source!**: changes in licensing and support terms for Metalama. > - [Announcing PostSharp 2025.1](https://postsharp.net/blog/postsharp-2025-1): changes in licensing and support terms for PostSharp. ## TL;DR - The Metalama product line has been reorganized, with most of the code now open source. IDE tooling is now free for individuals, non-commercial use, or companies with up to three users. - Starting with version 2025.1, only the _latest_ versions of our products will be available under an open-source or free license. Maintenance of older versions is reserved for commercial or enterprise customers based on their service level. If you have any questions or concerns regarding this transition, please contact us at . These changes would not have been possible without breaking changes. Please check the [release notes](https://doc.metalama.net/conceptual/release-notes/release-notes-2025-1) for details. ## Open-source transition Metalama is now primarily **free and open source**, released under the MIT license. The following components remain commercial: - Visual Studio extensions for Metalama. - Architecture validation (formerly the `Metalama.Framework.Validation` and `Metalama.Extensions.Architecture` namespaces). - Code fix toolkits (formerly the `Metalama.Framework.CodeFixes` namespace). - Redis and Azure adapters for `Metalama.Patterns.Caching`. ## New product catalogue The Metalama product line has been restructured to align with our new open-source business model, making our tools and frameworks free for individuals, small teams, and non-commercial use, while offering premium services and packages to larger organizations. We have introduced a simplified product catalogue: - **Metalama Community** — A **free** edition, including Visual Studio tools, for individuals, non-commercial use, and organizations with up to three users. - **Metalama Professional** — A full-featured edition for commercial use. - **Metalama Enterprise** — Includes all Professional features, plus **enhanced support and service benefits**. For a detailed comparison of editions, visit: . ## Transitioning your existing subscriptions Unless you are using Metalama Free, **your current license key will continue to work** with Metalama 2025.1 without any loss of functionality. Here's how the transition affects different licenses: * **Metalama Free**: This edition has been retired, and its license will not work with Metalama 2025.1. Your options are: - Use only the open-source components. - Switch to Metalama Community. - Upgrade to a Metalama Professional license. * **Metalama Professional**: Now the full-featured edition with unlimited functionality, but with limited support. * **Metalama Starter** and **Metalama Ultimate**: These editions have been retired. Your current license key will function as Metalama Professional in the new version, but you'll need to convert your subscription to Metalama Professional upon renewal. ## Pricing overhaul We’ve restructured Metalama’s pricing to reflect the new model: - **Metalama Professional**: First-year pricing is $89 for individuals and $295 for businesses. - **Volume Discounts** are no longer available. Larger teams are now served by Metalama Enterprise, which offers four tiers with varying benefits and team sizes. [See Metalama pricing](https://www.postsharp.net/pricing/metalama). If the pricing changes negatively impact you and your software licensing budget has already been approved, please contact us. We’ll honor the previous pricing to ensure a smooth transition. ## Support model updates We have updated our support model to align with the open-source transition. These changes apply **only to new or renewed subscriptions**. Existing contracts remain under the previous License and Support Services Agreement (LSSA). ### Servicing phases We have introduced **servicing phases** to clarify the lifecycle of Metalama versions (`YYYY.N`): - **Current:** Supported until the next version is released. - **Extended:** Supported for six months after the next release. - **Long-Term Support (LTS):** Supported for two years after the next LTS release or until the underlying platform reaches end-of-life. Starting with version 2025.1, only the **current** version will be maintained under the open-source model. Access to **Extended** and **LTS** builds will require a commercial license. Updates are never mandatory. Once a build becomes eligible for you, it will remain accessible indefinitely. Note that maintenance builds of previous versions remain accessible to all users, regardless of support level. For more details, visit: [Versioning Policies](https://www.postsharp.net/support/policies/versions). ### Premium support tiers We have formalized premium support into clear tiers, offering benefits such as: - Private support tickets - Access to Extended and LTS builds - Faster response times and issue escalation - Defined number of technical contacts - Access to proprietary source code - Advisory and video calls - GDPR/vendor form assistance - Private Slack or Teams channels - Priority bug fixes and remote debugging - EULA amendments and regression shields - License audit waivers - Open-source sponsorship tiers For _newly acquired or renewed subscriptions_, the support level is determined by the edition (Professional or Enterprise tiers E1–E4). For _existing subscriptions_, the support level is determined by the number of users. Please visit [Service Levels](https://www.postsharp.net/support/policies/sla) for details regarding support benefits and how they map to product editions. To know your current support level, please visit the [customer portal](https://www.postsharp.net/subscriptions). ### Source code access Metalama's open-source code is now available on [GitHub](https://github.com/metalama/Metalama) under the MIT license. Additionally, Enterprise customers gain exclusive access to private GitHub repositories for: - Premium packages (e.g., architecture validation, code fix toolkits, Redis and Azure adapters). - Core packages once they enter Extended Support or LTS phases. To request access, check your support level in the [customer portal](https://www.postsharp.net/subscriptions) and email us at with the GitHub usernames to be granted access. ## Conclusion By transitioning Metalama to an open-source business model, we aim to promote greater awareness and adoption of meta-programming within the developer community. We believe that vendor-led open source strikes the right balance between transparency and dependability: developers gain the ability to inspect and contribute to the code, while the vendor ensures long-term support through a sustainable commercial offering. If the vendor fails to uphold its commitment to maintaining the software, the community has the freedom to step in and take over. Our goal is to create a fair balance between open-source accessibility, proprietary features, and premium services. We’ve worked hard to ensure these changes benefit as many users as possible. However, if you feel these changes impact you unfairly, please don’t hesitate to reach out. We’re committed to finding a solution that works for you. Thank you for your continued support, and happy meta-programming! -gael --- # Announcing PostSharp 2025.1 > An overview of the licensing and pricing updates in PostSharp 2025.1 Published: 2025-05-02 · Author: Gaël Fraiteur · Canonical: https://postsharp.net/blog/postsharp-2025-1 Today's big announcement is that [we are releasing Metalama 2025.1](https://postsharp.net/blog/metalama-2025-1), our first open-source release. Be sure to visit the new website at and check out the [open-source announcement](https://postsharp.net/blog/metalama-open-source). Alongside this, we’re also launching PostSharp 2025.1 to align with our updated business model. This article explains the changes in licensing and support. > This article is part of a 3-post series announcing the launch of our 2025.1 versions: > - [Metalama Is Now Open Source – Adding Full Meta-Programming Capabilities to C#](https://postsharp.net/blog/metalama-open-source): an introduction to Metalama and to our open-source vision. > - [Announcing Metalama 2025.1: Going Open Source!](https://postsharp.net/blog/metalama-2025-1): changes in licensing and support terms for Metalama. > - **Announcing PostSharp 2025.1**: changes in licensing and support terms for PostSharp. Your current subscription and license keys remain unaffected by these updates. All changes will take effect upon renewal. ## Product consolidation To simplify our product lineup, the following products will no longer be available for separate purchase: - **PostSharp Logging** is now included in **PostSharp Framework**. - **PostSharp MVVM**, **Caching**, and **Threading** are now exclusively part of **PostSharp Ultimate**. If you already have a license or subscription for these products, don’t worry: your access will remain unchanged. You can continue to renew your subscription or add new users as needed. ## Pricing updates For the first time since 2016, we’ve adjusted PostSharp pricing to reflect currency fluctuations: - **USD prices** have increased by **5–10%**, depending on the product. - **EUR prices** have been adjusted by **-5% to +5%**, depending on the product. For detailed pricing information, visit: . ## Servicing phases Until now, we maintained legacy versions and made bug-fix releases available to everyone. Starting with PostSharp 2025.1, access to maintenance builds for legacy versions will be reserved for commercial customers. This change aligns PostSharp with Metalama's support model. We’ve introduced the concept of **servicing phases** to clarify the lifecycle of PostSharp versions (`YYYY.N`): - **Current:** Supported until the next version is released. Available to all users, including those with a free edition. - **Extended:** Supported for six months after the next release (instead of two months prior to 2025.1). Available to all commercial customers. - **Long-Term Support (LTS):** Supported for two years after the next LTS release or until the underlying platform reaches end-of-life. Available to customers with Enterprise support. Starting with version 2025.1, only the **current** version will be available to PostSharp Essentials users. Access to **Extended** builds will require a commercial license, and **LTS** builds will require an Enterprise support level. Updates are never mandatory. Once a build becomes eligible for you, it will remain accessible indefinitely. Maintenance builds of previous versions will remain available to all users, regardless of support level. For more details, visit: [Versioning Policies](https://www.postsharp.net/support/policies/versions). ## Support levels You might be wondering about the new Enterprise support level. Previously, we didn’t have a formalized approach to SLAs, and we informally prioritized customers based on their expenditures. Moving forward, your support level will depend on the number of licenses you acquire. For PostSharp Framework, Enterprise support starts at 15 users; for PostSharp Ultimate, it starts at 10. These thresholds are designed to align with the pricing of various [Metalama Enterprise support plans](https://www.postsharp.net/pricing/metalama/enterprise), as PostSharp subscriptions also include a Metalama license. Details about support levels and their mapping to the number of users can be found here: [Support Levels](https://www.postsharp.net/support/policies/sla). ## Plans to release the source code To further align PostSharp with Metalama's support model, we plan to release PostSharp's source code in a private GitHub repository accessible to enterprise customers, with rights to modify and build the product. We expect this to be available by next spring and will keep you updated. ## PostSharp Essentials improvements PostSharp Essentials now offers unlimited support for `async` methods, addressing a long-standing limitation. ## Website updates With Metalama now having its own dedicated website, we’ve streamlined and reorganized to provide a cleaner, more focused experience. ## Summary PostSharp 2025.1 introduces updates to licensing, pricing, and support models, aligning with Metalama's open-source vision. While no new features or platform updates are included, these changes simplify the product lineup and clarify support levels. Stay tuned for updates on the release of PostSharp's source code. --- # Metalama Is Now Open Source, Adding Full Meta-Programming to C# > Metalama, the meta-programming framework for C#, is now open source under the MIT license, adding code generation, architecture validation, and aspect-oriented programming to .NET. Published: 2025-04-04 · Author: Gaël Fraiteur · Canonical: https://postsharp.net/blog/metalama-open-source We’re excited to announce that **Metalama**, our meta-programming framework for C#, is now open source, released under the permissive MIT license. Metalama brings code generation, architecture validation, and aspect-oriented programming to .NET. Over 85% of the codebase is now open source, with only its IDE tooling and select extension packages remaining proprietary. We believe this is a big deal for our team and for the entire .NET ecosystem. Departing from the legacy of IL-based tools, Metalama uses Roslyn to bring meta-programming directly to the C# language, while remaining 100% syntax-compatible with C# and all editors. > This article is part of a 3-post series announcing the launch of our 2025.1 versions: > - **Metalama Is Now Open Source: Adding Full Meta-Programming Capabilities to C#**. > - [Announcing Metalama 2025.1](https://postsharp.net/blog/metalama-2025-1): changes in licensing and support terms for Metalama. > - [Announcing PostSharp 2025.1](https://postsharp.net/blog/postsharp-2025-1): changes in licensing and support terms for PostSharp. ## How Big Is Metalama? A Reality Check Metalama’s open-source release follows nearly five years of dedicated development, resulting in a large codebase and an (almost) feature-complete product. The core repository includes over **400,000 lines of code** and spans more than **17,000 commits**, representing approximately **ten man-years** (20,000 hours) of work by seasoned .NET compiler experts. While Metalama isn’t the largest .NET project (Roslyn or Mono are still larger by an order of magnitude), it is undoubtedly one of the most substantial non-Microsoft projects. For context: - Metalama has more commits than the combined top three .NET Foundation projects over the past five years, according to [dnfprojects.org](https://dnfprojects.org/). - Its codebase is roughly twice the combined size of projects like AwesomeAssertions, AutoMapper, and MediatR, three projects that recently transitioned from open source to commercial. - It surpasses the combined size of Cecil and Fody, including all its add-ins, by approximately 40%. - Metalama is 53% the size of Avalonia, one of the largest .NET vendor-led open-source projects, which counts 750 KLOC. ## What Exactly Is Metalama? Metalama is a code analyzer and generator built as a Roslyn extension. It integrates into the C# compilation pipeline and provides four capabilities: - **Generate code**: Add members, implement interfaces, or apply entire design patterns without modifying your source files. If you're tired of text-based generators, rejoice: Metalama uses a type-safe, object-oriented, C#-to-C# templating system with full IntelliSense support. - **Transform code**: Rewrite method bodies, insert pre/post logic, convert fields to properties, inject dependencies, and more. Unlike Roslyn generators, Metalama is not limited to adding partial classes and can override almost any part of your code. - **Validate code**: Enforce architectural and coding standards at compile time, preventing violations before they reach your repository. - **Suggest code fixes and refactorings**: Surface code generation and transformation via the IDE’s lightbulb or screwdriver menu to guide and assist developers. ## Goodbye, Fody 👋 Unlike legacy IL-weaving tools like Fody, which operate on compiled assemblies, Metalama leverages the Roslyn compiler to apply transformations at the source level. This allows for a level of usability that IL-based tools can't match: - **Immediate IDE feedback**: Generated members appear in IntelliSense, and diagnostics update live as you type. - **Step-through debugging** – Step into generated code, place breakpoints, and inspect variables exactly as you would in handwritten code. Metalama effectively makes older IL-based code generation tools obsolete. - **No lock-in** -- You can include the generated code back to your source code and remove Metalama from your project thanks to the Metalama Divorce feature. ## Real-World Use Cases: From Design Patterns to DevOps Metalama includes a rich collection of open-source aspect libraries to tackle common software scenarios. Examples include: - **Design Patterns**: [Singleton](https://postsharp.net/metalama/applications/modern-singleton), [Factory](https://postsharp.net/metalama/applications/factory), [Builder](https://postsharp.net/metalama/applications/builder), [Decorator](https://postsharp.net/metalama/applications/decorator), [Proxy](https://postsharp.net/metalama/applications/proxy), and more. - **UI and MVVM Patterns**: [Observable](https://postsharp.net/metalama/applications/inotifypropertychanged) to implement `INotifyPropertyChanged`, [dependency properties](https://postsharp.net/metalama/applications/dependency-property), [commands](https://postsharp.net/metalama/applications/command), undo/redo. - **Object Services**: Auto-generated `ToString`, deep cloning, equality, and comparison. - **DevOps**: [Logging and tracing](https://postsharp.net/metalama/applications/logging), [metrics](https://postsharp.net/metalama/applications/metrics), [caching](https://postsharp.net/metalama/applications/caching), [retry/fault-handling logic](https://postsharp.net/metalama/applications/exception-handling), and more. ## How Metalama Works Meta-programming in Metalama is based on two types of *meta-classes* that execute during compilation: - **Aspects** encapsulate reusable code generation and transformation patterns, such as logging, observability, or the Memento design pattern. They act as intelligent attributes that can generate, transform, or validate code in place. - **Fabrics** serve as centralized compile-time entry points, allowing you to apply aspects in bulk across your codebase using a LINQ-style API, eliminating the need to annotate every element manually. Metalama dynamically rewrites the syntax tree during compilation. Your source code remains clean and focused, while the compiled assembly includes all the generated logic. ### Aspect Here is a classic "hello, world" example: a logging aspect. ```c# using Metalama.Framework.Aspects; public class LogAttribute : OverrideMethodAspect { public override dynamic? OverrideMethod() { Console.WriteLine( $"{meta.Target.Method} started." ); try { var result = meta.Proceed(); Console.WriteLine( $"{meta.Target.Method} succeeded." ); return result; } catch ( Exception e ) { Console.WriteLine( $"{meta.Target.Method} failed: {e.Message}." ); throw; } } } ``` The `OverrideMethod` is a _template_. Parts of the code to the left of the `meta` pseudo-keyword are evaluated at compile time. The rest is runtime code. `meta.Proceed()` invokes the overridden method (it is replaced by the original method body in this case). You can now apply the aspect to a method as a custom attribute: ```c# internal static class Calculator { [Log] public static double Add( double a, double b ) => a + b; } ``` This transforms the code into the following: ```c# using System; internal static class Calculator { [Log] public static double Add( double a, double b ) { Console.WriteLine("Calculator.Add(double, double) started."); try { double result; result = a + b; Console.WriteLine("Calculator.Add(double, double) succeeded."); return (double)result; } catch (Exception e) { Console.WriteLine($"Calculator.Add(double, double) failed: {e.Message}."); throw; } } } ``` ### Fabric If you want to log thousands of methods, it would not be practical to annotate every single method with the `[Log]` attribute. That’s why we invented the concept of a _fabric_, a compile-time entry point to your project that lets you add any aspect to anything using LINQ-like code. The following code snippet adds logging to all public methods of all public types: ```c# public class Fabric : ProjectFabric { public override void AmendProject( IProjectAmender amender ) { amender .SelectTypes() .Where( t => t.Accessibility == Accessibility.Public ) .SelectMany( t => t.Methods ) .Where( m => t.Accessibility == Accessibility.Public ) .AddAspectIfEligible(); } } ``` ## Why Vendor-Led Open Source Matters We believe vendor-led open source is the new standard for serious software frameworks and libraries. Microsoft’s stewardship of the .NET platform has set the tone: today’s developers expect both **transparency** and **dependability** from the tools they choose. - **Transparency**, because developers need to understand how their tools work. They want to debug, diagnose, audit for security. Open source makes that possible by default. - **Dependability** means knowing the project will be maintained, bugs fixed, platforms supported, features added. That kind of long-term commitment requires a sustainable business model, often backed by a profitable company with real skin in the game. When a vendor fails to maintain a project, the open-source license gives the community the power to take over. It lowers the barrier for competitors to step in and raises the pressure on the original maintainer to stay sharp. That’s why open source has become the default model, and why it gets so much visibility. Let’s be honest: that visibility is a big reason for our choice. We could have *faked* open source, published a hollow core and kept the real value behind closed doors. But here's what we’re doing instead: we're releasing 85% of the codebase, a full-featured product. That's because we're committed to transparency and dependability, the two core values of vendor-led open-source. Our goal is broad adoption. Reducing any licensing friction gets the framework into more hands, and some of those teams will turn into customers. Under PostSharp’s freemium model, about 30% of users are paying customers. With Metalama, we expect that number to be much lower. And we’re fine with that! With a larger distribution, we can afford a lower rate of monetization. We’ve also seen what happens when unfunded open-source projects succeed too well: tools like AutoMapper, MediatR, and FluentAssertions eventually had to move to commercial models to survive in the long term. That’s not a failure of open source: it’s a failure of sustainability. They only got big because they were open. On the flip side, we’ve seen technically inferior tools outpace PostSharp just because they were open source. We’re not making that mistake again. We’re realistic about community contributions. Metalama is a large, complex codebase. We don’t expect a wave of unsolicited PRs, but we welcome contributions that meet our standards, and we’d love to be proven wrong. See the **How You Can Contribute** section below. Most importantly, this is a return to our roots. PostSharp was open source from 2004 to 2009, and that openness was key to its early success. With Metalama, we’re bringing that spirit back: stronger, smarter, and built for today. ## Proprietary Components and Editions Although Metalama’s compiler integration and core libraries are fully open source, we’ve retained a few advanced components under a commercial license to ensure long-term sustainability: - **Visual Studio Extensions**: The full-featured IDE experience is provided in Metalama Community and Metalama Professional. - **Architecture Validation**: Advanced architectural rules and analysis (formerly under `Metalama.Framework.Validation` and `Metalama.Extensions.Architecture`). - **Code Fix Toolkits**: Custom refactoring and quick-fix support (previously `Metalama.Framework.CodeFixes`). - **Distributed Caching Adapters**: Redis and Azure integrations for `Metalama.Patterns.Caching`. These premium packages will be bundled in **Metalama Professional**, available under commercial terms. We acknowledge that the proprietary status of these components may seem at odds with the open-source value of transparency. To address this, the source code for these proprietary components is available to enterprise customers under a permissive but proprietary license. This ensures that enterprise users can audit, understand, and even customize these components while maintaining the sustainability of the project. ## Free IDE Tooling for Individuals and Small Teams While Metalama's compiler integration and core framework are fully open source, our [Visual Studio tooling](https://postsharp.net/metalama/features/tooling) remains proprietary. These tools aren't required to use Metalama, but they do offer a significant productivity boost: - **Aspect Diff** – Compare original and transformed code using Visual Studio's diff viewer. - **CodeLens Integration** – Instantly see which aspects affect your code, right from the editor. - **Aspect Explorer** – Browse your project to discover where and how aspects are applied. We initially planned to monetize this tooling across the board. But we don’t want to charge freelancers, students, hobbyists, or open-source maintainers. We want to monetize Metalama only where it makes sense: within large, well-funded organizations. That’s why we’re making Visual Studio Tools for Metalama **free for individuals, non-commercial use, and companies with up to 3 developers**. No license key required. Just indicate your eligibility and you’re good to go. We trust you. With this move, everyone, including indie developers to small startups, can access a complete, modern meta-programming experience with first-class IDE support. ## Is This Project Really Sustainable? We understand the concern: how can such a large and ambitious open-source project remain viable over time? The answer is simple: we're building this on the solid financial and operational ground of a company founded in 2009. Metalama was fully funded through the profits of PostSharp. We’ve taken no outside investment and carry no debt. With Metalama now feature-complete, ongoing development primarily involves maintenance and incremental improvements, something that can be managed by a small, focused team. Our company has scaled down accordingly and is already operating profitably, independent of future Metalama revenues. Long term, sustainability will come from a dual business model: - **Premium features**: As mentioned above, while the core is fully open source, some components will remain proprietary and available under commercial terms. - **Premium support and consulting**: For teams that want guidance, custom tooling, long-term maintenance, or guaranteed SLAs, we offer paid services. This ensures that we will be able to create enough value for professional teams to justify commercial investment, while keeping Metalama free and open for the community. In short, Metalama is already sustainable in the near term. For the long term, everything hinges on adoption. The broader the user base, the stronger the community, and the more viable the commercial offerings. That’s why we’ve open-sourced the core: to make it easy for everyone to try, trust, and build on Metalama. ## Community and Enterprise Support We offer a support model tailored to meet the diverse needs of our users: - **Community Support**: The open-source core is self-supported through the GitHub Discussions forum. Community members are encouraged to ask questions, share best practices, and assist one another. Our team will monitor community activities weekly to provide guidance where needed, with no guarantees. - **Basic Professional Support**: Subscribers of Metalama Professional gain access to basic email support for private tickets. - **Enterprise Support**: For organizations needing high-touch support, Metalama Enterprise offers priority assistance, escalation, video consultations, advanced diagnostics, and consultancy services. This plan is designed for teams with critical projects that demand a higher level of engagement and expertise. Details about support offerings, response times, and SLA commitments can be found at [https://www.postsharp.net/support/policies](https://www.postsharp.net/support/policies). Only the current version (`YYYY.N`) will be maintained under open-source terms. Previous versions (`YYYY.N-1`) will be supported exclusively under the enterprise license for customers with long-term support agreements. ## How You Can Contribute Whether you’re just discovering Metalama or already using it in your projects, there are many meaningful ways to get involved: - **Star the GitHub repository**: Show your support and help improve Metalama’s visibility by starring [github.com/metalama/Metalama](https://github.com/metalama/Metalama). - **Spread the word**: Metalama is a mature, production-ready framework, but adoption is still growing. Share it with your colleagues, present it at meetups, or write a blog post to help raise awareness. - **Help newcomers**: If you’re familiar with Metalama, consider following issues and discussions on GitHub. Answering questions or clarifying concepts strengthens the community and helps others get started. - **Share your feedback and feature requests**: Let us know what works, what doesn’t, and what features you’d like to see in future releases. - **Contribute your aspects**: If you’ve built reusable aspects, consider sharing them with the community. You can: - Publish them under your own GitHub account and let us know so we can reference them on the [Metalama Marketplace](https://postsharp.net/metalama/marketplace). - Contribute them to the `Metalama.Community` repository. - **Contribute to the core**: Bug fixes, small enhancements, and documentation improvements are always welcome. We’ll soon migrate our internal backlog to GitHub, giving you better visibility into what’s needed and how you can help. For more details, visit [the contributor guide](https://doc.metalama.net/contributing). ## What’s Next for Metalama Open-sourcing Metalama is a major step, but we’re not stopping here. Here’s what’s coming up next in the short term: - **May 2025** – We're migrating our internal issue tracker to GitHub. This will give you full visibility into what we’re working on and let you report bugs, track features, and contribute more easily. - **Summer 2025** – We’re working on extending Metalama’s capabilities to support code transformations on C# `event` accessors and `await` expressions. This unlocks new possibilities for cross-cutting concerns in asynchronous and event-driven codebases. - **Autumn 2025** – Full support for .NET 10 and the next version of C#. As always, we’ll stay aligned with the latest evolution of the .NET platform. We're committed to improving the framework and responding to community needs. Even though Metalama is now open source, we’re not handing it off and walking away. We’re staying fully engaged, with two full-time engineers (myself and Daniel) dedicated to the project. ## Conclusion Many developers, upon discovering Metalama, have said, **"This should be a standard part of .NET!"** By open-sourcing Metalama, we’ve done what we could to make that vision a reality. Whether you’re a solo developer looking to eliminate boilerplate or part of a team enforcing architecture across a large codebase, Metalama is here to help. If you want to explore meta-programming in C#, here’s where to start: - Visit the website at [metalama.net](https://postsharp.net/metalama) and explore [use cases](https://postsharp.net/metalama/applications). - Read the [documentation](https://doc.metalama.net), especially [commented examples](https://doc.metalama.net/examples). - Browse the [Metalama Marketplace](https://postsharp.net/metalama/marketplace) for ready-made aspects and examples: pull one instead of writing your own. - Study the source code on [GitHub](https://github.com/metalama/Metalama) and see how it works. We’ve laid the foundation, but the real momentum will come from the community. Together, we can push the boundaries of what C# can achieve. Happy meta-programming! Gaël --- # Metalama Status Update, March 2025 > In March, we published the first open-source build of Metalama. Published: 2025-03-31 · Author: Gaël Fraiteur · Canonical: https://postsharp.net/blog/metalama-status-update-2025-03 This month, we released a preview of Metalama 2025.1, the first open-source version of Metalama. We will continue the open-source transition in April. ## Metalama 2025.1: open-source preview Earlier this year, we shared our [plans to make Metalama open-source](https://postsharp.net/blog/metalama-open-source-plans). Since then, we’ve been working toward it. In March, we released the first preview of Metalama 2025.1, with most components now licensed under MIT. This is a major step toward transitioning Metalama to an open-core business model. ### What's left to do While we’re excited to share this progress with the Metalama community, this isn’t the big announcement yet. There’s still work to be done: - Fixing some bugs - Testing on VMs and physical devices - Launching a new website - Updating documentation and README files - Migrating the issue tracker to GitHub - Aligning sales pages and processes with the new offering ### Licensing notes At this stage, licenses for the new business model are not yet available for purchase. Existing Metalama commercial licenses remain compatible with the commercial parts of Metalama 2025.1. However, Metalama Free is not compatible. To use Visual Studio Tools for Metalama, you’ll need either a Metalama license key or to activate Metalama Community. Metalama Community is available for non-commercial users, individuals, companies with up to three users, and contributors to open-source projects. ### Resources to get started Here’s how you can explore Metalama 2025.1 today: - [Source code on GitHub](https://github.com/metalama/Metalama) - [Packages on NuGet](https://www.nuget.org/packages/Metalama.Framework/2025.1.1-preview) - [Visual Studio Tooling](https://www.postsharp.net/downloads/metalama/metalama-2025.1/v2025.1.1-preview/PostSharpMetalama.2025.1.1-preview.vsix) ## End of support for Metalama 2024.2 As per our [support policies](https://www.postsharp.net/support/policies), support for Metalama 2024.2 has ended. The only supported version now is Metalama 2025.0. ## Metalama 2025.0 maintenance builds We’ve released several maintenance builds for Metalama 2025.0 ([2025.0.9](https://github.com/orgs/metalama/discussions/395) and [2025.0.10](https://github.com/orgs/metalama/discussions/400)), addressing bugs and integrating the latest stable Roslyn build. ## Updated roadmap Our focus in April is to finalize the open-source transition. Here’s the updated timeline: - **First week of April:** Release Candidate (RC) - **Late April or Early May:** General Availability (GA) Thank you for your continued support. Happy meta-programming! -gael --- # Metalama Status Update, February 2025 > In February we've been busy preparing the open-source release, and are waiting for the legal side of the project. Published: 2025-02-27 · Author: Gaël Fraiteur · Canonical: https://postsharp.net/blog/metalama-status-update-2025-02 Last month, we shared our [plans to make Metalama open-source](https://postsharp.net/blog/metalama-open-source-plans), and throughout February we’ve been working to make it happen. We’ve made good technical progress, but we’re not quite ready to release yet. On the technical side, we’ve successfully modularized key features in preparation for the transition, allowing us to keep some components proprietary. However, the legal aspects of dual-licensing Metalama are still in progress. It’s a complex process, and we want to ensure everything is done right from the start. This is the main factor delaying the release of our source code and binaries. ### Updated Roadmap Here’s our revised timeline for the open-source release: - **First half of March:** First open-source preview release - **Second half of March:** Release Candidate (RC) - **Second half of April:** General Availability (GA) If the legal process takes longer than expected, we’ll use the extra time to introduce a new feature in the 2025.1 release: **intercepting event invocations**. Between the RC and GA releases, our focus will shift toward supporting infrastructure, including: - Launching a dedicated Metalama website - Migrating project and issue management to GitHub - Updating our e-shop and CRM systems ### Stay Tuned We’re committed to making this transition smooth. Thank you for your patience. We’re almost there. More updates soon. --- # Metalama Status Update, January 2025 > New this winter: Metalama and PostSharp 2025.0 Released, Metalama Goes Open-Source! Published: 2025-01-31 · Author: Gaël Fraiteur · Canonical: https://postsharp.net/blog/metalama-status-update-2025-01 After the winter break, we launched **Metalama and PostSharp 2025.0**. And we have bigger news: **Metalama is going open-source**. ## Metalama and PostSharp 2025.0 are generally available Following the release of Metalama and PostSharp 2025.0 Release Candidate (RC) in November, we dedicated much of December to squashing bugs. With Metalama maturing steadily, we’ve started addressing long-term, low-priority issues from our backlog. Our first major milestone of the year was deploying **PostSharp and Metalama 2025.0** on January 4th. Here’s what’s new: - **Metalama 2025.0**: - Adds support for **C# 13** and **.NET 9**. - Introduces significant enhancements to the aspect framework. - Delivers substantial performance improvements. Check out the [Metalama 2025.0 announcement](https://postsharp.net/blog/metalama-2025-0-ga). - **PostSharp 2025.0**: - Adds support for **C# 13**, **.NET 9**, and long paths in projects targeting **.NET Framework**. - Improves runtime performance for `WeakEventHandler`. - Enhances the debugging experience with **Windows PDB files**. Learn more in the [PostSharp 2025.0 announcement](https://postsharp.net/blog/postsharp-2025-0-ga). ## **Metalama 2025.1: going open-source** Earlier this week, we announced that [Metalama 2025.1 will be free and open-source](https://postsharp.net/blog/metalama-open-source-plans). To ensure the project’s sustainability, we’ll offer commercial IDE tooling, extensions, and support. We fully commit to a cooperative development process, moving everything to GitHub. Metalama will get a dedicated website and GitHub organization, marking its evolution into an independent, community-driven project separate from PostSharp Technologies. Visit the [announcement](https://postsharp.net/blog/metalama-open-source-plans) to learn more about: - What will be free and open-source, and what won’t? - Our new commercial offerings. - Our updated policies for long-term support. Your feedback shapes these changes, so share your thoughts while the plans are still taking form. ## Godot + Metalama, by Philip Rotter Philip Rotter Godot, the open-source game engine, has gained popularity among developers. While many prefer using C# over GDScript due to its rich ecosystem, exporting C# properties to Godot often involves tedious boilerplate code. In his latest guest post, [Enhancing Godot Development with Metalama](https://postsharp.net/blog/metalama-with-godot), [Philip Rotter](https://www.linkedin.com/in/philip-rotter-588a53259/) demonstrates how Metalama can streamline this process. Philip eliminates redundant code and makes C# development with Godot both fun and efficient, all through a single aspect. Thanks to Philip for this contribution, a useful starting point for Godot users trying Metalama for the first time. ## **Looking ahead** With Metalama now open source, 2025 is about building in the open. Share your feedback on [Slack](https://www.postsharp.net/) or [GitHub](https://github.com/orgs/metalama/discussions/388). Much of what we build next will start there. Happy meta-programming! --- # Enhancing Godot Development with Metalama > The article shows how to use Metalama to reduce boilerplate in Godot, automatically implementing the _ValidateProperty method. Published: 2025-01-29 · Author: Philip Rotter · Canonical: https://postsharp.net/blog/metalama-with-godot Godot is an amazing open-source game engine that provides developers with the tools to build games efficiently. While GDScript is Godot’s native language, many developers prefer to use C# for its rich ecosystem and the ability to leverage third-party libraries. One such library is [Metalama](https://www.postsharp.net/metalama), a powerful meta-programming framework that can streamline repetitive or error-prone tasks. In this article, we’ll explore how to use Metalama to simplify property usage customization in Godot. Specifically, we’ll address a common problem: defining property usage for exported properties in the Godot editor. > ## About the author > Philip Rotter > Philip Rotter is a full-stack developer and passionate game creator with a deep knowledge of diverse game engines and development tools. Currently, he’s focused on building Gradual Warfare, a mobile game developed using the Godot Engine. Gradual Warfare is a future-punk strategy game that blends auto-battle and idle mechanics for an engaging and dynamic player experience. Connect with him on [LinkedIn](https://www.linkedin.com/in/philip-rotter-588a53259/). ## The Problem: Boilerplate Code for Property Usage The Godot editor allows developers to customize components and resources by overriding the `_ValidateProperty` method and setting flags that define property behavior. However, managing property usage flags often results in repetitive and boilerplate-heavy code. For example, imagine you want to create a class with an exported string property that’s only useful in the editor and another property that is hidden in the editor but serialized for runtime use. Here’s how you would traditionally handle this: ```csharp [GlobalClass] [Tool] public partial class ArmyContainer : Resource { [Export] private string ExportString; [Export] public bool ContainsHello; public override void _ValidateProperty(Dictionary property) { if (property["name"].AsStringName() == nameof(ExportString)) { property["usage"] = (int)PropertyUsageFlags.Editor; } else if (property["name"].AsStringName() == nameof(ContainsHello)) { property["usage"] = (int)PropertyUsageFlags.NoEditor; } } } ``` This approach works but quickly becomes cumbersome as the number of properties grows. What if we could eliminate this boilerplate and simplify the code? ## The Solution: Attributes and Metalama Using Metalama, we can replace the repetitive code with clean, declarative attributes. Here’s what the rewritten class will look like: ```csharp [GlobalClass] [Tool] public partial class ArmyContainer : Resource { [Export] [OnlyInEditor] private string ExportString; [Export] [HideInEditor] public bool ContainsHello; } ``` Much cleaner, right? With the help of Metalama, we’ll make these attributes automatically handle property usage without manually overriding `_ValidateProperty`. ## Step 1: Define Custom Attributes First, we’ll define the base attribute, `GenPropertyUsageAttribute`, which will serve as the foundation for specific attributes like `OnlyInEditor` and `HideInEditor`. ```csharp [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] [RunTimeOrCompileTime] public class GenPropertyUsageAttribute : Attribute { // Default value: 6 (store and show in editor) public int PropertyUsageFlags { get; set; } = 6; public GenPropertyUsageAttribute() { } // Constructor to override default PropertyUsageFlags public GenPropertyUsageAttribute(int propertyUsageFlags) { PropertyUsageFlags = propertyUsageFlags; } } ``` Now, let’s create two derived attributes for common use cases: - `OnlyInEditor`: The property is visible in the editor but not stored. - `HideInEditor`: The property is stored but not visible in the editor. ```csharp [RunTimeOrCompileTime] public class OnlyInEditorAttribute : GenPropertyUsageAttribute { public static new int PropertyUsageFlags { get; set; } = 4; // Editor-only, no storage public OnlyInEditorAttribute() : base(PropertyUsageFlags) { } } [RunTimeOrCompileTime] public class HideInEditorAttribute : GenPropertyUsageAttribute { public new static int PropertyUsageFlags { get; set; } = 2; // No editor, storage only public HideInEditorAttribute() : base(PropertyUsageFlags) { } } ``` ## Step 2: Create the Aspect Next, we’ll create a Metalama aspect that scans for attributes like `OnlyInEditor` and `HideInEditor`, then injects logic into the `_ValidateProperty` method to apply the appropriate `PropertyUsageFlags`. ```csharp internal sealed class GenPropertyUsageTypeAspect : IAspect { public void BuildEligibility(IEligibilityBuilder builder) { builder.MustHaveAttributeOfType(typeof(ToolAttribute)); builder.MustBeConvertibleTo(typeof(GodotObject)); } public void BuildAspect(IAspectBuilder builder) { // Select fields that have the [GenPropertyUsage] attribute. var fields = builder.Target.AllFieldsAndProperties .Where(property => property.Attributes.OfAttributeType(typeof(GenPropertyUsageAttribute)).Any()); // Skip the aspect if we don't have any field. if (!fields.Any()) { builder.SkipAspect(); return; } // Introduce a method named _ValidateProperty // using the template ValidatePropertyTemplate defined below. builder.IntroduceMethod( nameof(this._ValidateProperty), tags: new { Fields = fields }, whenExists: OverrideStrategy.Override ); } // Template for the new _ValidateProperty method. [Template] public void _ValidateProperty(Dictionary property) { // Call the base method or the hand-written implementation, if any. meta.Proceed(); var name = property["name"].AsStringName(); // Enumerate all fields identified in the BuildAspect method. // (This loop executes at build time.) foreach (var classFieldOrProperty in (IEnumerable)meta.Tags["Fields"]) { if (name == classFieldOrProperty.Name) { var attribute = classFieldOrProperty .Attributes .GetConstructedAttributesOfType() .First(); property["usage"] = attribute.PropertyUsageFlags; } } } } ``` ## Step 3: Create the Fabric Finally, we’ll use a Metalama fabric to apply our aspect to all eligible classes. ```csharp internal sealed class GenPropertyUsageTypeFabric : ProjectFabric { public override void AmendProject(IProjectAmender amender) { amender .SelectDeclarationsWithAttribute(typeof(ToolAttribute)) .OfType() .AddAspectIfEligible(t => new GenPropertyUsageTypeAspect()); } } ``` ## Testing the Result To test the implementation, you can create a `Tool` script in Godot with annotated properties: ```csharp [GlobalClass] [Tool] public partial class ArmyContainer : Resource { [Export] [OnlyInEditor] private string ExportString; [Export] [HideInEditor] public bool ContainsHello; } ``` In the Godot editor: - `ExportString` will be visible but not serialized. - `ContainsHello` will be serialized but not visible in the editor. This approach eliminates boilerplate code and makes property management much cleaner. --- ## Conclusion With Metalama, we extended Godot’s functionality by automating the customization of property usage. This approach saves time, reduces boilerplate, and minimizes errors. By leveraging attributes and meta-programming, we can focus on creating games rather than managing tedious editor logic. Stay tuned for more tutorials on how to enhance Godot development with powerful tools like Metalama. Have fun coding! --- # Metalama 2025.1 Will Be Open-Source > Metalama 2025.1 will be free and open-source. Its IDE tooling and some extensions will be sold under a proprietary license. Published: 2025-01-27 · Author: Gaël Fraiteur · Canonical: https://postsharp.net/blog/metalama-open-source-plans We’re thrilled to announce that **Metalama**, the most complete meta-programming framework for .NET, will soon become a free and open-source project. This is a major milestone for us and the .NET community, and we’re keen to share our plans and gather your feedback as we move forward. ## What is Metalama? [Metalama](https://www.postsharp.net/metalama) is a framework that enables developers to write cleaner, more maintainable code by automating repetitive tasks and enforcing architectural rules. Built on top of Roslyn, it allows you to write classes called _aspects_ that modify other code at compile time, reducing boilerplate and improving productivity, typically by 15%. Metalama provides a unique combination of simplicity, power, and extensibility thanks to its [type-safe C#-to-C# code templates](https://doc.metalama.net/conceptual/aspects/templates) that let you add new behaviors to hand-written code. After over ten person-years of work, it is now a stable and mature product, and we’re ready to share it with the world. If you haven't heard about Metalama before, you can give it a try now and follow this [getting started guide](https://doc.metalama.net/conceptual/getting-started), knowing that most features will soon be free and open-source. ## Why open source? We believe that open-sourcing Metalama will remove barriers to adoption and foster innovation within the .NET community. Over the past year, we’ve seen how difficult it can be for a framework to gain traction with a commercial-first approach. By making Metalama open-source, we aim to: - Encourage broader adoption and experimentation. - Build a vibrant community of contributors and users. - Ensure the long-term sustainability of the project through a new business model focused on tooling, extensions, and support. - Give the community a real say in Metalama’s future, with our continued commitment to its development and maintenance. ## What’s changing? As we transition Metalama to our new model, you will see that most of the components will be made open-source, while a few will stay proprietary. ### Open-source components We’re open-sourcing the majority of Metalama, including: - **Metalama.Compiler**: Our Roslyn fork allowing for code transformation. - **Metalama.Framework**: The core meta-programming framework. - **Metalama.Extensions**: Support for dependency injection and metrics. - **Metalama.Testing**: Our unit testing frameworks. - **Metalama.Patterns**: A collection of ready-made aspect libraries. - **Metalama.LinqPad**: A LINQPad driver for querying .NET solutions. We are leaning toward the **Apache 2.0** license, but this decision is not final. ### Proprietary components To ensure the project’s sustainability, some components will remain proprietary, including: - **Visual Studio Tooling**: Free for non-commercial use and small teams (up to 3 users per organization). - **Architecture Validation**: Advanced library for verifying hand-written code against architectural rules. - **Code Fixes and Refactorings**: Automated fixes for warnings reported by aspects. These components will be available through our commercial offerings. ## Our commercial offering To fund the ongoing development and maintenance of Metalama, we’ll offer four editions: | Edition | Price | Features | Support Level | |-----------------|---------------------|--------------------------------------------------------------------------|------------------------| | **Open Source** | Free | Core framework
Testing frameworks
Most extensions
All aspect libraries | Community support | | **Community** | Free for:
- non-commercial use,
- individuals,
- teams of 3 or less. | Open-source features
+ Visual Studio Tooling | Community support | | **Professional**| From $95/user/year | Community features
+ Architecture Validation
+ Code Fixes | Basic professional support | | **Enterprise** | From $5,000/year for 15 users | Professional features | Full support
Access to source code
Long-term maintenance | ## Dual licensing model and long-term maintenance To support both open-source and commercial users, we’re adopting a dual licensing model: - New developments and bug fixes in the latest version will always be open-source. - Maintenance of older versions will be available under a proprietary license for professional and enterprise customers: - Metalama Professional: 6 months back, - Metalama Enterprise: 2 years back for LTS versions. This approach ensures that Metalama remains sustainable while giving the community access to the latest innovations. ## Contributed pull requests We intend to encourage and accept pull requests from the community into our main repos as long as they meet the same quality criteria we apply to ourselves. You’ll need to sign a **Contributor License Agreement (CLA)**, similar to the one used by the .NET Foundation. This ensures we can continue to offer long-term support to enterprise customers. ## Timeline Here’s what to expect in the coming months: - **Late February 2025**: First open-source preview release. - **April 2025**: General availability of Metalama 2025.1. We’ll share regular updates on our progress. ## Why this matters We think this could become one of the biggest news in 2025 for the .NET community. Metalama will be one of the largest and most complex non-Microsoft open-source projects in .NET. But this is significant even beyond the .NET ecosystem. Metalama brings many innovations in the realm of meta-programming. It is the most advanced implementation of the concepts behind aspect-oriented programming, but completely reimagined for a modern programming language. ## What’s next? We're now in the process of reorganizing our codebases. We’re eager to hear your feedback and ideas as we finalize our plans. Share your thoughts on [GitHub](https://github.com/orgs/metalama/discussions/388) or join the conversation in our [Slack workspace](https://www.postsharp.net/slack). If you haven't tried Metalama before, you can start today, keeping in mind that a few namespaces will not be open-sourced. Together, we can build a strong ecosystem around Metalama that benefits the entire .NET community. Thanks for being part of it. --- # Metalama 2025.0 Generally Available: Support for C# 13 and .NET 9, Aspect Framework Improvements, and More > Announcing the general availability of Metalama 2025.0: support for C# 13, .NET 9, Roslyn generators and interceptors; many improvements in the aspect framework; nearly doubled performance; async and background WPF commands. Published: 2025-01-04 · Author: Gaël Fraiteur · Canonical: https://postsharp.net/blog/metalama-2025-0-ga Metalama 2025.0 is now generally available! This release supports C# 13 and .NET 9, adds significant enhancements to the aspect framework, and nearly doubles performance. This article highlights the most significant improvements. For a detailed discussion, see the [release notes](https://doc.metalama.net/conceptual/release-notes/release-notes-2025-0) of Metalama 2025.0. Note that we are simultaneously releasing [PostSharp 2025.0](https://postsharp.net/blog/postsharp-2025-0-ga). ## C# 13 and .NET 9 The primary goal of Metalama 2025.0 is to support the new .NET stack. All new C# features and the new .NET SDK have been implemented and tested. This includes: - `params` collections - `ref`/`unsafe` in iterators and async methods - `ref struct` can implement interfaces - Classes can have `ref struct` constraints - New escape character in strings - Locking on the `Lock` class - Implicit indexer access in object initializers - Overload resolution priority - `partial` properties We've updated third-party dependencies. Note that the minimum supported version of Visual Studio is now 17.6 LTSC (Roslyn 4.4). ## Support for code generators and interceptors We've improved the integration of Metalama with code generators and interceptors. We now consistently execute source generators _after_ any Metalama transformation. This approach has three benefits: - It offers a consistent experience both at design time and build time. - Aspects can introduce code that relies on code generators, such as the `[GeneratedRegex]` attribute. - You can now use Roslyn interceptors alongside Metalama since they no longer conflict with our code transformations. The downside is that you can no longer use Metalama aspects in Razor (`.cshtml`) pages. ## Aspect framework improvements - **Closing gaps with introduced types.** You can now use introduced types in type constructs like `Foo[]`, `List`, `Foo`, or `Foo*`, where `Foo` is the introduced type. - **Introduction of static virtual, abstract, and partial members.** We've lifted several limitations in member introductions. - **Introduction of generic interfaces.** You can now implement a generic interface without limitation. Previously, for a class `Bar`, it was impossible to implement an interface `IFoo`, which was crucial to implement, for instance, comparison patterns. - **Introduction of extension methods.** You can now introduce extension methods by adding the `[This]` attribute to the first parameter of the template method or setting the `IParameterBuilder.IsThis` property. - **Dynamic definition of local variables.** You can now dynamically define a local variable in a T# template thanks to the new `meta.DefineLocalVariable` method, which returns an `IExpression`. - **Suppression of well-known warnings in aspects.** We now suppress some compiler warnings in aspect code because they are generally irrelevant given the meta-programming nature of the code. - **Clean-up of IRef and overridden field representation.** We've refactored the implementation (and, to some extent, the public API) of the `IRef` interface and made the representation of overridden fields more consistent. ## Async and background WPF commands The `[Command]` aspect now supports asynchronous and background commands: - Asynchronous commands are those that return a `Task`. - Background commands, whether synchronous or not, execute in a background thread instead of the UI thread. Both asynchronous and background commands can be canceled through a `CancellationToken` parameter. Thanks to extensive refactoring of the code model and other efforts, Metalama 2025.0 is almost twice as fast as 2024.2 under a typical workload. The overhead of Metalama 2025.0 is about 60% of the overhead of the previous version. You should now find your builds are between 25% and 35% slower with Metalama than without it, depending on the number of aspects. At 25% to 35% overhead, performance should no longer be a concern when adopting Metalama. For precise measurements and methodology, see [this blog post](https://postsharp.net/blog/metalama-performance). ## Bug fixes We fixed 43 bugs in this release, most of them affecting the design-time experience. We are monitoring telemetry data to identify and prioritize remaining issues. ## Breaking changes As noted above, we've updated all third-party dependencies to their latest versions. Also, the minimum supported version of Visual Studio is now 17.6 LTSC (Roslyn 4.4). Additionally, we must declare a few breaking changes in the public API of `IRef` and `Metalama.Patterns.Commands`. For details, see the [release notes](https://doc.metalama.net/conceptual/release-notes/release-notes-2025-0#breaking-changes) ## Conclusion Metalama 2025.0 is a strong release from several angles. It is a platform update, and it also closes most feature gaps in the aspect framework and nearly doubles performance. Note that implementing these improvements required extensive refactoring of the code model. As a result, we may have destabilized some use cases that were not covered by any tests. Please be careful when updating to 2025.0 (i.e., don't do it in a rush), and don't hesitate to report any issues. We're looking forward to your feedback. Happy meta-programming with Metalama! --- # PostSharp 2025.0 Generally Available: Support for C# 13, .NET 9 and More > PostSharp 2025.0 is now generally available. It includes support for C# 13, .NET 9, and long paths for .NET Framework projects. It improves the support for Windows PDB and fixes a performance issue. Published: 2025-01-04 · Author: Gaël Fraiteur · Canonical: https://postsharp.net/blog/postsharp-2025-0-ga Alongside [Metalama 2025.0](https://postsharp.net/blog/metalama-2025-0-ga), we're announcing the general availability of PostSharp 2025.0. This release focuses on supporting C# 13 and .NET 9 while also including improvements like long path support for .NET Framework projects, runtime performance enhancements for `WeakEventHandler`, and a better debugging experience for projects using Windows PDB. ## C# 13 and .NET 9 We've thoroughly tested and enhanced PostSharp to support the new .NET 9 SDK and C# 13. Through this effort, we identified and resolved some old bugs. One was related to parameters of type `ref struct`, and the other involved the mapping of generic methods. ## Improved Windows PDB support We've rewritten the component that handles the Windows PDB format. Our previous implementation relied on source code from the outdated Microsoft CCI project, and was no longer maintainable. Our new implementation utilizes the `ISymUnmanagedReader` COM interface and should automatically benefit from all fixes and enhancements in the .NET Framework and Visual Studio. This work resolves several issues affecting the debugging experience when using Windows PDB files. It also makes it easier for us to implement support for new PDB features. ## Long path support We've finally addressed a limitation affecting projects targeting the .NET Framework: they had to remain under a directory with a path length shorter than 256 characters. We're both pleased and a bit embarrassed (considering how long it took) to say that you can now store your projects as deep in your file system as you like. ## WeakEventHandler performance We've improved the performance of the `WeakEventHandler` class (used, among other things, in the `[NotifyPropertyChanged]` aspect) in cases with a large number of subscribers. With 10,000 subscribers, our implementation could be 25 times slower than `MulticastDelegate` when removing subscribers. Yes, Big O analysis matters! We've fixed this issue, and we're happy to announce that our `WeakEventHandler` now has performance characteristics similar to `MulticastDelegate`. ## Conclusion While our new development efforts are focused on [Metalama](https://postsharp.net/blog/metalama-2025-0-ga), PostSharp 2025.0 demonstrates our commitment to supporting loyal customers with the latest .NET stack while maintaining compatibility with the legacy .NET Framework as long as possible. We continually address issues that affect our customers and plan to remain a reliable partner in the future. Happy meta-programming with PostSharp! --- # Hi-Res Logging in .NET Aspire Without Touching Business Code > Trace method calls in a .NET Aspire distributed cloud-native app with minimal boilerplate, using the Metalama [Log] aspect and a fabric. Published: 2024-12-18 · Author: Metalama Team · Canonical: https://postsharp.net/blog/aspire-logging-metalama A .NET Aspire app gives you distributed tracing and metrics almost for free, except the log lines you still hand-write in every method. Here's how to delete them: add the Metalama `[Log]` aspect to every public method with one fabric, then read the result in the Aspire dashboard. ## Introduction There's a saying that computers always do what you tell them to do, which isn't necessarily what you want them to do. When you find yourself in such a situation and it's not clear where the difference originates, you have two options: - **Debugging** requires the app to be interrupted and it may not always be possible to attach to the failing instance or reproduce the behavior with another one. - **Tracing** allows you to follow the execution of the app without interrupting it, providing a detailed log of what happened. Here is how to trace _method calls_ in a .NET Aspire app without boilerplate code, using Metalama. As we discussed in the article about [caching in .NET Aspire](https://postsharp.net/blog/aspire-caching-metalama), .NET Aspire is a development platform that simplifies building distributed cloud-native applications. It manages all the wiring between the various services of your application and other components like databases, messaging, and caching. Using Open Telemetry, it provides essential features for the [observability of your app](https://learn.microsoft.com/en-us/dotnet/aspire/fundamentals/telemetry), such as logging, tracing, and metrics. ## Step 1. Setting up the projects To show you how to trace method calls in a .NET Aspire app, we'll use a base example of a to-do list app. The app consists of an ASP.NET Core Minimal API backend and a Blazor front-end, orchestrated using .NET Aspire. The app is structured as a solution with multiple projects, each serving a different purpose. The solution contains the following projects: * The _app host_ project, containing the service orchestration. You can think of this project as the bootstrapper of other projects. * The _web API_ project, * The _web front-end_ project, * The _service default_ project that contains shared service configuration, * The _data_ project shared between the API and front-end projects, * The _aspects_ project mentioned above implementing the `LogAttribute` and `LogAllPublicMethodsFabric`. The _aspects_ project is then referenced in both the _web API_ and the _web front-end_ projects. The full source code of examples in this article is available on [GitHub](https://github.com/postsharp/TimelessDotNetEngineer/tree/main/src/aspire/logging-metalama). When you run the app, you should see this: Front-end with one finished and one unfinished task. ## Step 2. Creating a logging aspect An _aspect_ is a special kind of class, usually a custom attribute, that executes within the compiler or the IDE and dynamically transforms your source code. There are several [aspect frameworks](https://www.postsharp.net/solutions/aspect-oriented-programming) for .NET. Here we'll use the most modern of them: [Metalama](https://www.postsharp.net/metalama). We will use the [example logging aspect](https://doc.metalama.net/examples/log) of the reference documentation. Here is the `[Log]` aspect in real code: ```csharp public partial class TodoApiClient( HttpClient httpClient ) { public async Task GetTodosAsync( CancellationToken cancellationToken = default ) => await httpClient.GetFromJsonAsync( "/todo", cancellationToken ); } ``` Under the hood, the `[Log]` aspect: - Pulls the [ILogger](https://learn.microsoft.com/en-us/dotnet/core/extensions/logging?tabs=command-line) service into your class, - Logs the method entry, including the argument values, - Logs the method exit, including the returned value and output parameter values, - Logs the method failure, including the exception message. The source code is transformed into this before it is compiled into assembly instructions: ```csharp public partial class TodoApiClient { private readonly ILogger _logger; private readonly HttpClient httpClient; public TodoApiClient(HttpClient httpClient, ILogger ) { this.httpClient = httpClient; this._logger = logger; } public async Task GetTodosAsync( CancellationToken cancellationToken = default ) { var isTracingEnabled = _logger.IsEnabled(LogLevel.Trace); if (isTracingEnabled) { using (var guard = LoggingRecursionGuard.Begin()) { if (guard.CanLog) { _logger.LogTrace($"TodoApiClient.GetTodosAsync({{cancellationToken}}) started.", [cancellationToken]!); } } } try { var result = await this.GetTodosAsync_Source(cancellationToken); if (isTracingEnabled) { using (var guard_1 = LoggingRecursionGuard.Begin()) { if (guard_1.CanLog) { _logger.LogTrace($"TodoApiClient.GetTodosAsync({{cancellationToken}}) returned {{result}}.", [cancellationToken, result]!); } } } return result; } catch (Exception e) when (_logger.IsEnabled(LogLevel.Warning)) { using (var guard_2 = LoggingRecursionGuard.Begin()) { if (guard_2.CanLog) { _logger.LogWarning($"TodoApiClient.GetTodosAsync({{cancellationToken}}) failed: {e.Message}", [cancellationToken]!); } } throw; } } } ``` This code is arguably very verbose, but you don't have to write it by hand! To make it more compact, you could use some [string interpolation techniques](https://postsharp.net/blog/structured-logging-with-string-interpolation). ## Step 3. Adding logging to all public methods In the previous step, we've shown how to add logging without _almost_ any change in the source code. However, you still had to add a custom attribute to every single method. This is still quite cumbersome! It would be handy to have some sort of SQL that would allow me to "select" all the public methods and then iterate over the result to apply the [Log] aspect... no? This is where [the fabrics](https://doc.metalama.net/conceptual/using/fabrics) come in. In a nutshell, a fabric is a feature of Metalama that allows you to apply aspects in bulk. In the next example, we're going to use the [transitive project fabric](https://doc.metalama.net/api/metalama-framework-fabrics-transitiveprojectfabric). A transitive fabric is a build-time entry point that is executed when any project _referencing_ the project containing that fabric is built. The same works, of course, at design time. Here is the code of the fabric: ```csharp internal class LogAllPublicMethodsFabric : TransitiveProjectFabric { public override void AmendProject( IProjectAmender amender ) => amender .SelectTypes() .Where( type => type.Accessibility == Accessibility.Public ) .SelectMany( type => type.Methods ) .Where( method => method.Accessibility == Accessibility.Public && method.Name != "ToString" ) .AddAspectIfEligible(); } ``` The previous code snippet shows a fabric that adds the `LogAttribute` aspect (that's the name of the class that implements the `[Log]` aspect) to all public methods (except `ToString`). Since we're adding the transitive fabric to the `TodoList.Aspects` project, all projects referencing `TodoList.Aspects` now have logging on their public methods. This approach works for classes that are instantiated by the [.NET dependency injection](https://learn.microsoft.com/en-us/dotnet/core/extensions/dependency-injection) container because it relies on the [ILogger](https://learn.microsoft.com/en-us/dotnet/core/extensions/logging) service. You will have to pass this dependency manually to classes that are instantiated manually, without the DI container. The fabric can be tuned for a different set of methods: all information about all methods is available, so your filter can be as fine-grained as you need. ## Step 4. Analyzing the logs .NET Aspire comes with a handy dashboard that includes a tab showing structured logs. There, you can see all the logs of all the services of your application. To check that our method calls are being logged (and the fabric and aspect work), we've added some tasks to the to-do list app and marked one of them as completed. Back to the .NET Aspire dashboard, on the "Structured" tab, the log records can be filtered according to any parameter, including a trace ID, so you can view individual requests. The next screenshot shows all the filtered log records coming from our `TodoService` class of the API service: .NET Aspire dashboard showing structured log of method calls. The values shown on the dashboard can be further improved using a [custom log record processor](https://github.com/open-telemetry/opentelemetry-dotnet/blob/main/docs/logs/extending-the-sdk/README.md#processor), as we did in our sample, or by wiring up a logging framework with better support for structured logging of .NET objects like [Serilog](https://serilog.net/). ## Summary In this article, we've shown how to trace method calls in a .NET Aspire app without boilerplate code using Metalama. We've used a base example of a to-do list app with an ASP.NET Core Minimal API backend and a Blazor front-end, orchestrated using .NET Aspire. We've demonstrated how to log all public methods of the app using the `[Log]` aspect using a special kind of class called a fabric. We've also shown how to analyze the logs using the .NET Aspire dashboard. --- # The State of Aspect-Oriented Programming in C# [2025] > This article reviews the state of aspect-oriented programming in C# and .NET in 2025. It addresses misconceptions, lists and compares implementations, and studies how the main challenges have been resolved. Published: 2024-12-09 · Author: Gaël Fraiteur · Canonical: https://postsharp.net/blog/state-of-aop Aspect-oriented programming (AOP) was invented in the early 2000s and enjoyed a few years of hype. Although it has been implemented in many languages, it seems to have faded into the background. Even Metalama, the most comprehensive AOP framework for .NET, does not primarily market itself as an AOP framework. What ever happened to AOP, and what is its status in the .NET world today? I'm writing this article in the context of the [C# Advent 2024](https://www.csadvent.christmas/) as a wink to its creator [Matt Groves](https://www.linkedin.com/in/mgroves/). In 2013, Matt wrote a book named [AOP in .NET](https://www.manning.com/books/aop-in-net), covering both PostSharp and Castle DynamicProxy. This article reviews how the .NET landscape has evolved since that book was written. ## What is aspect-oriented programming? If you haven't heard about AOP before, it's a programming concept that extends Object-Oriented Programming and focuses on separating the implementation of _repetitive code patterns_ from the main business logic, improving modularity and separation of concerns. Without AOP, repetitive code patterns are often required to implement _cross-cutting concerns_, i.e., non-business features, such as logging, security, observability, or thread synchronization, that apply to several business features. With AOP, you can encapsulate these code patterns in a special kind of class named _aspects_. Aspects know how to mechanically add behaviors to your source code. For instance, you can create aspects for logging, security, and observability or implement design patterns such as Memento or Builder. ### Example: a caching aspect Here's how to implement a _caching_ aspect using Metalama. Here is the implementation of an aspect that adds logging to its target method: ```csharp public class CacheAttribute : OverrideMethodAspect { // Pull a dependency into the target class. [IntroduceDependency] private readonly ICache _cache; public override dynamic? OverrideMethod() { // Builds the caching string. var cacheKey = CacheKeyBuilder.BuildCacheKey( meta.Target.Method ).ToValue(); // Cache lookup. if ( this._cache.TryGetValue( cacheKey, out object value ) ) { // Cache hit. return value; } // Cache miss. Go and invoke the method. var result = meta.Proceed(); // Add to cache. this._cache.TryAdd( cacheKey, result ); return result; } } ``` In the code above, `OverrideMethod` is a _template_ applied to the method to which we want to add caching. You can consider `meta` as a "magic" keyword (actually, it's a static class) that gives you access to meta-programming features. `meta.Proceed()` is replaced by the _original_ method implementation (before caching is applied), and `meta.Target.Method` represents the method to which the aspect is applied. `CacheKeyBuilder` is a compile-time utility method. Read [this article](https://doc.metalama.net/examples/caching) for details about this aspect. Since this class is a custom attribute, we can add it to any method we want to cache. Here is our _source_ code, i.e., the one we write manually and commit to _git_: ```csharp public class Calculator { public int InvocationCounts { get; private set; } [Cache] public int Add( int a, int b ) { Console.WriteLine( "Thinking..." ); this.InvocationCounts++; return a + b; } } ``` At compile time, Metalama transparently transforms your source code using the `[Cache]` aspect. Here is the code that gets _executed_: ```csharp public class Calculator { ICache _cache; public Calculator( ICache cache ) { this._cache = cache; } public int InvocationCounts { get; private set; } [Cache] public int Add( int a, int b ) { var cacheKey = $"Calculator.Add({a}, {b})"; if ( this._cache.TryGetValue( cacheKey, out object value ) ) { return (int) value; } Console.WriteLine( "Thinking..." ); this.InvocationCounts++; var result = a + b; this._cache.TryAdd( cacheKey, result ); return result; } } ``` As you can see, the aspect: 1. Pulls the new `ICache` dependency into the `Calculator` class, and 2. Overrides the `Add` method to add caching. Your caching logic is now decoupled from your business logic. You can apply caching to dozens of methods without repeating yourself. That sounds nice. So, why isn't AOP more popular in .NET today? ## A horrendous communication strategy Before we go through the state of AOP in .NET, I'd like to rant about the _horrendous_ communication strategy chosen by the AOP researchers in the early 2000s. Remember, when you're an innovator, your role is not only to come up with a _technology_ or an _implementation_. Most importantly, as an innovator, your role is also to imagine new _concepts_ to analyze the world and a new _language_ to describe it. This discipline is known as _messaging and positioning_ in technical marketing. It eventually defines how people will talk about your new product: which words they will use and how they will frame the technology when talking to their friends. Microsoft is especially good at this exercise. Looking closely, you will never see a product published without a strong communication framework. Occasionally, they will have an excellent communication framework with a very poor product! That's because Microsoft is not an innovative company... The first mistake of AOP founders was positioning the technology as a _paradigm_ and to name it aspect _oriented_ programming. This choice consequently framed AOP as an _alternative_ to object-oriented programming, which I think was a big mistake. In my opinion, AOP is _not_ a paradigm of its own. It is an _extension_ of object-oriented programming, certainly not an alternative. Another communication mistake was to rely on jargon terms like _advice_, _joinpoint_, _pointcut_ or even _cross-cutting concerns_. This leads to this terrible definition: _Aspect-oriented programming is a programming paradigm aiming to improve the separation of cross-cutting concerns by encapsulating them into aspects, which use pointcuts to add advice to select joinpoints_. Although I think this initial lexicon was a mistake, it was probably understandable given the context of the early 2000s. When AspectJ was released, Java did not have custom attributes yet (called _annotations_ in Java). Other than using specially formatted comments, there was no way to specify that some methods should be logged or made transactional. Therefore, the researchers had to invent a mechanism to select which elements of code would be advised, and this mechanism was called _pointcut_. From the early days, AOP was positioned as a technology that aimed to extend the longevity of codebases by making it possible to alter their behavior without modifying their source code by adding aspect files purely orthogonal to the original source. Some people and OOP influencers misunderstood AOP as a _hacking_ or _patching_ technology to get around bad object-oriented designs. As with any tool, we must acknowledge that AOP can be used for better or worse. Thankfully, after a quarter of a century of existence, we now understand better how to talk about aspect-oriented programming, when it is useful, and when it is harmful. ## Talking about AOP today Despite the progress of programming languages and software engineering practices, despite the catastrophic communication around AspectJ, I still think that classic languages have not completely addressed the gap identified by the early AOP researchers. I'm talking about the abstraction gap between human reasoning and programming languages. Software architects, like all humans, think in terms of _patterns_ and say things like: "Add logging to all public methods of public classes in namespace `X`, and implement `INotifyPropertyChanged` for all classes derived from `BaseEntity`." However, the C# language does not have a way to do "for each declaration `X`, add behavior `Y`." Aspect-oriented programming (AOP) comes as a mechanism to fill this abstraction gap. [Aspect-oriented programming](https://www.postsharp.net/solutions/aspect-oriented-programming) is a form of meta-programming that operates at the same level of abstraction as the C# language. It attempts to _raise_ the level of abstraction, not _lower_ it. Therefore, IL rewriting is _not_ AOP. Nor are Roslyn source generators. The question is whether aspect-oriented programming, redefined as a mechanism to bridge this abstraction gap, is still AOP. Due to communication issues, we no longer primarily position Metalama, our flagship product, as an AOP framework. We say that it is a code generation and validation framework. Like AOP, Metalama encourages well-engineered practices. It focuses on robustness and predictability. It aims to make the code more understandable, not less. Yet, Metalama is now the most extensive and mature AOP framework for .NET. ## Is AOP still useful in modern C#? AOP was formalized when Java and C# were still in their infancy. In the meantime, both languages and development practices have evolved. The generalization of higher-order functions (delegates, anonymous methods, lambdas) and dependency injection have greatly improved modularity, covering some of the initial use cases of aspect-oriented programming. Therefore, some developers question the usefulness of AOP in modern applications. * **Higher-order functions** (methods accepting delegates) can help encapsulate cross-cutting logic like, for instance, caching (see the [IMemoryCache.GetOrCreateAsync](https://learn.microsoft.com/en-us/dotnet/api/microsoft.extensions.caching.memory.cacheextensions.getorcreateasync) extension method). However, they still have limitations: - You still have to manually pass the context (i.e., parameters), and - You still have to manually add a method call everywhere, potentially in hundreds of methods. AOP solves these two problems. * **Dependency injection** has popularized the practice of separating interfaces from implementations, which is especially useful for unit testing. This design allows the addition of behaviors through _proxies_ (often dynamically generated ones) instead of by modifying the source or IL code. However, this approach only allows the addition of behaviors in the communication lines _between_ components. Unlike compile-time AOP, dependency injection does not allow you to add behaviors _inside_ components. In .NET, examples of code patterns that are still better handled by AOP than by other mechanisms include: * Instrumentation: [logging](https://doc.metalama.net/examples/log), profiling; * Security: authorization, audit, encryption; * Resilience: [caching](https://postsharp.net/blog/aspire-caching-metalama), transaction handling, [exception handling](https://doc.metalama.net/examples/exception-handling); * UI: [observability](https://postsharp.net/blog/inotifypropertychanged-metalama) (`INotifyPropertyChanged`), change tracking, undo/redo, WPF [dependency properties](https://postsharp.net/blog/wpf-dependency-property-metalama) and [commands](https://postsharp.net/blog/wpf-command-metalama); * Classic design patterns: [Builder](https://postsharp.net/blog/builder-pattern-with-metalama), [Singleton](https://doc.metalama.net/examples/singleton), [Memento](https://doc.metalama.net/examples/memento), Factory, Proxy, [Decorator](https://postsharp.net/blog/decorator-pattern); * Multi-threading: synchronization (locking), freezable, background execution; * Classic object services: equality, `ToString`; * Persistence: object-database mapping, serialization, object-configuration mapping. ## When to use AOP? I often hear you can implement the above use cases with standard OOP. To this criticism, I want to refute this: Yes, you are right. There is nothing you can do with AOP that OOP alone cannot do. Eventually, all languages are compiled into assembly language. So, this is a question of cost and complexity rather than an all-or-nothing ability. Without AOP, your initial costs of implementing these features will certainly be lower. But your _marginal_ cost will be higher. You'll pay the cost of coding the same pattern hundreds of times and also the cost of refactoring if you ever change your mind. With AOP, you are trading a near-zero marginal cost for a higher initial cost. The same applies to complexity. Yes, using AOP adds complexity, but this is a one-off, while _not_ using AOP adds complexity for each new feature added to your product. With AOP, scale is everything. Just because you need to cache one or two methods does not mean that you should use an aspect for it. As a rule of thumb, you need _dozens_ of uses of an aspect to justify building one. _Getting Started_ guides will always insist on how easy it is to create something easy. Perhaps. But with aspects, the devil is in the detail. It can take days to create a robust aspect, one that will act as a productivity multiplier for your team instead of one of those the-boss-wants-us-to-use-it libraries. Of course, if you're using ready-made, tested aspects ([and we release a lot of them open-source for free](https://doc.metalama.net/patterns)), it will pay off sooner than if you build your own. Let's be honest. AOP frameworks like PostSharp or Metalama are heavyweight dependencies. You don't want them in your projects until it _really_ makes a difference. In a small project, it's probably not going to make a big difference. However, one of our customers confessed to having saved _tens of millions of euros_ thanks to AOP. ## How is AOP implemented in .NET today? Today's aspect-oriented frameworks all rely on one of the following approaches: * [MSIL Rewriting](https://www.postsharp.net/solutions/msil-rewriting) is the process of modifying the binary assembly during the build process. An additional step is added to this process just after the C# compiler. This approach was pioneered by PostSharp when the C# compiler was a black box. It is now considered obsolete, but many tools still rely on it. * **Roslyn-based** frameworks interact directly with the compiler without requiring an additional process. They are based on official Roslyn extension points such as analyzers and code generators, plus unofficial extension points added by [Metalama.Compiler](https://github.com/metalama/Metalama.Compiler), an open-source Roslyn fork, which allows arbitrary code transformations. [Metalama](https://www.postsharp.net/metalama) is currently the only framework in this category. Roslyn-based frameworks can give you real-time feedback as you are typing, while MSIL-based ones require you to rebuild. * [Middleware-based frameworks](https://www.postsharp.net/solutions/middleware) generally rely on a dependency injection framework and generate dynamic proxies at runtime. They are limited to intercepting interface methods and adding new interfaces to types. Here is an overview of the main aspect-oriented frameworks available for .NET in 2024. This list is followed by a table comparing their features. ### PostSharp [PostSharp](https://www.postsharp.net/il), launched in 2008, was the first complete implementation of AOP concepts in .NET. It is based on [MSIL rewriting](https://www.postsharp.net/solutions/msil-rewriting). PostSharp became a source of inspiration for several MSIL-based AOP frameworks. PostSharp includes a broad set of ready-made aspects. It has complete documentation. It comes with a Visual Studio extension that provides visibility into the transformations performed by aspects. ### Metalama [Metalama](https://www.postsharp.net/metalama), built by the same team as PostSharp and first launched in 2023, is PostSharp's successor. Based on Roslyn, Metalama works both at design time (within the IDE) and compile time. It is today's most complete implementation of aspect-oriented principles. Metalama uses a C#-to-C# template language coined _T#_. T# is 100% C#-compatible, so you can get full IntelliSense support with any IDE. Since Metalama generates C# and not MSIL, you can preview and even debug the code generated by your aspects. Metalama shares the same Visual Studio extension as PostSharp. ### AspectInjector Like PostSharp, [AspectInjector](https://github.com/pamidur/aspect-injector) is based on [MSIL rewriting](https://www.postsharp.net/solutions/msil-rewriting). While far from PostSharp regarding features, AspectInjector supports most code overriding and introduction features expected from an AOP framework. ### Rougamo [Rougamo](https://github.com/inversionhourglass/Rougamo) is another compile-time AOP framework based on [MSIL rewriting](https://www.postsharp.net/solutions/msil-rewriting). Its code transformation abilities are limited. It implements an AspectJ-inspired pointcut mechanism to select code to be modified. ### AspectCore [AspectCore](https://github.com/dotnetcore/AspectCore-Framework) is an aspect-oriented framework based on [dynamic proxies](https://www.postsharp.net/solutions/middleware). This approach operates at runtime by generating a _proxy_ type between the consumer and the implementation of an interface. It works only with components served by a dependency injection framework. ### Fody [Fody](https://github.com/Fody/Fody) is an extensible tool for weaving .NET assemblies. It is not an aspect framework in itself, but [some](https://github.com/vescon/MethodBoundaryAspect.Fody) [plug-ins](https://github.com/Fody/MethodDecorator) allow for PostSharp-style decoration of methods, allowing for the implementation of some simple aspects. Fody has a long list of [plug-ins](https://github.com/Fody/Home/blob/master/pages/addins.md) that implement specific code transformations. Because these transformations must be coded directly in MSIL and not in C#, Fody does not fully qualify as an aspect-oriented framework. ## Unit-testing aspects in 2025 When we discussed with Matt about his book back in 2013, we had a disagreement about unit testing of aspects. The only way to test PostSharp aspects was to apply them to a method, invoke that method, and check its output or its side effect. Matt argued that this was not real unit testing. And he was right. Ten years later, you can finally unit-test aspects! Metalama, PostSharp's successor, now generates C# code instead of MSIL. You can now test that your aspect generates the code you expect. We've built an [aspect test framework](https://doc.metalama.net/conceptual/aspects/testing/aspect-testing) for this purpose. It works as a _snapshot test_, similar to [Verify](https://github.com/VerifyTests/Verify), and integrates with [DiffEngine](https://github.com/VerifyTests/DiffEngine). ## Dependency injection in aspect in 2025 Another area that has greatly improved in the last decade is the integration of aspects with dependency injection (DI) containers. The reason that the concepts of DI and AOP were not properly integrated from the beginning is that they were both formalized around the same time, in the early 2000s. The first AOP implementations, including PostSharp, were not designed for dependency injection. This was a serious limitation because many aspects depend on services that are typically exposed in a dependency injection container. For instance, a logging aspect uses `ILogger`, caching uses `IMemoryCache`, and so on. That led to excessive use of static methods in aspects, which made them difficult to test. Metalama now has first-class support for dependency injection. As you have seen in the example above, aspects can [import dependencies](https://doc.metalama.net/patterns/dependency-injection) using the `Metalama.Extensions.DependencyInjection` package. With the default .NET Core dependency injection pattern, dependencies are [added as constructor parameters](https://doc.metalama.net/conceptual/aspects/advising/introducing-constructor-parameters) and pulled from derived classes. Therefore, I consider that today, dependency injection and AOP are complementary and almost orthogonal concepts. ## Comparing AOP frameworks for .NET | | Metalama | PostSharp | AspectInjector | Rougamo | AspectCore | |----------|----------|-----------|----------------|---------|------------| | Technology | Roslyn | MSIL | MSIL | MSIL | Dynamic Proxies | | Override virtual members | Yes | Yes | Yes | Yes | Yes | | Override non-virtual members | Yes | Yes | Yes | Yes | No | | Implement interfaces | Yes | Yes | Yes | No | Yes | | Introduce new members | Yes | Yes | Yes | No | No | | Reference introduced members from source code | Yes | No | No | No | No | | Allocationless context passing | Yes | No | Yes | No | No | | IDE: Aspect Explorer | Yes | Yes | No | No | No | | IDE: CodeLens | Yes | Yes | No | No | No | | View/Debug Generated C# | Yes | No | No | No | No | | Large library of pre-built aspects | Yes | Yes | No | No | No | | Unit testing of aspects | Yes | No | No | No | No | | Pulling dependencies from aspects | Yes | No | No | No | Yes | ## Conclusion Although the initial creators of aspect-oriented programming made a couple of communication mistakes, they identified a gap that the C# language still fails to address. This is the gap between the abstraction level of human reasoning and the abstraction level of the C# language. This problem has not yet been completely addressed by the C# language. It is still responsible for a large amount of boilerplate. According to our telemetry data, using PostSharp or Metalama typically reduces hand-written code by 15%. This shows that disciplined, robust meta-programming is still an axis on which mainstream programming languages can be improved. For those who don't want to wait for C# 22, there's Metalama! --- # The Factory design pattern in C# > This article explains the Factory Method and Abstract Factory patterns and provides examples in C#, two creational patterns that help you create objects without specifying the exact class of object that will be created. Published: 2024-12-03 · Author: Metalama Team · Canonical: https://postsharp.net/blog/factory-pattern _Factories_ are methods or objects whose only role is to create other objects. The _Factory Method_ and _Abstract Factory_ patterns are two creational design patterns originally identified in the seminal "Gang of Four" _Design Patterns_ book. They are still crucial today in .NET. This article explains, with concrete examples, when to use this pattern in modern C# applications and how to make sure that they are properly applied. ## Example: setting up the scene This article will use a concrete, real-world example. Let's set up the scene. Let's say you are a software developer at a startup. You need to read and write binary files. Initially, you might have chosen to use local file storage because it was simple and met the immediate needs of your business -- to make the demo work for potential investors. Your company gets more funding, and management decides it's time to deploy the application to the cloud. File-system storage is no longer sufficient. You now need to support cloud storage too. For the sake of simplicity in this article, we will assume that the cloud storage has a simple HTTP interface. Since your app can still be used on-premises as well as on the cloud, you decide to abstract storage access under the following interface: ```csharp internal interface IStorageAdapter { Task OpenReadAsync(); Task WriteAsync( Func write ); } ``` The implementations of these classes, `FileSystemStorageAdapter` and `HttpStorageAdapter`, are not very interesting. Please check the article's [source code](https://github.com/postsharp/TimelessDotNetEngineer/tree/main/src/patterns/factory) for details. The full source code of examples in this article is available on [GitHub](https://github.com/postsharp/TimelessDotNetEngineer/tree/main/src/patterns/factory). ## Step 0. Without the factory pattern When drafting the first prototype of an app, it’s common to start by directly creating objects where they are needed. For instance, in the context we mentioned before, you might instantiate specific implementations like `FileSystemStorageAdapter` or `HttpStorageAdapter` directly in the code. ```csharp internal class Program { private static async Task Main( string[] args ) { var url = args[0]; // Direct instantiation IStorageAdapter storageAdapter = url.StartsWith( "https://" ) ? new HttpStorageAdapter(url) : new FileSystemStorageAdapter( "url" ); // Using the adapter. await using var stream = await storageAdapter.OpenReadAsync(); using var reader = new StreamReader( stream ); Console.WriteLine( await reader.ReadToEndAsync() ); } } ``` This approach works well for simple use cases here, but what if we need to get a storage adapter from dozens of other locations. As you can see, simplicity comes at the cost of flexibility. ### Control flow Here is the representation of the control flow without any factory pattern. We will see in this article that it gets more complex as we add abstraction. ```mermaid flowchart LR Consumer --calls--> Adapter ``` ### The problem with directly calling constructors As the project grows, directly creating objects can lead to tight coupling between the code and specific implementations. For example, if a new requirement emerges to dynamically select a specialized HTTP adapter based on the domain name, selectively handling authentication, significant changes may be needed. The Main method (or any other consumer of these adapters) would need logic to determine which adapter class to create. This setup not only violates the [Single Responsibility Principle](https://en.wikipedia.org/wiki/Single-responsibility_principle) but also makes the code harder to test and maintain. A more scalable solution is required as the complexity increases. ## Step 1. The Factory Method pattern A _Factory Method_ is a creational design pattern used in object-oriented programming to define an interface for creating objects, but allowing subclasses to alter the type of objects that will be created. Instead of instantiating objects directly, the Factory Method delegates the responsibility of object creation to a dedicated method or class, promoting loose coupling and scalability. The Factory Method is typically a static method. It can be either in the class that it instantiates or in a dedicated class. The `CreateStorageAdapter` method will return an instance of the `HttpStorageAdapter` or `FileSystemStorageAdapter` based on the URL's scheme (HTTP or file path): ```csharp namespace Factory { internal sealed class StorageAdapterFactory { public static IStorageAdapter CreateStorageAdapter( string pathOrUrl ) { // Logic to determine storage adapter type if ( pathOrUrl.StartsWith( "https://" ) ) { return new HttpStorageAdapter( pathOrUrl ); } else { return new FileSystemStorageAdapter( pathOrUrl ); } } } } ``` The instantiation logic can now be removed from the consumers. The client code will remain agnostic to specific implementations, allowing the `OpenReadAsync` method to be called without knowing the exact type of the concrete storage adapter being used or its family. ```csharp internal class Program { private static async Task Main( string[] args ) { var storageAdapter = StorageAdapterFactory.CreateStorageAdapter( "path/to/file.txt" ); await using var stream = await storageAdapter.OpenReadAsync(); using var reader = new StreamReader( stream ); Console.WriteLine( await reader.ReadToEndAsync() ); } } ``` As you can see, this pattern provides a way to encapsulate the instantiation logic and make the system more flexible and extensible. By using a Factory Method, you can introduce new classes with minimal changes to existing code, as new object types can be added by extending the factory method rather than modifying existing client code. For example, if a new storage adapter is required to read data from a database, a developer can create a `DatabaseStorageAdapter` class and update the factory method to include this new type. ### Control flow Here is a representation of the control flow with the Factory Method pattern: ```mermaid flowchart LR Consumer --calls--> Factory Factory --creates--> Adapter ``` ### When to use a Factory Method? #### Case 1. Dynamically choosing the right class and constructor The main use case for the Factory Method pattern is when a class can't anticipate the class of objects it must create. This pattern is particularly useful when the exact class of objects to be created is determined at runtime, based on user input, configuration settings, or other factors. By delegating object creation to subclasses, the Factory Method pattern allows for greater flexibility and extensibility in the system. As we saw, our `CreateStorageAdapter` factory method uses a simple string comparison to determine the type of storage adapter to create. In a real-world scenario, the logic for choosing the right constructor might be more complex, involving: - runtime multiple conditions - certain calculations - specific configurations - external dependencies As an example, let's say we want to perform some validations before creating the storage adapter. We can add this logic to the factory method without affecting the client code. ```cs public static IStorageAdapter CreateStorageAdapter(string input) { // Logic to determine storage adapter type if (input.StartsWith("https://")) { // Check if URL is reachable if (IsReachable(input)) { var httpClientFactory = new DefaultHttpClientFactory(); // Simulated DI return new HttpStorageAdapter(httpClientFactory, input); } else { throw new InvalidOperationException($"Cannot access the URL {input}"); } } else { // Validate the file path or check disk space before creating the adapter if (File.Exists(input)) { return new FileSystemStorageAdapter(input); } else { throw new FileNotFoundException($"The file at {input} does not exist."); } } } ``` This Factory Method will now encapsulate this decision-making process within a single method by determining which constructor to use and whether the conditions for its invocation are met. #### Case 2. Async object creation Another scenario where the Factory Method pattern can be useful is when object creation involves asynchronous operations. For example, if the creation of a storage adapter requires fetching data from a remote server or performing other asynchronous tasks, the Factory Method can handle these operations without blocking the main thread. In the previous code snippet, we can modify the `CreateStorageAdapter` method to asynchronously perform the reachability and file existence validations before creating the storage adapter. Doing so, the Factory Method will return a `Task` instead of an `IStorageAdapter` allowing the method to perform asynchronous operations without blocking the main thread. ```cs public async Task CreateStorageAdapter(string input) { // Logic to determine storage adapter type if (input.StartsWith("https://")) { var isSecure = await CheckUrlSecurityAsync(input); // Asynchronous validation // Check if URL is reachable and secure if (IsReachable(input) && isSecure) { return new HttpStorageAdapter(input); } else { throw new InvalidOperationException($"Cannot access the URL {input} or it is not secure."); } } else { var fileExists = await Task.Run(() => File.Exists(input)); // Simulated async file check if (fileExists) { return new FileSystemStorageAdapter(input); } else { throw new FileNotFoundException($"The file at {input} does not exist."); } } } ``` #### Case 3. Improving maintainability As we mentioned, the project will grow, and new requirements will emerge. The Factory Method pattern makes it easier to maintain and extend the system over time without affecting the code _consuming_ the factory method. You can easily: * **Add or modify implementation types**: Adding support for new implementation types becomes seamless with the Factory Method. If a new `RedisStorageAdapter` is required, it can be implemented and incorporated into the factory without modifying existing client code. This approach adheres to the Open/Closed Principle, enabling the application to extend functionality by adding new product types without modifying the core factory logic or client interactions. * **Modify the construction logic**: The Factory Method pattern lets you make code changes without converting them into "breaking changes". For instance, if you need to change how a `FileSystemStorageAdapter` is initialized (perhaps to add a logging mechanism or encryption), this can be done directly within the factory without altering the consumers of the factory. Since consumers interact only with the factory and not the implementation details, the changes remain localized and avoid ripple effects in the codebase. #### Case 4. Controlling object lifetime The Factory Method pattern provides a natural mechanism to control the lifetime of objects, enabling reuse rather than creating new instances every time. Initially, a factory simply returns new instances of a class each time it is called. However, if the object creation process becomes expensive, such as initializing a resource-heavy storage adapter, it can easily be changed to a resource pool, optimizing performance and resource utilization. In our storage context, consider an `HttpStorageAdapter` that establishes a persistent connection to a remote server. Repeatedly creating new instances could involve unnecessary overhead, such as reinitializing the connection and duplicating validation steps. Instead, the factory can cache and reuse a single instance of the `HttpStorageAdapter` for a given URL. ### Limitations While Factory Methods encapsulate object creation logic and provide numerous benefits, they introduce a tight coupling between the factory and the specific types. This occurs because the factory knows about all the concrete classes it can create. For example, the `StorageAdapterFactory` directly instantiates `HttpStorageAdapter` or `FileSystemStorageAdapter`, making it responsible for managing the creation of these classes. This tight coupling can also make testing difficult. For example, if the factory directly creates an `HttpStorageAdapter` that depends on real network connections, testing the behavior of the factory in isolation becomes a challenge without creating intricate mocks or stubs. While dependency injection can mitigate some of these problems, it adds another layer of complexity. To avoid these drawbacks, an alternative is to use an Abstract Factory or delegate the instantiation logic entirely to external DI or configuration containers, allowing the application to decouple the build process from the specific implementation details. ## Step 2. The Abstract Factory pattern The Abstract Factory pattern addresses the limitations of the Factory Method by making the factory logic abstract and therefore replaceable. The principal use case is unit testing. Unit tests, as you know, must be fast and should not perform I/O, except when you are intentionally testing I/O adapters. Translate this to our example: unless we are explicitly testing `HttpStorageAdapter` and `FileSystemStorageAdapter`, we will want to run all other unit tests under an _in-memory_ implementation of `IStorageAdapter`. Instead of the default `StorageAdapter`, our unit tests will use a `TestStorageAdapter` that will return an in-memory implementation _regardless_ of the protocol of the URL. To make this possible, we need to abstract the factory as an interface. This is why this pattern is called the _Abstract Factory_. ```csharp internal interface IStorageAdapterFactory { IStorageAdapter CreateStorageAdapter( string url ); } ``` The default implementation of `IStorageAdapterFactory`, used in production, is the following: ```csharp internal class StorageAdapterFactory : IStorageAdapterFactory { private readonly IHttpClientFactory _httpClientFactory; public StorageAdapterFactory( IHttpClientFactory httpClientFactory ) { this._httpClientFactory = httpClientFactory; } public IStorageAdapter CreateStorageAdapter( string url ) { if ( url.StartsWith( "https://" ) ) { return new HttpStorageAdapter( this._httpClientFactory, url ); } else { return new FileSystemStorageAdapter( url ); } } } ``` Abstract factories are often added to the service collection (also called the dependency injection container). Here is how you would initialize your app: ```csharp var services = new ServiceCollection(); services.AddSingleton(); services.AddHttpClient(); ``` Here is how the abstract factory is typically consumed: ```csharp var factory = serviceProvider.GetRequiredService(); var storage = factory.CreateStorageAdapter( "https://www.google.com" ); ``` ### Control flow As we see in the control flow diagram, we added one more abstraction layer between the consuming code and the service. ```mermaid flowchart LR Consumer --calls--> IServiceProvider IServiceProvider --creates--> Factory Factory --creates--> Adapter ``` ### When to use abstract factories? We will cover some scenarios where the Abstract Factory pattern is particularly useful and how it can help you address complex object creation requirements. #### Case 1. Better support of unit testing The most common use of an Abstract Factory is to improve testability by allowing you to easily replace real implementations with test-specific ones. For example, during unit tests, a `TestStorageAdapterFactory` can implement the `IStorageAdapterFactory` interface and return mock adapters that simulate the behavior of file or network storage without relying on external resources. The consuming code is not aware of these substitutions because it only interacts with the Abstract Factory interface. Unit tests would use the following implementation instead of the default one: ```csharp internal class TestStorageAdapterFactory : IStorageAdapterFactory { private readonly ConcurrentDictionary _storageAdapters = new(); public IStorageAdapter CreateStorageAdapter( string url ) => this._storageAdapters.GetOrAdd( url, s => new TestStorageAdapter() ); } ``` ```csharp internal class TestStorageAdapter : IStorageAdapter { private byte[] _buffer = []; public Task OpenReadAsync() => Task.FromResult( new MemoryStream( this._buffer ) ); public async Task WriteAsync( Func write ) { using var memoryStream = new MemoryStream(); await write( memoryStream ); this._buffer = memoryStream.ToArray(); } } ``` #### Case 2. More abstraction needed over the construction process The Abstract Factory pattern stands out in scenarios where a higher level of abstraction is required, particularly when managing a family of related objects. By encapsulating multiple factories under a unified interface, it allows client code to remain agnostic to the particular classes being created. This abstraction ensures that the client code only depends on the factory interface and not the specific implementations, promoting decoupling and adherence to the Dependency Inversion Principle. It is particularly beneficial in complex systems where the object creation logic must be consistent across all interrelated components, yet flexible enough to adapt to new requirements or configurations. In the storage example, let's say we want to support multiple storage families such as: - Local file storage (i.e., Local, Network attached storage - NAS) - Database Storage (i.e., SQL Server, MongoDB) - Cloud storage (i.e., AWS S3, Azure Blob) Then, we can create a new `SqlServerStorageAdapterFactory`, `MongoDbStorageAdapterFactory`, `S3StorageAdapterFactory`, ... that implement the `AdapterFactory` interface to create the corresponding storage adapter. Each of the factories will encapsulate the creation logic for their respective storage systems, such as establishing database connections or configuring cloud storage services. Now, to instantiate these factory classes, we need _another_ factory class, a factory of factories, which are often named _Providers_ in the Microsoft space. The provider can be implemented with a hardcoded `switch...case` or can support a plug-in model using, for instance, [MEF](https://learn.microsoft.com/en-us/dotnet/framework/mef/). ```mermaid flowchart LR Consumer --calls--> IServiceProvider IServiceProvider --creates--> Provider Provider --creates--> Factory Factory --creates--> Adapter ``` This approach allows the application to switch between different storage families seamlessly without altering the client code, providing a scalable and maintainable solution for managing diverse storage requirements. ## Validating the Abstract Factory pattern Let's be honest, we are human, and we can make mistakes. What happens if you get distracted and instantiate a concrete factory product (e.g., `HttpStorageAdapter`) from somewhere else in your code? This would break the abstraction and the benefits of the Abstract Factory pattern. Generally, the `StorageAdapterFactory` class is the only class that should be allowed to create instances of concrete adapters. More precisely, any class that implements the `IStorageAdapterFactory` interface should be the only one responsible for creating instances of the corresponding storage adapters. The only potential exceptions may be unit tests. To validate this constraint, we can use [Metalama architecture validation](https://doc.postsharp.net/metalama/conceptual/architecture/usage) and define a custom aspect that verifies if the constructor of a concrete adapter is only called from a class that implements the `IStorageAdapterFactory` interface or from a test namespace. If the caller is not the factory or a test, the aspect can raise a warning to let the developer know that the code is violating the intended design. ```csharp internal class ConcreteStorageAdapterAttribute : TypeAspect { public override void BuildAspect( IAspectBuilder builder ) => builder.Outbound.SelectMany( t => t.Constructors ) .CanOnlyBeUsedFrom( scope => scope.Namespace( "**.Tests" ) .Or() .Type( typeof(IStorageAdapterFactory) ), """ The class is a concrete factory and can be only instantiated from a class implementing IStorageAdapterFactory. """ ); } ``` Here we're applying the aspect to the `FileSystemStorageAdapter` class to ensure that their constructors are only called from the factory or test classes. The same applies to the `HttpStorageAdapter` class. ```csharp [ConcreteStorageAdapter] internal class FileSystemStorageAdapter : IStorageAdapter { private readonly string _filePath; public FileSystemStorageAdapter( string filePath ) { this._filePath = filePath; } public Task OpenReadAsync() { return Task.FromResult( (Stream) File.OpenRead( this._filePath ) ); } public async Task WriteAsync( Func write ) { await using var stream = File.OpenWrite( this._filePath ); await write( stream ); } } ``` Thus, if you want to create an instance of `FileSystemStorageAdapter` from a class that is not a factory or a test (I'll do it from the program class for the sake of the example), Metalama will raise a warning. ![Metalama Diff tool](https://postsharp.net/assets/images/2024/2024-12-factory/architectural-warning-4e17f6b198.png#unzoom150) ### Can we automatically generate the boilerplate? In theory, Metalama could also generate the factory classes and the abstract factory pattern. You could define a custom aspect for it, and you have the tools to do it. We tried, but it proved impractical. It only works if the only thing factory methods are doing is calling a constructor with exactly the same signature as the factory method. We found that the aspect added too little value for too much complexity and would not recommend you go this way in most cases. ## Relationships with other patterns The beauty of design patterns is that they can be combined to address complex problems and provide more flexible and scalable solutions. The Factory and Abstract Factory patterns can be used in conjunction with other patterns to create more sophisticated systems that meet specific requirements. Here are some patterns that can be combined with the Factory and Abstract Factory patterns to enhance their capabilities. ### Builder pattern The Abstract Factory can provide builders as its products when families of complex objects are needed. The [Builder pattern](https://postsharp.net/blog/builder-pattern) is used to construct complex objects step by step, allowing the construction process to vary independently from the final object representation. Builders are objects that must be created, and the Factory patterns are a possible way to instantiate them, offering all the benefits presented in this article. For example, instead of creating storage adapters just based on the `url` parameter, a factory could provide a more sophisticated interface allowing for step-by-step configuration of different options such as authentication headers, timeouts, and caching policies. ```csharp public interface IStorageAdapterOptions { string Url { get; } int Timeout { get; } } public interface IStorageAdapterOptionsBuilder { string Url { get; set; } int Timeout { get; set; } IStorageAdapterOptions Build(); } public interface IStorageAdapterFactory { IStorageAdapterOptionsBuilder CreateStorageAdapterOptionsBuilder(); IStorageAdapter CreateStorageAdapter(IStorageAdapterOptions options); } ``` ### Singleton pattern As we briefly said earlier, the [Singleton pattern](https://postsharp.net/blog/singleton-pattern) can be used to manage the lifecycle of an Abstract Factory, to ensure that only one instance of the factory is created and used throughout the application. ## Conclusion In this article, we explored the Factory and Abstract Factory design patterns, two creational patterns that help you create objects without specifying the exact class of object that will be created. We discussed the benefits of using these patterns, such as encapsulating object creation logic, promoting loose coupling, and improving scalability and maintainability. We also covered the limitations of these patterns, such as tight coupling and testing challenges, and how to address them. By understanding the Factory and Abstract Factory patterns, you can design more flexible and extensible systems that adapt to changing requirements and support complex object creation scenarios. --- # Metalama Status Update, November 2024 > New at PostSharp Technologies in November 2025: RC builds of Metalama and PostSharp 2025.0 supporting .NET 9 and C# 13, performance improvements, talk at BuildStuff, and 5 published articles. Published: 2024-12-01 · Author: Gaël Fraiteur · Canonical: https://postsharp.net/blog/metalama-status-update-2024-11 The focus this month for the whole community was on Microsoft's release of .NET 9. Almost immediately, we published the RC builds of Metalama and PostSharp 2025.0 to support the new stacks. After running our reference performance test suite, we measured 30-40% faster builds in Metalama 2025.0 under typical load. ## Metalama 2025.0 RC As is now tradition in our development community, Microsoft released the new version of the .NET stack on the first day of [.NET Conf](https://www.dotnetconf.net/). A few days later, we released the first release candidate of Metalama 2025.0. Here's a short list of features in Metalama 2025.0. New features from this month are marked with stars (✨). * Support for .NET 9 SDK and C# 13. * Support for Roslyn interceptors. * Dramatic performance improvement: [30-40% faster](https://postsharp.net/blog/metalama-performance) than 2024.2 under typical load. * Async and background WPF commands. * Implementation of generic interfaces. * Creation of generic instances of introduced types. * Define local variables dynamically using the new `meta.DefineLocalVariable` method. * Introduction of static virtual, abstract, and partial members. ✨ * Support for `this` parameters in introduced methods. ✨ * Suppression of some irrelevant warnings on aspects. ✨ For more details, check out the [Metalama 2025.0 release notes](https://doc.metalama.net/conceptual/release-notes/release-notes-2025-0), or the individual changelogs for versions [2025.0.6-rc](https://github.com/orgs/metalama/discussions/382) and [2025.0.5-preview](https://github.com/orgs/metalama/discussions/379). We don't release on Fridays, nor in the week before Christmas, so we plan to announce the general availability of Metalama 2025.0 in the very first days of the year. In the meantime, we will publish another RC build. ## Metalama 2024.2 We've merged Roslyn 4.12 (part of .NET 9) into Metalama 2024.2. Although using the new C# language features requires an update to Metalama 2025.0, we want to ensure you're getting the latest Roslyn fixes with all currently supported versions of Metalama. See the changelog for Metalama [2024.2.30](https://github.com/orgs/metalama/discussions/381). ## PostSharp 2025.0 RC Along with Metalama, we've released the first RC build of PostSharp 2025.0 with the following additional features: - Support for .NET 9 SDK and C# 13. - Long path support in projects targeting .NET Framework. - Improved Windows PDB support. We plan to announce the GA of PostSharp 2025.0 in early 2025, with a new RC in the meantime. ## Roadmap In December, we plan to slow down due to the holiday season. We will focus on fixing bugs, targeting both 2024.2 and 2025.0. We are planning to uncommission the online Metalama Sandbox because of its high maintenance and operation costs but insignificant usage. This web app was a fork of [.NET Try](https://github.com/dotnet/try), which is no longer maintained by Microsoft. ## Talking code quality at BuildStuff Overcoming the Broken Window Syndrome at Build Stuff. I had the privilege to talk about code quality at Build Stuff 2024 in Vilnius, delivering my talk: [Overcoming the Broken Window Syndrome: Code Verification Techniques for .NET Developers](https://sessionize.com/s/gael-fraiteur/overcoming-broken-window-syndrome-code-verificatio/87566). Getting back on stage after a long hiatus that started with Covid and continued until we completed Metalama's first version felt like returning to the village after years in a cave. I loved interacting with the audience and speakers. It's the first time I was thanked with a hug for a talk! Well, that was after the party, and we were all a bit less inhibited than usual... Build Stuff is a great conference: informative, inspirational, and fun. And Vilnius is a beautiful city, even under the cold November rain, which, I overheard, doesn't last forever. The talk recordings aren't available yet. Follow [Build Stuff on YouTube](https://www.youtube.com/@BuildStuff) to get notified when they are. ## Five Published Articles This month, we published five articles in the [Timeless .NET Engineer series](https://postsharp.net/blog/use-cases), all related to WPF.
Implement ICommand with Metalama 4 Ways to Implement ICommand Implementing WPF Dependency Properties with Metalama Implementing Custom WPF Dependency Properties WPF Best Practices
## Summary We are ready to launch Metalama and PostSharp 2025.0 in the first days of the new year. Metalama 2025.0 is a substantial release. It supports .NET 9 and also brings 30-40% faster builds, removes previous constraints on implementing interfaces or using introduced types in generic types, and brings long-anticipated features like the introduction of partial, abstract, or static virtual members. Happy meta-programming! --- # Metalama Performance Improvements Analysis [Updated for 2025.0] > This article tracks performance improvements of Metalama over releases since 2024.0. Published: 2024-11-28 · Author: Gaël Fraiteur · Canonical: https://postsharp.net/blog/metalama-performance Starting from Metalama 2024.1, we've spent significant effort to optimize performance. After analyzing the benchmarks, we can report that Metalama's performance has more than doubled, typically adding 25-35% to the standard build time. ```text Article Updates --------------- 2024-04-22: Originally published with Metalama 2024.1 performance 2024-07-30: Added Metalama 2024.2 performance. 2024-11-28: Added Metalama 2025.0 performance. ``` ## TL;DR Using Metalama instead of the standard C# compiler will increase your `dotnet build` time by typically 25%. This overhead varies linearly with the number and complexity of aspects in your project. Even with one aspect on every single method, your build should not be more than 60% slower with Metalama than without it. ## Our methodology So, how did we arrive at these results? We forked the [NopCommerce](https://github.com/nopSolutions/nopCommerce) open-source project and introduced aspects to methods at random, regulated by two factors: - the percentage of types that will have aspects (denoted $\text{Types}$ in the equation below); and - the percentage of methods _in each target type_ with aspects (denoted $\text{Methods}$). Using BenchmarkDotNet, we analyzed the execution time of `dotnet build /t:rebuild` for 12 different combinations of the factors. We used a dedicated computer with a minimal Alpine installation to ensure no background process could interfere with the measurements. We then computed a linear regression based on two factors, $\text{Types}$ (the percentage of types affected) and $\text{Types} \times \text{Methods}$ (the percentage of methods affected), where both $\text{Types}$ and $\text{Methods}$ are included as a percentage between 0 and 1. The output of this function, $\text{TimeRatio}$, is the ratio between the build time with Metalama and without Metalama. $\text{TimeRatio} = 1 + a + b \times \text{Types} + c \times \text{Types} \times \text{Methods}$ Here are the results of the regression: | Version | Constant overhead factor ($a$) | Type load factor ($b$) | Method load factor ($c$) | |---------|-----------------|-------------|---------------| | 2024.0 | 0.41 | 0.26 | 0.72 | | 2024.1 | 0.45 | 0.15 | 0.40 | | 2024.2 | 0.43 | 0.12 | 0.37 | | 2025.0 | 0.25 | 0.06 | 0.29 | Our bilinear model has an $R^2$ of 0.98, suggesting that only 2% of the variance is unexplained. Given that benchmark results variance is higher than 2%, we can be confident in the model accuracy. In simple terms: - Building your code with Metalama will always be at least 25% slower than without Metalama, and - Metalama's build time is linearly dependent on two factors: the percentage of affected methods in your projects and the percentage of types containing these affected methods. The dependency on the number of types containing affected methods is because each type is typically implemented in its own file, and part of Metalama's cost depends on the number of files that need to be rewritten. ## Our improvements Let's see the improvements we made in different Metalama versions compared to the 2024.0 baseline: | Version | Constant overhead factor | Type load factor | Method load factor | |---------|-----------------|-------------|---------------| | 2024.0 | 100% (baseline) | 100% (baseline) | 100% (baseline) | | 2024.1 | 109% | 58% | 55% | | 2024.2 | 105% | 46% | 51% | | 2025.0 | 61% | 23% | 40% | As you can see, the efficiency of Metalama in processing aspects has quadrupled from 2024.0 to 2025.0! Even the constant overhead factor is 61% of what it was at the beginning of 2024. ## Wrapping up Our efforts in enhancing Metalama's performance in 2024 have been largely successful, with aspect processing speed now twice as fast as in v2024.0. Sure, there is still a 20-35% longer build time in typical projects, but hey, the Metalama compiler is doing _much_ more than the plain Jane Roslyn! We'll continue to work on optimizations whenever we see fit. --- # Implementing custom dependency properties in WPF (+example) > Explore various methods to implement the dependency properties efficiently with minimal manual coding. Published: 2024-11-25 · Author: Metalama Team · Canonical: https://postsharp.net/blog/wpf-dependency-property If you are creating an app with Windows Presentation Foundation (WPF) and plan to build a user control, you must learn about [dependency properties](https://doc.postsharp.net/metalama/patterns/wpf/dependency-property). They allow your user control to use powerful WPF features such as data binding, change notification, styling, and animations -- just as the system controls. This article explains how to define and use dependency properties in WPF. It compares them with traditional CLR properties and shows their importance through a practical example. ## What is a dependency property in WPF? In WPF, a [dependency property](https://learn.microsoft.com/en-us/dotnet/desktop/wpf/properties/dependency-properties-overview) is a special kind of property that extends the functionality of standard .NET properties. The main features of the dependent properties are: - **Data binding and change notification**: Dependency properties have built-in support for change notifications thanks to data binding: dependency properties are not directly assigned to a _value_but to a _source_ of values. - **Value precedence**: WPF features a refined system for setting the final value of a dependency property based on [multiple providers](https://learn.microsoft.com/en-us/dotnet/desktop/wpf/properties/dependency-property-value-precedence), including styles, templates, animations, inheritance, and of course explicitly-set values. - **Default values**: Unlike standard C# properties, have a concept of being _set_ or _unset_, and make a difference between being _unset_ or _set to its default value_. This distinction is important to support the design-time experience. Unset values do not need to be materialized as XAML code. They also don't need to be stored in memory, which means that controls can have a large number of properties without significantly affecting memory usage. - **Styling, animations, and triggers**: As mentioned above, dependency properties support advanced WPF features such as styling, animations, and triggers. - **Data validation and coercion**: Dependency properties include a mechanism to validate and coerce (i.e. convert) assigned values. ## When to use dependency properties? Use dependency properties whenever you build user controls in WPF. As a rule of thumb, every public property of your user control should be implemented as a dependency property. Dependency properties give your user controls the same features as standard WPF controls. ## What is the difference between WPF dependency properties and CLR properties? Dependency properties and CLR (Common Language Runtime) properties are both _things_ that can be assigned. However, they are almost entirely different in their implementation: - [**CLR properties**](https://learn.microsoft.com/en-us/dotnet/standard/base-types/common-type-system#properties) are standard C# properties, defined with simple get and set accessors and backed by a private field within the class. These properties are straightforward, efficient, and suited for scenarios where basic data storage and retrieval are sufficient. - **Dependency properties** are managed by WPF through the [DependencyObject](https://learn.microsoft.com/en-us/dotnet/api/system.windows.dependencyobject?view=windowsdesktop-8.0) class (from which most WPF visual controls derive) and come with enhanced capabilities. Unlike CLR properties, dependency properties store their values in a dedicated property system maintained by WPF rather than directly in the class. If your application follows the MVVM pattern (and [it should be](https://postsharp.net/blog/wpf-best-practices-2024)), binding UI elements to view model properties is done smoothly with the dependency properties. As we said before, WPF’s dependency properties allow for a lot of features that CLR properties simply cannot provide. One of the most notable features is the automatic change notification mechanism. When a dependency property value changes, WPF automatically notifies any UI elements bound to that property, ensuring the UI stays in sync with the underlying data model. This is crucial for building responsive and interactive user interfaces. Here is a list of other differences between dependency properties and CLR properties: | Feature | Dependency Properties | CLR Properties | |------------------|--------------------------------------------|---------------------------------------------| | Memory | More efficient when using default values | Stores value for each instance | | Implementation | Requires `DependencyProperty` registration | Simple get/set implementation | | Data Binding | Full support | Basic support with `INotifyPropertyChanged` | | Value Resolution | Complex precedence system | Direct value storage | | Performance | Slightly slower for get/set operations | Faster for direct access | ## 1. Implementing a dependency property To show you how to create a custom dependency property in WPF, we will use a simple example with a custom control called `LimitedTextBox`. This custom control exposes two dependency properties named `MaxLength` and `Text`, which will allow users to set the maximum length of the text and the text itself, respectively. As the user types the text into the `LimitedTextBox`, the control will automatically update a `TextBlock` control with the number of characters remaining to reach the limit. To show how the control can be used, we've created a simple WPF application with an instance of the `LimitedTextBox` control in the main window. The `MaxLength` property is set to 50, and the `Text` property is initially set to "Initial text".

WPF HighlightedText app

Here's how the dependency property `Text` is implemented and registered inside the `LimitedTextBox`: ```csharp public static readonly DependencyProperty TextProperty = DependencyProperty.Register( nameof(Text), typeof(string), typeof(LimitedTextBox), new PropertyMetadata( string.Empty ) ); ``` This code registers the dependency property using the `DependencyProperty.Register` method, which takes following parameters: - The **property name** is just a string that matches the property's name in the class. - The **property type** is the property type, in this case, an integer. - The **owner type** is the class that owns the property. - The [PropertyMetadata](https://learn.microsoft.com/en-us/dotnet/desktop/wpf/properties/dependency-property-metadata) parameter allows us, among other things, to specify the default value of the property. In this case, we set the default value to a default string The code above is all you need for your property to work in XAML. If you want to easily set the property using C#, add the following code: ```csharp public string Text { get => (string) this.GetValue( TextProperty ); set => this.SetValue( TextProperty, value ); } ``` The full source code of examples in this article is available on [GitHub](https://github.com/postsharp/TimelessDotNetEngineer/tree/main/src/wpf/dependencyproperties). Registering the `MaxLength` property is similar. Let us now see how the `Text` and `MaxLength` properties are used when the `LimitedTextBox` control is added to the main window: ```xml ``` In the XAML code snippet above, we set the `Test` property of the `LimitedTextBox` control to `50`, which sets the maximum length of the text that should be entered into the control. The same we explained before applies to the `Text` property, which is set to "Initial text". Note that the C# property setters are ignored when setting property values through XAML. You should consider the C# property as a wrapper around the dependency property, not the opposite. ## 2. Reacting upon property changes We must implement a feature in our `LimitedTextBox` example: to modify, as the user is typing the text, the note informing of the number of remaining characters. For this, we need to add a _property changed_ method. Let's call it `OnTextChanged`: ```csharp private static void OnTextChanged( DependencyObject d, DependencyPropertyChangedEventArgs e ) { var control = (LimitedTextBox) d; control.UpdateRemainingCharsText( (string) e.NewValue ); } private void UpdateRemainingCharsText( string updateTextValue ) { var remainingChars = this.MaxLength - updateTextValue.Length; this._remainingCharsTextBlock.Text = $"{remainingChars} characters remaining"; } ``` As you can see, this method casts the `DependencyObject` to a `LimitedTextBox` instance and call the `UpdateRemainingCharsText` method to update the text block with the remaining characters. To register this method, we add a parameter to the `PropertyMetadata` object ```csharp public static readonly DependencyProperty TextProperty = DependencyProperty.Register( nameof(Text), typeof(string), typeof(LimitedTextBox), new PropertyMetadata( string.Empty, OnTextChanged ) ); ``` The `OnTextChanged` method is now called whenever the property value changes, allowing us to perform additional logic based on the new value. ## 3. Adding validation Finally, let's see how we can validate that the `MaxLength` property is assigned a non-negative value. First, we define a validation method: ```csharp private static bool ValidateMaxLength( object value ) => value is > 0; ``` This method perform a simple validation check to ensure that the `MaxLength` property is a positive integer. If the value is valid, the method returns `true`; otherwise, it returns `false`. Then, we register this callback by appending a paraneter to `DependencyProperty.Register`: ```csharp public static readonly DependencyProperty MaxLengthProperty = DependencyProperty.Register( nameof(MaxLength), typeof(int), typeof(LimitedTextBox), new PropertyMetadata( 100 ), ValidateMaxLength ); ``` This validation callback is called before the property value is set, allowing us to enforce constraints on the property value. We would like to enforce the `Text` property to have a value whose length is smaller or equal to `MaxLength`, but we cannot do that using this approach because the `ValidateValueCallback` delegate does not give us the object's instance. An alternative approach is to use pass a [CoerceValueCallback](https://learn.microsoft.com/en-us/dotnet/api/system.windows.coercevaluecallback) to the `PropertyMetalama` constructor. ## 4. Reducing boilerplate code Implementing dependency properties manually involves a lot of boilerplate code and complexity. They make your code less readable and harder to maintain. A few tools can simplify this process by automating boilerplate code generation and providing intuitive ways to handle common patterns in implementing dependency properties. * [Metalama](https://www.postsharp.net/metalama) is an on-the-fly code generation and validation framework. It comes with a ready-made and well-tested `[DependencyProperty]` aspect that turns a C# automatic property into a WPF dependency property. Metalama's `[DependencyProperty]` aspect integrates with contracts such as `[NonNegative]` or `[NotEmpty]`, so it also eases the implementation of value validation. * [DependencyPropertyGenerator](https://github.com/HavenDV/DependencyPropertyGenerator) is a Roslyn source generator that generates both the C# and the dependency property from type-level custom attributes. This approach is in our opinion non-idiomatic, potentially making the code _less_ readable. Let's see what your code would look like with Metalama: ```csharp [StrictlyGreaterThan( 0 )] [DependencyProperty] public int MaxLength { get; set; } = 100; ``` To read more about implementing dependency properties with Metalama, see [this blog post](https://postsharp.net/blog/wpf-dependency-property-metalama) or the [reference documentation](https://doc.postsharp.net/metalama/patterns/wpf/dependency-property). ## Summary Dependency properties are a powerful tool for creating flexible, responsive, feature-rich, and highly reusable user controls in WPF. In fact, it's so important that whenever you build a user control, you should expose all its properties as dependency properties. The downside of dependency properties is that it requires some boilerplate code to define the property and register the validation and change-processing logic, if any. Fortunately, tools like [Metalama](https://www.postsharp.net/metalama) can help [generate the boilerplate](https://postsharp.net/blog/wpf-dependency-property-metalama) for you, keeping your UI logic clean and succinct. --- # Implementing WPF dependency properties with Metalama > Learn how to implement custom WPF dependency properties with minimal manual effort using Metalama, demonstrated through an example application. Published: 2024-11-21 · Author: Darío Macchi · Canonical: https://postsharp.net/blog/wpf-dependency-property-metalama When building user controls in WPF, it's [best practice](https://postsharp.net/blog/wpf-best-practices-2024) to expose dependency properties in addition to normal C# properties. Unfortunately, implementing custom dependency properties requires a fair amount of redundant code. Metalama eliminates it. [Dependency properties](https://postsharp.net/blog/wpf-dependency-property) allow WPF to assign these properties to a _source_ of values, enabling the UI to refresh when the source changes or implement animations (a mechanism called _data binding_). In contrast, C# properties are directly assigned to a _value_, one time. The downside of dependency properties is that implementing them manually can be tedious and error-prone. It requires writing significant boilerplate code to register the property (using `DependencyProperty.Register`) and manage property-changed and validation callbacks. Each custom WPF dependency property takes a dozen lines of ceremony. Metalama's `[DependencyProperty]` aspect generates that ceremony from a single field. This article shows how, including validation and callbacks. ## Example app Our example is a simple custom control called `LimitedTextBox`. This control has two dependency properties: `MaxLength` and `Text`. The `MaxLength` property specifies the maximum number of characters allowed in the text box, while the `Text` property holds the text entered by the user. As the user types into the `LimitedTextBox` control, it automatically updates the counter showing the number of characters remaining to reach the limit.

WPF HighlightedText app

If you were to implement the `MaxLength` dependency [manually](https://postsharp.net/blog/wpf-dependency-property), you'd end up with the following _three_ snippets: ```csharp public static readonly DependencyProperty MaxLengthProperty = DependencyProperty.Register( nameof(MaxLength), typeof(int), typeof(LimitedTextBox), new PropertyMetadata( 100, OnMaxLengthChanged ), ValidateMaxLength ); public int MaxLength { get => (int) this.GetValue( MaxLengthProperty ); set => this.SetValue( MaxLengthProperty, value ); } ``` ```csharp private static void OnMaxLengthChanged( DependencyObject d, DependencyPropertyChangedEventArgs e ) { var control = (LimitedTextBox) d; control.UpdateRemainingCharsText( control._textBox.Text ); } ``` ```csharp private static bool ValidateMaxLength( object value ) => value is > 0; ``` The full source code of examples in this article is available on [GitHub](https://github.com/postsharp/TimelessDotNetEngineer/tree/main/src/wpf/dependencyproperties). Aside from the complexity of the `DependencyProperty.Register` method, you can see how this manual implementation can easily lead to errors and inconsistencies, especially as the number of properties grows. This is where Metalama reduces the manual work required to implement dependency properties. ## Implementing dependency properties with Metalama [Metalama](https://www.postsharp.net/metalama) is a tool that facilitates real-time code generation and validation in C# through the use of aspects. Aspects are special classes that work within the compiler to dynamically transforms code when you build, never committing the changes to your source code. This tool helps automate the creation of repetitive code, such as implementing dependency properties, [INotifyPropertyChanged](https://postsharp.net/blog/inotifypropertychanged-metalama), [WPF commands](https://postsharp.net/blog/wpf-command-metalama), and many others. If you need to generate boilerplate code for a specific situation (like this one), you can create an aspect from scratch for it. However, as this task is quite common among WPF developers, Metalama simplifies it by offering a built-in solution. The `[DependencyProperty]` aspect is one of the many open-source, production-ready aspects provided by Metalama. This aspect is specifically designed to automate the generation of the boilerplate code needed to implement dependency properties while maintaining flexibility. If you're interested in exploring more of these aspects, be sure to check out the [Metalama Marketplace](https://www.postsharp.net/metalama/marketplace). The `[DependencyProperty]` aspect turns a plain C# automatic property into a dependency property. To use the `[DependencyProperty]` aspect in your project, you must: 1. Add the [Metalama.Patterns.Wpf](https://www.nuget.org/packages/Metalama.Patterns.Wpf) package to your project. 2. Add the `[DependencyProperty]` custom attribute to a standard C# automatic property. Note that the containing type of the property must be derived from `DependencyObject`. Let's see how the `MaxLength` property can be implemented using the `[DependencyProperty]` aspect: ```csharp [DependencyProperty] public int MaxLength { get; set; } = 100; ``` In the code snippet above, we use the `[DependencyProperty]` aspect to decorate the `MaxLength` property in the `LimitedTextBox` class. Note that the property should be auto-implemented (no backing field required), and the default value is set directly in the property declaration. The aspect takes care of generating the necessary boilerplate code, including the property registration, metadata, and validation callbacks. You can take a look at what the generated code will look like using our [Metalama Diff tool](https://doc.metalama.net/conceptual/using/understanding-your-code-with-aspects#metalama-diff) (included in [Visual Studio Tools for Metalama](https://marketplace.visualstudio.com/items?itemName=PostSharpTechnologies.PostSharp)). ![Metalama Diff tool](https://postsharp.net/assets/images/2024/2024-11-wpf-dependency-properties-metalama/dependency-props-metalama_diff-7a9b7bb207.png#unzoom150) By using the `[DependencyProperty]` aspect, we eliminate the need to manually implement the dependency property registration, metadata, and validation callbacks. This approach significantly reduces the amount of boilerplate code and ensures consistency across different dependency properties in the project. ## Adding validation with an attribute If you've implemented dependency properties manually, you're likely familiar with _validation callback_ methods. With Metalama, validating a property can often be done using a simple _contract_ custom attribute from the [Metalama.Patterns.Contracts](https://doc.metalama.net/patterns/contracts) package. Some examples are the [[Email], [Phone], and [Url]](https://doc.metalama.net/patterns/contracts/contract-types#email-phone-and-url), or [[NotEmpty]](https://doc.metalama.net/patterns/contracts/contract-types#notempty) contracts. Here you can see an example where we apply the [[StrictlyGreaterThan]](https://doc.metalama.net/api/metalama-patterns-contracts-strictlygreaterthanattribute) contract to the `MaxLength` property: ```csharp [StrictlyGreaterThan( 0 )] [DependencyProperty] public int MaxLength { get; set; } = 100; ``` This approach results in compact and readable source code. ## Adding a validation callback Let's now turn to the second dependency property: `Text`. We want to validate that the text is only made of letters or whitespaces. Although we could implement this requirement by using the [[RegularExpression]](https://doc.metalama.net/api/metalama-patterns-contracts-regularexpressionattribute) contract, we'll show here how to do this using a callback method. Validation callbacks are methods that run _before_ the property is set. If they fail, the property is not set. There are two ways to add a validation contract: * _Implicitly_ by following a _naming convention_ and creating a method whose name corresponds to the property name, plus the `Validate` prefix. In this case, the property name is `Text`, so the validation method should be named `ValidateText`. * _Explicitly_, by setting the `ValidateMethod` parameter of the `[DependencyProperty]` type. Metalama supports [several signatures](https://doc.metalama.net/patterns/wpf/dependency-property#adding-validation-through-a-callback-method) for the validation callback. Here is the validation callback for the `Text` property: ```csharp private void ValidateText( string value ) { if ( !string.IsNullOrWhiteSpace( value ) && !value.All( c => char.IsLetter( c ) || char.IsWhiteSpace( c ) ) ) { throw new ArgumentException( "Invalid Text value. Only letters and whitespace are allowed." ); } } ``` ```csharp [DependencyProperty(ValidateMethod = "WhateverValidationMethodNameYouWant")] public string Text { get; set; } ``` Unlike the method used in the `DependencyProperty.Register`, the validation method used by Metalama doesn't return a boolean value; instead, it throws an exception if the value is invalid. ## Adding a PropertyChanged callback Property-changed callbacks are invoked _after_ the value of a dependency property has changed. As with the validation callback, there are two ways to specify it: * _Implicitly_ by following a naming convention. For example, the name of our property is `MaxLength`, so the PropertyChanged method should be named `OnMaxLengthChanged`. The same applies to the `Text` property and the `OnTextChanged` method. Metalama will automatically detect and use them as property-changed callbacks. * _Explicitly_ by setting the `PropertyChangedMethod` of the `[DependencyProperty]` attribute. As with the validation callback, there are several signatures for the `PropertyChanged` method (see them in the [documentation here](https://doc.metalama.net/patterns/wpf/dependency-property#adding-validation-through-a-callback-method)), so you can choose the one that best fits your needs. An important detail compared to the method used in the manual implementation (using the `DependencyProperty.Register`) is that the `PropertyChanged` method does not need to be static and can access the instance of the class. Here are the `PropertyChanged` methods for our dependency properties: ```csharp private void OnMaxLengthChanged( int oldValue, int newValue ) { this.UpdateRemainingCharsText( this._textBox.Text ); } private void OnTextChanged( string oldValue, string newValue ) { this.UpdateRemainingCharsText( newValue ); } ``` ## Why use the Metalama approach? The Metalama approach to implementing dependency properties offers several advantages over the manual approach. Here are some key benefits. ### Improved code readability and maintainability By using the `[DependencyProperty]` aspect, you can eliminate the (ugly) boilerplate code typically associated with dependency property registration. This results in cleaner, more concise code that is easier to read and maintain. The use of idiomatic C# code with aspects makes it easier for developers to understand the purpose of the code and its intended behavior. ### Enhanced developer productivity With Metalama, developers can focus on more critical aspects of their application, rather than getting bogged down in repetitious tasks. Automating common patterns leads to faster development cycles and more reliable code. The Metalama approach reduces the manual work needed to implement dependency properties. This is partly due to the reduction in boilerplate code and partly because contracts from the `Metalama.Patterns.Contracts` package provide common validation methods you'd otherwise write yourself. This approach minimizes the likelihood of errors and makes code maintenance and readability easier. By automatically generating the necessary code using aspects like `DependencyProperty` and contracts, developers can focus more on core functionality rather than repetitive code. As a result, development processes are sped up, and overall efficiency in software projects is improved. ## Conclusion Manually implementing custom dependency properties in WPF can be a complex and error-prone task. It demands meticulous attention to detail, which can be time-consuming and may lead to inconsistencies or errors if not managed carefully. However, tools like Metalama can significantly streamline this process. By using the `[DependencyProperty]` aspect, developers can automate the generation of the required code to implement dependency properties. This automation ensures consistency and reduces the potential for errors, simplifying the development of custom controls. It also allows developers to concentrate more on the core functionality of their applications, rather than getting bogged down by repetitive coding tasks. --- # 10 WPF Best Practices [2024] > Lists 10 essential best practices to build high-performant and maintainable WPF applications with minimal effort in 2024. Published: 2024-11-19 · Author: Metalama Team · Canonical: https://postsharp.net/blog/wpf-best-practices-2024 Windows Presentation Foundation, or WPF, remains a vital technology for building visually appealing desktop applications on Windows. In this article, we'll explore some essential best practices for creating high-performance and maintainable WPF applications with minimal effort -- updated with new tools and features released in 2024. We'll cover strategies like using the MVVM pattern, employing commands for user interactions, optimizing the complexity of the visual tree, and utilizing code generation tools. ## What is WPF? [Windows Presentation Foundation](https://learn.microsoft.com/en-us/dotnet/desktop/wpf/overview/) is a graphical system created by Microsoft for designing user interfaces in Windows applications. It was first introduced with the .NET Framework 3.0. WPF offers a consistent way to build applications by keeping the user interface separate from the business logic. It uses a language called [XAML](https://learn.microsoft.com/en-us/dotnet/desktop/wpf/xaml/), which stands for eXtensible Application Markup Language, to define UI elements. This approach allows designers and developers to collaborate effectively. Moreover, WPF comes with a range of features, including data binding, 2D and 3D graphics, animations, styles, templates, and media services. These features make WPF a powerful platform for developing rich desktop client applications with visually impressive interfaces. ## What are WPF applications used for? WPF is one of the most popular UI frameworks for building desktop applications for Windows. Its principal strengths include high adaptability and performance. Its main weakness is being Windows-specific. Since WPF is shipped with Windows as a part of the .NET Framework, it is possible to build very applications with a minimal download overhead. For those looking for a more modern development toolset, WPF has been ported to .NET Core. WPF continues to be actively maintained and improved and remains compatible with the latest Windows versions. Although it may not receive as much attention as some newer technologies, WPF still holds its ground as a preferred choice for developers building native Windows desktop applications. This is especially true in industries where the highest levels of performance and reliability are required. In addition to these strengths, there are exciting developments within the WPF ecosystem that promise even greater potential. For example, [Avalonia XPF](https://avaloniaui.net/xpf) extends the lifespan of existing WPF applications by enabling cross-platform deployment to macOS and Linux. There is also potential for future expansion to iOS, Android, and web browsers. As WPF has been around for almost two decades, some best practices have emerged and are still valid today. ## Practice 1. Never use UI elements as your primary data storage When developing applications, it's crucial to adopt a mindset that separates the user interface from the underlying data logic. This is a common pitfall that can lead to code that's hard to maintain and test. One way to achieve this separation is by imagining your application running smoothly without a user interface. Focus on the fundamental data structures and operations that power its functionality. By doing so, developers are encouraged to create robust model classes that store and manage data independently of UI components. For instance, instead of storing temporary values in UI controls like text boxes, it's better to use backing variables within model classes. By linking these variables to UI elements, you establish a clear separation of concerns. This reduces fragility and maintenance challenges in the code, making your application more scalable and easier to test. This principle also applies to complex UI structures such as tree views or stateful dependent controls like a master-detail view. In these cases, it's far better to manipulate the underlying data models and leverage WPF's data binding principles. By doing so, you have **a single "source of truth"**; any changes made are automatically reflected in the UI. This promotes a more maintainable and elegant design. This approach not only simplifies your code but also aligns with best practices for modern software development by ensuring your application's logic is cleanly abstracted from its presentation layer. With WPF, the best design pattern for separating UI from data storage is MVVM. ## Practice 2. Use MVVM - the best architecture for WPF applications MVVM (Model-View-ViewModel), in its most basic form, is about keeping the data (**Model**) separate from the interface (**View**) by using a mediator (**ViewModel**) between them. Instead of having all the logic directly linked to the UI controls in the code file, MVVM separates the logic from the UI, making the code more modular, easier to maintain, and easier to test. Ok, you may have heard that a lot of times. Let's see what it means with an example: ```cs // Model - Just holds data public class Person { public string Name { get; set; } public int Age { get; set; } } // ViewModel - Connects View and Model public class MainViewModel : INotifyPropertyChanged { private Person _person = new Person(); public string Name { get { return _person.Name; } set { _person.Name = value; OnPropertyChanged(nameof(Name)); OnPropertyChanged(nameof(Caption)); } } public string Caption => $"{Name} ({_person.Age})"; public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged(string name) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name)); } } ``` The code above shows a simple example of a Model and ViewModel in WPF. - The `Person` class is the Model, which holds the data. - The `MainViewModel` class is the ViewModel, which connects the View (UI) to the Model. The ViewModel implements the `INotifyPropertyChanged` interface to notify the View of any changes in the data. [Implementing the INotifyPropertyChanged interface](https://postsharp.net/blog/inotifypropertychanged) can require a lot of redundant code. We'll see below how to use a code generation tool to [automate the INotifyPropertyChanged boilerplate](https://postsharp.net/blog/inotifypropertychanged-metalama). - The XAML code below shows the View, which is the UI of the application. The `TextBox` and `TextBlock` elements are bound to the `Name` property of the ViewModel using data binding. This allows the UI to display and update the data in the ViewModel. ```xml ``` This separation of concerns makes it easy to maintain each part without affecting the others. For example, if you need to change the way the data is displayed, you can modify the View without touching the ViewModel or the Model. Additionally, the model—and more importantly, the view model—can be tested independently of the user interface. This offers a significant advantage when writing unit tests. It's a good practice to add computed properties to the ViewModel, like `Caption` in this example. The ViewModel can also contain WPF-specific properties, for instance dynamically computing the `Visibility` of an item. The objective is to make the XAML code simple. Ideally, it should be editable by non-programmers! A problem of exposing WPF-specific properties to your ViewModel is that you cannot reuse it when porting the UI to a different platform. If this matters in your project, you can create two ViewModel layers in your application: one platform-specific, one platform-neutral. An alternative is to use a multi-platform UI framework, for instance, MAUI, Uno Framework, or Avalonia. For more regarding the MVVM pattern, see read [4 Ways to Implement INotifyPropertyChanged](https://postsharp.net/blog/inotifypropertychanged) on this blog. ## Practice 3. Use WPF Commands to manage user interactions Let's continue our discussion on maintaining a clear separation between UI logic and business logic in WPF applications. Now, we'll focus on using [WPF Commands](https://postsharp.net/blog/wpf-command) to handle user interactions. Commands offer a more structured and maintainable approach compared to traditional event handlers. They help achieve this separation by allowing actions to be bound directly to properties in the ViewModel. Another benefit of the commands is that it defines a unified mechanism to bind to the `IsEnabled` state of a button or menu item. This approach results in cleaner code because it ensures that the View is solely responsible for displaying data, while the ViewModel takes care of handling interactions and enforcing business rules. This architecture is crucial for following the MVVM pattern, as it allows the ViewModel to manage application behavior in response to user inputs without being directly tied to UI elements. Let's take our previous code example and incorporate a command into the ViewModel. This command will handle a button click event, serving as an illustration of how commands function in practice. ```cs public class SaveNameCommand : ICommand { private readonly MainViewModel _viewModel; public SaveNameCommand(MainViewModel viewModel) { _viewModel = viewModel; } public event EventHandler CanExecuteChanged; public bool CanExecute(object parameter) { return !string.IsNullOrEmpty(_viewModel.Name); } public void Execute(object parameter) { // Save the data } } public class MainViewModel : INotifyPropertyChanged { // Properties and methods from previous example // (...) public ICommand SaveNameCommand { get; } public MainViewModel() { SaveNameCommand = new SaveNameCommand(this); } } ``` ```xml