Open sandboxFocusImprove this doc
  • Article

Configuring contracts

There are two types of configuration options: compile-time and run-time. Compile-time options affect code generation and must be configured using a fabric. Run-time settings, on the other hand, must be configured during application startup.

Let's begin with run-time settings.

Changing the validation logic

Some contracts utilize flexible run-time settings for their validation. These settings are modifiable properties of the ContractHelpers class.

Aspect Property Description
CreditCardAttribute IsValidCreditCardNumber The Func<string?, bool> validates the string as a credit card number.
EmailAttribute EmailRegex A regular expression validates the string as an email address.
UrlAttribute UrlRegex A regular expression validates the string as a URL.
PhoneAttribute PhoneRegex A regular expression validates the string as a phone number.

These properties can be set during the initialization sequence of your application, and any changes will affect the entire application.

Currently, there is no method to modify these settings for a specific namespace or type within your application.

Changing compile-time options

All other configurable options are compile-time ones, represented by the ContractOptions class. These options can be configured granularly from a fabric using the configuration framework described in Configuring aspects with fabrics.

Enabling and disabling contracts

Contracts are often only useful during the early phases of development. As the code stabilizes, they can be disabled. However, when a problem arises, it may be beneficial to re-enable them for troubleshooting.

The ContractOptions class provides three properties that allow you to enable or disable contracts for the entire project, or more specifically for a given namespace or type:

These options are enabled by default. If you disable them, the code supporting these features will not be generated.

Example: disabling invariants in a namespace

In the example below, we have invariants in two sub-namespaces: Invoicing and Fulfillment. We use a ProjectFabric and the ContractOptions to disable invariants for the Fulfillment namespace.

1using Metalama.Framework.Fabrics;
2using Metalama.Framework.Options;
3using Metalama.Patterns.Contracts;
4
5namespace Doc.Invariants_Suspend;
6
7public class Fabric : ProjectFabric
8{
9    public override void AmendProject( IProjectAmender amender )
10    {
11        amender.SetOptions( new ContractOptions { IsInvariantSuspensionSupported = true } );
12    }
13}

Changing the default inheritance or contract direction options

By default, contract inheritance is enabled and contract direction is set to Default. To change these default values, use the IsInheritable and Direction properties of the ContractOptions object.

Customizing the exception type or text

The default behavior of Metalama Contracts is to generate code that throws the default .NET exception with a hard-coded error message. This default behavior is implemented by the ContractTemplates class.

To customize the type of exceptions thrown or the exception messages (for instance, to localize them), you need to override the ContractTemplates class. Follow these steps:

  1. Create a class derived from the ContractTemplates.
  2. Override any or all templates. You may want to refer to the original source code of the ContractTemplates class for inspiration.
  3. Using a ProjectFabric or a NamespaceFabric, set the Templates property of the ContractOptions object.

Example: translating error messages

The following example demonstrates how to translate the exception messages into French.

1using Metalama.Framework.Fabrics;
2using Metalama.Framework.Options;
3using Metalama.Patterns.Contracts;
4
5// ReSharper disable StringLiteralTypo
6
7namespace Doc.Localize;
8
9internal class Fabric : ProjectFabric
10{
11    public override void AmendProject( IProjectAmender amender )
12    {
13        amender.SetOptions( new ContractOptions { Templates = new FrenchTemplates() } );
14    }
15}