9

私はここSOを含めて多くの研究を行ってきましたが、明確な方向性を見つけることができないようです。現在、リポジトリの上にサービスレイヤーを配置したASP.NETMVC3アプリケーションがあります。

私のサービスレイヤーには、次のような機能があります。

public class MyService{

    public void CreateDebitRequest(int userId, int cardId, decimal Amount, .... )
    {
    //perform some sort of validation on parameters, save to database
    }

    public void CreateCreditRequest(.....)
    }
        //perform some sort of validation on parameters, save to database
    }

    public void CreateBatchFile()
    {
        //construct a file using a semi-complex process which could fail
        //write the file to the server, which could fail
    }


    public PaymentTransaction ChargePaymentCard(int paymentCardId, decimal amount)
    {
        //validate customer is eligible for amount, call 3rd party payments api call,
        //...save to database, other potential failures, etc.
    }

}

パラメータの検証はそれほど例外的ではないため、例外をスローすることはあまり適切ではないと人々が言うのを見てきました。また、文字列などの出力パラメータを渡して、空の値をチェックするというアイデアも好きではありません。ValidationDictionaryクラスを実装し、それを任意のサービスクラスのプロパティにすることを検討しました(IsValidブール値とエラーメッセージのリストが含まれ、サービスレイヤーで特定の関数呼び出しの後にチェックして方法を確認できます)物事は行きました)。特定の関数を実行した後、ValidationDictionaryのステータスを確認できます。

var svc = new MyService();
svc.CreateBatchFile();
if (svc.ValidationDictionary.IsValid)
    //proceed
else
   //display values from svc.ValidationDictionary.Messages...

これについて私が気に入らないのは、古い値を保持しないように、サービスレイヤーの関数呼び出しごとに更新する必要があることです(多くの関数またはほとんどの関数に使用しないことを選択した場合でも、それを期待できます)任意の関数を実行した後、意味のある値またはnull値を持つようにします)。私が検討したもう1つのことは、詳細な検証情報が含まれている可能性のある関数呼び出しごとにValidationDictionaryを渡すことですが、その後、outパラメーターの使用に戻ります...

何かお勧めはありますか?私はこれを行うためのきれいな方法を理解できないようです。関数に対してnullを返すだけで十分な情報である場合もありますが、呼び出し元にもう少し検証情報を返したい場合もあります。アドバイスをいただければ幸いです。

編集して明確にする: 私のサービスレイヤーは、それを消費しているのがMVCアプリケーションであることを認識していません。サービスレイヤーには、CreateBatchFile()やAddDebitRequest()などの特定のパブリック関数があります。コンシューマー(この場合はコントローラーですが、他の何かである可能性があります)が何が起こったのかを知るには、nullを返すだけで十分な場合もあります。また、コンシューマーがサービスレイヤーからさらに情報を求めている場合もあります(コンシューマーがコントローラー)。これをサービスレイヤー自体からバブルするにはどうすればよいですか?

4

4 に答える 4

10

これが私がすることです。検証用のクラスを用意し、パラメーターを渡す代わりにビュー モデルを渡します。したがって、あなたの場合、 ValidationResult は MemberName および ErrorMessage プロパティを持つ単純なクラスである、次のようなものです。

public class DebitRequestValidator{

  public IEnumerable<ValidationResult> Validate(DebitRequestModel model){

    //do some validation
    yield return new ValidationResult {
      MemberName = "cardId",
      ErrorMessage = "Invalid CardId."
    }
  }  

}

次に、コントローラー拡張メソッドを作成して、これらの検証結果をモデルの状態にコピーします。

public static class ControllerExtensions
{
    public static void AddModelErrors(this ModelStateDictionary modelState, IEnumerable<ValidationResult> validationResults)
    {
        if (validationResults == null) return;

        foreach (var validationResult in validationResults)
        {
            modelState.AddModelError(validationResult.MemberName, validationResult.ErrorMessage);
        }
    }
}

次に、コントローラーで次のようなことを行います

[HttpPost]
public ActionResult DebitRequest(DebitRequestModel model) {
  var validator = new DebitRequestValidator();
  var results = validator.Validate(model);
  ModelState.AddModelErrors(results);
  if (!ModelState.IsValid)
    return View(model)

  //else do other stuff here
}

