12

私のアプリケーションには、大量のフォームがありますが、そのほとんどは、それらがバインドする独自のモデルを持っています! もちろん、データの検証は重要ですが、すべてのモデルに IDataErrorInfo を実装し、すべてのプロパティのコードを記述してそれらを検証するよりも優れたソリューションはありませんか?

実際の検証コードの多くを削除する検証ヘルパーを作成しましたが、それでもトリックが 1 つか 2 つ欠けていると感じずにはいられません。これは私が MVVM を使用した最初のアプリケーションであるため、このテーマについて学ぶことがたくさんあると確信しています!

編集:

これは、私が本当に気に入らない典型的なモデルのコードです (説明させてください)。

    string IDataErrorInfo.Error
    {
        get
        {
            return null;
        }
    }

    string IDataErrorInfo.this[string propertyName]
    {
        get
        {
            return GetValidationError(propertyName);
        }
    }

    #endregion

    #region Validation

    string GetValidationError(String propertyName)
    {
        string error = null;

        switch (propertyName)
        {
            case "carer_title":
                error = ValidateCarerTitle();
                break;
            case "carer_forenames":
                error = ValidateCarerForenames();
                break;
            case "carer_surname":
                error = ValidateCarerSurname();
                break;
            case "carer_mobile_phone":
                error = ValidateCarerMobile();
                break;
            case "carer_email":
                error = ValidateCarerEmail();
                break;
            case "partner_title":
                error = ValidatePartnerTitle();
                break;
            case "partner_forenames":
                error = ValidatePartnerForenames();
                break;
            case "partner_surname":
                error = ValidatePartnerSurname();
                break;
            case "partner_mobile_phone":
                error = ValidatePartnerMobile();
                break;
            case "partner_email":
                error = ValidatePartnerEmail();
                break;
        }

        return error;
    }

    private string ValidateCarerTitle()
    {
        if (String.IsNullOrEmpty(carer_title))
        {
            return "Please enter the carer's title";
        }
        else
        {
            if (!ValidationHelpers.isLettersOnly(carer_title))
                return "Only letters are valid";
        }

        return null;
    }

    private string ValidateCarerForenames()
    {
        if (String.IsNullOrEmpty(carer_forenames))
        {
            return "Please enter the carer's forename(s)";
        }
        else
        {
            if (!ValidationHelpers.isLettersSpacesHyphensOnly(carer_forenames))
                return "Only letters, spaces and dashes are valid";
        }

        return null;
    }

    private string ValidateCarerSurname()
    {
        if (String.IsNullOrEmpty(carer_surname))
        {
            return "Please enter the carer's surname";
        }
        else
        {
            if (!ValidationHelpers.isLettersSpacesHyphensOnly(carer_surname))
                return "Only letters, spaces and dashes are valid";
        }

        return null;
    }

    private string ValidateCarerMobile()
    {
        if (String.IsNullOrEmpty(carer_mobile_phone))
        {
            return "Please enter a valid mobile number";
        }
        else
        {
            if (!ValidationHelpers.isNumericWithSpaces(carer_mobile_phone))
                return "Only numbers and spaces are valid";
        }

        return null;
    }

    private string ValidateCarerEmail()
    {
        if (String.IsNullOrWhiteSpace(carer_email))
        {
            return "Please enter a valid email address";
        }
        else
        {
            if (!ValidationHelpers.isEmailAddress(carer_email))
                return "The email address entered is not valid";
        }
        return null;
    }

    private string ValidatePartnerTitle()
    {
        if (String.IsNullOrEmpty(partner_title))
        {
            return "Please enter the partner's title";
        }
        else
        {
            if (!ValidationHelpers.isLettersOnly(partner_title))
                return "Only letters are valid";
        }

        return null;
    }

    private string ValidatePartnerForenames()
    {
        if (String.IsNullOrEmpty(partner_forenames))
        {
            return "Please enter the partner's forename(s)";
        }
        else
        {
            if (!ValidationHelpers.isLettersSpacesHyphensOnly(partner_forenames))
                return "Only letters, spaces and dashes are valid";
        }

        return null;
    }

    private string ValidatePartnerSurname()
    {
        if (String.IsNullOrEmpty(partner_surname))
        {
            return "Please enter the partner's surname";
        }
        else
        {
            if (!ValidationHelpers.isLettersSpacesHyphensOnly(partner_surname))
                return "Only letters, spaces and dashes are valid";
        }

        return null;
    }

    private string ValidatePartnerMobile()
    {
        if (String.IsNullOrEmpty(partner_mobile_phone))
        {
            return "Please enter a valid mobile number";
        }
        else
        {
            if (!ValidationHelpers.isNumericWithSpaces(partner_mobile_phone))
                return "Only numbers and spaces are valid";
        }

        return null;
    }

    private string ValidatePartnerEmail()
    {
        if (String.IsNullOrWhiteSpace(partner_email))
        {
            return "Please enter a valid email address";
        }
        else
        {
            if (!ValidationHelpers.isEmailAddress(partner_email))
                return "The email address entered is not valid";
        }
        return null;
    }

    #endregion

正しいプロパティを識別するための switch ステートメントを用意し、各プロパティに固有の検証関数を記述しなければならないという考えは、(やるべき作業の点ではなく、必要なコードの量の点で) あまりにも多く感じられます。これはエレガントなソリューションかもしれませんが、そうは思えません!

注:回答の1つで推奨されているように、検証ヘルパーを拡張機能に変換します(Sheridanに感謝します)

