| Severity | Warning |
| Reported by | Metalama.Framework (Roslyn analyzer) |
| Message | '<DeclaringType>' does not satisfy the [Durable] contract because '<MemberType>' is not durable. Retention path: <RetentionPath>. |
Cause
At design time, the analysis process lives as long as the solution is open, and Roslyn produces a new compilation on almost every keystroke. An object that outlives a single request must therefore not hold a compilation, a syntax tree, a semantic model, a symbol, a code model declaration, or anything else that transitively reaches one: a single retained compilation pins every syntax tree of the project. The [Durable] attribute, defined in the Metalama.Framework.Utilities namespace, lets you declare that a type is safe to keep across compilations, and a Roslyn analyzer verifies this declaration.
Metalama reports LAMA0870 when an instance field, or an automatically implemented instance property, of a type that is subject to the contract has a declared type that isn't durable. A type is subject to the contract when it is marked [Durable], or when it derives from or implements a type marked [Durable]. The warning is reported on the field or property. In the message, <DeclaringType> is the type that must be durable, <MemberType> is the declared type of the member that isn't, and <RetentionPath> is the chain of members and types that leads from <DeclaringType> to the type responsible, followed by the reason in parentheses, for instance A -> _tree -> SyntaxTree.
A type is durable in the following cases:
- It is an intrinsic type or another value-like type of the base class library, such as
string,Guid, orDateTime. - It is a system collection whose type arguments are durable.
- It is a durable reference, such as IDurableRef, or a serializable declaration identifier.
- It is a class or struct marked
[Durable], which is verified where it is declared.
A type that is none of these isn't durable, because durability is opt-in. A delegate type is never durable, because a delegate holds its target and everything its closure captured. The type object isn't durable either, because it doesn't constrain what may be stored.
The analyzer runs in the compiler and in the IDE, not in the Metalama aspect pipeline, so this diagnostic can't be shown in an example that runs a Metalama aspect test.
Resolution
Choose the fix that matches what the member really needs:
- Store an identifier instead of the object. Replace a symbol, a declaration, or a reference by a durable equivalent: store an IDurableRef (call
ToDurableRef()on the declaration), or a SerializableDeclarationId, and resolve it when you need the object. - Mark the member type
[Durable]. If the type is yours and it holds only durable members, add[Durable]to it. The analyzer then verifies its members in turn. - Use
DurableLazy<T>instead ofLazy<T>.Lazy<T>holds its factory delegate, and therefore that delegate's closure. - Mark the member
[Durable]. For a field or an automatically implemented property typed as an interface,object, or a delegate, applying[Durable]to the member waives the check on its declared type and requires instead that every assigned value is durable. The analyzer then reports non-durable assignments with LAMA0871. The member must be read-only or private, or, for a property, have a private setter. - Use
DurableDangerous<T>. If the value is durable in fact but the analyzer can't establish it, wrap it inDurableDangerous<T>. Prefer this to suppressing the warning, because the assertion then appears in the signature of the member.
If the declared type is an interface or an abstract type that isn't marked [Durable], the analyzer reports a different diagnostic, LAMA0876, whose resolution is to mark the interface [Durable].