I am trying to apply a specification template to my validation logic. But I have some problems with asynchronous validation.
Let's say I have an AddRequest entity (has 2 string properties FileName and Content) that need to be checked.
I need to create 3 validators:
Check to see if filename contains invalid characters
Validate Content
Async checks to see if a file named FileName exists in the database. In this case, I should have something like Task<bool> IsSatisfiedByAsync
But how can I implement both IsSatisfiedBy and IsSatisfiedByAsync ? Should I create 2 interfaces, for example ISpecification and IAsyncSpecification , or can I do this in one?
My version of ISpecification (I only need And)
public interface ISpecification { bool IsSatisfiedBy(object candidate); ISpecification And(ISpecification other); }
Andspecification
public class AndSpecification : CompositeSpecification { private ISpecification leftCondition; private ISpecification rightCondition; public AndSpecification(ISpecification left, ISpecification right) { leftCondition = left; rightCondition = right; } public override bool IsSatisfiedBy(object o) { return leftCondition.IsSatisfiedBy(o) && rightCondition.IsSatisfiedBy(o); } }
To check if a file exists, I should use:
await _fileStorage.FileExistsAsync(addRequest.FileName);
How can I write IsSatisfiedBy for this check, do I really need this asynchronous?
For example, here is my validator (1) for FileName
public class FileNameSpecification : CompositeSpecification { private static readonly char[] _invalidEndingCharacters = { '.', '/' }; public override bool IsSatisfiedBy(object o) { var request = (AddRequest)o; if (string.IsNullOrEmpty(request.FileName)) { return false; } if (request.FileName.Length > 1024) { return false; } if (request.FileName.Contains('\\') || _invalidEndingCharacters.Contains(request.FileName.Last())) { return false; } return true } }
I need to create FileExistsSpecification and use as:
var validations = new FileNameSpecification().And(new FileExistsSpecification()); if(validations.IsSatisfiedBy(addRequest)) { ... }
But how can I create FileExistsSpecification if I need async?