Open sandboxFocusImprove this doc

List of contract attributes

Below is a list of available contract attributes for your selection:

Nullability contracts

[NotNull]

The NotNullAttribute contract verifies that the assigned value is not null.

Example: [NotNull]

Source Code
1using Metalama.Patterns.Contracts;
2

3namespace Doc.NotNullContract;
4
5public class Instrument
6{
7    [NotNull]
8    public string Name { get; set; }


9
10    [NotNull]












11    public Category Category { get; set; }






12
13    public Instrument( [NotNull] string name, [NotNull] Category category )























14    {
15        this.Name = name;
16        this.Category = category;



17    }
18}
19
20public class Category { }
Transformed Code
1using System;
2using Metalama.Patterns.Contracts;
3
4namespace Doc.NotNullContract;
5
6public class Instrument
7{
8    private string _name = default!;
9
10    [NotNull]
11    public string Name
12    {
13        get
14        {
15            return _name;
16        }
17
18        set
19        {
20            if (value == null!)
21            {
22                throw new ArgumentNullException("value", "The 'Name' property must not be null.");
23            }
24
25            _name = value;
26        }
27    }
28
29    private Category _category = default!;
30
31    [NotNull]
32    public Category Category
33    {
34        get
35        {
36            return _category;
37        }
38
39        set
40        {
41            if (value == null!)
42            {
43                throw new ArgumentNullException("value", "The 'Category' property must not be null.");
44            }
45
46            _category = value;
47        }
48    }
49
50    public Instrument([NotNull] string name, [NotNull] Category category)
51    {
52        if (name == null!)
53        {
54            throw new ArgumentNullException("name", "The 'name' parameter must not be null.");
55        }
56
57        if (category == null!)
58        {
59            throw new ArgumentNullException("category", "The 'category' parameter must not be null.");
60        }
61
62        this.Name = name;
63        this.Category = category;
64    }
65}
66
67public class Category { }

[Required]

Similar to the NotNullAttribute contract, the RequiredAttribute contract verifies that the value is not null. Additionally, it requires the string to be non-empty.

Example: [Required]

Source Code
1using Metalama.Patterns.Contracts;
2

3namespace Doc.RequiredContract;
4
5public class Instrument
6{
7    [Required]
8    public string Name { get; set; }


9
10    [Required]





11    public Category Category { get; set; }




















12
13    public Instrument( [Required] string name, [Required] Category category )





14    {
15        this.Name = name;
16        this.Category = category;




























17    }
18}
19
20public class Category { }
Transformed Code
1using System;
2using Metalama.Patterns.Contracts;
3
4namespace Doc.RequiredContract;
5
6public class Instrument
7{
8    private string _name = default!;
9
10    [Required]
11    public string Name
12    {
13        get
14        {
15            return _name;
16        }
17
18        set
19        {
20            if (string.IsNullOrWhiteSpace(value))
21            {
22                if (value == null!)
23                {
24                    throw new ArgumentNullException("value", "The 'Name' property is required.");
25                }
26                else
27                {
28                    throw new ArgumentException("The 'Name' property is required.", "value");
29                }
30            }
31
32            _name = value;
33        }
34    }
35
36    private Category _category = default!;
37
38    [Required]
39    public Category Category
40    {
41        get
42        {
43            return _category;
44        }
45
46        set
47        {
48            if (value == null!)
49            {
50                throw new ArgumentNullException("value", "The 'Category' property is required.");
51            }
52
53            _category = value;
54        }
55    }
56
57    public Instrument([Required] string name, [Required] Category category)
58    {
59        if (string.IsNullOrWhiteSpace(name))
60        {
61            if (name == null!)
62            {
63                throw new ArgumentNullException("name", "The 'name' parameter is required.");
64            }
65            else
66            {
67                throw new ArgumentException("The 'name' parameter is required.", "name");
68            }
69        }
70
71        if (category == null!)
72        {
73            throw new ArgumentNullException("category", "The 'category' parameter is required.");
74        }
75
76        this.Name = name;
77        this.Category = category;
78    }
79}
80
81public class Category { }
Warning

All contracts listed below accept null values without validating them.

String contracts

[NotEmpty]

The NotEmptyAttribute contract requires the string to be non-empty. Please note that this contract does not validate the string against being null. If you want to prohibit both null and empty strings, use the RequiredAttribute constraint.

Example: [NotEmpty] vs [NotNull] vs [Required]

Source Code
1using Metalama.Patterns.Contracts;
2

