35

I have Category model that has Name field, and every category name must be unique. I have made validation and it works when I try to create new Category but I have problem when trying to edit it. For now it's just checking if the name exists and of course it does when I try to edit same category.

Model

[Validator(typeof(CategoryValidator))]
public class Category
{
    public int ID { get; set; }
    public string Name { get; set; }
    virtual public ICollection<Image> Images { get; set; }
}

public class CategoryValidator : AbstractValidator<Category>
{
    public CategoryValidator()
    {
        RuleFor(x => x.Name).NotEmpty().WithMessage("Category name is required.").Must(UniqueName).WithMessage("This category name already exists.");
    }

    private bool UniqueName(string name)
    {
        ProjecteDataContext _db = new ProjecteDataContext();
        var category = _db.Categories.Where(x => x.Name.ToLower() == name.ToLower()).SingleOrDefault();

        if (category == null) return true;
        return false;
    }
}

As you can see I have UniqueName(string name) function, but how can I pass ID, or whole model in it so I can check if it's same id as model I'm trying to edit then it pass. How could I pass something like UniqueName(string name, int? id)? I discovered FluentValidation only today and I can't figure out.

Stan
  • 25,744
  • 53
  • 164
  • 242

1 Answers1

38

Predicate Validator (aka Must) has an overload, which accepts predicate with two parameters (validated object and property value). In your case predicate will have type Func<Category, string, bool>. So just add Category parameter to your unique name validation method:

private bool UniqueName(Category category, string name)
{
        ProjecteDataContext _db = new ProjecteDataContext();
        var dbCategory = _db.Categories
                            .Where(x => x.Name.ToLower() == name.ToLower())
                            .SingleOrDefault();

        if (dbCategory == null) 
            return true;

        return dbCategory.ID == category.ID;
}
Bjarki Heiðar
  • 3,117
  • 6
  • 27
  • 40
Sergey Berezovskiy
  • 232,247
  • 41
  • 429
  • 459
  • 7
    side note: you should dispose of the DbContext as soon as you're done with it, so "using (var _db = new ProjecteDataContext())" – Hamid Sadeghian Nov 14 '16 at 13:55
  • Is it possible to inject DbContext? – Ish Thomas Mar 26 '20 at 02:40
  • 1
    @IshThomas short answer is "yes, you can inject the context into the validator". Consider searching specifically for injection into validators and you should find answers related to it. The documentation on FluentValidation also mentions how to do this. – julealgon Apr 16 '20 at 11:54