Написание универсального FluentValidation пользовательский валидатор для проверки уникальности
Действительно новый для C#, ASP.NET MVC и FluentValidation.
У меня есть модель пользователя, такая как:
public class UserDetails{
public int ID { get; set; }
public string UserName { get; set; }
public string Email { get; set; }
}
На данный момент я проверяю имя пользователя и адрес электронной почты с помощью FluentValidation, что-то вроде:
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;
}
}
Но я бы предпочел более универсальный UniqueValidator, который я могу использовать с несколькими классами и свойствами. Или, по крайней мере, мне не нужно создавать отдельную функцию для каждого свойства. Поэтому я заглянул в пользовательские валидаторы. Но я понятия не имею, как я могу использовать эту функцию для моего потребности.
Я хочу сделать что-то вроде этого:
RuleFor(ad => ad.Email).NotNull().SetValidator(new UniquePropertyValidator<UserDbContext>(userDetails.Email).WithMessage("This Email id has already been registered");
Возможно ли это вообще? Я хочу передать DbContext в качестве параметра типа и свойства в качестве аргумента (или некоторого его варианта, в зависимости от того, что работает). и метод может проверить свойство по таблице и вернуть, является ли оно уникальным или нет.
2 ответа:
Вы рассматривали возможность использования лямбд и дженериков? Я не использовал FluentValidation, поэтому это может быть неправильным методом для валидатора.
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; } }
Я также пытался найти элегантное решение для этого валидатора, но решение, предоставленное до сих пор, похоже, извлекает все данные, а затем проверяет уникальность. На мой взгляд, это не очень хорошо.
При попытке использовать реализацию, предложенную ниже, я получаю ошибку, что LINQ to Entities не поддерживает Invoke (т. е. выполнение
Func<>
внутри предложенияWhere
). Есть ли обходной путь?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)); } }