2014-12-09 9 views
5

naprawdę nowych do C#, ASP.NET MVC i FluentValidation.Pisanie rodzajowe FluentValidation niestandardową walidatora do sprawdzenia UNIQUE

Mam modelu użytkownika, takich jak:

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

teraz, byłem walidacji nazwę użytkownika i e-mail przy użyciu FluentValidation, coś jak:

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; 
     } 
    } 

Ale wolałbym chcą bardziej ogólna UniqueValidator, którą mogę używać z wieloma klasami i właściwościami. Lub Atleast, nie muszę wykonywać osobnych funkcji dla każdej nieruchomości. Więc przyjrzałem się niestandardowym walidatorom. Ale nie mam pojęcia, jak mogę użyć tej funkcji do moich potrzeb. chcę zrobić coś takiego:

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

Czy to w ogóle możliwe, aby to zrobić? Chcę przekazać parametr DbContext jako typ i właściwość jako argument (lub jego pewną odmianę, zależnie od tego, która funkcja działa). i metoda może sprawdzić właściwość względem tabeli i zwrócić, jeśli jest unikalna lub nie.

Odpowiedz

0

Czy sprawdziłeś, używając lambdas i generics? Nie użyłem FluentValidation, więc może to nie być poprawna metoda dla walidatora.

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

Być może nie chcesz wstępnie tworzyć kontekstu db. – abatishchev

+0

Tak dobry punkt, normalnie bym prawdopodobnie walidatora skonstruowany przy użyciu iniekcji zależność, ale nie chcą, aby sprawa bardziej skomplikowana, to już jest –

+0

Dzięki za szybką odpowiedź (nie można tego samego powiedzieć o sobie .. był zajęty innym projektem). Rozumiem większość sugestii, z wyjątkiem parametrów wewnątrz 'UniquePropertyValidator (....)'. Od razu rzuca mi to spojrzenie. Czy też "ud => ud.Email" nie będzie wartością właściwości? jak mogę go zaakceptować jako funkcję ('Func propertyAccessorFucn')? –

0

Mam również próbuje znaleźć eleganckie rozwiązanie dla tego walidatora, ale rozwiązanie przedstawione do tej pory wydaje się pobrać wszystkie dane, a następnie sprawdzić, wyjątkowości. Nie wydaje mi się to zbyt dobre.

Podczas próby zastosowania proponowanej poniżej implementacji pojawia się błąd, że LINQ do jednostek nie obsługuje funkcji Invoke (tj. Wykonanie Func<> wewnątrz klauzuli Where). Czy istnieje jakieś obejście?

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)); 
    } 
} 
Powiązane problemy