Metalama 2026.1 is a consolidation release, and the first long-term support (LTS) release of Metalama. It completes the C# 14 support started in 2026.0, extends the initialization and parameter-introduction advice, ports a major redesign of PostSharp.Patterns.Caching into the Redis caching backend, trims third-party dependencies, improves design-time performance, and closes most of the bug backlog.
Highlights:
- C# 14 completion. Every feature listed as a limitation in Metalama 2026.0 is now implemented, including extension block introductions, contracts on extension block parameters, and introduction of user-defined compound assignment operators.
- Initialization advice. New
AfterObjectInitializerandAfterLastInstanceConstructorkinds, records supported byBeforeInstanceConstructor, forwarding overloads for constructor parameter introduction, and parameter reuse for dependency injection. - Redis caching backend. Retry and exception-handling policies, key compression, overload detection, and many new configuration options.
- Reduced third-party dependencies.
System.Text.JsonreplacesNewtonsoft.Json,System.IO.HashingreplacesK4os.Hash, and optional HTML/diff-tool components have been extracted into opt-in extension packages. - Performance enhancements. Source-generated JSON serializers, MessagePack for design-time RPC, and pattern-matching optimizations.
- Over 100 bug fixes, i.e. almost the entire backlog.
Requirements
Metalama 2026.1 has the same requirements as 2026.0. See Requirements for the full matrix.
- Visual Studio 2022 LTSC 17.12, 2022 17.14, or 2026 18.0 (latest build).
- .NET SDK 8.0, 9.0, or 10.0.
- C# 12, 13, or 14.
C# 14 completion
All C# 14 features listed as limitations in Metalama 2026.0 are now implemented in 2026.1.
Introducing extension blocks
The new IntroduceExtensionBlock advice lets aspects introduce C# 14 extension blocks into a static class. The advice takes the receiver type and an optional receiver parameter name to choose between instance and static extension blocks, and supports the usual IntroduceMethod, IntroduceProperty, and so on for adding members.
See Introducing types for a full example.
Contracts on extension-block receiver parameters
Contracts such as [NotNull] or attributes from Metalama.Patterns.Contracts can now be applied to the receiver parameter of an extension block (#1127). Metalama propagates the contract to every extension member in the block, so a single declaration validates this-equivalent receiver access across the entire extension block, including members introduced by other aspects.
Introducing user-defined compound assignment operators
C# 14 lets types define their own compound assignment operators (+=, -=, *=, etc.). Aspects can now introduce these user-defined overloads (#1131). Operator introduction goes through the standard IntroduceMethod advice by setting OperatorKind.
For details, see the Introducing operators section of Introducing members.
Other C# 14 completions
- #1109: Use null-conditional assignments in templates.
- #1114: Use the
fieldkeyword in templates. - #1036: Generate run-time code for extension members using invoker interfaces.
- #1143: Introduce parameters into partial constructors.
Initialization advice
Metalama 2026.1 extends the initialization advice with two new initializer kinds: AfterObjectInitializer and AfterObjectInitializer. It improves BeforeInstanceConstructor on records. It also adds features to the IntroduceParameter advice.
AfterLastInstanceConstructor
InitializerKind.AfterLastInstanceConstructor injects your initialization logic into a protected virtual OnConstructed method and ensures it runs after all instance constructors in the chain have completed. An InitializationContext parameter threaded through the constructor chain coordinates this across inheritance hierarchies.
AfterObjectInitializer
InitializerKind.AfterObjectInitializer runs after the constructor and any object initializer or with expression have completed. Metalama makes the target type implement IInitializable and rewrites call sites to invoke Initialize automatically once construction and object initialization are done. Use this kind to validate or compute derived state after all fields and properties have been set in the object initializer.
BeforeInstanceConstructor on records
InitializerKind.BeforeInstanceConstructor now supports records, including positional records. The initializer code is injected into the primary constructor.
See Adding initializers for examples.
Constructor parameter introduction with forwarding overloads
The IntroduceParameter advice now supports two mechanisms, both source-compatible:
- Adding an optional parameter: the overload taking a compile-time-constant
defaultValueadds the parameter directly to the existing constructor. The IL signature changes, so binary compatibility is not preserved. This was the only available strategy prior to 2026.1, and remains the right choice for dependency-injection scenarios. - Adding a required parameter, pulled from a forwarding constructor: the overload without
defaultValueadds the new parameter as required, and generates a forwarding constructor that retains the pre-mutation signature and chains to the mutated constructor via: this(...), preserving binary compatibility. The value supplied by the forwarding constructor comes from an IPullStrategy: for instance, UseExpression for a non-constant expression such asDateTime.Now.
The new ForwardSourceConstructors and ForwardDefaultConstructor strategies control when forwarding constructors are generated. Both expose a WithObsoleteAttribute method so generated constructors can be deprecated.
See Introducing constructor parameters.
Dependency injection
IntroduceParameterAndPull now accepts a reuseExistingParameterOfCompatibleType argument (#1552). 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 pulling ILogger<T> no longer adds a second ILogger<T> parameter if the target constructor already accepts one.
See Injecting dependencies into aspects.
Redis caching backend
The refactoring performed in the late summer 2025 to PostSharp.Patterns.Caching.Redis is now integrated into Metalama 2026.1. The motivation and full background are described in the PostSharp blog post. For full documentation, see Using Redis as a distributed cache.
New data schema
The data schema has been redesigned so that cache items are read with a single, observationally-consistent operation. The previous implementation reconciled the cache item and its dependencies, stored under separate Redis keys, through a client-side retry loop that could fail under sustained load in master/replica deployments. Dependencies are no longer flattened into the cache item; transitive dependencies are walked recursively at invalidation time instead. Cache keys are now versioned, so future schema changes can be rolled out alongside the running application.
A cache purge (FLUSHDB) is required after upgrading. See Breaking changes below.
Cluster and multi-node support
The backend now supports sharded Redis clusters through hash tags on cache keys, ensuring that related keys map to the same hash slot. ReadCommandFlags (default: PreferReplica) and WriteCommandFlags (default: PreferMaster) let you steer reads and writes between primary and replica nodes.
Retry and exception-handling policies
Configurable retry and exception-handling policies are now exposed directly on RedisCachingBackendConfiguration, replacing the previous ExceptionHandlingCachingBackendEnhancer.
- Retry policies (IRetryPolicy): configurable retry logic with exponential backoff and jitter for transactions, background tasks, and recovery actions.
- Exception handling policies (IExceptionHandlingPolicy): control how exceptions are handled after retries are exhausted. The DefaultExceptionHandlingPolicy logs exceptions and attempts to recover from failed write operations.
Key compression
Cache keys that exceed a configurable threshold (default: 128 characters) can now be automatically hashed using the CacheKeyHashingAlgorithm enum (XxHash64 or XxHash128), avoiding Redis key length limits and improving performance with long keys.
Overload detection
<xref:Metalama.Patterns.Caching.Backends.Redis.RedisCachingBackend> now monitors its background task queue and exposes <xref:Metalama.Patterns.Caching.Backends.Redis.RedisCachingBackend.IsBackgroundTaskQueueOverloaded> and <xref:Metalama.Patterns.Caching.Backends.Redis.RedisCachingBackend.IsBackgroundTaskQueueOverloadedChanged>. The RedisCacheDependencyGarbageCollector automatically pauses real-time notification processing when the backend is overloaded.
New configuration options
New properties on RedisCachingBackendConfiguration:
TransactionRetryPolicy,BackgroundTasksRetryPolicy,BackgroundRecoveryRetryPolicyfor resilience.BackgroundTasksMaxConcurrencyandBackgroundTasksOverloadedThresholdfor overload management.InvalidationMaxConcurrencyfor throttling large graph invalidations.KeyCompressingThresholdfor key compression.DisposeTimeout,SupportsEvents,ReadCommandFlags,WriteCommandFlags.
The RedisCacheDependencyGarbageCollectorOptions class now provides configuration for the periodic cleanup process, including CacheCleanupDelay and CacheCleanupOptions (with RemediationDelay and MaxConcurrency).
Supply chain security
Several transitive dependencies have been replaced with platform-provided APIs or extracted into optional extension packages, so projects that don't use a given component are no longer exposed to it.
System.Text.JsonreplacesNewtonsoft.Json(#741).System.IO.HashingreplacesK4os.Hash(#742). Non-cryptographic hashes now use xxHash from the .NET runtime libraries.- Diff-tool integration extracted to
Metalama.Extensions.DiffEngine(#446). Add the package to have the diff tool launched automatically when an aspect test fails; without it, tests run normally and the diff tool is silently disabled. See Snapshot testing of aspects and Configuring the external diff tool. - HTML test output extracted to
Metalama.Extensions.HtmlWriter(#447). The only typical use of this package is the generation of Metalama's own documentation. - MD5 is no longer used internally for non-cryptographic hashing (#525), so Metalama assemblies no longer trip security scanners that flag MD5 usage regardless of intent.
Performance enhancements
- Source-generated JSON serializers. Metalama now uses
System.Text.Jsonsource generators for its configuration files. - MessagePack for design-time RPC (#1299). The protocol between the Metalama analyzer and the design-time services has been migrated from JSON to MessagePack.
- Pattern-matching optimization. Polymorphic matching on interface types has been restructured into matching by
DeclarationKindorTypeKind. - Improved WPF compatibility (#1590). XAML files can now reference declarations introduced by Metalama aspects. The WPF temporary compilation phase (
MarkupCompilePass1) runs a reduced pipeline that emits member signatures only, skipping the linker step since the temporary assembly is discarded after XAML type resolution.
Other enhancements
Contracts
All numeric contracts in Metalama.Patterns.Contracts now accept types implementing INumber<T> (including generic type parameters), in addition to built-in numeric types (#1543).
See the Generic math support section of List of contract attributes.
Code model
- ICompilation.EntryPoint returns the
Program.Mainmethod of the compilation, ornullfor library projects. - IEvent.RaiseMethod returns
nullfor events that cannot be raised (non-field-like, interface, and abstract events). The new IEventInvoker.CanRaise property lets you check before attempting to raise (#771). OfExactSignatureoverloads on IMethodCollection and IConstructorCollection (#846) locate methods or constructors by exact signature.- Generic parameter constraints are now honored when matching signatures on IMethodCollection (#842).
- TypeFactory.TryGetType returns
falseinstead of throwing when a type cannot be resolved. - IMethodBaseBuilder.InsertParameter adds a parameter at an arbitrary position in an introduced method or constructor, not just at the end (#657).
- IProject.Features exposes a ProjectFeatures object describing compiler and target-framework capabilities (for instance,
SupportsCovariantReturnTypes), so aspects can adapt their output to the target project (#1532).
Templates and compile-time code
- TypedConstant.NamedConstant creates a
TypedConstantthat references an enum member or aconstfield by name, for instance run-time-only enum value. - IMethodInvoker.CreateDelegateExpression generates an expression that references a method as a delegate. Useful when you need to pass a method reference (e.g., to
EventHandlerorFunc<…>) rather than invoke it. See Generating code based on the code model. - Compile-time serialization now supports value tuples (
ValueTuplethroughValueTuple<T1,…,T7,TRest>) and Index/Range. See Serialization of aspects and other compile-time classes. - IAttribute.TryConstruct instantiates a custom attribute from a template, returning
falseif the attribute cannot be constructed at compile time (#723).
Fabrics
ITypeAmender now implements IAdviser<T> (#1487), so you can call IntroduceMethod, Override, ImplementInterface, and every other extension method from AdviserExtensions directly on the amender parameter; no more amender.Advice.* boilerplate. Use With to target a specific member.
A new Diagnostics property of type ScopedDiagnosticSink (#724) lets you report or suppress diagnostics scoped to the target type, just as you would from an aspect's BuildAspect method.
See Advising a single type with a fabric.
IDE and tooling
- You can now hide an aspect from Aspect Explorer via the HideFromAspectExplorer property of the EditorExperienceAttribute attribute (#697).
- Read-only generated files. The transformed files emitted under
obj/<Config>/<TFM>/metalama/are now marked read-only, so accidental edits during a debugging session no longer go unnoticed (the edits would be overwritten on the next build) (#745).
Bug backlog
More than 100 bug fixes shipped across the 2026.1 preview builds, covering the template engine, linker, code model, design-time services, dependency injection, and diagnostics.
For the full, per-build list of fixes, see the 2026.1 releases on GitHub.
Breaking changes
AddInitializerordering (#1529): initializer order now respectsAspectOrderDirection.RunTime. If you define an ordering with[assembly: AspectOrder(AspectOrderDirection.RunTime, typeof(FirstAspect), typeof(SecondAspect))], the initializer fromFirstAspectruns before the one fromSecondAspect.IntroduceParameteron record primary constructors (#1555): the introduced parameter is no longer materialized as part of the record's value shape by default: it does not generate an auto-property, does not appear inDeconstruct, and does not participate inEquals,GetHashCode, orToString. Opt in explicitly withPullStrategy.IntroduceParameterAndPull(materializeOnRecord: true).- IConstructor.InitializerKind now returns
Base(instead ofNone) for implicitbase()calls. - TemplateAttribute.IsVirtual is disregarded when the target type is
sealed. - IEvent.RaiseMethod returns
nullfor events that cannot be raised. Use IEventInvoker.CanRaise to check before raising. - Removed test base classes: the
AspectTestClass,DefaultAspectTestClass,CurrentDirectoryAttribute, andCurrentProjectAttributeclasses have been removed fromMetalama.Testing.AspectTesting. Tests are now discovered automatically; no test-runner code is needed.
Redis caching
- Data schema redesign: the Redis data schema has been redesigned. A cache purge (
FLUSHDB) is required after upgrading to 2026.1. - Removed
ExceptionHandlingCachingBackendEnhancer: use the ExceptionHandlingPolicy property instead. TransactionMaxRetriesis obsolete: use TransactionRetryPolicy instead.CacheCleanupOptions.Sequentialis obsolete: use MaxConcurrency instead.CacheItem.Dependencies: now exposes direct dependencies only. Transitive dependencies are resolved at invalidation time rather than flattened into the item.