如何在C#和DataAnnotation中创建通用UniqueValidationAttribute?

如何在C#和DataAnnotation中创建通用UniqueValidationAttribute?,c#,linq-to-sql,validation,asp.net-mvc-2,data-annotations,C#,Linq To Sql,Validation,Asp.net Mvc 2,Data Annotations,我正在尝试使用System.ComponentModel.DataAnnotations.ValidationAttribute 我希望这是通用的,因为我可以传递linqdatacontext、表名、字段,并验证传入值是否唯一 下面是一个我目前一直无法编译的代码片段: using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ComponentModel.D

我正在尝试使用
System.ComponentModel.DataAnnotations.ValidationAttribute

我希望这是通用的,因为我可以传递linqdatacontext、表名、字段,并验证传入值是否唯一

下面是一个我目前一直无法编译的代码片段:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel.DataAnnotations;
using System.Data.Linq;
using System.ComponentModel;

namespace LinkDev.Innovation.Miscellaneous.Validation.Attributes
{
    public class UniqueAttribute : ValidationAttribute
    {
        public string Field { get; set; }

        public override bool IsValid(object value)
        {
            string str = (string)value;
            if (String.IsNullOrEmpty(str))
                return true;

            // this is where I'm stuck
            return (!Table.Where(entity => entity.Field.Equals(str)).Any());           
        }
    }
}
我应该在我的模型中使用它,如下所示:

[Required]
[StringLength(10)]
[Unique(new DataContext(),"Groups","name")]
public string name { get; set; }
编辑: 请注意,根据这一点: 我无法将泛型类型与属性一起使用


因此,我的新方法是使用反射/表达式树动态构造Lambda表达式树。

我已经看到的一个问题是,不能将类型实例化为属性的参数

属性要求所有参数都是编译时常量。因此,用法:

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

不会编译。您可能可以省略
new DataContext()
——但是我怀疑您的验证逻辑不会包含有关要查询的实体类型的信息。

正如@LBushkin提到的,
属性需要编译时常量

我会把你的班级从:

public class UniqueAttribute : ValidationAttribute
致:

并将其用作:

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

这一次:)

嗯,经过一番搜索,我发现: 我找到了答案,虽然它需要相当多的代码

用法:

[Required]
[StringLength(10)]
[Unique(typeof(ContactsManagerDataContext),typeof(Group),"name",ErrorMessage="Group already exists")]
public string name { get; set; }
验证程序代码:

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);
}
public类UniqueAttribute:ValidationAttribute
{
公共类型DataContextType{get;private set;}
公共类型EntityType{get;private set;}
公共字符串PropertyName{get;private set;}
公共UniqueAttribute(类型dataContextType、类型entityType、字符串propertyName)
{
DataContextType=DataContextType;
EntityType=EntityType;
PropertyName=PropertyName;
}
公共覆盖布尔值有效(对象值)
{
字符串str=(字符串)值;
if(String.IsNullOrWhiteSpace(str))
返回true;
//清理字符串
str=str.Trim();
//构建数据上下文
ConstructorInfo构造函数=DataContextType.GetConstructor(新类型[0]);
DataContext=(DataContext)构造函数.Invoke(新对象[0]);
//收拾桌子
ITable table=dataContext.GetTable(EntityType);
//取得财产
PropertyInfo PropertyInfo=EntityType.GetProperty(PropertyName);
//表述:“实体”
ParameterExpression参数=Expression.parameter(EntityType,“entity”);
//表达式:“entity.PropertyName”
MemberExpression属性=Expression.MakeMemberAccess(参数,propertyInfo);
//表达:“价值”
object convertedValue=Convert.ChangeType(值,propertyInfo.PropertyType);
ConstantExpression rhs=表达式常数(convertedValue);
//表达式:“entity.PropertyName==value”
BinaryExpression equal=表达式.equal(属性,rhs);
//表达式:“entity=>entity.PropertyName==value”
LambdaExpression lambda=表达式.lambda(等于,参数);
//使用正确的TSource(我们的实体类型)实例化count方法
MethodInfo countMethod=QueryableCountMethod.MakeGenericMethod(EntityType);
//执行Count()并说“如果没有匹配项,则为有效”
int count=(int)countMethod.Invoke(null,新对象[]{table,lambda});
返回计数==0;
}
//获取可查询的.Count(IQueryable,表达式)
私有静态MethodInfo QueryableCountMethod=typeof(Queryable).GetMethods().First(m=>m.Name==“Count”&&m.GetParameters().Length==2);
}

我不介意它很难看,因为我会将它打包到一个DLL中并重用它,这比在每个表/字段中实现多个UniqueAttribute要好得多。

我编辑了这一个..它与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);
}
public类UniqueAttribute:ValidationAttribute
{
公共UniqueAttribute(类型dataContextType、类型entityType、字符串propertyName)
{
DataContextType=DataContextType;
EntityType=EntityType;
PropertyName=PropertyName;
}
公共类型DataContextType{get;private set;}
公共类型EntityType{get;private set;}
公共字符串PropertyName{get;private set;}
公共覆盖布尔值有效(对象值)
{
//构建数据上下文
//ConstructorInfo构造函数=DataContextType.GetConstructor(新类型[0]);
//DataContext=(DataContext)构造函数.Invoke(新对象[0]);
var repository=DependencyResolver.Current.GetService(DataContextType);
var data=repository.GetType().InvokeMember(“GetAll”,BindingFlags.Instance | BindingFlags.InvokeMethod | BindingFlags.Public,null,repository,null);
//收拾桌子
//ITable table=dataContext.GetTable(EntityType);
//取得财产
PropertyInfo PropertyInfo=EntityType.GetProperty(PropertyName);
//我们的最终目标是:
//“entity=>entity.PropertyName==值”
//表达:“价值”
object convertedValue=Convert.ChangeType(值,propertyInfo.PropertyType);
var rhs=表达式常数(convertedValue);
//表述:“实体”
var参数=Expression.parameter(EntityType,“实体”);
//表达式:“entity.PropertyName”
var property=Expression.MakeMemberAccess(参数,propertyInfo);
//表达式:“entity.PropertyName==value”
var equal=表达式.equal(属性,rhs);
//表达式:“entity=>entity.PropertyName==value”
var lambda=Expression.lambda(等于,参数).Compile();
//使用正确的TSource(我们的实体类型)实例化count方法
MethodInfo countMethod=QueryableCountMethod.MakeGenericMethod(EntityType);
//执行Count()并说“如果没有,则为有效”
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);
}
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);
}