数据标注唯一约束 [英] Unique constraint with data annotation

查看:180
本文介绍了数据标注唯一约束的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm using the System.ComponentModel.DataAnnotations namespace to validate my domain classes. How can I create a custom attribute to validate the uniqueness of a property regardless of the database (through some interface, for example)?

推荐答案

这是我想出了这种情况的解决方案,它只是检查表,用不同的ID具有的属性是相同的值的记录验证。它假定您将使用LinqToSQL,并在其上​​需要这种验证的任何表有一个ID列。

This is the solution I came up with for this situation, it simply checks the table for a record with a different id that has the same value for the property being validated. It assumes that you will be using LinqToSQL, and that any table on which this kind of validation is required has a single ID column.

我还放了唯一约束基础表在数据库中。这个属性让我把一个漂亮的错误消息,在表格上,并与相应的属性相关联。

I'd also put a unique constraint on the underlying table in the database. This attribute allows me to put a nice error message on the form and associate it with the appropriate property.

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

这篇关于数据标注唯一约束的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