2010-08-04 121 views
6

我使用System.ComponentModel.DataAnnotations命名空間來驗證我的域類。如何創建自定義屬性來驗證屬性的唯一性,而不考慮數據庫(例如通過某個接口)?數據註釋的唯一約束

回答

1

如果我正確地理解了你,你應該能夠創建一個自定義的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以爲新數據庫返回新的存儲庫。

8

這是我針對這種情況提出的解決方案,它只是檢查表中是否有一個具有相同值的待驗證屬性的記錄。它假定你將使用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; } 
    } 
} 
0

@ 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; 
    } 
} 
1

只是做這樣的事情在你的模型

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