I have a validator that I use for both an insert, and an update. One of the checks I do is to see if what is being inserted already exists. The code for the validator is:
public GrapeColourValidator(IGrapeRepository grapeRepository)
{
_grapeRepository = grapeRepository;
RuleFor(x => x.Colour)
.NotEmpty()
.WithMessage("Colour is required")
.MaximumLength(_maxLength)
.WithMessage($"Colour cannot be more that {_maxLength} characters");
RuleFor(x => x)
.MustAsync(async (grapeColour, context, cancellation) =>
{
return await GrapeColourExists(grapeColour.Colour).ConfigureAwait(false);
})
.WithMessage($"Grape colour already exists");
}
private async Task<bool> GrapeColourExists(string grapeColour)
{
var colourResult = await _grapeRepository.GetByColour(grapeColour).ConfigureAwait(false);
return !colourResult.Any(x => x.Colour == grapeColour);
}
The issue with this is that it runs for Update also, so the colour will definitely exists. What I want to do is pass a parameter, so I could do something like:
if(isInsert)
{
RuleFor(x => x)
.MustAsync(async (grapeColour, context, cancellation) =>
{
return await GrapeColourExists(grapeColour.Colour).ConfigureAwait(false);
})
.WithMessage($"Grape colour already exists");
}
Is this possible?
I usually accomplish this with a property on the view model I am validating. Basically, you need a flag on the view model indicating whether it represents "new" or "existing" data.
If you have a numeric identifier (which includes Guid's) just test for the default value of the Id:
// For int identifiers:
public class ViewModel
{
public int Id { get; set; }
public bool IsNew => Id == default(int);
}
// For GUID identifiers:
public class ViewModel
{
public Guid Id { get; set; }
public bool IsNew => Id == default(Guid );
}
And then add a When(...)
clause to the validation rule:
RuleFor(x => x.Property)
.Must(...)
.When(x => x.IsNew);
The downside with testing a view model property is that it could be vulnerable to request tampering. Someone can POST a non default Guid or int in the request and make the validator think it is validating a persisted object.
Then again, you should be authenticating and authorizing every request, as well as checking anti-forgery tokens.