From b8ebd57b7d75aa6943e8ccd1c2135d013529bb2f Mon Sep 17 00:00:00 2001 From: Jack-Edwards Date: Fri, 4 Mar 2022 15:37:34 -0600 Subject: [PATCH] Update Validation section in README --- README.md | 104 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 101 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 6187c7a..c3d62c2 100644 --- a/README.md +++ b/README.md @@ -30,18 +30,116 @@ You can use C# 7 Tuples for more complex Types with multiple values: ### Validation -You can add validation to your Types by overriding the `protected void Validate() { } ` method: +There are two, independent ways to add validation to your Types. Both ways validate during Type creation. + +#### Throw an exception during creation + +You can add validation to your Types by overriding the `protected void Validate() { }` method to throw an exception when the type is created using `.From()`: + +``` +public class EmailAddress : ValueOf +{ + protected override void Validate() + { + if (string.IsNullOrWhiteSpace(Value)) + throw new ArgumentException("Value cannot be null or empty"); + } +} + +void Main() +{ + // This will throw an ArgumentException + var myEmailAddress = EmailAddress.From(""); +} +``` + +#### Return false while using an `out` argument + +You may also implement validation to your Types by overriding the `protected bool TryValidate() { }` method to return `false`. This will cause the `TryFrom()` creation method to return false: + +``` +public class EmailAddress : ValueOf +{ + protected override bool TryValidate() + { + if (string.IsNullOrWhitespace(Value)) + return false; + } +} + +void Main() +{ + if (!EmailAddress.TryFrom("", out var myEmailAddress) + { + Console.WriteLine("Invalid email address"); + } +} +``` + +#### Validation best practices + +It is recommended to override both `Validate` and `TryValidate` with the same failure cases, if you choose to implement validation at all. + +For example: ``` -public class ValidatedClientRef : ValueOf +public class EmailAddress : ValueOf +{ + protected override void Validate() + { + if (!IsValidEmailAddress(Value)) + throw new ArgumentException("Invalid email address"); + } + + protected override bool TryValidate() + { + return IsValidEmailAddress(Value); + } + + // Prevent circular references from Validate and TryValidate by + // breaking out the logic into a separate method + public static bool IsValidEmailAddress(string value) + { + return !string.IsNullOrWhitespace(value) + } +} +``` + +If you prefer one method over the other, then consider at least overriding the alternative method to always fail. This will prevent someone from creating an invalid Type using the creation method you did not override. + +For example: + +``` +public class EmailAddress : ValueOf +{ + protected override void Validate() + { + throw new NotSupportedException("Use TryValidate() and TryFrom() instead"); + } + + protected override bool TryValidate() + { + return !string.IsNullOrWhitespace(Value) + } +} +``` + +or + +``` +public class EmailAddress : ValueOf { protected override void Validate() { if (string.IsNullOrWhiteSpace(Value)) throw new ArgumentException("Value cannot be null or empty"); } -} + protected override bool TryValidate() + { + return false; // Use Validate() and From() instead + } +} ``` ## See Also