次に、ビューで通常のようにエラーを表示できます。

@Html.ValidationMessageFor(m => m.CardId)
于 2012-04-11T16:43:46.860 に答える
1

メッセージの配列 (またはクラスのコレクション) を渡すシステムを使用しました。各要素には、コード、説明、わかりやすいメッセージが含まれていました。何かあるかどうかを確認するだけでした。UIと別の「サービス」レイヤーの間でうまく機能し、すべての例外が適切にキャッチされ、これらの検証ルールに変換されました...単なるアイデア

于 2012-04-11T16:20:10.323 に答える
1

ビューとコントローラー アクション メソッドの間で渡される ViewModel オブジェクトを使用します。ViewModel オブジェクトは、Validate(ValidationDictionary validationDictionary)メソッドによって Validation を処理できます。

コントローラーは、サービス層でメソッドを呼び出す前に、ViewModel オブジェクトで Validate メソッドを呼び出す必要があります。これは、http POST アクションにのみ必要です。

次に、ビューに検証メッセージを表示する必要があります。

この解決策では、ビューモデル オブジェクトがコントローラー アクションとビューの間で渡される必要がありますが、現在ではほとんどが MVC の ModelBinder によって処理されます。

コントローラー (http ポスト) アクションは次のようになります。

[HttpPost]
public ActionResult Foo(BarViewModel viewModel)
{
    viewModel.Validate(ValidationDictionary);

    if (!ModelState.IsValid)
    {
        return View(viewModel);
    }

    // Calls to servicelayer
}

ViewModel の Validate メソッドは次のようになります。

public void Validate(ValidationDictionary validationDictionary)
{
    if (SomeProperty.Length > 30)
    {
        validationDictionary.AddError("SomeProperty", "Max length is 30 chars");
    }
}
于 2012-04-11T16:27:06.597 に答える
1

ViewModel Validation を行っているだけなら、FluentValidationは優れたライブラリです。

ビジネス検証をユーザーへのフィードバックとして含めたい場合は、アダプター パターンを使用できます。これにより、必要なものが得られます。

インターフェイスを作成します (IValidationDictionary など)。このインターフェイスは AddError メソッドを定義し、エラー メッセージを追加するためにサービスに渡されます。

public interface IValidationDictionary
{
    void AddError(string key, string errorMessage);
}

mvc アプリケーションの ModelStateAdapter を作成します。

public class ModelStateAdapter : IValidationDictionary
{
    private ModelStateDictionary _modelState;

    public ModelStateAdapter(ModelStateDictionary modelState)
    {
        _modelState = modelState;
    }

    public void AddError(string key, string errorMessage)
    {
        _modelState.AddModelError(key, errorMessage);
    }
}

検証が必要なサービス呼び出しには IValidationDictionary が必要です

    public class MyService
    {        
        public void CreateDebitRequest(int userId, int cardId, decimal Amount, .... , IValidationDictionary validationDictionary)
          {
                if(userId == 0)
                    validationDictionary.AddError("UserId", "UserId cannot be 0");
          }
     }

IValidationDictionaryその後、ソリューションをテスト可能にするMVCには依存しませんが、MVCには依存しません。

を持たないアプリにサービスを実装する必要がある場合は、エラーを保持するために使用されるクラスにインターフェイスをModelStateDictionary実装するだけです。IValidationDictionary

コントローラーの例:

public ActionResult Test(ViewModel viewModel)
{
    var modelStateAdapter = new ModelStateAdapter(ModelState);
    _serviceName.CreateDebitRequest(viewModel.UserId, viewModel.CardId, ... , modelStateAdapter);

    if(ModelState.IsValid)
        return View("Success")

    return View(viewModel);
}

このアプローチの長所:

  • 呼び出しライブラリに依存しない
  • テストのために IValidationDictionary をモックすることができます。

このアプローチの短所:

  • ユーザーに返される検証を行うすべてのメソッドに IValidationDictionary を渡す必要があります。

    または

    検証する各コントローラー アクションで、サービスの検証辞書を初期化する必要があります (プライベート フィールドとして IValidationDictionary を使用することにした場合)。

于 2014-05-09T06:20:48.937 に答える