3namespace Doc.NotEmptyContract;
4
5public class Instrument
6{
7    // Neither null nor empty strings are allowed.
8    [NotNull]


9    [NotEmpty]
10    public string Name { get; set; }
11
12    // Null strings are allowed but not empty strings.























13    [NotEmpty]
14    public string? Description { get; set; }
15
16    // Equivalent to [NotNull, NotEmpty]





17    [Required]













18    public string Currency { get; set; }
19
20    public Instrument( string name, string currency, string? description )



















21    {
22        this.Name = name;
23        this.Description = description;




24        this.Currency = currency;
25    }
26}
Transformed Code
1using System;
2using Metalama.Patterns.Contracts;
3
4namespace Doc.NotEmptyContract;
5
6public class Instrument
7{
8    private string _name = default!;
9
10    // Neither null nor empty strings are allowed.
11    [NotNull]
12    [NotEmpty]
13    public string Name
14    {
15        get
16        {
17            return _name;
18        }
19
20        set
21        {
22            if (value == null!)
23            {
24                throw new ArgumentNullException("value", "The 'Name' property must not be null.");
25            }
26
27            if (value.Length <= 0)
28            {
29                throw new ArgumentException("The 'Name' property must not be null or empty.", "value");
30            }
31
32            _name = value;
33        }
34    }
35
36    private string? _description;
37
38    // Null strings are allowed but not empty strings.
39    [NotEmpty]
40    public string? Description
41    {
42        get
43        {
44            return _description;
45        }
46
47        set
48        {
49            if (value != null && value.Length <= 0)
50            {
51                throw new ArgumentException("The 'Description' property must not be null or empty.", "value");
52            }
53
54            _description = value;
55        }
56    }
57
58    private string _currency = default!;
59
60    // Equivalent to [NotNull, NotEmpty]
61    [Required]
62    public string Currency
63    {
64        get
65        {
66            return _currency;
67        }
68
69        set
70        {
71            if (string.IsNullOrWhiteSpace(value))
72            {
73                if (value == null!)
74                {
75                    throw new ArgumentNullException("value", "The 'Currency' property is required.");
76                }
77                else
78                {
79                    throw new ArgumentException("The 'Currency' property is required.", "value");
80                }
81            }
82
83            _currency = value;
84        }
85    }
86
87    public Instrument(string name, string currency, string? description)
88    {
89        this.Name = name;
90        this.Description = description;
91        this.Currency = currency;
92    }
93}

[CreditCard]

The CreditCardAttribute contract validates that the string is a valid credit card number.

It utilizes the delegate exposed by the ContractHelpers.IsValidCreditCardNumber property. You can replace this delegate with your own implementation.

Example: [CreditCard]

Source Code
1using Metalama.Patterns.Contracts;
2

3namespace Doc.CreditCardContract;
4
5public class Customer
6{
7    [CreditCard]
8    public string? CreditCard { get; set; }


9}
Transformed Code
1using System;
2using Metalama.Patterns.Contracts;
3
4namespace Doc.CreditCardContract;
5
6public class Customer
7{
8    private string? _creditCard;
9
10    [CreditCard]
11    public string? CreditCard
12    {
13        get
14        {
15            return _creditCard;
16        }
17
18        set
19        {
20            if (!ContractHelpers.IsValidCreditCardNumber(value))
21            {
22                throw new ArgumentException("The 'CreditCard' property must be a valid credit card number.", "value");
23            }
24
25            _creditCard = value;
26        }
27    }
28}

[Email], [Phone], and [Url]

The PhoneAttribute, EmailAttribute and UrlAttribute contracts can be used to validate strings against well-known regular expressions.

These regular expressions can be customized by setting the PhoneRegex, EmailRegex, UrlRegex properties of the ContractHelpers class.

Example: [Email], [Phone], and [Url]

Source Code
1using Metalama.Patterns.Contracts;
2

3namespace Doc.WellKnownRegexContracts;
4
5public class Customer
6{
7    [Phone]
8    public string? Phone { get; set; }


9
10    [Email]




















11    public string? Email { get; set; }
12
13    [Url]





14    public string? Profile { get; set; }















15}
Transformed Code
1using System;
2using Metalama.Patterns.Contracts;
3
4namespace Doc.WellKnownRegexContracts;
5
6public class Customer
7{
8    private string? _phone;
9
10    [Phone]
11    public string? Phone
12    {
13        get
14        {
15            return _phone;
16        }
17
18        set
19        {
20            var regex = ContractHelpers.PhoneRegex;
21            if (value != null && !regex.IsMatch(value))
22            {
23                var regex_1 = regex;
24                throw new ArgumentException("The 'Phone' property must be a valid phone number.", "value");
25            }
26
27            _phone = value;
28        }
29    }
30
31    private string? _email;
32
33    [Email]
34    public string? Email
35    {
36        get
37        {
38            return _email;
39        }
40
41        set
42        {
43            var regex = ContractHelpers.EmailRegex;
44            if (value != null && !regex.IsMatch(value))
45            {
46                var regex_1 = regex;
47                throw new ArgumentException("The 'Email' property must be a valid email address.", "value");
48            }
49
50            _email = value;
51        }
52    }
53
54    private string? _profile;
55
56    [Url]
57    public string? Profile
58    {
59        get
60        {
61            return _profile;
62        }
63
64        set
65        {
66            var regex = ContractHelpers.UrlRegex;
67            if (value != null && !regex.IsMatch(value))
68            {
69                var regex_1 = regex;
70                throw new ArgumentException("The 'Profile' property must be a valid URL.", "value");
71            }
72
73            _profile = value;
74        }
75    }
76}

