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]
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}
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 (category == null!)
53 {
54 throw new ArgumentNullException("category", "The 'category' parameter must not be null.");
55 }
56
57 if (name == null!)
58 {
59 throw new ArgumentNullException("name", "The 'name' 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]
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}
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 (category == null!)
60 {
61 throw new ArgumentNullException("category", "The 'category' parameter is required.");
62 }
63
64 if (string.IsNullOrWhiteSpace(name))
65 {
66 if (name == null!)
67 {
68 throw new ArgumentNullException("name", "The 'name' parameter is required.");
69 }
70 else
71 {
72 throw new ArgumentOutOfRangeException("name", "The 'name' parameter is required.");
73 }
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]
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}
1using System;
2using Metalama.Patterns.Contracts;
3
4namespace Doc.NotEmptyContract
5{
6 public class Instrument
7 {
8
9
10 private string _name = default!;
11 // Neither null nor empty strings are allowed.
12 [NotNull]
13 [NotEmpty]
14 public string Name
15 {
16 get
17 {
18 return this._name;
19 }
20
21 set
22 {
23 if (value == null!)
24 {
25 throw new ArgumentNullException("value", "The 'Name' property must not be null.");
26 }
27
28 if (value.Length <= 0)
29 {
30 throw new ArgumentException("The 'Name' property must not be null or empty.", "value");
31 }
32
33 this._name = value;
34 }
35 }
36
37
38 private string? _description;
39
40 // Null strings are allowed but not empty strings.
41 [NotEmpty]
42 public string? Description
43 {
44 get
45 {
46 return this._description;
47 }
48
49 set
50 {
51 if (value != null && value!.Length <= 0)
52 {
53 throw new ArgumentException("The 'Description' property must not be null or empty.", "value");
54 }
55
56 this._description = value;
57 }
58 }
59
60
61 private string _currency = default!;
62
63 // Equivalent to [NotNull, NotEmpty]
64 [Required]
65 public string Currency
66 {
67 get
68 {
69 return this._currency;
70 }
71
72 set
73 {
74 if (string.IsNullOrWhiteSpace(value))
75 {
76 if (value == null!)
77 {
78 throw new ArgumentNullException("value", "The 'Currency' property is required.");
79 }
80 else
81 {
82 throw new ArgumentOutOfRangeException("value", "The 'Currency' property is required.");
83 }
84 }
85
86 this._currency = value;
87 }
88 }
89
90 public Instrument(string name, string currency, string? description)
91 {
92 this.Name = name;
93 this.Description = description;
94 this.Currency = currency;
95 }
96 }
97}
[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{
5 public class Customer
6 {
7 [CreditCard]
8 public string? CreditCard { get; set; }
9 }
10}
1using System;
2using Metalama.Patterns.Contracts;
3
4namespace Doc.CreditCardContract
5{
6 public class Customer
7 {
8
9
10 private string? _creditCard;
11 [CreditCard]
12 public string? CreditCard
13 {
14 get
15 {
16 return this._creditCard;
17 }
18
19 set
20 {
21 if (!ContractHelpers.IsValidCreditCardNumber(value))
22 {
23 throw new ArgumentException("The 'CreditCard' property must be a valid credit card number.", "value");
24 }
25
26 this._creditCard = value;
27 }
28 }
29 }
30}
[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{
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}
1using System;
2using Metalama.Patterns.Contracts;
3
4namespace Doc.WellKnownRegexContracts
5{
6 public class Customer
7 {
8
9
10 private string? _phone;
11 [Phone]
12 public string? Phone
13 {
14 get
15 {
16 return this._phone;
17 }
18
19 set
20 {
21 var regex = ContractHelpers.PhoneRegex!;
22 if (value != null && !regex.IsMatch(value!))
23 {
24 var regex_1 = regex;
25 throw new ArgumentException("The 'Phone' property must be a valid phone number.", "value");
26 }
27
28 this._phone = value;
29 }
30 }
31
32
33 private string? _email;
34
35 [Email]
36 public string? Email
37 {
38 get
39 {
40 return this._email;
41 }
42
43 set
44 {
45 var regex = ContractHelpers.EmailRegex!;
46 if (value != null && !regex.IsMatch(value!))
47 {
48 var regex_1 = regex;
49 throw new ArgumentException("The 'Email' property must be a valid email address.", "value");
50 }
51
52 this._email = value;
53 }
54 }
55
56
57 private string? _profile;
58
59 [Url]
60 public string? Profile
61 {
62 get
63 {
64 return this._profile;
65 }
66
67 set
68 {
69 var regex = ContractHelpers.UrlRegex!;
70 if (value != null && !regex.IsMatch(value!))
71 {
72 var regex_1 = regex;
73 throw new ArgumentException("The 'Profile' property must be a valid URL.", "value");
74 }
75
76 this._profile = value;
77 }
78 }
79 }
80}
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}
1namespace Doc.CustomRegexContract
2{
3 public class Customer
4 {
5 [Password]
6 public string? Password { get; set; }
7 }
8}
1using System;
2using Metalama.Patterns.Contracts;
3
4namespace Doc.CustomRegexContract
5{
6 public class Customer
7 {
8
9
10 private string? _password;
11 [Password]
12 public string? Password
13 {
14 get
15 {
16 return this._password;
17 }
18
19 set
20 {
21 var regex = ContractHelpers.GetRegex("^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[@$!%*?&#])[A-Za-z\\d@$!%*?&#]{8,20}$\n", 0)!;
22 if (value != null && !regex.IsMatch(value!))
23 {
24 var regex_1 = regex;
25 throw new ArgumentException($"The 'Password' property must match the regular expression '{regex_1}'.", "value");
26 }
27
28 this._password = value;
29 }
30 }
31 }
32}
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{
5 public class Customer
6 {
7 [StringLength( 12, 64 )]
8 public string? Password { get; set; }
9 }
10}
1using System;
2using Metalama.Patterns.Contracts;
3
4namespace Doc.StringLengthContract
5{
6 public class Customer
7 {
8
9
10 private string? _password;
11 [StringLength(12, 64)]
12 public string? Password
13 {
14 get
15 {
16 return this._password;
17 }
18
19 set
20 {
21 if (value != null && (value!.Length < 12 || value.Length > 64))
22 {
23 throw new ArgumentException($"The 'Password' property must be a string with length between {12} and {64}.", "value");
24 }
25
26 this._password = value;
27 }
28 }
29 }
30}
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{
6 public class Message
7 {
8 [EnumDataType( typeof(ConsoleColor) )]
9 public string? Color { get; set; }
10 }
11}
1using Metalama.Patterns.Contracts;
2using System;
3
4namespace Doc.EnumDataTypeContract
5{
6 public class Message
7 {
8
9
10 private string? _color;
11 [EnumDataType(typeof(ConsoleColor))]
12 public string? Color
13 {
14 get
15 {
16 return this._color;
17 }
18
19 set
20 {
21 if (value != null! && !EnumDataTypeAttributeHelper.IsValidEnumValue(value, typeof(ConsoleColor)))
22 {
23 throw new ArgumentException("The 'Color' property must be a valid string?.", "value");
24 }
25
26 this._color = value;
27 }
28 }
29 }
30}
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
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}
1using System;
2using Metalama.Patterns.Contracts;
3
4namespace Doc.NumericContracts
5{
6 public class OrderLine
7 {
8
9
10 private readonly decimal _nominalPrice;
11 [Positive]
12 public decimal NominalPrice
13 {
14 get
15 {
16 return this._nominalPrice;
17 }
18
19 private init
20 {
21 if (value < 0M)
22 {
23 throw new ArgumentOutOfRangeException("value", "The 'NominalPrice' property must be greater than 0.");
24 }
25
26 this._nominalPrice = value;
27 }
28 }
29
30
31 private readonly decimal _quantity;
32
33 [StrictlyPositive]
34 public decimal Quantity
35 {
36 get
37 {
38 return this._quantity;
39 }
40
41 private init
42 {
43 if (value < 0.0000000000000000000000000001M)
44 {
45 throw new ArgumentOutOfRangeException("value", "The 'Quantity' property must be strictly greater than 0.");
46 }
47
48 this._quantity = value;
49 }
50 }
51
52
53 private readonly int _discount;
54
55 [Range(0, 100)]
56 public int Discount
57 {
58 get
59 {
60 return this._discount;
61 }
62
63 private init
64 {
65 if (value < 0 || value > 100)
66 {
67 throw new ArgumentOutOfRangeException("The 'Discount' property must be between 0 and 100.", "value");
68 }
69
70 this._discount = value;
71 }
72 }
73 }
74}
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{
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}
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 (items == null!)
16 {
17 throw new ArgumentNullException("items", "The 'items' parameter must not be null.");
18 }
19
20 if (string.IsNullOrWhiteSpace(name))
21 {
22 if (name == null!)
23 {
24 throw new ArgumentNullException("name", "The 'name' parameter is required.");
25 }
26 else
27 {
28 throw new ArgumentOutOfRangeException("name", "The 'name' parameter is required.");
29 }
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}