Writing a standard FluentValidation validator to test for a unique constraint

Really new to C #, ASP.NET MVC, and FluentValidation.

I have a user model, for example:

public class UserDetails{ public int ID { get; set; } public string UserName { get; set; } public string Email { get; set; } } 

now I checked the username and email using FluentValidation, for example:

  public AdminDetailsValidator(){ RuleFor(ad => ad.UserName).NotNull().Must(UniqueUserName(UserName)).WithMessage("UserName not Available"); RuleFor(ad => ad.Email).NotNull().Must(UniqueEmail(Email)).WithMessage("This Email id has already been registered"); ; } public bool UniqueUserName(string un) { if (UserDbContext.userDetails.SingleOrDefault(p => p.UserName == un) == null) { return true; } else { return false; } } public bool UniqueEmail(string em) { if (UserDbContext.userDetails.SingleOrDefault(p => p.Email == em) == null) { return true; } else { return false; } } 

But I would prefer the more generic UniqueValidator, which I can use with several classes and properties. Or Atleast, I do not need to do a separate function for each property. So I looked at custom validators. But I have no idea how I can use this function for my needs. I want to do something like this:

 RuleFor(ad => ad.Email).NotNull().SetValidator(new UniquePropertyValidator<UserDbContext>(userDetails.Email).WithMessage("This Email id has already been registered"); 

Is it possible to do this? I want to pass a DbContext as a parameter and a type property as an argument (or its variant, depending on what works). and the method can check the property against the table and return if it is unique or not.

+5
source share
2 answers

Have you studied the use of lambda and generics? I have not used FluentValidation, so this may not be the right method for the validator.

 var dbContext = new UserDbContext(); RuleFor(ud => ud.Email) .NotNull() .SetValidator( new UniquePropertyValidator<UserDetails> (ud, ud => ud.Email, () => dbcontext.userDetails) .WithMessage("This Email id has already been registered"); public class UniquePropertyValidator<T> { public UniquePropertyValidator(T entity, Func<T,string> propertyAccessorFunc, Func<IEnumerable<T>> collectionAccessorFunc) { _entity = entity; _propertyAccessorFunc = propertyAccessorFunc; _collectionAccessorFunc =collectionAccessorFunc; } public bool Validate(){ //Get all the entities by executing the lambda var entities = _collectionAccessorFunc(); //Get the value of the entity that we are validating by executing the lambda var propertyValue = _propertyAccessorFunc(_entity); //Find the matching entity by executing the propertyAccessorFunc against the //entities in the collection and comparing that with the result of the entity //that is being validated. Warning SingleOrDefault will throw an exception if //multiple items match the supplied predicate //http://msdn.microsoft.com/en-us/library/vstudio/bb342451%28v=vs.100%29.aspx var matchingEntity = entities.SingleOrDefault(e => _propertyAccessorFunc(e) == propertyValue); return matchingEntity == null; } } 
0
source

I am also trying to find an elegant solution for this validator, but the solution presented so far seems to extract all the data and then check the uniqueness. In my opinion, this is not very good.

When I try to use the implementation suggested below, I get an error message that LINQ to Entities does not support Invoke (i.e., it executes Func<> inside the Where clause). Is there a workaround?

 public class UniqueFieldValidator<TObject, TViewModel, TProperty> : PropertyValidator where TObject : Entity where TViewModel : Entity { private readonly IDataService<TObject> _dataService; private readonly Func<TObject, TProperty> _property; public UniqueFieldValidator(IDataService<TObject> dataService, Func<TObject, TProperty> property) : base("La propiedad {PropertyName} tiene que ser unica.") { _dataService = dataService; _property = property; } protected override bool IsValid(PropertyValidatorContext context) { var model = context.Instance as TViewModel; var value = (TProperty)context.PropertyValue; if (model != null && _dataService.Where(t => t.Id != model.Id && Equals(_property(t), value)).Any()) { return false; } return true; } } public class ArticuloViewModelValidator : AbstractValidator<ArticuloViewModel> { public ArticuloViewModelValidator(IDataService<Articulo> articuloDataService) { RuleFor(a => a.Codigo).SetValidator(new UniqueFieldValidator<Articulo, ArticuloViewModel, int>(articuloDataService, a => a.Codigo)); } } 
0
source

Source: https://habr.com/ru/post/1208634/


All Articles