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 Web サイト プロジェクトとは別のプロジェクト)。データ層は、データベースに関係するすべてを排他的に処理します。よろしければ、属性自体から CoinDataContext を分離する方法の例をいくつか書いていただけますか?

別の懸念に対処するために、ここでは属性内のクエリの必要性を取り除きますが、データベースへの呼び出しと、使用するテーブルの指定が必要です。

ただし、これは属性であるため、属性で linq ラムダ式を使用できるかどうかは 100% 確信が持てないため、属性はこの方法で一般化されたままにする必要があります。

データレイヤー プロジェクト

このレイヤーには、さまざまなテーブルに関連するさまざまなクラスが含まれます。以下のクラスは email テーブル専用です。

メールマッパークラス

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;
  }
}

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 に答える