240

この質問から、ビューが表示しようとしているモデルをより正確に反映するViewModelをコントローラーに作成させることは理にかなっているように見えますが、いくつかの規則に興味があります (MVC パターンは初めてです) 、まだ明らかでない場合)。

基本的に、次の質問がありました。

  1. 私は通常、1 つのクラス/ファイルを持ちたいと思っています。コントローラーからビューにデータを渡すためにのみ作成されている場合、これはViewModelで意味がありますか?
  2. ViewModelが独自のファイルに属し、ディレクトリ/プロジェクト構造を使用して物事を分離している場合、ViewModel ファイルはどこに属しますか? コントローラーのディレクトリに?

今のところ基本的にはそれだけです。さらにいくつか質問があるかもしれませんが、これはこの 1 時間ほど私を悩ませてきました。他の場所で一貫したガイダンスを見つけることができるようです。

編集: CodePlex のサンプルNerdDinner アプリを見ると、ViewModels がControllersの一部であるように見えますが、それでも独自のファイルに含まれていないことに不快感を覚えます。

4

11 に答える 11

213

ビューごとに「ViewModel」と呼ばれるものを作成します。MVC Web プロジェクトの ViewModels というフォルダーにそれらを配置しました。それらが表すコントローラーとアクション (またはビュー) にちなんで名前を付けます。したがって、Membership コントローラーの SignUp ビューにデータを渡す必要がある場合は、MembershipSignUpViewModel.cs クラスを作成し、それを ViewModels フォルダーに配置します。

次に、必要なプロパティとメソッドを追加して、コントローラーからビューへのデータ転送を容易にします。Automapper を使用して ViewModel から Domain Model に移動し、必要に応じて再び戻ってきます。

これは、他の ViewModel のタイプのプロパティを含む複合 ViewModel にも適しています。たとえば、メンバーシップ コントローラーのインデックス ページに 5 つのウィジェットがあり、部分ビューごとに ViewModel を作成した場合、Index アクションから部分ビューにデータを渡すにはどうすればよいでしょうか。タイプ MyPartialViewModel の MembershipIndexViewModel にプロパティを追加し、パーシャルをレンダリングするときに Model.MyPartialViewModel に渡します。

このようにすると、Index ビューをまったく変更せずに、部分的な ViewModel プロパティを調整できます。Model.MyPartialViewModel を渡すだけなので、パーシャル ViewModel にプロパティを追加するだけで何かを修正するために、パーシャルのチェーン全体を調べなければならない可能性は低くなります。

また、名前空間「MyProject.Web.ViewModels」を web.config に追加して、各ビューに明示的なインポート ステートメントを追加しなくても、どのビューでもそれらを参照できるようにします。少しだけきれいになります。

于 2009-03-29T05:55:07.943 に答える
129

クラスをカテゴリ (コントローラー、ビューモデル、フィルターなど) で分けるのはナンセンスです。

Web サイトのホーム セクション (/) のコードを記述する場合は、Home という名前のフォルダーを作成し、そこに HomeController、IndexViewModel、AboutViewModel など、およびホーム アクションで使用されるすべての関連クラスを配置します。

ApplicationController などの共有クラスがある場合は、それをプロジェクトのルートに配置できます。

関連するもの (HomeController、IndexViewModel) を分離し、まったく関係のないもの (HomeController、AccountController) をまとめるのはなぜですか?


このトピックに関するブログ記事を書きました。

于 2009-10-06T23:07:42.260 に答える
21

私は自分のアプリケーション クラスを "Core" (または別のクラス ライブラリ) という名前のサブ フォルダーに保持し、KIGGサンプル アプリケーションと同じメソッドを使用しますが、アプリケーションをより DRY にするために若干の変更を加えています。

/Core/ViewData/ に BaseViewData クラスを作成し、サイト全体の共通プロパティを格納します。

この後、同じフォルダー内にすべてのビュー ViewData クラスも作成します。これらは BaseViewData から派生し、ビュー固有のプロパティを持ちます。