解決:

したがって、私が受け入れた答えに従うと、これは最初に機能させるために実装したものの骨子です(明らかに、部分を改善します-しかし、ラムダ式またはリフレクションを使用した経験がほとんどなかったので、最初にそれを実行したかっただけですこれを実装する前に)。

Validtion Dictionary クラス (主な機能を表示):

    private Dictionary<string, _propertyValidators> _validators;
    private delegate string _propertyValidators(Type valueType, object propertyValue);


    public ValidationDictionary()
    {
        _validators = new Dictionary<string, _propertyValidators>();
    }

    public void Add<T>(Expression<Func<string>> property, params Func<T, string>[] args)
    {
        // Acquire the name of the property (which will be used as the key)
        string propertyName = ((MemberExpression)(property.Body)).Member.Name;

        _propertyValidators propertyValidators = (valueType, propertyValue) =>
        {
            string error = null;
            T value = (T)propertyValue;

            for (int i = 0; i < args.Count() && error == null; i++)
            {
                error = args[i].Invoke(value);
            }

            return error;
        };

        _validators.Add(propertyName, propertyValidators);
    }

    public Delegate GetValidator(string Key)
    {
        _propertyValidators propertyValidator = null;
        _validators.TryGetValue(Key, out propertyValidator);
        return propertyValidator;
    }

モデルの実装:

public FosterCarerModel()
    {
        _validationDictionary = new ValidationDictionary();
        _validationDictionary.Add<string>( () => carer_title, IsRequired);
    }

    public string IsRequired(string value)
    { 
        string error = null;

        if(!String.IsNullOrEmpty(value))
        {
            error = "Validation Dictionary Is Working";
        }

        return error;
    }

IDataErrorInfo 実装 (モデル実装の一部):

string IDataErrorInfo.this[string propertyName]
    {
        get
        {
            Delegate temp = _validationDictionary.GetValidator(propertyName);

            if (temp != null)
            {
                string propertyValue = (string)this.GetType().GetProperty(propertyName).GetValue(this, null);
                return (string)temp.DynamicInvoke(typeof(string), propertyValue);
            }                

            return null;
        }
    }

私のスラップダッシュの命名規則を無視し、コーディングの場所で、私はこれがうまくいったことをとても嬉しく思います! もちろん、nmclean に特に感謝しますが、この質問に貢献してくれたすべての人にも感謝します。すべての返信は非常に役に立ちましたが、いくつか検討した結果、このアプローチを採用することにしました!

4

4 に答える 4

6

メソッドを使用extensionして、記述しなければならない検証テキストの量を減らします。それらに慣れていない場合は、MSDNの拡張メソッド (C# プログラミング ガイド)extensionページを参照して、メソッドについて調べてください。私はあらゆる状況を検証するこれらの数十を持っています. 例として:

if (propertyName == "Title" && !Title.ValidateMaximumLength(255)) error = 
    propertyName.GetMaximumLengthError(255);

Validation.csクラスで:

public static bool ValidateMaximumLength(this string input, int characterCount)
{
    return input.IsNullOrEmpty() ? true : input.Length <= characterCount;
}

public static string GetMaximumLengthError(this string input, int characterCount, 
    bool isInputAdjusted)
{
    if (isInputAdjusted) return input.GetMaximumLengthError(characterCount);
    string error = "The {0} field requires a value with a maximum of {1} in it.";
    return string.Format(error, input, characterCount.Pluralize("character"));
}

入力値が 1 に等しくない場合、入力パラメーターの末尾に単に「s」を追加するPluralize別の方法があることに注意してください。別の方法は次のようになります。extension

public static bool ValidateValueBetween(this int input, int minimumValue, int 
    maximumValue)
{
    return input >= minimumValue && input <= maximumValue;
}

public static string GetValueBetweenError(this string input, int minimumValue, int 
    maximumValue)
{
    string error = "The {0} field value must be between {1} and {2}.";
    return string.Format(error, input.ToSpacedString().ToLower(), minimumValue, 
        maximumValue);
}

もちろん、必要なすべてのメソッドを実装するにはしばらく時間がかかりますが、後で時間を大幅に節約でき、すべてのエラー メッセージが一貫しているという追加の利点があります。

于 2013-09-18T10:21:11.203 に答える
5

私は個人的に FluentValidation アプローチが好きです。

これにより、スイッチ テーブルが次のような式ベースのルールに置き換えられます。

            RuleFor(x => x.Username)
                .Length(3, 8)
                .WithMessage("Must be between 3-8 characters.");

            RuleFor(x => x.Password)
                .Matches(@"^\w*(?=\w*\d)(?=\w*[a-z])(?=\w*[A-Z])\w*$")
                .WithMessage("Must contain lower, upper and numeric chars.");

            RuleFor(x => x.Email)
                .EmailAddress()
                .WithMessage("A valid email address is required.");

            RuleFor(x => x.DateOfBirth)
                .Must(BeAValidDateOfBirth)
                .WithMessage("Must be within 100 years of today.");

http://stevenhollidge.blogspot.co.uk/2012/04/silverlight-5-validation.htmlから

このhttp://fluentvalidation.codeplex.com/に関する詳細情報があります- ドキュメントは主に web-MVC ベースですが。Wpf については、 http: //blogsprajeesh.blogspot.co.uk/2009/11/fluent-validation-wpf-implementation.html のようなブログ投稿もいくつかあります。

于 2013-09-18T14:18:14.377 に答える