asp.net-mvcvalidationdata-annotationsxval

Validate object based on external factors (ie. data store uniqueness)


Description

My solution has these projects:

DAL, BL and WEB all reference DTO which is great.
The process usually executes this way:

  1. A web request is made to the WEB
  2. WEB gets DTOs posted
    • DTOs get automagically validated via custom ActionFilter
    • validation errors are auto-collected
  3. (Validation is OK) WEB calls into BL providing DTOs
  4. BL calls into DAL by using DTOs (can either pass them through or just use them)

DTO Validation problem then...

My DTOs are able to validate themselves based on their own state (properties' values). But right now I'm presented with a problem when this is not the case. I need them to validate using BL (and consequently DAL).

My real-life example: User registers and WEB gets a User DTO that gets validated. The problematic part is username validation. Its uniqueness should be checked against data store.
How am I supposed to do this?

There's additional info that all DTOs implement an interface (ie. User DTO implements IUser) for IoC purposes and TDD. Both are part of the DTO project.

Impossible tries

  1. I can't reference BL in DTO because I'll get circular reference.
    Compilation error
  2. I can't create an additional DTO.Val project that would reference partial DTO classes and implement their validation there (they'd reference BL + DTO).
    Partial classes can't span assemblies.

Possible tries

  1. Create a special ActionFilter that would validate object against external conditions. This one would be created within WEB project thus seeing DTO and BL that would be used here.
  2. Put DTOs in BL and keep DTO interfaces as actual DTOs referenced by other projects and refactor all code to use interfaces instead of concrete classes.
  3. Don't handle external dependant validation and let external dependencies throw an exception - probably the worst solution to this issue

What would you suggest?


Solution

  • Resulting solution

    I ended up using controller action filter that was able to validate object against external factors that can't be obtained from the object itself.

    I created the filter that takes the name of the action parameter to check and validator type that will validate that particular parameter. Of course this validator has to implement certain interface to make it all reusable.

    [ValidateExternalFactors("user", typeof(UserExternalValidator))]
    public ActionResult Create(User user)
    

    validator needs to implement this simple interface

    public interface IExternalValidator<T>
    {
        bool IsValid(T instance);
    }
    

    It's a simple and effective solution to a seemingly complex problem.