2

MVC コントローラーのサイズを縮小しようとして、多くのロジックをサービスにリファクタリングしています (ただし、モデルにファクタリングされている場合は、これと同じ問題が発生します)。次のように、ユーザーに表示したい情報を直接設定ViewDataしたり、情報を使用したりすることがよくあります。TempData

var repoUser = new UserRepository();
var foundUser = repoUser.GetUser(userId);
if (foundUser == null) {
    ViewData["ErrorMessage"] = "Could not find user with ID {0}.".FormatWith(userId);
    return View("UsersRegister");
}

もちろん、サービス クラスに移行するとすぐにViewData、 、 、およびやTempDataなどのメソッドに直接アクセスできなくなるため、これを処理するためのベスト プラクティスを見つけようとしています。2 つの解決策が思い浮かびます。View()RedirectToAction()

  1. 次のような、サービスから返されるコントローラーの動作に関するさまざまな情報を含むクラスを作成します。

    public class ResponseInfo {
        public string Name { get; set; }
        public bool DoRedirect { get; set; }
        public object RedirectRouteValues { get; set; }
        public string InfoMessage { get; set; }
        public string ErrorMessage { get; set; }
    }
    
  2. ViewDataサービス メソッドがや などに直接アクセスできるようにしてみてくださいTempData

    public ActionResult ToggleUserAttended(int eventId, int userId, HttpRequestBase request, TempDataDictionary tempData, ViewDataDictionary viewData, Func<string, object, ActionResult> redirectAction) {
        //...
        var repoUser = new UserRepository();
        var foundUser = repoUser.GetUser(userId);
        if (foundUser == null) {
            tempData["ErrorMessage"] = "Could not find user with ID {0}.".FormatWith(userId);
            return redirectAction("UsersRegister", new { id = eventId, returnUrl = request.QueryString["returnUrl"] });
        }
        //...
    }
    

    ...そしてコントローラで:

    return _svcEvent.ToggleUserAttended(123, 234, Request, TempData, ViewData, (name, routeVals) => RedirectToAction(name, routeVals));
    

ResponseInfo番号 1 の場合、コントローラーは、オブジェクトを見て、リダイレクトするかどうか、ビューを表示するかどうか、エラーまたは情報メッセージをTempDataorにプラグインするかどうかなどを決定するために、より多くのロジックを実行する必要がありますViewData。番号 2 は、ワンライナー コントローラーをほとんど許可します。ただし、サービスにコントローラー固有のものを非常に認識させています。ただし、とにかくサービスは常にコントローラーと密接に結びついているので、これは問題ですか? 1つまたは2つがベストプラクティスでしょうか、それともリストにないものでしょうか?

4

1 に答える 1

1

何か誤解しているかもしれませんが、プレゼンテーションに関する情報をサービスに提供したいのは不思議です。IMO、サービスはビジネスロジックであり、プレゼンテーション層に依存すべきではないため、これを行うのは非常に悪い考えです。

あなたが提案したどちらのアプローチも、プレゼンテーションとサービスの境界線を曖昧にしているように見えるので、私は本当に好きではありません。

たとえば、サービスがユーザーを見つけられない場合はサービスをスローし、コントローラーは適切なUIメカニズム(エラーメッセージ、HTTPエラー、またはアプリに適合するもの)を使用してこのエラーを処理する必要があります。同様に、サービスはどのようにしてリダイレクト先を知ることができますか?GUIを認識していますか?他のコンテキスト(API、非Web GUIなど)でどのように使用する予定ですか?

私が通常行うことは、フォーム/パラメーターに基づいてコマンド/ dtoを作成し、それをサービスに提供することです。そうすれば、どのサービスクライアントも同じコマンドを使用できます。データを表示する必要がある場合は、必要なものをサービス/リポジトリに要求し、それをプレゼンテーションフォーム(必要な場合)にマッピングして、ViewData / TempData / Model/Whateverに配置します。

次にいくつかの例を示します(疑似コードとして扱います)。

[HttpPost]
public ActionResult ChangeUserInfo(UserInfoModel model)
{
  var cmd = new ChangeUserInfoCommand(CurrentUserId, model.FirstName, model.LastName);
  try {
      userSvc.UpdateUser(cmd); 
      return RedirectToAction(...);
  }
  catch(XxxxException e) { // e.g. something wrong (business logic)
      TempData["Error"] = "an error occurred: " + e.FriendlyError();
      return RedirectToAction(...); // error action
  }
  catch(SecurityException e) {
      throw new HttpException(404, "NotFound");
  }
}

public ActionResult ShowUsers()
{
  var userInfo = svc.GetUsers().Select(u => { Id = u.id, FullName = u.First + " " + u.Last);
  // or var userInfo = svc.GetUsers().Select(u => Mapper.To<UserDTO>(u));
  return View(userInfo);

  // or without model
  // ViewData["users"] = userInfo;
  // return View();
}

これは実例であることに注意してください-私は通常、まったく異なる方法で行いますが、他の多くのインフラストラクチャがあります(たとえば、このような例外を明示的に処理しない、サービスに1つのメソッドしかない場合がありますExecute(IEnumerable<Command> cmds)、ビューモデルなどとしての匿名クラス)

于 2013-03-19T10:53:51.910 に答える