C#でFluentValidationをラップしたのに、結局AbstractValidatorだらけで、もう見ていられない

C#でFluentValidationをラップしたのに、結局AbstractValidatorだらけで、もう見ていられない

FluentValidationは、厳密に型指定された検証ルールを構築するための非常に強力な.NETフレームワークです。

最終更新 2022/06/09 22:12
黑哥聊dotNet
読了目安 3 分
カテゴリ
.NET
タグ
.NET C# FluentValidation

ストーリー

先日、会社の新しいプロジェクトで FluentValidation が使われているのを見ました。ご存知の通り、FluentValidation は厳密に型指定された検証ルールを構築するための非常に強力な .NET フレームワークで、プログラマーから面倒な検証問題を解決してくれ、非常に便利です。しかし、それでも私はとても不愉快なことに遭遇しました。以下のコードをご覧ください:

public class UserInformationValidator : AbstractValidator<UserInformation>
{
 public UserInformationValidator()
 {
     RuleFor(o => o.UserName).Length(2, 20).WithMessage("名前の長さが間違っています");
     RuleFor(o => o.Sex).Must(o=>o=="男"||o=="女").WithMessage("性別が間違っています");
     RuleFor(o => o.Age).ExclusiveBetween(0, 200).WithMessage("年齢が間違っています");
     RuleFor(o => o.Email).EmailAddress().WithMessage("メールアドレスが間違っています");
  }
}


static void Main(string[] args)
{

    UserInformation userInformation = new UserInformation();
    userInformation.UserName = "";
    userInformation.Sex = "不男不女";
    userInformation.Age = 2200;
    userInformation.Email = "xxxxx";
    UserInformationValidator validationRules = new UserInformationValidator();
    var result=   validationRules.Validate(userInformation);
    if (!result.IsValid)
    {
      Console.WriteLine( string.Join(Environment.NewLine, result.Errors.Select(x => x.ErrorMessage).ToArray()));
    }

}

オブジェクトを検証するたびに、新しいバリデーターの型を作成しなければなりません。上記の UserInformationValidator のように、論理的には問題ありませんが、私は潔癖症なので、次にこれをカプセル化してみましょう。えへへ、より少ないコードでより多くのことを行います。

インストール

バリデーターを作成する前に、プロジェクトに FluentValidation.dll への参照を追加する必要があります。最も簡単な方法は、NuGet パッケージマネージャーまたは dotnet CLI を使用することです。

テンプレート化されたコードのカプセル化の探求

テンプレート化されたコードを親クラスに抽出する

上記のコードをよく見ると、新しいバリデーターを作成するたびに、AbstractValidator<T> を継承したクラスを作成しなければなりません。ここで T は検証したいクラスの型です。バリデーターの親クラスをカプセル化します。

public class CommonVaildator<T> : AbstractValidator<T>
{

}

検証ルールの追加

実際のビジネスロジックは UserInformationValidator バリデーター内に記述されており、このコードブロックで必要なのは RuleFor だけです。それ以外はすべて親クラスに統一してカプセル化できるはずです。この考え方に従って、長さバリデータールールをカプセル化してみましょう。

まず、RuleFor のプロトタイプを見てみましょう。

public IRuleBuilderInitial<T, TProperty> RuleFor<TProperty>(Expression<Func<T, TProperty>> expression)

そのパラメータは Func デリゲートです。では、Expression とは何でしょうか?

Expression式木です!

式木は、ラムダ式を実行可能なロジックではなく、ツリー状のデータ構造として表現できるようにするコードです。

C# では Expression で定義され、構文木、つまりデータ構造の一種です。主に計算や演算が必要な構造を格納するために使用され、格納機能のみを提供し、演算は行いません。通常、ExpressionLambda と一緒に使用されます。ここでは詳しく説明しません!

これで、簡単に長さバリデータールールをカプセル化できます。

public void LengthVaildator(Expression<Func<T, string>> expression, int min, int max, string Message)
{
    RuleFor(expression).Length(min, max).WithMessage(Message);
}

同様に、述語バリデータールールメールバリデータールールなどもカプセル化できます。

public void MustVaildator(Expression<Func<T, string>> expression ,Func<T,string, bool> expression2, string Message)
{
    RuleFor(expression).Must(expression2).WithMessage(Message);
}
public void EmailAddressVaildator(Expression<Func<T, string>> expression, string Message)
{
    RuleFor(expression).EmailAddress().WithMessage(Message);
}

検証メソッドのカプセル化

上記でバリデーターをカプセル化しました。では、var result= validationRules.Validate(userInformation); のような検証メソッドをカプセル化するのは簡単です。コードは次のとおりです。

public static string ModelValidator<T>(T source, AbstractValidator<T> sourceValidator) where T : class
{
    var results = sourceValidator.Validate(source);
    if (!results.IsValid)
        return string.Join(Environment.NewLine, results.Errors.Select(x => x.ErrorMessage).ToArray());
    else
        return "";

}

カプセル化後のコードのテスト

CommonVaildator<UserInformation> commonUserInformation = new CommonVaildator<UserInformation>();
commonUserInformation.LengthVaildator(o => o.UserName, 2, 30, "名前の長さが間違っています");
commonUserInformation.MustVaildator(o => o.Sex, (user, _) => user.Sex =="男"||user.Sex=="女" , "性別が間違っています");
commonUserInformation.ExclusiveBetweenVaildator(o=>o.Age,0, 200, "年齢が間違っています");
commonUserInformation.EmailAddressVaildator(o => o.Email, "メールアドレスが間違っています");
string msg= VaildatorHelper.ModelValidator(userInformation, commonUserInformation);
Console.WriteLine(msg);

このようにコードがずっと簡潔になりましたね。ここでは4種類の検証ルールのみをカプセル化しましたが、他のものはここではカプセル化しません。

まとめ

この記事は仕事の細かな部分から生まれました。これは私の即興のカプセル化です。より良いカプセル化コードをお持ちの方がいらっしゃいましたら、ぜひご意見をお聞かせください。独り善がりより皆で楽しむ方が良いですからね。今回はここまでです。お役に立てれば幸いです。

さらに探索

関連読書

その他の記事
同じカテゴリ / 同じタグ 2026/04/22

各OSバージョンの.NETサポート状況(250707更新)

仮想マシンとテストマシンを使用して、各OSバージョンの.NETサポート状況を確認します。OSインストール後、対応するランタイムをインストールし、Stardustエージェントを実行できることを確認します(合格条件)。

続きを読む
同じカテゴリ / 同じタグ 2026/02/07

AOTの使用経験のまとめ

プロジェクト作成当初から、新機能を追加したり新しい構文を使用したりした場合には、すぐにAOT公開テストを実施するという良い習慣を身につけるべきです。

続きを読む