MetalamaConceptual documentationUsing Metalama PatternsContractsValidating field, property and parameter valuesContract types
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{
5    public 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
20    public class Category { }
21}
Transformed Code
1using System;
2using Metalama.Patterns.Contracts;
3
4namespace Doc.NotNullContract
5{
6    public class Instrument
7    {
8        private string _name = default!;
9
10        [NotNull]
11        public string Name
12        {
13            get
14            {
15                return this._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                this._name = value;
26            }
27        }
28
29        private Category _category = default!;
30
31        [NotNull]
32        public Category Category
33        {
34            get
35            {
36                return this._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                this._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
67    public class Category { }
68}

[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{
5    public 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
20    public class Category { }
21}
Transformed Code
1using System;
2using Metalama.Patterns.Contracts;
3
4namespace Doc.RequiredContract
5{
6    public class Instrument
7    {
8        private string _name = default!;
9
10        [Required]
11        public string Name
12        {
13            get
14            {
15                return this._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 ArgumentOutOfRangeException("value", "The 'Name' property is required.");
29                    }
30                }
31
32                this._name = value;
33            }
34        }
35
36        private Category _category = default!;
37
38        [Required]
39        public Category Category
40        {
41            get
42            {
43                return this._category;
44            }
45
46            set
47            {
48                if (value == null!)
49                {
50                    throw new ArgumentNullException("value", "The 'Category' property is required.");
51                }
52
53                this._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 ArgumentOutOfRangeException("name", "The 'name' parameter is required.");
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
81    public class Category { }
82}
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{
5    public 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    }
27}
Transformed Code
1using System;
2using Metalama.Patterns.Contracts;
3
4namespace Doc.NotEmptyContract
5{
6    public 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 this._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                this._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 this._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                this._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 this._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 ArgumentOutOfRangeException("value", "The 'Currency' property is required.");
80                    }
81                }
82
83                this._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    }
94}

[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{
5    public class Customer
6    {
7        [CreditCard]
8        public string? CreditCard { get; set; }


9    }
10}
Transformed Code
1using System;
2using Metalama.Patterns.Contracts;
3
4namespace Doc.CreditCardContract
5{
6    public class Customer
7    {
8        private string? _creditCard;
9
10        [CreditCard]
11        public string? CreditCard
12        {
13            get
14            {
15                return this._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                this._creditCard = value;
26            }
27        }
28    }
29}

[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{
5    public 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    }
16}
Transformed Code
1using System;
2using Metalama.Patterns.Contracts;
3
4namespace Doc.WellKnownRegexContracts
5{
6    public class Customer
7    {
8        private string? _phone;
9
10        [Phone]
11        public string? Phone
12        {
13            get
14            {
15                return this._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                this._phone = value;
28            }
29        }
30
31        private string? _email;
32
33        [Email]
34        public string? Email
35        {
36            get
37            {
38                return this._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                this._email = value;
51            }
52        }
53
54        private string? _profile;
55
56        [Url]
57        public string? Profile
58        {
59            get
60            {
61                return this._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                this._profile = value;
74            }
75        }
76    }
77}

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]
7    public class PasswordAttribute : RegularExpressionAttribute
8    {
9        public PasswordAttribute() : base( "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[@$!%*?&#])[A-Za-z\\d@$!%*?&#]{8,20}$\n" ) { }
10    }
11}
Source Code
1namespace Doc.CustomRegexContract
2{



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


7    }
8}
Transformed Code
1using System;
2using Metalama.Patterns.Contracts;
3
4namespace Doc.CustomRegexContract
5{
6    public class Customer
7    {
8        private string? _password;
9
10        [Password]
11        public string? Password
12        {
13            get
14            {
15                return this._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                this._password = value;
28            }
29        }
30    }
31}

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{
5    public class Customer
6    {
7        [StringLength( 12, 64 )]
8        public string? Password { get; set; }
9    }


10}
Transformed Code
1using System;
2using Metalama.Patterns.Contracts;
3
4namespace Doc.StringLengthContract
5{
6    public class Customer
7    {
8        private string? _password;
9
10        [StringLength(12, 64)]
11        public string? Password
12        {
13            get
14            {
15                return this._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                this._password = value;
26            }
27        }
28    }
29}

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{
6    public class Message
7    {
8        [EnumDataType( typeof(ConsoleColor) )]
9        public string? Color { get; set; }
10    }


11}
Transformed Code
1using Metalama.Patterns.Contracts;
2using System;
3
4namespace Doc.EnumDataTypeContract
5{
6    public class Message
7    {
8        private string? _color;
9
10        [EnumDataType(typeof(ConsoleColor))]
11        public string? Color
12        {
13            get
14            {
15                return this._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                this._color = value;
26            }
27        }
28    }
29}

Numeric contracts

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

Attribute Description
LessThanAttribute Verifies that the value is less than or equal to the specified maximum.
GreaterThanAttribute Verifies that the value is greater than or equal to the specified minimum.
NegativeAttribute Verifies that the value is less than or equal to zero.
PositiveAttribute Verifies that the value is greater than or equal to zero.
RangeAttribute Verifies that the value is greater than or equal to a specified minimum and less than or equal to a specified maximum.
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.

Example: numeric contracts

Source Code
1using Metalama.Patterns.Contracts;
2

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


9
10        [StrictlyPositive]












11        public decimal Quantity { get; }






12
13        [Range( 0, 100 )]





14        public int Discount { get; }
15    }













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

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{
6    public class Submenu
7    {
8        public string Name { get; }
9
10        public IReadOnlyCollection<MenuItem> Items { get; }
11
12        public Submenu( [Required] string name, [NotNull] [NotEmpty] IReadOnlyCollection<MenuItem> items )
13        {
14            this.Name = name;
15            this.Items = items;






















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