Skip to content

Latest commit

 

History

History
53 lines (29 loc) · 3.71 KB

File metadata and controls

53 lines (29 loc) · 3.71 KB
uid summary
invariants
The document explains how to check type invariants in Metalama, including adding invariants, opting out from invariant checking, and suspending enforcement of invariants, with examples provided.

Checking type invariants

Invariants are methods that verify the consistency of the state of the current object and throw an exception if inconsistencies are found. We recommend throwing an exception of type xref:Metalama.Patterns.Contracts.InvariantViolationException, but the final decision is entirely up to you.

Adding invariants

To add an invariant to a class:

  1. Create a void, non-static, and parameterless method in your class. This method should typically be private.
  2. Add the xref:Metalama.Patterns.Contracts.InvariantAttribute?text=[Invariant] custom attribute to this method.
  3. Add the validation logic to this method and throw an xref:Metalama.Patterns.Contracts.InvariantViolationException in case of a violation.

Warning

An invariant method should not have any side effects other than throwing an exception in case of an invariant violation.

Example: adding invariants

In the following example, an Invoice entity has two properties, DiscountPercent and DiscountAmount, that allow providing discounts. These two properties are additive, but their sum cannot result in a negative price. The CheckDiscounts method enforces this condition.

[!metalama-test ~/code/Metalama.Documentation.SampleCode.Contracts/Invariants.cs]

Opting out from invariant checking

When a type has any invariant, the implementation of all public or internal methods in this type will be wrapped into a try...finally block, and invariants will be verified from the finally block.

To prevent a method from being enhanced with this invariant checking logic, you can use the xref:Metalama.Patterns.Contracts.DoNotCheckInvariantsAttribute?text=[DoNotCheckInvariants] custom attribute.

Note that this will not waive the enforcement of invariants in methods called by the target of the xref:Metalama.Patterns.Contracts.DoNotCheckInvariantsAttribute?text=[DoNotCheckInvariants] attribute. Therefore, this attribute is mainly useful to optimize performance, not to relax invariants.

Suspending enforcement of invariants

If you have a code snippet that temporarily breaks invariants, you can suspend invariant enforcement.

First, enable the xref:Metalama.Patterns.Contracts.ContractOptions.IsInvariantSuspensionSupported option for this type. This option is disabled by default because it generates additional code. You can set this option from a xref:Metalama.Framework.Fabrics.ProjectFabric or xref:Metalama.Framework.Fabrics.NamespaceFabric as described in xref:configuring-contracts.

Once this feature is enabled, there are two ways to suspend invariant enforcement:

  • To disable enforcement while the entire method is executing, add the xref:Metalama.Patterns.Contracts.SuspendInvariantsAttribute?text=[SuspendInvariants] custom attribute to this method.
  • To disable enforcement while a code snippet is executing, wrap this snippet in a call to using ( this.SuspendInvariants() ). The SuspendInvariants method is generated by the xref:Metalama.Patterns.Contracts.ContractOptions.IsInvariantSuspensionSupported option.

Example: suspending invariants

The following example builds upon the previous one. We added a fabric to enable the xref:Metalama.Patterns.Contracts.ContractOptions.IsInvariantSuspensionSupported option. The Invoice method now has two new methods, UpdateDiscounts1 and UpdateDiscounts2, which update DiscountPercent and DiscountAmount while suspending invariants.

[!metalama-test ~/code/Metalama.Documentation.SampleCode.Contracts/Invariants_Suspend.cs]