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...
You can't pass dynamic values in Attributes, so you will have to do the validation in the method body.