次に、すべてのコントローラーの派生元である ApplicationController を作成します。ApplicationController には、次のような一般的な GetViewData メソッドがあります。

protected T GetViewData<T>() where T : BaseViewData, new()
    {
        var viewData = new T
        {
           Property1 = "value1",
           Property2 = this.Method() // in the ApplicationController
        };
        return viewData;
    }

最後に、コントローラ アクションで、ViewData モデルを構築するために次のことを行います。

public ActionResult Index(int? id)
    {
        var viewData = this.GetViewData<PageViewData>();
        viewData.Page = this.DataContext.getPage(id); // ApplicationController
        ViewData.Model = viewData;
        return View();
    }

これは非常にうまく機能し、ビューをきれいに保ち、コントローラーをスリムに保つことができると思います。

于 2009-03-25T21:53:06.503 に答える
13

モデルを保持するのに適した場所はありません。プロジェクトが大きく、ViewModel (データ転送オブジェクト) が多数ある場合は、モデルを別のアセンブリに保持できます。また、サイト プロジェクトの別のフォルダーに保存することもできます。たとえば、Oxiteでは、さまざまなクラスも多数含まれている Oxite プロジェクトに配置されます。Oxite のコントローラーは別のプロジェクトに移動され、ビューも別のプロジェクトに移動されます。CodeCampServer
では、ViewModel は *Form という名前で、Models フォルダーの UI プロジェクトに配置されます。MvcPressプロジェクトでは、それらは Data プロジェクトに配置されます。 このプロジェクトには、データベースを操作するためのすべてのコードと、もう少し多くのコードが含まれています (ただし、このアプローチはお勧めしませんでした。これは単なるサンプル用です)。

このように、多くの視点があることがわかります。私は通常、ViewModel (DTO オブジェクト) をサイト プロジェクトに保持します。ただし、モデルが 10 個を超える場合は、それらを別のアセンブリに移動することを好みます。通常、この場合、コントローラーも別のアセンブリに移動します。
もう 1 つの問題は、すべてのデータをモデルから ViewModel に簡単にマップする方法です。AutoMapperライブラリをご覧になることをお勧めします。私はそれがとても好きです、それは私のためにすべての汚い仕事をします。また、 SharpArchitectureプロジェクト
も参照することをお勧めします。プロジェクトに非常に優れたアーキテクチャを提供し、多くの優れたフレームワークとガイダンス、および優れたコミュニティが含まれています。

于 2009-03-25T21:47:59.510 に答える
13

ViewModel クラスは、クラスのインスタンスによって表される複数のデータを、View に渡すことができる 1 つの管理しやすいオブジェクトにカプセル化するために存在します。

ViewModel クラスを独自のディレクトリ内の独自のファイルに配置することは理にかなっています。私のプロジェクトには、ViewModels という Models フォルダーのサブフォルダーがあります。それは私のViewModels(例えばProductViewModel.cs)が住んでいる場所です。

于 2009-03-19T22:34:00.700 に答える
6

これが私のベスト プラクティスのコード スニペットです。

    public class UserController : Controller
    {
        private readonly IUserService userService;
        private readonly IBuilder<User, UserCreateInput> createBuilder;
        private readonly IBuilder<User, UserEditInput> editBuilder;

        public UserController(IUserService userService, IBuilder<User, UserCreateInput> createBuilder, IBuilder<User, UserEditInput> editBuilder)
        {
            this.userService = userService;
            this.editBuilder = editBuilder;
            this.createBuilder = createBuilder;
        }

        public ActionResult Index(int? page)
        {
            return View(userService.GetPage(page ?? 1, 5));
        }

        public ActionResult Create()
        {
            return View(createBuilder.BuildInput(new User()));
        }

        [HttpPost]
        public ActionResult Create(UserCreateInput input)
        {
            if (input.Roles == null) ModelState.AddModelError("roles", "selectati macar un rol");

            if (!ModelState.IsValid)
                return View(createBuilder.RebuildInput(input));

            userService.Create(createBuilder.BuilEntity(input));
            return RedirectToAction("Index");
        }

        public ActionResult Edit(long id)
        {
            return View(editBuilder.BuildInput(userService.GetFull(id)));
        }

        [HttpPost]
        public ActionResult Edit(UserEditInput input)
        {           
            if (!ModelState.IsValid)
                return View(editBuilder.RebuildInput(input));

            userService.Save(editBuilder.BuilEntity(input));
            return RedirectToAction("Index");
        }
}
于 2010-07-08T06:31:38.060 に答える
5

