Reputation:
I use FluentValidation and PostSharp in my business layer. My database has an Admin table, and the UserName column in this table is unique. I want to check the uniqueness with an "Aspect". My codes are as follows.
AdminValidator
public class AdminValidator : AbstractValidator<Admin>
{
public AdminValidator(IEnumerable<Admin> admins)
{
RuleFor(x => x.Fullname).MaximumLength(50);
RuleFor(x => x.Username).Matches(@"^\S{3,15}$").IsUnique(admins);
RuleFor(x => x.Password).Matches(@"^\S{5,20}$");
}
}
IsUnique extension method
public static class Extensions
{
public static IRuleBuilderOptions<TItem, TProperty> IsUnique<TItem, TProperty>(
this IRuleBuilder<TItem, TProperty> ruleBuilder, IEnumerable<TItem> items)
where TItem : class
{
return ruleBuilder.SetValidator(new UniqueValidator<TItem>(items));
}
}
public class UniqueValidator<T> : PropertyValidator
where T : class
{
private readonly IEnumerable<T> _items;
public UniqueValidator(IEnumerable<T> items)
: base("{PropertyName} must be unique")
{
_items = items;
}
protected override bool IsValid(PropertyValidatorContext context)
{
var editedItem = context.Instance as T;
var newValue = context.PropertyValue as string;
var property = typeof(T).GetTypeInfo().GetDeclaredProperty(context.PropertyName);
return _items.All(item =>
item.Equals(editedItem) || property.GetValue(item).ToString() != newValue);
}
}
FluentValidation aspect
[Serializable]
public class FluentValidationAspect : OnMethodBoundaryAspect
{
private readonly Type _validatorType;
private readonly object[] _parameters;
public FluentValidationAspect(Type validatorType, params object[] parameters)
{
_validatorType = validatorType;
_parameters = parameters;
}
public override void OnEntry(MethodExecutionArgs args)
{
var validator = (IValidator)Activator.CreateInstance(_validatorType, _parameters);
var entityType = _validatorType.BaseType?.GetGenericArguments()[0];
var entities = args.Arguments.Where(x => x.GetType() == entityType);
foreach (var entity in entities)
ValidatorTool.FluentValidate(validator, entity);
}
}
and AdminManager
public class AdminManager : IAdminService
{
private readonly IAdminDal _adminDal;
public AdminManager(IAdminDal adminDal)
{
_adminDal = adminDal;
}
public Admin GetByUsername(string username)
=> string.IsNullOrEmpty(username) ? null : _adminDal.Get(x => x.Username.Equals(username));
[FluentValidationAspect(typeof(AdminValidator))] // I need to pass IEnumerable<Admin> as a second parameter.
public void Add(Admin admin) => _adminDal.Add(admin);
[FluentValidationAspect(typeof(AdminValidator))] //
public void Update(Admin admin) => _adminDal.Update(admin);
public void DeleteById(int id) => _adminDal.Delete(new Admin { Id = id });
}
As I mentioned in the comment line, I need to pass IEnumerable to the FluentValidationAspect. But, dynamic parameters can not be passed to attributes. As a result, I'm blocked here. What is the best way to check for uniqueness?
Thank you in advance for your help? Best regards...
Upvotes: 0
Views: 365
Reputation: 2320
You can't pass dynamic values in Attributes, so you will have to do the validation in the method body.
public void Add(Admin admin)
{
var admins = GetAll():
var val = new AdminValidator(admins);
validator.ValidateAndThrow(admin);
_adminDal.Add(admin);
}
Upvotes: 0