I just want to use code like this to implement a customer validate attribute named UniqueAttribute in MVC, but I am not clearly knowing about lamda expression.
The edit model class
The UniqueAttribute class :
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);
}
}
I have create a "Contains" method in my class where implement the interface 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;
}
So I want to define my edit model like below:
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; }
}
The error message is:
Cannot convert source type 'System.Linq.Expression.Expression<System.Func<User,string>>' to target type 'System.Linq.Expression.Expression<System.Func<TEntity,TKey>>'
How to correct it ? Any help would be much appreciated, thanks.
UPDATE:
I modified validate class, and it works well.
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;
}
The instance of ServiceType implement a Get(Expression> condition) method:
public virtual T Get(Expression<Func<T, bool>> condition)
{
return _repository.Get(condition);
}
In edit model, it is used like:
[Required]
[Unique(typeof(IUserService),typeof(User),"LoginName")]
[StringLength(50)]
[Display(Name = "Login Name")]
public string LoginName { get; set; }
Hope it can help you.