web-dev-qa-db-ja.com

Asp.net MVC Web APIモデル検証のベストプラクティス

私はasp.netコアapiに迷惑なコードの臭いがあり、それを回避しているので、修正する方法を思いつきません。

MVCコントローラーアクションでは、通常、少なくとも私の場合は非常に単純なロジックがあります。

  • モデルを検証する(アクションを呼び出す前に)
  • 関連データをクエリする
  • ビジネスロジックを実行する
  • 返信を送信

いくつかのアクションでは、ロジックをもう少し複雑にする必要があります。

時々、モデルの検証にはデータベースへのクエリが必要です。

FluentValidation を使用すると、通常、必要な依存関係を挿入し、それを使用してデータベースをクエリするだけで十分です。その後、コントローラーに他の関連データをクエリさせます。

ここですべてがめちゃくちゃになります。コントローラーアクションと同じデータを必要とする多くのモデル検証があります。

だから、私が今何をしているのか、これらの場合にはコードの匂いがするようです。

  • モデルを検証し、コントローラーが必要としないデータのみをクエリします
  • 関連データをクエリし、コントローラーで検証する
  • ビジネスロジックを実行する
  • 応答を送信します。

コントローラ内でデータを検証するという考えは好きではありません。60行のアクションが発生することが多く、そのほとんどがモデルの検証であり、SOLIDが壊れるので、アプリが大きくなります。

データを2回クエリするオプションは非常に悪いオプションのように思えますが、率直に言って、実際にそれを選択する理由はわかりません。

だから問題は基本的に、データベースに対してBindingModelsを検証することをどのように提案するのですか?

私にとっての理想的な方法は、クエリされたデータを何らかの方法でAbstractValidator実装(または検証を担当する他のクラス)とコントローラーに注入するようなもので、同じ方法で同じデータを2回クエリすることなく、元のロジックを維持できます。ただし、実際に実装する方がはるかに悪いと確信しています。

2
gilmishal

私はあなたのケースで使用できる2つのアプローチを見ます

1。検証方法を変更して、以降のアクションに必要なデータを返すことができます。だから検証方法は以下のようになります

public ValidationResult Validate(DataModel model, IDataService dataService)
{
    var requiredData = dataService.Load();
    var isModelValid = // do validation
    return new ValidationResult
    {
        IsValid = isModelValid,
        Model = dataModel,
        Data = requiredData
    };               
}

コントローラメソッドの責任は同じままです。必要なアクションを組み合わせて結果を返します。

public IActionResult DoSomething(Model model)
{
    var result = _validationService.Validate(model, _dataService);

    var otherRelatedData = _dataService.LoadOtherData();
    var data = CombineData(result.Data, otherRelatedData);

    var actionResult = _businessLogic.Execute(model, data);

    return Ok(actionResult);
}

上記のコードはある種の疑似コードです-自由にリファクタリングして、アプリケーション設計を満たす責任を移してください。

検証結果の一部として既にロードされたデータを返すことにより、データサービスインスタンスで「隠された」キャッシュ状態が発生するのを防ぐことができます。入力関数と出力関数を使用すると、コードを追跡/理解するのが困難になります。

2。もう1つのアプローチは、必要なデータをロードして、Validationメソッドに渡すことです。その後、このデータをビジネスアクションに再利用できます。

public IActionResult DoSomething(Model model)
{
    var dataForValidation = _dataService.LoadValidationData();
    var result = _validationService.Validate(model, dataForValidation);

    var otherRelatedData = _dataService.LoadOtherData();
    var data = CombineData(dataForValidation, otherRelatedData);

    var actionResult = _businessLogic.Execute(model, data);

    return Ok(actionResult);
}
1
Fabio

私が提案するのはデータレイヤーを作ることです。

これで、データベースに対してクエリを実行するのではなく、データレイヤーに対してクエリを実行します。そのデータレイヤーはリクエストの期間中は同じです(検証ロジックとコントローラーの両方で同じデータレイヤーを返すだけのお気に入りの依存関係注入フレームワークを選択してください)。

再利用するデータがわかったら、データレイヤーにキャッシュロジックを追加するだけです。コントローラーが既に照会したものと同じデータを要求する場合、それはすでにそこにあり、2番目の照会は必要ありません。コントローラーと検証が実際に一部のコードパスのデータを使用していない場合は、データを照会する必要すらありません(事前に関連するデータを知る必要はありません)。

更新:呼び出しの間にいくつかの利点を得ることができます。たとえば、このstackexchangeの質問を読んだ場合、それに対する回答を作成する可能性が高くなります。しかし、質問のデータは既にそれを読み取ってキャッシュにあるので、回答の部分で再クエリする必要はありません。

0
Batavia