Skip to content

Commit

Permalink
Update Validation section in README
Browse files Browse the repository at this point in the history
  • Loading branch information
Jack-Edwards committed Mar 4, 2022
1 parent 84e9e84 commit b8ebd57
Showing 1 changed file with 101 additions and 3 deletions.
104 changes: 101 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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<string, EmailAddress>
{
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<string, EmailAddress>
{
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<string, ValidatedClientRef>
public class EmailAddress : ValueOf<string, EmailAddress>
{
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<string, EmailAddress>
{
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<string, EmailAddress>
{
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
Expand Down

0 comments on commit b8ebd57

Please sign in to comment.