2010-08-04 49 views
6

Tôi đang sử dụng không gian tên System.ComponentModel.DataAnnotations để xác thực các lớp miền của mình. Làm cách nào tôi có thể tạo thuộc tính tùy chỉnh để xác thực tính duy nhất của thuộc tính bất kể cơ sở dữ liệu (thông qua một số giao diện) chẳng hạn?Ràng buộc duy nhất với chú thích dữ liệu

Trả lời

1

Nếu tôi hiểu bạn đúng cách, bạn sẽ có thể tạo một ValidationAttribute tùy chỉnh và nhận bối cảnh cho kho lưu trữ của bạn thông qua một nhà máy tùy chỉnh.

Validator:

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); 
    } 
} 

Bạn sẽ có một giao diện IRepository với một phương pháp IsUnique(). MyRepositoryFactory sẽ có một phương thức tĩnh gọi là Create() để tạo ra Kho lưu trữ cụ thể cần thiết cho cơ sở dữ liệu của bạn. Nếu kiểu cơ sở dữ liệu thay đổi, bạn chỉ cần cập nhật Nhà máy để trả về một Kho lưu trữ mới cho cơ sở dữ liệu mới của bạn.

8

Đây là giải pháp tôi đã đưa ra cho tình huống này, nó chỉ cần kiểm tra bảng cho một bản ghi với một id khác có cùng giá trị cho thuộc tính được xác thực. Nó giả định rằng bạn sẽ sử dụng LinqToSQL và rằng bất kỳ bảng nào mà loại xác thực này được yêu cầu có một cột ID duy nhất.

Tôi cũng sẽ đặt một ràng buộc duy nhất trên bảng cơ sở trong cơ sở dữ liệu. Thuộc tính này cho phép tôi đặt một thông báo lỗi đẹp trên biểu mẫu và liên kết nó với thuộc tính thích hợp.

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

sử dụng Ví dụ:

[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

tôi tình yêu @ giải pháp daveb của. Thật không may, ba năm sau đó nó yêu cầu một số sửa đổi khá nặng đối với tôi. Đây là giải pháp của anh được cập nhật cho EF6. Hy vọng rằng sẽ tiết kiệm một ai đó một giờ hoặc lâu hơn không quan trọng.

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

chỉ làm một cái gì đó như thế này trên mô hình của bạn

[StringLength(100)] 
[Index("IX_EntidadCodigoHabilitacion", IsUnique = true)] 
public string CodigoHabilitacion { get; set; } 
Các vấn đề liên quan