如何在C#和DataAnnotation中创建通用的UniqueValidationAttribute?
我正在尝试使用System.ComponentModel.DataAnnotations.ValidationAttribute
创建UniqueAttribute
我希望这是通用的,因为我可以传递Linq DataContext,表名,字段并validation传入值是否唯一。
这是一个不可编译的代码片段,我现在卡在这里:
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; }
编辑:请注意,根据这个: 为什么C#禁止通用属性类型? 我不能使用属性的generics类型。
所以我的新方法是使用Reflection / Expression树来动态构建Lambda表达式树。
好吧,经过一番搜索后,我遇到了: http : //forums.asp.net/t/1512348.aspx ,我想出来了,虽然它涉及到相当多的代码。
用法:
[Required] [StringLength(10)] [Unique(typeof(ContactsManagerDataContext),typeof(Group),"name",ErrorMessage="Group already exists")] public string name { get; set; }
validation码:
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(IQueryable , Expression>) private static 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(IQueryable , Expression>) //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); }
我看到的一个问题是你不能将类型实例化为属性的参数。
属性要求所有参数都是编译时常量。 所以用法:
[Unique(new DataContext(),"Groups","name")]
不会编译。 您可以省略new DataContext()
– 但我怀疑您的validation逻辑不会有关于要查询的实体类型的信息。
正如@LBushkin所提到的, Attributes
需要编译时常量。
我会改变你的class级:
public class UniqueAttribute : ValidationAttribute
至:
public class UniqueAttribute : ValidationAttribute where T : DataContext{ protected T Context { get; private set; } ... }
并用它作为:
[Required] [StringLength(10)] [Unique("Groups","name")] public string name { get; set; }
这将帮助您在需要时注入DataContext对象,而不是每次都创建一个实例
HTH
编辑:由于属性不能采用通用参数,这可能是另一个潜在的代码:
public class UniqueAttribute : ValidationAttribute{ public UniqueAttribute(Type dataContext, ...){ if(dataContext.IsSubClassOf(typeof(DataContext))){ var objDataContext = Activator.CreateInstance(dataContext); } } }
并用它作为:
[Required] [StringLength(10)] [Unique(typeof(DataContext), "Groups","name")] public string name { get; set; }
HTH这次:)
- 如何注销不是当前用户的多个MembershipUser?
- ASP.Net MVC 2 Controller的TryValidate不validation模型中的List 项
- MVC2 TextBoxFor提交后值不更新?
- 扩展ASP.NET MVC 2 Model Binder以适用于0,1个布尔值
- 在entity framework中编辑对象并将其保存到ASP.NET MVC 2.0中的数据库中
- 使用Moq模拟HttpContext.Current.Server.MapPath?
- 实现自定义SessionIDManager
- 自定义validation属性MVC2
- 如何从Request.Form获取所有元素值,而无需使用.GetValues(“ElementIdName”)确切指定哪个元素值