Asp.net mvc 用于检查linq到sql数据上下文中唯一性的常规验证属性
我已经为asp.net编程好几天了。有一个问题我自己都想不出来 我希望从代码中可以明显看出我想要完成什么,我已经做到了,但这并不漂亮。此外,我想在任何表、任何字段上使用它,即根据我指定的表和字段检查值的唯一性,并将其全部传递给属性构造函数Asp.net mvc 用于检查linq到sql数据上下文中唯一性的常规验证属性,asp.net-mvc,linq-to-sql,data-annotations,Asp.net Mvc,Linq To Sql,Data Annotations,我已经为asp.net编程好几天了。有一个问题我自己都想不出来 我希望从代码中可以明显看出我想要完成什么,我已经做到了,但这并不漂亮。此外,我想在任何表、任何字段上使用它,即根据我指定的表和字段检查值的唯一性,并将其全部传递给属性构造函数 public class UniqueEmailAttribute : ValidationAttribute { public UniqueEmailAttribute() { } public override Boole
public class UniqueEmailAttribute : ValidationAttribute
{
public UniqueEmailAttribute()
{
}
public override Boolean IsValid(Object value)
{
//not pretty. todo: do away with this.
var db = new CoinDataContext();
int c = db.Emails.Count(e => e.Email1 == value.ToString());
return (Boolean) (c == 0);
}
}
我对LINQ不感兴趣,但似乎你在试图加强客户端的独特性。那是不可能的。必须在数据库中强制执行唯一性约束。如果在检查完成后并发事务提交了一个电子邮件地址,您认为会发生什么
即使您只是为了提供一条“对不起,该地址已被使用”消息而进行检查,也有可能另一个事务会插入相同的地址。首先,让我们看看如何重写该属性
public override bool IsValid(object value)
{
var db = new CoinDataContext();
//Return whether none of the email contains the specified value
return db.Emails.Count(e => e.Email1 == value.ToString()) == 0;
}
此外,不需要将(c==0)
强制转换为布尔值,因为该操作的结果已经是布尔值。类型bool
是Boolean
的别名,就像int
是Int32
的别名一样。两者都可以接受。我自己更喜欢小写版本
正如Alex在他的文章中所建议的,这并不是确定电子邮件地址进入数据库时是否唯一的一种方法。只是在检查时它是唯一的
最后,有点偏离切线。。。我有一些linq扩展,比如下面的类。使用它将允许我将属性返回重写为db.Emails.None(e=>e.Email1==value.ToString())代码>。这使它更具可读性
更新
如果不到数据库并将行与写入的值进行比较,就无法确定数据库中值的唯一性。您仍然需要为数据库创建一个实例。不过,我要做的是研究服务层和数据层(与MVC网站项目分开的项目)等领域。您的数据层将专门处理与数据库有关的任何事情。如果您愿意,我可以写一些示例,说明如何将CoinDataContext与属性本身分离
为了解决您的另一个问题,这里我们不再需要在属性内部进行查询,但您仍然需要调用数据库,并指定要使用的表
但是,因为这是一个属性,所以我不能100%确定是否可以在属性it中使用linq lambda表达式,因此您的属性必须以这种方式保持泛化
数据层项目
public override Boolean IsValid(Object value)
{
return EmailService.IsValid(value.ToString());
}
该层将包含与不同表相关的不同类。下面的类专用于电子邮件表
电子邮件映射器类
public static class EmailMapper
{
public static void IsValid(Func<string, bool> query)
{
var db = new CoinDataContext();
return db.Emails.Count(query) == 0;
}
}
public static class EmailService
{
public static IsValid(string address)
{
bool isValid = false;
//...Check email is valid first with regex. Not done.
isValid = RegexHelper.IsEmailAddressValid(address);
//Go to the database and determine it's valid ONLY if the regex passes.
return isValid ? EmailMapper.IsValid(x=> x.Email == address) : false;
}
}
web项目中的属性类
public override Boolean IsValid(Object value)
{
return EmailService.IsValid(value.ToString());
}
这是布拉德·威尔逊刚刚发来的。我很高兴。没有错误处理
using System;
using System.ComponentModel.DataAnnotations;
using System.Data.Linq;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
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]);
// 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);
ConstantExpression rhs = Expression.Constant(convertedValue);
// Expression: "entity"
ParameterExpression parameter = Expression.Parameter(EntityType, "entity");
// Expression: "entity.PropertyName"
MemberExpression property = Expression.MakeMemberAccess(parameter, propertyInfo);
// 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);
}
使用系统;
使用System.ComponentModel.DataAnnotations;
使用System.Data.Linq;
使用System.Linq;
使用System.Linq.Expressions;
运用系统反思;
公共类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]);
//收拾桌子
ITable table=dataContext.GetTable(EntityType);
//取得财产
PropertyInfo PropertyInfo=EntityType.GetProperty(PropertyName);
//我们的最终目标是:
//“entity=>entity.PropertyName==值”
//表达:“价值”
object convertedValue=Convert.ChangeType(值,propertyInfo.PropertyType);
ConstantExpression rhs=表达式常数(convertedValue);
//表述:“实体”
ParameterExpression参数=Expression.parameter(EntityType,“entity”);
//表达式:“entity.PropertyName”
MemberExpression属性=Expression.MakeMemberAccess(参数,propertyInfo);
//表达式:“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);
}
谢谢您的回答。但不完全是我想要的。您的linq扩展将对我非常有用,非常好的工作。我希望IsValid没有CoinDataContext,也没有提到电子邮件或Email1。称之为UniqueAttribute,而不是UniqueMailAttribute。超一般化。我把类移动到了,因为它不属于这个答案。马丁:最好的方法是将数据上下文从属性本身拉出来,并考虑一个分隔符。