10

説明

私のソリューションには次のプロジェクトがあります。

  • DAL =変更されたエンティティフレームワーク
  • DTO =自分自身を検証できるデータ転送オブジェクト
  • BL =ビジネスレイヤーサービス
  • WEB =プレゼンテーションAsp.netMVCアプリケーション

DAL、BL、およびWEBはすべてDTOを参照しています。これはすばらしいことです。
プロセスは通常、次のように実行されます。

  1. WEBに対してWebリクエストが行われます
  2. WEBはDTOを投稿します
    • DTOは、カスタムActionFilterを介して自動的に検証されます
    • 検証エラーは自動収集されます
  3. (検証はOKです)DTOを提供するBLへのWEB呼び出し
  4. BLはDTOを使用してDALを呼び出します(DTOを通過させるか、単に使用することができます)

DTO検証の問題は...

私のDTOは、自分の状態(プロパティの値)に基づいて自分自身を検証できます。しかし、今のところ、そうでない場合に問題が発生します。BL(および結果としてDAL)を使用して検証する必要があります。

私の実際の例:ユーザーが登録し、WEBが検証されるユーザーDTOを取得します。問題のある部分はusername検証です。その一意性はデータストアに対してチェックする必要があります。
これをどのように行うのですか?

すべてのDTOがIoCとTDDのインターフェイスを実装する(つまり、UserDTOが実装する)という追加情報があります。IUserどちらもDTOプロジェクトの一部です。

不可能な試み

  1. 循環参照を取得するため、DTOでBLを参照できません。
    Compilation error
  2. 部分的なDTOクラスを参照し、そこで検証を実装する追加のDTO.Valプロジェクトを作成することはできません(BL + DTOを参照します)。
    Partial classes can't span assemblies.

可能な試み

  1. ActionFilter外部条件に対してオブジェクトを検証するスペシャルを作成します。これはWEBプロジェクト内で作成されるため、ここで使用されるDTOとBLが表示されます。
  2. DTOをBLに配置し、DTOインターフェイスを他のプロジェクトによって参照される実際のDTOとして保持し、すべてのコードをリファクタリングして、具象クラスの代わりにインターフェイスを使用します。
  3. 外部依存の検証を処理せず、外部依存に例外をスローさせます-おそらくこの問題の最悪の解決策です

あなたは何を提案しますか?

4

4 に答える 4

6

私は先週かそこらの間だけ試している実験を提案するでしょう。

このインスピレーションに基づいて、アプローチとは少し異なる方法で検証するDTOを作成していDataAnnotationsます。サンプルDTO:

public class Contact : DomainBase, IModelObject
{
    public int ID { get; set; }
    public string Name { get; set; }
    public LazyList<ContactDetail> Details { get; set; }
    public DateTime Updated { get; set; }


    protected override void ConfigureRules()
    {
        base.AddRule(new ValidationRule()
        {
            Properties = new string[] { "name" },
            Description = "A Name is required but must not exceed 300 characters in length and some special characters are not allowed",
            validator = () => this.Name.IsRequired300LenNoSpecial()
        });

        base.AddRule(new ValidationRule()
        {
            Properties = new string[] { "updated" },
            Description = "required",
            validator = () => this.Updated.IsRequired()
        });
    }
}

これはもっとDataAnnotationsうまくいくように見えるかもしれません、それはそれですが、それは巨大ではありません。私はそれがクラスでより見栄えがよいと思います(私は今DataAnnotations属性を持ついくつかの本当に醜いDTOクラスを持っています-あなたはもうプロパティを見ることさえできません)。そして、このアプリケーションでの匿名のデリゲートの力は、ほとんど本に値するものです(私は発見しています)。

基本クラス:

public partial class DomainBase : IDataErrorInfo
{
    private IList<ValidationRule> _rules = new List<ValidationRule>();

    public DomainBase()
    {
        // populate the _rules collection
        this.ConfigureRules();
    }

    protected virtual void ConfigureRules()
    {
        // no rules if not overridden
    }

    protected void AddRule(ValidationRule rule)
    {
        this._rules.Add(rule);
    }





    #region IDataErrorInfo Members

    public string Error
    {
        get { return String.Empty; }    // Validation should call the indexer so return "" here
    }                                   // ..we dont need to support this property.

    public string this[string columnName]
    {
        get
        {
            // get all the rules that apply to the property being validated
            var rulesThatApply = this._rules
                .Where(r => r.Properties.Contains(columnName));

            // get a list of error messages from the rules
            StringBuilder errorMessages = new StringBuilder();
            foreach (ValidationRule rule in rulesThatApply)
                if (!rule.validator.Invoke())   // if validator returns false then the rule is broken
                    if (errorMessages.ToString() == String.Empty)
                        errorMessages.Append(rule.Description);
                    else
                        errorMessages.AppendFormat("\r\n{0}", rule.Description);

            return errorMessages.ToString();
        }
    }

    #endregion
}

