我一直在为asp.net编程,哦,现在几天。这是一个我甚至无法为自己弄清楚的问题。
我希望从代码中可以看出我想要完成的事情,而且我有,但它并不漂亮。此外,我想在任何表上使用它,无论哪个字段,即检查我指定的表和字段的值的唯一性,将其全部传递给属性构造函数。
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);
}
}
答案 0 :(得分:4)
这是布拉德威尔逊的asp.net forums。很高兴。没有错误处理!
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);
}
答案 1 :(得分:2)
首先,让我们看一下重写属性......
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。类型bool
是Boolean
的{{3}},与int
是Int32
的别名的方式相同。要么是可以接受的。我自己更喜欢小写版本。
正如Alex已经在他的alias中建议的那样,这不是确定电子邮件地址进入数据库时是否唯一的可靠方法。只是在检查时它是唯一的。
最后,有点偏离......我有answer一些linq扩展,如下面的类。使用它可以让我将属性的返回值重写为db.Emails.None(e => e.Email1 == value.ToString());
。这使得一点点更具可读性。
<强>更新强> 如果没有进入数据库并将行与写入的值进行比较,则无法确定数据库中值的唯一性。您仍然需要为数据库创建实例。我要做的是将written放在服务层和数据层等区域(MVC网站项目中的单独项目)。您的数据层将专门处理与数据库有关的任何事情。如果您愿意我可以写一些关于如何将CoinDataContext与属性本身分开的示例?
解决您的另一个问题,我们在此处删除了对属性内部查询的需要,但您仍需要调用数据库,并指定要使用的表。
因为这是一个属性,但是我不能100%确定你是否可以在属性中使用linq lambda表达式,所以你的属性必须以这种方式保持一般化。
数据层项目
此图层将包含与不同表相关的不同类。下面的课程专门用于电子邮件表。
电子邮件Mapper类
public static class EmailMapper
{
public static void IsValid(Func<string, bool> query)
{
var db = new CoinDataContext();
return db.Emails.Count(query) == 0;
}
}
服务层项目
该层负责对象的一般验证,但也用于转到其他层,例如外部API。
EmailService类
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;
}
}
网络项目中的属性类
public override Boolean IsValid(Object value)
{
return EmailService.IsValid(value.ToString());
}
答案 2 :(得分:0)
我不是进入LINQ,但似乎你正试图强制执行唯一性客户端。那是不可能的。必须在数据库中强制执行唯一性约束 。如果并发事务在检查完成后提交了一个电子邮件地址,您认为会发生什么?
即使您只是为了提供“抱歉,该地址已被使用”-message,也有仍然可能另一个事务插入相同的地址。