2

我已经为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);
    }
}
4

3 回答 3

4

这只是来自Brad Wilson 的asp.net 论坛。很满意。没有错误处理!

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);
}
于 2010-01-10T01:06:49.007 回答
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是 的别名Boolean其方式与int的别名相同Int32。任何一个都可以接受。我自己更喜欢小写版本。

正如亚历克斯在他的回答中已经建议的那样,这不是确定电子邮件地址在进入数据库时​​是否唯一的可靠方法。只是在检查时它是唯一的。

最后,有点离题了……我了一些 linq 扩展,比如下面的类。使用它可以让我将属性的返回重写为db.Emails.None(e => e.Email1 == value.ToString());. 这使它更具可读性。

更新 如果不访问数据库并将行与写入的值进行比较,就无法确定数据库中值的唯一性。您仍然需要为数据库创建一个实例。不过,我要做的是将这些关注点分成服务层和数据层(与 MVC 网站项目分开的项目)等领域。您的数据层将专门处理与数据库有关的任何事情。如果您愿意,我可以写一些示例来说明如何将 CoinDataContext 与属性本身分开?

解决您的另一个问题,这里我们不需要在属性内进行查询,但您仍然需要调用数据库,并指定要使用的表。

但是,因为这是一个属性,所以我不能 100% 确定您是否可以在属性中使用 linq lambda 表达式,因此您的属性必须以这种方式保持泛化。

数据层项目

该层将包含与不同表相关的不同类。下面的课程专门用于电子邮件表。

电子邮件映射器类

public static class EmailMapper
{
  public static void IsValid(Func<string, bool> query)
  {
    var db = new CoinDataContext();
    return db.Emails.Count(query) == 0;
  }
}

服务层项目

该层负责对象的一般验证,但也用于访问其他层,例如外部 API。

电子邮件服务类

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());
}
于 2010-01-09T12:46:16.320 回答
0

我不喜欢 LINQ,但您似乎正在尝试强制客户端的唯一性。那是不可能的。必须在数据库中强制执行唯一性约束。如果一个并发事务在检查完成后提交一个电子邮件地址,你认为会发生什么?

即使您检查只是为了提供“抱歉,该地址已被使用”消息,仍然有可能另一个事务插入相同的地址。

于 2010-01-08T23:47:00.653 回答