Custom regular expressions

The RegularExpressionAttribute contract validates a string against a custom regular expression. If the same regular expression is to be used multiple times, it may be preferable to create a derived class instead of using the RegularExpressionAttribute class directly.

Example: custom regular expression

1using Metalama.Framework.Aspects;
2using Metalama.Patterns.Contracts;
3
4namespace Doc.CustomRegexContract;
5
6[RunTimeOrCompileTime]
7public class PasswordAttribute : RegularExpressionAttribute
8{
9    public PasswordAttribute() : base(
10        "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[@$!%*?&#])[A-Za-z\\d@$!%*?&#]{8,20}$\n" ) { }
11}
Source Code
1namespace Doc.CustomRegexContract;
2



3public class Customer
4{
5    [Password]
6    public string? Password { get; set; }


7}
Transformed Code
1using System;
2using Metalama.Patterns.Contracts;
3
4namespace Doc.CustomRegexContract;
5
6public class Customer
7{
8    private string? _password;
9
10    [Password]
11    public string? Password
12    {
13        get
14        {
15            return _password;
16        }
17
18        set
19        {
20            var regex = ContractHelpers.GetRegex("^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[@$!%*?&#])[A-Za-z\\d@$!%*?&#]{8,20}$\n", 0);
21            if (value != null && !regex.IsMatch(value))
22            {
23                var regex_1 = regex;
24                throw new ArgumentException($"The 'Password' property must match the regular expression '{regex_1}'.", "value");
25            }
26
27            _password = value;
28        }
29    }
30}

String length

The StringLengthAttribute contract validates that the length of a string falls within a specified range.

Example: [StringLength]

Source Code
1using Metalama.Patterns.Contracts;
2

3namespace Doc.StringLengthContract;
4
5public class Customer
6{
7    [StringLength( 12, 64 )]
8    public string? Password { get; set; }
9}
Transformed Code
1using System;
2using Metalama.Patterns.Contracts;
3
4namespace Doc.StringLengthContract;
5
6public class Customer
7{
8    private string? _password;
9
10    [StringLength(12, 64)]
11    public string? Password
12    {
13        get
14        {
15            return _password;
16        }
17
18        set
19        {
20            if (value != null && (value.Length < 12 || value.Length > 64))
21            {
22                throw new ArgumentException($"The  'Password' property must be a string with length between {12} and {64}.", "value");
23            }
24
25            _password = value;
26        }
27    }
28}

Enum contracts

The EnumDataTypeAttribute contract can validate values of type string, object, or of any integer type. It throws an exception if the value is not valid for the given enum type.

Example: [EnumDataType]

Source Code
1using Metalama.Patterns.Contracts;
2using System;
3
4namespace Doc.EnumDataTypeContract;
5
6public class Message
7{
8    [EnumDataType( typeof(ConsoleColor) )]
9    public string? Color { get; set; }
10}
Transformed Code
1using Metalama.Patterns.Contracts;
2using System;
3
4namespace Doc.EnumDataTypeContract;
5
6public class Message
7{
8    private string? _color;
9
10    [EnumDataType(typeof(ConsoleColor))]
11    public string? Color
12    {
13        get
14        {
15            return _color;
16        }
17
18        set
19        {
20            if (value != null! && !EnumDataTypeAttributeHelper.IsValidEnumValue(value, typeof(ConsoleColor)))
21            {
22                throw new ArgumentException("The 'Color' property must be a valid string?.", "value");
23            }
24
25            _color = value;
26        }
27    }
28}

Numeric contracts

The following contracts can be used to verify that a value falls within a specified range:

