Open sandboxFocusImprove this doc

LAMA5004: Invariant suspension isn't enabled for the type

Severity Error
Reported by Metalama.Patterns.Contracts
Message The [SuspendInvariantsAttribute] aspect cannot be applied to the method '<Method>' because the IsInvariantSuspensionSupported contract option is not enabled for the type '<Type>'. Enable this option in ContractOptions using a fabric.

Cause

The SuspendInvariantsAttribute aspect suspends the verification of invariants while a method executes. Suspending invariants requires additional generated code in the type, so this feature is disabled by default and must be enabled with the IsInvariantSuspensionSupported option.

Metalama reports LAMA5004 when you apply [SuspendInvariants] to a method, but the option isn't enabled for the declaring type of the method. If invariants are disabled for the type with the AreInvariantsEnabled option, the aspect is silently skipped instead.

In the following example, the Update method has the [SuspendInvariants] aspect, but no fabric enables invariant suspension.

1using Metalama.Patterns.Contracts;
2
3namespace Doc.LAMA5004.Error;
4
5public partial class Invoice
6{
7    public decimal Amount { get; set; }
8
9    public decimal Discount { get; set; }
10
11    [Invariant]
12    private void CheckDiscount()
13    {
14        if ( this.Discount > this.Amount )
15        {
16            throw new InvariantViolationException( "The discount cannot exceed the amount." );
17        }
18    }
19
20    // Error: invariant suspension is not enabled for Invoice.
21    [SuspendInvariants]
    Error LAMA5004: The [SuspendInvariantsAttribute] aspect cannot be applied to the method 'Invoice.Update(decimal, decimal)' because the IsInvariantSuspensionSupported contract option is not enabled for the type 'Invoice'. Enable this option in ContractOptions using a fabric.

22    public void Update( decimal amount, decimal discount )
23    {
24        this.Amount = amount;
25        this.Discount = discount;
26    }
27}
28

Resolution

Set the IsInvariantSuspensionSupported option to true from a fabric, for the whole project as in the following example, or for a namespace or a type. See Configuring contracts for details. Alternatively, remove the [SuspendInvariants] aspect if the method doesn't need to break invariants temporarily.

1using Metalama.Framework.Fabrics;
2using Metalama.Framework.Options;
3using Metalama.Patterns.Contracts;
4
5namespace Doc.LAMA5004.Fixed;
6
7internal class Fabric : ProjectFabric
8{
9    public override void AmendProject( IProjectAmender amender )
10    {
11        // Enables the [SuspendInvariants] aspect and the SuspendInvariants method.
12        amender.SetOptions( new ContractOptions { IsInvariantSuspensionSupported = true } );
13    }
14}
15
Source Code
1using Metalama.Patterns.Contracts;
2
3namespace Doc.LAMA5004.Fixed;
4
5public partial class Invoice
6{
7    public decimal Amount { get; set; }
8


9    public decimal Discount { get; set; }





10



















11    [Invariant]





12    private void CheckDiscount()

















13    {
14        if ( this.Discount > this.Amount )
15        {
16            throw new InvariantViolationException( "The discount cannot exceed the amount." );
17        }
18    }
19
20    // Fixed: the fabric enables invariant suspension.
21    [SuspendInvariants]
22    public void Update( decimal amount, decimal discount )
23    {
24        this.Amount = amount;
25        this.Discount = discount;




26    }
27}





























28
Transformed Code
1using Metalama.Patterns.Contracts;
2
3namespace Doc.LAMA5004.Fixed;
4
5public partial class Invoice
6{
7    private decimal _amount;
8
9    public decimal Amount
10    {
11        get
12        {
13            return _amount;
14        }
15
16        set
17        {
18            try
19            {
20                _amount = value;
21                return;
22            }
23            finally
24            {
25                if (!this.AreInvariantsSuspended())
26                {
27                    this.VerifyInvariants();
28                }
29            }
30        }
31    }
32
33    private decimal _discount;
34
35    public decimal Discount
36    {
37        get
38        {
39            return _discount;
40        }
41
42        set
43        {
44            try
45            {
46                _discount = value;
47                return;
48            }
49            finally
50            {
51                if (!this.AreInvariantsSuspended())
52                {
53                    this.VerifyInvariants();
54                }
55            }
56        }
57    }
58
59    [Invariant]
60    private void CheckDiscount()
61    {
62        if (this.Discount > this.Amount)
63        {
64            throw new InvariantViolationException("The discount cannot exceed the amount.");
65        }
66    }
67
68    // Fixed: the fabric enables invariant suspension.
69    [SuspendInvariants]
70    public void Update(decimal amount, decimal discount)
71    {
72        using (this.SuspendInvariants())
73        {
74            try
75            {
76                this.Amount = amount;
77                this.Discount = discount;
78            }
79            finally
80            {
81                if (!this.AreInvariantsSuspended())
82                {
83                    this.VerifyInvariants();
84                }
85            }
86
87            return;
88        }
89    }
90
91    private readonly InvariantSuspensionCounter _invariantSuspensionCounter = new();
92
93    protected bool AreInvariantsSuspended()
94    {
95        return _invariantSuspensionCounter.AreInvariantsSuspended;
96    }
97
98    protected SuspendInvariantsCookie SuspendInvariants()
99    {
100        _invariantSuspensionCounter.Increment();
101        return new SuspendInvariantsCookie(_invariantSuspensionCounter);
102    }
103
104    protected virtual void VerifyInvariants()
105    {
106        CheckDiscount();
107    }
108}
109

See also