Custom validation attributes are convenient if you want to reuse validation code for multiple properties. They can even be used for type-level validation (which doesn’t make sense for the other validation attributes). But the DbContext
also offers an alternative: the IValidatableObject
interface, which can be implemented on your classes.
The IValidatableObject
interface has a single method, Validate()
, which takes a ValidationObject
parameter and returns an IEnumerable<ValidationError>
result:
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
Public Function Validate(ByVal validationContext As ValidationContext)
As IEnumerable(Of ValidationResult)
Implements IValidatableObjectValidate
Within the Validate()
method, you perform your tests and, if the object fails, add a ValidationResult
to the response. In Visual Basic this is done by adding each result to a List(Of ValidationResult)
, as shown opposite, while in C# it’s customary to use the yield
statement, as shown below:
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
MyClass obj = validationContext.ObjectInstance;
if (property is not valid)
yield return
new ValidationResult("message", new string[] { "Property" });
if (some other property is not valid)
yield return
new ValidationResult("message", new string[] {"Other Property"});
...
}
Public Function Validate(ByVal validationContext As ValidationContext)
As IEnumerable(Of ValidationResult)
Implements IValidatableObject.Validate
Dim results = New List(Of ValidationResult)
Dim obj = validationContext.ObjectInstance
If (property is not valid)
result.Add(New ValidationResult("message", New String() {"Property"}))
End If
If (some other property is not valid)
result.Add(New ValidationResult("message", New String() {"Other Property"}))
End If
...
Return result
End Function
Put On Your Thinking Hat
Ready to give it a try? Write a Validate()
method for the RecipeIngredient
class that checks that the Amount
property is a positive number and only the IngredientName
or the SourceId
property can contain values, but not both.
Put On Your Thinking Hat
How’d you do?
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
RecipeIngredient ri = validationContext.ObjectInstance;
if (ri.Amount < 0)
yield return
new ValidationResult("Amount must be positive", new string[] { "Amount" });
if (ri.IngredientName != null && ri.SourceId != null)
yield return
new ValidationResult("You cannot enter both name and source",
new string[] {"IngredientName", "SourceId"});
...
}
Public Function Validate(validationContext As ValidationContest) _
As IEnumerable(Of ValidationResult)
Dim ri As RecipeIngredient = validationContext.ObjectInstance
Dim results = New List(Of ValidationResult)
If ri.Amount < 0 Then
results.Add(New _
ValidationResult("Amount must be positive", New String() { "Amount" }))
End If
If ri.IngredientName IsNot Nothing AndAlso ri.SourceId IsNot Nothing Then
results.Add(New _
ValidationResult("You cannot enter both name and source", _
New String() {"IngredientName", "SourceId"}))
End If
...
return results
End Function
Take A Break
And that’s it for the DbContext
validations and the chapter. Why don’t you take a break before you complete the Review and we move on to the final project?