2

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.

Marc
  • 3,683
  • 8
  • 34
  • 48
workingbird
  • 67
  • 1
  • 6
  • 4
    You cannot use generic attributes. And [this is why](http://stackoverflow.com/questions/294216/why-does-c-sharp-forbid-generic-attribute-types). – AgentFire Nov 29 '13 at 18:12
  • 2
    You also can't use lambdas in attributes. – svick Nov 29 '13 at 21:55
  • Thanks for AgentFire and svick. I am just trying another way to implement this function. If succeed, I will update my question post. – workingbird Nov 30 '13 at 05:23
  • If you've found an answer to your question, please add it below and set is as answer. That way other people know that this problem has been solved. – jessehouwing May 28 '15 at 07:05

0 Answers0