ValidationRuleと私の検証機能:

public class ValidationRule
{
    public string[] Properties { get; set; }
    public string Description { get; set; }
    public Func<bool> validator { get; set; }
}


/// <summary>
/// These extention methods return true if the validation condition is met.
/// </summary>
public static class ValidationFunctions
{
    #region IsRequired

    public static bool IsRequired(this String str)
    {
        return !str.IsNullOrTrimEmpty();
    }

    public static bool IsRequired(this int num)
    {
        return num != 0;
    }

    public static bool IsRequired(this long num)
    {
        return num != 0;
    }

    public static bool IsRequired(this double num)
    {
        return num != 0;
    }

    public static bool IsRequired(this Decimal num)
    {
        return num != 0;
    }

    public static bool IsRequired(this DateTime date)
    {
        return date != DateTime.MinValue;
    }

    #endregion


    #region String Lengths

    public static bool IsLengthLessThanOrEqual(this String str, int length)
    {
        return str.Length <= length;
    }

    public static bool IsRequiredWithLengthLessThanOrEqual(this String str, int length)
    {
        return !str.IsNullOrTrimEmpty() && (str.Length <= length);
    }

    public static bool IsRequired300LenNoSpecial(this String str)
    {
        return !str.IsNullOrTrimEmpty() &&
            str.RegexMatch(@"^[- \r\n\\\.!:*,@$%&""?\(\)\w']{1,300}$",
                RegexOptions.Multiline) == str;
    }

    #endregion

}

私のコードが乱雑に見える場合、それは私がこの数日間この検証アプローチに取り組んでいるだけだからです。いくつかの要件を満たすには、このアイデアが必要です。

  • IDataErrorInfoMVCレイヤーが自動的に検証されるように、インターフェイスをサポートする必要があります
  • 複雑な検証シナリオ(私が推測する質問の要点全体)をサポートできる必要があります。同じオブジェクト(つまり、StartDateとFinishDate)の複数のプロパティに対して検証できるようにしたいです。オブジェクトグラフにあるような、異なる/複数の/関連付けられたオブジェクトのプロパティ。そして、私がまだ考えていない他のことさえ。
  • 複数のプロパティに適用されるエラーのアイデアをサポートする必要があります
  • TDDとDDDの旅の一環として、ドメインオブジェクトにサービスレイヤーメソッドよりも多くの「ドメイン」を記述させたいので、これらの複雑な条件をモデルオブジェクト(DTOではない)に配置すると、これが達成されるようです。

このアプローチは私が欲しいものを私に与えると思います、そして多分あなたもそうです。

あなたがこれについて私と一緒に飛び乗ったら、私たちは「自分たちで」かなりなるだろうと想像しますが、それは価値があるかもしれません。MVC 2の新しい検証機能について読んでいましたが、カスタム変更を行わないと、上記のウィッシュリストを満たしていません。

お役に立てれば。

于 2009-11-16T12:17:23.370 に答える
2

S#arpアーキテクチャには、クラスレベルのバリデーター[HasUniqueDomainSignature]で使用される[DomainSignature]メソッド識別子が機能します。以下のサンプルコードを参照してください。

[HasUniqueDomainSignature]
public class User : Entity
{
    public User()
    {
    }

    public User(string login, string email) : this()
    {
        Login = login;
        Email = email;
    }

    [DomainSignature]
    [NotNullNotEmpty]
    public virtual string Login { get; set; }

    [DomainSignature]
    public virtual string Email { get; set; }

}

http://www.sharparchitecture.net/を詳しく見てください

于 2009-12-02T16:51:37.127 に答える
1

これとまったく同じ問題が発生し、何日も何日も回避策を見つけようとした後、DTO、DAL、およびBLを1つのライブラリにマージすることになりました。プレゼンテーション層を分離しました。それがあなたの選択肢かどうかわからない。私にとって、データストアを変更する可能性はごくわずかであるため、個別の層は実際には必要ないと考えました。

また、すべてのDTO検証にMicrosoft検証アプリケーションブロックを実装しました。複雑な検証を実行できる「自己検証」メソッドがあります。

于 2009-12-08T19:30:31.817 に答える
1

結果として得られるソリューション

オブジェクト自体からは取得できない外部要因に対してオブジェクトを検証できるコントローラーアクションフィルターを使用することになりました。

チェックするアクションパラメーターの名前と、その特定のパラメーターを検証するバリデータータイプを取得するフィルターを作成しました。もちろん、このバリデーターは、すべてを再利用できるようにするために特定のインターフェースを実装する必要があります。

[ValidateExternalFactors("user", typeof(UserExternalValidator))]
public ActionResult Create(User user)

バリデーターはこのシンプルなインターフェースを実装する必要があります

public interface IExternalValidator<T>
{
    bool IsValid(T instance);
}

これは、一見複雑に見える問題に対する単純で効果的な解決策です。

于 2011-05-04T19:53:12.080 に答える