Open sandboxFocusImprove this doc

LAMA5005: Invariant suspension is redundant on a type without invariants

Severity Warning
Reported by Metalama.Patterns.Contracts
Message The [SuspendInvariantsAttribute] aspect on method '<Method>' is redundant because the type '<Type>' does not contain any invariants.

Cause

The [SuspendInvariants] aspect suspends the verification of the invariants of an object while the target method executes. Invariants are the methods of the type, or of its base types, marked with [Invariant].

Metalama reports LAMA5005 when invariant suspension is enabled through the IsInvariantSuspensionSupported option, but the declaring type of the method has no invariant. The aspect would have no effect, so Metalama ignores it and leaves the method unchanged.

In the following example, the fabric enables invariant suspension, but the Account class doesn't declare any [Invariant] method.

1using Metalama.Framework.Fabrics;
2using Metalama.Patterns.Contracts;
3
4namespace Doc.LAMA5005.Error;
5
6public class Fabric : ProjectFabric
7{
8    public override void AmendProject( IProjectAmender amender )
9    {
10        amender.ConfigureContracts( new ContractOptions { IsInvariantSuspensionSupported = true } );
11    }
12}
13
1using Metalama.Patterns.Contracts;
2
3namespace Doc.LAMA5005.Error;
4
5public partial class Account
6{
7    public decimal Balance { get; set; }
8
9    public decimal CreditLimit { get; set; }
10
11    // Warning: the type has no [Invariant] method, so there is nothing to suspend.
12    [SuspendInvariants]
    Warning LAMA5005: The [SuspendInvariantsAttribute] aspect on method 'Account.Reset(decimal, decimal)' is redundant because the type 'Account' does not contain any invariants.

13    public void Reset( decimal balance, decimal creditLimit )
14    {
15        this.CreditLimit = creditLimit;
16        this.Balance = balance;
17    }
18}
19

Resolution

Depending on your intent, do one of the following:

  • If the type should verify invariants, add the missing method marked with [Invariant], as in the following example.
  • If the type doesn't need invariants, remove the [SuspendInvariants] attribute from the method.

1using Metalama.Framework.Fabrics;
2using Metalama.Patterns.Contracts;
3
4namespace Doc.LAMA5005.Fixed;
5
6public class Fabric : ProjectFabric
7{
8    public override void AmendProject( IProjectAmender amender )
9    {
10        amender.ConfigureContracts( new ContractOptions { IsInvariantSuspensionSupported = true } );
11    }
12}
13
Source Code
1using Metalama.Patterns.Contracts;
2
3namespace Doc.LAMA5005.Fixed;
4
5public partial class Account
6{
7    public decimal Balance { get; set; }
8


9    public decimal CreditLimit { get; set; }





10



















11    [SuspendInvariants]






















12    public void Reset( decimal balance, decimal creditLimit )
13    {
14        this.CreditLimit = creditLimit;
15        this.Balance = balance;




16    }
17
18    // Fixed: the type now has an invariant that [SuspendInvariants] can suspend.







19    [Invariant]



20    private void CheckBalance()
21    {
22        if ( this.Balance < -this.CreditLimit )
23        {


24            throw new InvariantViolationException( "The balance exceeds the credit limit." );
25        }











26    }

27}




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

See also