すべての ViewModel を Models フォルダーに配置します (すべてのビジネス ロジックは別の ServiceLayer プロジェクトにあります)。

于 2009-09-23T04:07:18.937 に答える
4

個人的には、ViewModel が簡単ではない場合は、別のクラスを使用することをお勧めします。

複数のビュー モデルがある場合は、少なくともディレクトリに分割することをお勧めします。ビュー モデルが後で共有される場合、ディレクトリに含まれる名前空間により、新しいアセンブリへの移動が容易になります。

于 2009-03-19T22:21:48.560 に答える
2

私たちの場合、ビューとは別のプロジェクトにモデルとコントローラーがあります。

経験則として、ViewData["..."] のほとんどを ViewModel に移動して回避しようとしたため、キャストやマジック ストリングを回避できました。これは良いことです。

ViewModel には、リストのページネーション情報やパンくずリストとタイトルを描画するためのページのヘッダー情報など、いくつかの一般的なプロパティも保持されています。現時点では、基本クラスはあまりにも多くの情報を保持していると私は考えています。基本ビュー モデルの 99% のページに最も基本的で必要な情報、次にリストとモデルのモデルの 3 つの部分に分けることができます。そのシナリオの特定のデータを保持し、基本シナリオから継承するフォームの場合。

最後に、特定の情報を処理するために、各エンティティのビュー モデルを実装します。

于 2009-05-21T15:49:20.257 に答える
0

コントローラーのコード:

    [HttpGet]
        public ActionResult EntryEdit(int? entryId)
        {
            ViewData["BodyClass"] = "page-entryEdit";
            EntryEditViewModel viewMode = new EntryEditViewModel(entryId);
            return View(viewMode);
        }

    [HttpPost]
    public ActionResult EntryEdit(Entry entry)
    {
        ViewData["BodyClass"] = "page-entryEdit";            

        #region save

        if (ModelState.IsValid)
        {
            if (EntryManager.Update(entry) == 1)
            {
                return RedirectToAction("EntryEditSuccess", "Dictionary");
            }
            else
            {
                return RedirectToAction("EntryEditFailed", "Dictionary");
            }
        }
        else
        {
            EntryEditViewModel viewModel = new EntryEditViewModel(entry);
            return View(viewModel);
        }

        #endregion
    }

ビューモデルのコード:

public class EntryEditViewModel
    {
        #region Private Variables for Properties

        private Entry _entry = new Entry();
        private StatusList _statusList = new StatusList();        

        #endregion

        #region Public Properties

        public Entry Entry
        {
            get { return _entry; }
            set { _entry = value; }
        }

        public StatusList StatusList
        {
            get { return _statusList; }
        }

        #endregion

        #region constructor(s)

        /// <summary>
        /// for Get action
        /// </summary>
        /// <param name="entryId"></param>
        public EntryEditViewModel(int? entryId)
        {
            this.Entry = EntryManager.GetDetail(entryId.Value);                 
        }

        /// <summary>
        /// for Post action
        /// </summary>
        /// <param name="entry"></param>
        public EntryEditViewModel(Entry entry)
        {
            this.Entry = entry;
        }

        #endregion       
    }

プロジェクト:

  • DevJet.Web (ASP.NET MVC Web プロジェクト)

  • DevJet.Web.App.Dictionary (別のクラス ライブラリ プロジェクト)

    このプロジェクトでは、次のようなフォルダーを作成しました: DAL、BLL、BO、VM (ビュー モデルのフォルダー)

于 2010-01-18T04:10:03.820 に答える