Search code examples
servicestackfluentvalidation

Fluent validator to check if entity with ID exists in database


I'm trying to write a custom validator that will check if an entity exists in the database, using OrmLite. The problem is that the type arguments for IRuleBuilder can no longer be inferred from usage.

I have to write the method call like this:

RuleFor(r => r.Id).Exists<DtoName, int, EntityName>()

But I want to write it like this:

Rulefor(r => r.Id).Exists<EntityName>()

This happens because IRuleBuilder has two type parameters and the method is an extension method. Is there a smart, fluent way to design this and make the function call preferably like the second version?

Here is code for my extension method and my validator:

    public static class AbstractValidatorExtensions
    {
        public static IRuleBuilderOptions<T, TProperty> Exists<T, TProperty, U>(this IRuleBuilder<T, TProperty> ruleBuilder)
        {
            return ruleBuilder.SetValidator(new EntityExistsValidator<U>());
        }                
    }

    public class EntityExistsValidator<T> : PropertyValidator
    {
        public EntityExistsValidator() : base("Entity does not exist") {}

        protected override bool IsValid(PropertyValidatorContext context)
        {
            return HostContext.Resolve<Repository>()
                .Exists<T>((int)context.PropertyValue);
        }
    }

Solution

  • You'll need to a Custom Validator for custom validation to access dependencies, something like:

    RuleFor(x => x.Id)
        .Must(id =>
        {
            using (var db = HostContext.AppHost.GetDbConnection(base.Request))
            {
                return !db.Exists<EntityName>(x => x.Id == id);
            }
        })
        .WithErrorCode("AlreadyExists")
        .WithMessage("...");
    

    I'd also consider just doing validation that use dependencies in your Services instead:

    if (Db.Exists<EntityName>(x => x.Id == request.Id))
        throw new ArgumentException("Already Exists", nameof(request.Id));