Как использовать лямбда-выражение для реализации UniqueAttribute?
Я просто хочу использовать подобный код для реализации атрибута проверки клиента с именем UniqueAttribute в MVC, но я не очень хорошо знаю о выражении lamda.
Класс редактирования модели
Класс UniqueAttribute:
public class UniqueAttribute<TService,TEntity,TKey> : ValidationAttribute
where TService : IDML<TEntity>
where TEntity:IPMMIdentity
{
public Expression<Func<TEntity, TKey>> UniqueExpression { get; set; }
public override bool IsValid(object value)
{
var service = IocContainerHelper.Resolve<TService>();
return !service.Contains(UniqueExpression, (TKey)value);
}
}
Я создал метод "Содержит" в моем классе, где реализует интерфейс IDML:
public virtual bool Contains<TKey>(Expression<Func<T, TKey>> selector, TKey value)
{
var predicate = Expression.Lambda<Func<T, bool>>(
Expression.Equal(selector.Body, Expression.Constant(value, typeof(TKey)))
, selector.Parameters);
return _repository.Count(predicate)>0;
}
Поэтому я хочу определить свою модель редактирования, как показано ниже:
public class EditUser
{
public int Id { get; set; }
[Unique<IUserService,User,string>(UniqueExpression = t=>t.LoginName)] // error line,can not be complied.
public string LoginName { get; set; }
}
Сообщение об ошибке:
Cannot convert source type 'System.Linq.Expression.Expression<System.Func<User,string>>' to target type 'System.Linq.Expression.Expression<System.Func<TEntity,TKey>>'
Как это исправить? Любая помощь будет высоко ценится, спасибо.
ОБНОВИТЬ:
Я изменил класс проверки, и он работает хорошо.
public class UniqueAttribute:ValidationAttribute
{
public UniqueAttribute(Type serviceType, Type entityType, string propertyName)
{
ServiceType = serviceType;
EntityType = entityType;
PropertyName = propertyName;
}
public Type ServiceType { get; private set; }
public Type EntityType { get; private set; }
public string PropertyName { get; private set; }
protected override ValidationResult IsValid(object value, System.ComponentModel.DataAnnotations.ValidationContext validationContext)
{
var propertyInfo = EntityType.GetProperty(PropertyName);
const string methodName = "Get";
object convertedValue = Convert.ChangeType(value, propertyInfo.PropertyType);
var constExpression = Expression.Constant(convertedValue);
var parameter = Expression.Parameter(EntityType, "te");
var memberExpression = Expression.MakeMemberAccess(parameter, propertyInfo);
var equal = Expression.Equal(memberExpression, constExpression);
var lambda = Expression.Lambda(equal, parameter);
var service = IocContainerHelper.Resolve(ServiceType);
const BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.InvokeMethod | BindingFlags.Public;
var method = service.GetType().GetMethod(methodName, bindingFlags);
if (method == null)
{
throw new NullReferenceException(string.Format("{0} have not suitable method named '{1}'", service.GetType().FullName, methodName));
}
object id = validationContext.ObjectInstance.GetId();
object data = method.Invoke(service, new object[] { lambda });
if (!data.GetId().Equals(id))
{
var errorMessage = string.Format("{0} is used。", convertedValue);
return new ValidationResult(errorMessage);
}
return ValidationResult.Success;
}
Экземпляр ServiceType реализует метод Get(Expression> condition):
public virtual T Get(Expression<Func<T, bool>> condition)
{
return _repository.Get(condition);
}
В редактируемой модели он используется как:
[Required]
[Unique(typeof(IUserService),typeof(User),"LoginName")]
[StringLength(50)]
[Display(Name = "Login Name")]
public string LoginName { get; set; }
Надеюсь, это поможет вам.