Attribute Description
LessThanOrEqualAttribute Verifies that the value is less than or equal to the specified maximum.
GreaterThanOrEqualAttribute Verifies that the value is greater than or equal to the specified minimum.
NonNegativeAttribute Verifies that the value is greater than or equal to zero.
NonPositiveAttribute Verifies that the value is less than or equal to zero.
StrictlyLessThanAttribute Verifies that the value is strictly less than the specified maximum.
StrictlyGreaterThanAttribute Verifies that the value is strictly greater than the specified minimum.
StrictlyNegativeAttribute Verifies that the value is strictly less than zero.
StrictlyPositiveAttribute Verifies that the value is strictly greater than zero.
StrictRangeAttribute Verifies that the value is strictly greater than a specified minimum and strictly less than a specified maximum.
RangeAttribute Verifies that the value is greater than or equal to a specified minimum and less than or equal to a specified maximum.
Warning

The library also contains the contracts [Positive], [Negative], [LessThan] and [GreaterThan] for backward compatibility. These contracts are oddly named; they enforce a non-strict inequality while the English convention mandates to enforce strict inequalities. Using any of these contracts will result in a warning requesting you to specify the strictness by using one of the contracts listed above or by setting the DefaultInequalityStrictness contract option is set using the ConfigureContracts fabric extension method.

Example: numeric contracts

Source Code
1using Metalama.Patterns.Contracts;
2

3namespace Doc.NumericContracts;
4
5public class OrderLine
6{
7    [NonNegative]
8    public decimal NominalPrice { get; }


9
10    [StrictlyPositive]












11    public decimal Quantity { get; }






12
13    [Range( 0, 100 )]





14    public int Discount { get; }
15}
Transformed Code
1using System;
2using Metalama.Patterns.Contracts;
3
4namespace Doc.NumericContracts;
5
6public class OrderLine
7{
8    private readonly decimal _nominalPrice;
9
10    [NonNegative]
11    public decimal NominalPrice
12    {
13        get
14        {
15            return _nominalPrice;
16        }
17
18        private init
19        {
20            if (value < 0M)
21            {
22                throw new ArgumentOutOfRangeException("value", value, "The 'NominalPrice' property must be greater than or equal to 0.");
23            }
24
25            _nominalPrice = value;
26        }
27    }
28
29    private readonly decimal _quantity;
30
31    [StrictlyPositive]
32    public decimal Quantity
33    {
34        get
35        {
36            return _quantity;
37        }
38
39        private init
40        {
41            if (value <= 0M)
42            {
43                throw new ArgumentOutOfRangeException("value", value, "The 'Quantity' property must be strictly greater than 0.");
44            }
45
46            _quantity = value;
47        }
48    }
49
50    private readonly int _discount;
51
52    [Range(0, 100)]
53    public int Discount
54    {
55        get
56        {
57            return _discount;
58        }
59
60        private init
61        {
62            if (value is < 0 or > 100)
63            {
64                throw new ArgumentOutOfRangeException("value", value, "The 'Discount' property must be in the range [0, 100].");
65            }
66
67            _discount = value;
68        }
69    }
70}

Collections contracts

The NotEmptyAttribute contract can be used on any collection, including arrays or immutable arrays. It requires the collection or the array to contain at least one element.

Note that this contract does not validate the collection object against being null (or, in the case of immutable arrays, default ones). If you want to prohibit both null and empty collections, consider adding the NotNullAttribute constraint.

Example: [NotEmpty] on collection

Source Code
1using Metalama.Patterns.Contracts;
2using System.Collections.Generic;
3

4namespace Doc.NotEmptyCollectionContract;
5
6public class Submenu
7{
8    public string Name { get; }
9
10    public IReadOnlyCollection<MenuItem> Items { get; }
11
12    public Submenu(
13        [Required] string name,
14        [NotNull] [NotEmpty] IReadOnlyCollection<MenuItem> items )
15    {
16        this.Name = name;
17        this.Items = items;






















18    }
19}
20
21public record MenuItem( string Name );
Transformed Code
1using Metalama.Patterns.Contracts;
2using System;
3using System.Collections.Generic;
4
5namespace Doc.NotEmptyCollectionContract;
6
7public class Submenu
8{
9    public string Name { get; }
10
11    public IReadOnlyCollection<MenuItem> Items { get; }
12
13    public Submenu(
14        [Required] string name,
15        [NotNull][NotEmpty] IReadOnlyCollection<MenuItem> items)
16    {
17        if (string.IsNullOrWhiteSpace(name))
18        {
19            if (name == null!)
20            {
21                throw new ArgumentNullException("name", "The 'name' parameter is required.");
22            }
23            else
24            {
25                throw new ArgumentException("The 'name' parameter is required.", "name");
26            }
27        }
28
29        if (items == null!)
30        {
31            throw new ArgumentNullException("items", "The 'items' parameter must not be null.");
32        }
33
34        if (items.Count <= 0)
35        {
36            throw new ArgumentException("The 'items' parameter must not be null or empty.", "items");
37        }
38
39        this.Name = name;
40        this.Items = items;
41    }
42}
43
44public record MenuItem(string Name);