score:-1

This may work for you, but has the downside of needing to retrieve the full list of entities and loop through them to look for a match. You can specify the entity and property as arguments to the attribute.

public class UniqueAttribute : ValidationAttribute
{
    public UniqueAttribute(Type entityType, string propertyName)
    {
        _entityType = entityType;
        _propertyName = propertyName;
    }    

    private Type _entityType;
    private string _propertyName;

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        Context db = new Context();

        foreach (var item in db.Set(_entityType).ToList())
        {
           if (value.Equals(GetPropertyValue(item, _propertyName))
           {
               return new ValidationResult(validationContext.DisplayName + " is already taken.");
           }
        }

        return null;
    }

    private object GetPropertyValue(object item, string propertyName)
    {
        var type = item.GetType();
        var propInfo = type.GetProperty(propertyName);

        return (propInfo != null) ? propInfo.GetValue(value, null) : null;
    }
}

}

Usage:

[Unique(typeof(User), "Login")]
public string Login { get; set; }

score:1

This will work. You need to build the expression tree manually.

Usage

Expression<Func<User, string>> userExp = x => x.Login;
UniqueAttribute u = new UniqueAttribute(userExp);`

EDIT: Removed generics to work w/ Attribute. You need to use reflection w/ the runtime types to get the appropriate SingleOrDefault method. Please note there is no compile time type checking on your expressions with this code. You should always declare the expression first (the way I did in the usage sample) to avoid type problems.

public class UniqueAttribute 
    {
        private LambdaExpression Selector { get; set; }
        private Type EntityType { get; set; }

        public UniqueAttribute(LambdaExpression selector) {
            this.EntityType = selector.Parameters[0].Type;
            this.Selector = selector;
        }

        private LambdaExpression GeneratePredicate(object value) {
                ParameterExpression param = Selector.Parameters[0];
                Expression property = Selector.Body;
                Expression valueConst = Expression.Constant(value);
                Expression eq = Expression.Equal(property, valueConst);
                LambdaExpression predicate = Expression.Lambda(eq, new ParameterExpression[]{param});

                return predicate;
        }

        private TEntity SingleOrDefault<TEntity>(IQueryable<TEntity> set, LambdaExpression predicate) {
            Type queryableType = typeof(Queryable);
            IEnumerable<MethodInfo> allSodAccessors = queryableType.GetMethods(BindingFlags.Static | BindingFlags.Public).Where(x => x.Name=="SingleOrDefault");
            MethodInfo twoArgSodAccessor = allSodAccessors.Single(x => x.GetParameters().Length == 2);
            MethodInfo withGenArgs = twoArgSodAccessor.MakeGenericMethod(new []{typeof(TEntity)});

            return (TEntity) withGenArgs.Invoke(null, new object[]{set, predicate});
        }

        protected override ValidationResult IsValid(object value, ValidationContext validationContext)
            {

                Context db = new Context();
                if (SingleOrDefault(db.Set(EntityType), GeneratePredicate(value)) != null)
                {
                    return new ValidationResult(validationContext.DisplayName + " is already taken.");
                }
                return null;
            }
    }

Related Articles