【问题标题】:Unique constraint with data annotation具有数据注释的唯一约束
【发布时间】:2010-08-04 14:32:22
【问题描述】:

我正在使用System.ComponentModel.DataAnnotations 命名空间来验证我的域类。无论数据库如何(例如,通过某些接口),我如何创建自定义属性来验证属性的唯一性?

【问题讨论】:

    标签: asp.net-mvc validation data-annotations


    【解决方案1】:

    这是我针对这种情况提出的解决方案,它只是检查表中具有不同 id 的记录,该记录与正在验证的属性具有相同的值。它假定您将使用 LinqToSQL,并且任何需要此类验证的表都有一个 ID 列。

    我还将对数据库中的基础表设置一个唯一约束。这个属性允许我在表单上放置一个很好的错误消息并将其与适当的属性相关联。

    public class UniqueAttribute : ValidationAttribute
    {
        public Func<DataContext> GetDataContext { get; private set; }
        public string IDProperty { get; private set; }
        public string Message { get; private set; }
    
        public UniqueAttribute(Type dataContextType, string idProperty, string message)
        {
            IDProperty = idProperty;
            Message = message;
            GetDataContext = () => (DataContext)Activator.CreateInstance(dataContextType);
        }
    
        public UniqueAttribute(Type dataContextType, string idProperty, string message, string connectionString)
        {
            IDProperty = idProperty;
            Message = message;
            GetDataContext = () => (DataContext)Activator.CreateInstance(dataContextType, new object[] { connectionString });
        }
    
        protected override ValidationResult IsValid(object value, ValidationContext validationContext)
        {
            var idProperty = validationContext.ObjectType.GetProperty(IDProperty);
            var idType = idProperty.PropertyType;
            var id = idProperty.GetValue(validationContext.ObjectInstance, null);
    
            // Unsightly hack due to validationContext.MemberName being null :(
            var memberName = validationContext.ObjectType.GetProperties()
                .Where(p => p.GetCustomAttributes(false).OfType<DisplayAttribute>().Any(a => a.Name == validationContext.DisplayName))
                .Select(p => p.Name)
                .FirstOrDefault();
            if (string.IsNullOrEmpty(memberName))
            {
                memberName = validationContext.DisplayName;
            }
            // End of hack
    
            var validateeProperty = validationContext.ObjectType.GetProperty(memberName);
            var validateeType = validateeProperty.PropertyType;
            var validatee = validateeProperty.GetValue(validationContext.ObjectInstance, null);
    
            var idParameter = Expression.Constant(id, idType);
            var validateeParameter = Expression.Constant(validatee, validateeType);
            var objectParameter = Expression.Parameter(validationContext.ObjectType, "o");
            var objectIDProperty = Expression.Property(objectParameter, idProperty);
            var objectValidateeProperty = Expression.Property(objectParameter, validateeProperty);
            var idCheck = Expression.NotEqual(objectIDProperty, idParameter);
            var validateeCheck = Expression.Equal(objectValidateeProperty, validateeParameter);
            var compositeCheck = Expression.And(idCheck, validateeCheck);
            var lambda = Expression.Lambda(compositeCheck, objectParameter);
            var countMethod = typeof(Queryable).GetMethods().Single(m => m.Name == "Count" && m.GetParameters().Length == 2);
            var genericCountMethod = countMethod.MakeGenericMethod(validationContext.ObjectType);
    
            using (var context = GetDataContext())
            {
                var table = context.GetTable(validationContext.ObjectType) as IQueryable<Models.Group>;
                var count = (int)genericCountMethod.Invoke(null, new object[] { table, lambda });
                if (count > 0)
                {
                    return new ValidationResult(Message);
                }
            }
    
            return null;
        }
    }
    

    示例用法:

    [MetadataType(typeof(UserMetadata))]
    public partial class Group : IDatabaseRecord
    {
        public class UserMetadata
        {
            [Required(ErrorMessage = "Name is required")]
            [StringLength(255, ErrorMessage = "Name must be under 255 characters")]
            [Unique(typeof(MyDataContext), "GroupID", "Name must be unique")]
            public string Name { get; set; }
        }
    }
    

    【讨论】:

      【解决方案2】:

      在你的模型上做这样的事情

      [StringLength(100)]
      [Index("IX_EntidadCodigoHabilitacion", IsUnique = true)]
      public string CodigoHabilitacion { get; set; }
      

      【讨论】:

        【解决方案3】:

        如果我对您的理解正确,您应该能够创建自定义 ValidationAttribute 并通过自定义工厂获取存储库的上下文。

        验证器:

        using System.ComponentModel.DataAnnotations;
        public class DBUniqueAttribute : ValidationAttribute
        {
            private IRepository Repository{ get; set;}
            public DBUniqueAttribute()
            {
                this.Repository = MyRepositoryFactory.Create();
            }
        
            public override bool IsValid(object value)
            {
                string stringValue = Convert.ToString(value, CultureInfo.CurrentCulture);
                return Repository.IsUnique(stringValue);
            }
        }
        

        您将拥有一个带有 IsUnique() 方法的 IRepository 接口。 MyRepositoryFactory 将有一个名为 Create() 的静态方法,它将创建数据库所需的具体存储库。如果数据库类型发生变化,您只需更新 Factory 即可为您的新数据库返回一个新的 Repository。

        【讨论】:

          【解决方案4】:

          喜欢 @daveb 的解决方案。不幸的是,三年后它需要对我进行一些相当大的修改。这是他为 EF6 更新的解决方案。希望可以节省一个小时左右的时间。

          public class UniqueAttribute : ValidationAttribute
          {
              public UniqueAttribute(string idProperty, string message)
              {
                  IdProperty = idProperty;
                  Message = message;
              }
          
              [Inject]
              public DataContext DataContext { get; set; }
              private string IdProperty { get; set; }
              private string Message { get; set; }
          
              protected override ValidationResult IsValid(object value, ValidationContext validationContext)
              {
                  var objectType = validationContext.ObjectType;
                  if (objectType.Namespace == "System.Data.Entity.DynamicProxies")
                  {
                      objectType = objectType.BaseType;
                  }
          
                  var idProperty = objectType.GetProperty(IdProperty);
                  var idType = idProperty.PropertyType;
                  var id = idProperty.GetValue(validationContext.ObjectInstance, null);
          
                  var memberName = validationContext.MemberName;
                  var validateeProperty = objectType.GetProperty(memberName);
                  var validateeType = validateeProperty.PropertyType;
                  var validatee = validateeProperty.GetValue(validationContext.ObjectInstance, null);
          
                  var idParameter = Expression.Constant(id, idType);
                  var validateeParameter = Expression.Constant(validatee, validateeType);
                  var objectParameter = Expression.Parameter(objectType, "o");
                  var objectIdProperty = Expression.Property(objectParameter, idProperty);
                  var objectValidateeProperty = Expression.Property(objectParameter, validateeProperty);
                  var idCheck = Expression.NotEqual(objectIdProperty, idParameter);
                  var validateeCheck = Expression.Equal(objectValidateeProperty, validateeParameter);
                  var compositeCheck = Expression.And(idCheck, validateeCheck);
                  var lambda = Expression.Lambda(compositeCheck, objectParameter);
                  var countMethod = typeof(Queryable).GetMethods().Single(m => m.Name == "Count" && m.GetParameters().Length == 2);
                  var genericCountMethod = countMethod.MakeGenericMethod(objectType);
          
                  var table = DataContext.Set(objectType);
                  var count = (int)genericCountMethod.Invoke(null, new object[] { table, lambda });
                  if (count > 0)
                  {
                      return new ValidationResult(Message);
                  }
          
                  return null;
              }
          }
          

          【讨论】:

            猜你喜欢
            • 2018-12-22
            • 2016-04-06
            • 2017-01-14
            • 1970-01-01
            • 1970-01-01
            • 2011-11-21
            • 1970-01-01
            • 1970-01-01
            • 2018-09-06
            相关资源
            最近更新 更多