Metalama 2023.4 is Generally Available: Caching and Source Code

by Gael Fraiteur on 06 Nov 2023

We are pleased to announce the general availability of Metalama 2023.4. This release focuses on porting Metalama Contracts and Metalama Caching from PostSharp. We are taking this opportunity to publish 99.9% of Metalama’s source code under an open-source or source-available license. This release also fixes a critical issue with .NET 8. To prevent any issues with the upcoming Visual Studio version, we strongly recommend updating your Metalama packages now.

Open Source and Source-Available Commitments

To enhance transparency and simplify troubleshooting, we have made the source code of all Metalama components available for review. You are invited to examine every commit we’ve made:

Please note that the source-available license of Metalama.Framework allows the use of the source code for reference and troubleshooting but does not permit modifications to the source code or building the packages from the source code. This right requires a Source Code Subscription, available for an additional fee. Please get in touch with our team for more information.

Visual Studio 17.8 Readiness

The new Visual Studio version, which Microsoft is expected to release in a few days, will be based on .NET 8. To avoid incompatibilities with Metalama, we recommend updating your packages now to prevent design-time errors in your code after updating Visual Studio.

Please note that Metalama 2023.4 does not aim to provide complete support for .NET 8 and C# 12, only to avoid issues. Supporting the next version of the .NET stack will be the focus of Metalama 2024.0 and PostSharp 2024.0, which we expect to be generally available at the end of this month.

Options Framework

Complex and widely-used aspects often require a centralized, project-wide method for configuring compile-time behavior.

To address this need, we’ve built a new configuration framework within the Metalama.Framework.Options namespace. This framework enables aspect authors to design APIs that allow for the entire project, specific namespaces, or class families to be configured from a single line of code. Metalama.Framework.Options facilitates both programmatic configuration using fabric extension methods and declarative configuration via custom attributes. It also supports configuration inheritance from base types, extending even across project borders.

This new options framework is pivotal to Metalama Contracts, Metalama Caching, and other ready-made aspects currently in development.

Explore Making aspects configurable in our documentation for an in-depth guide.

Metalama Contracts

With v2023.4, we’re unveiling the first stable release of Metalama Contracts. This framework streamlines the application of Contract-Based Programming principles, a software engineering practice that significantly enhances software reliability and clarity. Within this paradigm, a contract establishes a series of obligations and expectations between a caller and a callee.

Metalama Contracts implements three core tenets of contract-based programming:

  • Preconditions, exemplified by contracts like [GreaterThan], [NotEmpty], or [Url] applied to input parameters, fields, or properties;
  • Postconditions, where contracts are applied to return values or out parameter values;
  • Type invariants: methods affirming object integrity, which, upon detecting discrepancies, throw exceptions. These are automatically executed following each public method.

Furthermore, Metalama Contracts incorporate a fabric extension method VerifyNotNullableDeclarations, which systematically injects code that verifies that all non-nullable parameters, fields, and properties received a non-null value. (As you know, C# nullability annotations are purely symbolic and do not throw any exceptions in case of null assignment.)

For details, see Metalama Contracts in our documentation.

Metalama Caching

Another highlight in v2023.4 is the stabilization of Metalama Caching, an open-source, aspect-oriented caching framework that simplifies the caching of method return values as a function of its parameters. Not only does it save a lot of boilerplate code, but it also practically eliminates inconsistencies in cache key generation and significantly reduces the complexities associated with cache dependencies.

Metalama Caching, a port of the battle-proven PostSharp Caching, has been overhauled to align with contemporary coding practices, including dependency injection, an immutability-centric approach for initialization, and the latest C# 11 features.

The framework supports various caching topologies: local in-memory cache, Redis-based distributed cache, combined L1 in-memory layer with an L2 distributed caching layer, and multiple cache instances synchronized over Azure Service Bus or Redis Pub/Sub.

It introduces two solid cache invalidation approaches: direct invalidation, which necessitates each updating method to be aware of all reading methods for invalidation purposes, and indirect invalidation via a dependency graph. An object-oriented encapsulation and a helper aspect address the often-tricky issue of correct cache key creation.

Metalama Caching is entirely open-source, with most of its distinctive capabilities usable independently of any aspect-oriented framework. Only the Metalama.Patterns.Caching.Aspects package requires the Metalama Framework.

Dive into Metalama Caching documentation for further insights.

Memoization

Memoization is a simple yet highly efficient form of caching applicable to computed properties and parameterless methods. Unlike the key-value store approach of Metalama Caching, memoization stores values directly within the object itself and has no concept of a caching key whatsoever. This aspect is particularly convenient wherever computed properties unconditionally return the same output. We use this pattern extensively in the implementation of Metalama Framework itself.

Consult the documentation for detailed information.

Observability (In Progress)

We’re excited to offer a sneak peek at the Observable aspect, which automatically implements the INotifyPropertyChanged interface. Unlike other approaches reliant on MSIL or source generators, the [Observable] aspect accommodates computed properties, including dependencies on child object properties, such as the following one:

public string CustomerName => $"{this.Customer.FirstName} {this.Customer.LastName}";

Whereas alternative approaches work only with trivial properties, our implementation shines with real-world view-model layers.

Anticipate full maturation of this feature in Metalama 2024.1 — subsequent to the 2024.0 release, which is dedicated to embracing .NET 8 and C# 12.

Additional Enhancements

  • Added MSBuild properties MetalamaCompileTimeTargetFrameworks and MetalamaRestoreSources to configure the compile-time target frameworks and to specify the package restore sources, respectively.
  • Added MSBuild property MetalamaCreateLamaDebugConfiguration to disable the creation of the LamaDebug build configuration.
  • Added new member ICompilation.Cache to cache often-used declarations across aspect instances.
  • DependencyInjection aspect: IsRequired’s default value follows nullability of the target field.
  • Added an environment variable METALAMA_TEMP to customize the location of Metalama temp directory.
  • Annotations: a facility to add and query custom annotations. See IAnnotation, IAdviceFactory.AddAnnotation and declaration.Enhancements().GetAnnotations.
  • IMemberOrNamedType.GetBase extension method: gets the base type or overridden member.
  • IAttribute.TryConstruct extension method: creates a CLR instance of a compile-time custom attribute.
  • ICompilation.GetAllAttributesOfType: gets all attributes of a given type in a project.
  • IMemberOrNamedType.Definition: navigates to the generic definition.
  • LocationExtensions.ToDiagnosticLocation( this Location? location ): converts a Roslyn Location into a Metalama IDiagnosticLocation.
  • 39 bug fixes. For details, see the changelogs:

Breaking Changes

We still indulge in introducing low-impact breaking changes in the compile-time API as we believe the platform is too young to enforce a strict forward-compatibility policy.

  • The ContractAspect.Direction property has become the ContractAspect.GetDirection method.
  • The IConditionallyInheritable.IsInheritable property has become a method.
  • Metalama.Extensions.DependencyInjection
    • The DependencyProperties record constructor has changed.
    • The framework should now be configured with the new hierarchical options framework. The object DependencyInjectionOptions has completely changed. It may not be the final design.
  • For external namespaces, the ContainingDeclaration of the namespace now returns the declaring assembly instead of the current compilation

Conclusion

With Metalama 2023.4, our main objective was to provide production-ready implementations of caching and design-by-contract aspects and lay down the foundations of our INotifyPropertyChanged aspect. To achieve this, we had to build a flexible configuration framework and implement several other enhancements.

Metalama 2023.4 is also the first version to be completely source-available.

To avoid any surprises, don’t forget to update Metalama before Microsoft starts rolling out the next Visual Studio version.

Happy meta-programming!

-gael