How can I create a generic UniqueValidationAttribute in C# and DataAnnotation?

Error processing SSI file

Answers

  1. Judah

    • 2016/10/13

    as @LBushkin mentioned, Attributes need compile time constants.

    I would change your class from:

    public class UniqueAttribute : ValidationAttribute
    

    to:

    public class UniqueAttribute<T> : ValidationAttribute  
    where T : DataContext{
    
        protected T Context { get; private set; }
    
      ...    
    
        }
    

    and use it as:

    [Required]
    [StringLength(10)]
    [Unique<DataContext>("Groups","name")]
    public string name { get; set; }
    

    This will help you inject a DataContext object, if needed, instead of creating an instance everytime

    HTH

    Edit: since an attribute cannot take a generic parameter, this could be another potential code:

    public class UniqueAttribute : ValidationAttribute{
    
        public UniqueAttribute(Type dataContext, ...){
            if(dataContext.IsSubClassOf(typeof(DataContext))){
                var objDataContext = Activator.CreateInstance(dataContext);
            }
        }
    
    }
    

    and use it as:

    [Required]
    [StringLength(10)]
    [Unique(typeof(DataContext), "Groups","name")]
    public string name { get; set; }
    

    HTH this time :)

  2. Jaxxon

    • 2016/2/23

    When writing Validation Attributes, you can use ValidationContext to gain some information about validation such as Name of Property that 

  3. Raymond

    • 2020/7/6

    Edit: since an attribute cannot take a generic parameter, this could be another potential code: public class UniqueAttribute : ValidationAttribute{ public UniqueAttribute(Type dataContext, ){ if(dataContext.IsSubClassOf(typeof(DataContext))){ var objDataContext = Activator.CreateInstance(dataContext); } } }

  4. Khalid

    • 2021/1/16

    Check out this post: ValidationAttribute that validates a unique field against its fellow rows in the database. – Shimmy Weitzhandler. May 3 '12 at 23:08. Add a 

  5. Vihaan

    • 2017/3/28

    One problem I see already is that you can't instantiate types as parameters of attributes.

    Attributes require that all arguments be compile-time constants. So the usage:

    [Unique(new DataContext(),"Groups","name")]
    

    won't compile. You may be able to omitt new DataContext() - but then I suspect your validation logic won't have information about the entity types to query.

  6. Jimenez

    • 2021/11/2

    Edit: since an attribute cannot take a generic parameter, this could be another potential code: public class UniqueAttribute: ValidationAttribute { public UniqueAttribute (Type dataContext, ) { if (dataContext.IsSubClassOf(typeof (DataContext))){ var objDataContext = Activator.CreateInstance(dataContext); } } }

  7. Stewart

    • 2020/1/22

    Adding model validation error dynamically in the controller action method; Creating a custom remote attribute and override IsValid() method. a) 

  8. Campbell

    • 2017/10/17

    In the last part of the article, to make the CustomRemoteValidation attribute reusable irrespective of properties and class, help has been taken from the very famous KudVenkat blog. Let's Start. Suppose we have an Inventory where there is a Product table/ class which is used to track all products as a list of products.

  9. Thomas

    • 2015/6/2

    I edited this one..and it works perfectly with DI..:D

    public class UniqueAttribute : ValidationAttribute
    {
        public UniqueAttribute(Type dataContextType, Type entityType, string propertyName)
        {
            DataContextType = dataContextType;
            EntityType = entityType;
            PropertyName = propertyName;
        }
    
    
        public Type DataContextType { get; private set; }
    
    
        public Type EntityType { get; private set; }
    
    
        public string PropertyName { get; private set; }
    
    
        public override bool IsValid(object value)
        {
            // Construct the data context
            //ConstructorInfo constructor = DataContextType.GetConstructor(new Type[0]);
            //DataContext dataContext = (DataContext)constructor.Invoke(new object[0]);
            var repository = DependencyResolver.Current.GetService(DataContextType);
            var data = repository.GetType().InvokeMember("GetAll", BindingFlags.Instance | BindingFlags.InvokeMethod | BindingFlags.Public, null, repository, null);
    
            // Get the table
            //ITable table = dataContext.GetTable(EntityType);
    
    
            // Get the property
            PropertyInfo propertyInfo = EntityType.GetProperty(PropertyName);
    
    
            // Our ultimate goal is an expression of:
            //   "entity => entity.PropertyName == value"
    
    
            // Expression: "value"
            object convertedValue = Convert.ChangeType(value, propertyInfo.PropertyType);
            var rhs = Expression.Constant(convertedValue);
    
    
            // Expression: "entity"
            var parameter = Expression.Parameter(EntityType, "entity");
    
    
            // Expression: "entity.PropertyName"
            var property = Expression.MakeMemberAccess(parameter, propertyInfo);
    
    
            // Expression: "entity.PropertyName == value"
            var equal = Expression.Equal(property, rhs);
    
    
            // Expression: "entity => entity.PropertyName == value"
            var lambda = Expression.Lambda(equal, parameter).Compile();
    
            // Instantiate the count method with the right TSource (our entity type)
            MethodInfo countMethod = QueryableCountMethod.MakeGenericMethod(EntityType);
    
            // Execute Count() and say "you're valid if you have none matching"
            int count = (int)countMethod.Invoke(null, new object[] { data, lambda });
            return count == 0;
        }
    
    
        // Gets Queryable.Count<TSource>(IQueryable<TSource>, Expression<Func<TSource, bool>>)
        //private static MethodInfo QueryableCountMethod = typeof(Enumerable).GetMethods().First(m => m.Name == "Count" && m.GetParameters().Length == 2);
        private static MethodInfo QueryableCountMethod = typeof(System.Linq.Enumerable).GetMethods().Single(
            method => method.Name == "Count" && method.IsStatic && method.GetParameters().Length == 2);
    }
    
  10. Romeo

    • 2019/9/12

    Using Remote Validation Attribute. There is no problem with the first way, but suddenly I realized that there is a loophole in the second one.

  11. Reuben

    • 2020/6/13

    Generics in C++. Generics is the idea to allow type (Integer, String, … etc and user-defined types) to be a parameter to methods, classes and interfaces. For example, classes like an array, map, etc, which can be used using generics very efficiently. We can use them for any type.

  12. Cairo

    • 2020/8/19

    Learn how to use the different types of validator attributes and work with Although the Data Annotations Model Binder was created by the 

  13. Chance

    • 2021/6/22

    Well, after a bit of searching, I came across: http://forums.asp.net/t/1512348.aspx and I figured it out, although it involves a fair bit of code.

    Usage:

    [Required]
    [StringLength(10)]
    [Unique(typeof(ContactsManagerDataContext),typeof(Group),"name",ErrorMessage="Group already exists")]
    public string name { get; set; }
    

    The validator code:

    public class UniqueAttribute : ValidationAttribute
    {
        public Type DataContextType { get; private set; }
        public Type EntityType { get; private set; }
        public string PropertyName { get; private set; }
    
        public UniqueAttribute(Type dataContextType, Type entityType, string propertyName)
        {
            DataContextType = dataContextType;
            EntityType = entityType;
            PropertyName = propertyName;
        }
    
        public override bool IsValid(object value)
        {
            string str = (string) value;
            if (String.IsNullOrWhiteSpace(str))
                return true;
    
            // Cleanup the string
            str = str.Trim();
    
            // Construct the data context
            ConstructorInfo constructor = DataContextType.GetConstructor(new Type[0]);
            DataContext dataContext = (DataContext)constructor.Invoke(new object[0]);
    
            // Get the table
            ITable table = dataContext.GetTable(EntityType);
    
            // Get the property
            PropertyInfo propertyInfo = EntityType.GetProperty(PropertyName);
    
            // Expression: "entity"
            ParameterExpression parameter = Expression.Parameter(EntityType, "entity");
    
            // Expression: "entity.PropertyName"
            MemberExpression property = Expression.MakeMemberAccess(parameter, propertyInfo);
    
            // Expression: "value"
            object convertedValue = Convert.ChangeType(value, propertyInfo.PropertyType);
            ConstantExpression rhs = Expression.Constant(convertedValue);
    
            // Expression: "entity.PropertyName == value"
            BinaryExpression equal = Expression.Equal(property, rhs);
    
            // Expression: "entity => entity.PropertyName == value"
            LambdaExpression lambda = Expression.Lambda(equal, parameter);
    
            // Instantiate the count method with the right TSource (our entity type)
            MethodInfo countMethod = QueryableCountMethod.MakeGenericMethod(EntityType);
    
            // Execute Count() and say "you're valid if you have none matching"
            int count = (int)countMethod.Invoke(null, new object[] { table, lambda });
            return count == 0;
        }
    
        // Gets Queryable.Count<TSource>(IQueryable<TSource>, Expression<Func<TSource, bool>>)
        private static MethodInfo QueryableCountMethod = typeof(Queryable).GetMethods().First(m => m.Name == "Count" && m.GetParameters().Length == 2);
    }
    

    I don't mind it being ugly since I will package it in a DLL and reuse it, much better than implementing multiple UniqueAttribute per table/field.

  14. Valente

    • 2017/8/20

    How Validation Occurs in the Create View and Create Action Method. You might wonder how the validation UI was generated without any updates to the code in the controller or views. The next listing shows what the Create methods in the MovieController class look like. They're unchanged from how you created them earlier in this tutorial.

  15. Stefan

    • 2019/8/25

    This class enforces validation, based on the metadata that is associated with the data table. You can override this class to create custom validation 

  16. Madden

    • 2018/7/16

    Reasoning: First create an enumeration without 0s. Out of the remaining numbers, if its distinct list is the same length as the actual list, then there are no repeats. or: If the list of unique numbers is smaller than the actual list, then you must have a repeated number. This is the one-liner version. The a.Where(x=>x>0) list could be factored

  17. Kasen

    • 2015/9/24

    The ValidationAttribute base class lives in the System.ComponentModel.DataAnnotations namespace. This article is showing how to create a 

  18. Reed

    • 2016/6/11

    So, here we will create our custom data annotation attribute, like Required, MaxLength(10) that we frequently use. Please note, with MaxLength(10) annotation we can pass a value as a parameter to validate. And also these annotations can be used anywhere in the application. Here is the image of what we will create.

  19. Sebastian

    • 2015/12/4

    Dim validationAttribute As IAttribute = 6 Me.attributes. Error) End If End If End If End Sub End Class C#: File: Generic; using System.

  20. Ambrose

    • 2021/2/10

    MinValue ), in the general case we compare a value to the default for that data type. For consistency with other validation attributes, we won't 

  21. Mitchell

    • 2015/8/9

    Such error responses should point to the parent "cityOfOrigin" field. As a good DDD citizen, you'll have to create 3 value objects: City to 

Comments are closed.

More Posts