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]
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 { }
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]
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 { }
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]
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}
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]
1using Metalama.Patterns.Contracts;
2
3namespace Doc.CreditCardContract;
4
5public class Customer
6{
7 [CreditCard]
8 public string? CreditCard { get; set; }
9}
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]
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}
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}
1namespace Doc.CustomRegexContract;
2
3public class Customer
4{
5 [Password]
6 public string? Password { get; set; }
7}
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]
1using Metalama.Patterns.Contracts;
2
3namespace Doc.StringLengthContract;
4
5public class Customer
6{
7 [StringLength( 12, 64 )]
8 public string? Password { get; set; }
9}
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]
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}
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
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}
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
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 );
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);