web-dev-qa-db-ja.com

ASP.Net MVCと状態-リクエスト間で状態を維持する方法

MVCを使い始めたばかりのかなり経験のあるASP.Net開発者として、従来の「サーバーコントロールとイベントハンドラー」のやり方から、より動的なMVCのやり方へと考え方を変えるのに少し苦労していることに気付きました。私はゆっくりそこに着いていると思いますが、MVCの「魔法」が時々私を投げ出します。

私の現在のシナリオは、ユーザーがローカルファイルを参照してサーバーにアップロードできるWebページを作成し、操作するファイルのリストができるまでこれを繰り返すことです。ファイルリスト(ページのグリッドに表示されます)に満足したら、ボタンをクリックしてファイルを処理し、データベースに保存されるデータを抽出します。

最後の部分はそれほど重要ではありません。現在、ファイルのリストを作成し、リクエスト間でそのリストを保持するのと同じくらい些細なことに苦労しています。従来のアプローチでは、これは非常に簡単でした。データはViewStateに保持されていました。しかし、MVCでは、コントローラーとビューの間でデータを渡す必要があり、これがどのように機能するのか完全にはわかりません。

問題を説明するために、これをコーディングするというやや不完全な試みを投稿した方が良いと思います。

ファイルリストのデータを保持するために、基本的にファイルの型付きリストであるビューモデルと、追加のメタデータを作成しました。

public class ImportDataViewModel
{
    public ImportDataViewModel()
    {
        Files = new List<ImportDataFile>();
    }

    public List<ImportDataFile> Files { get; set; }
...

ビューには、ファイルを参照してアップロードするためのフォームがあります。

<form action="AddImportFile" method="post" enctype="multipart/form-data">
  <label for="file">
    Filename:</label>
  <input type="file" name="file" id="file" />
  <input type="submit" />
  </form>

ビューはモデルとしてviewmodelを使用しています:

@model MHP.ViewModels.ImportDataViewModel

これにより、ファイルがアクションに送信されます。

public ActionResult AddImportFile(HttpPostedFileBase file, ImportDataViewModel importData)
    {

        if (file.ContentLength > 0)
        {
            ImportDataFile idFile = new ImportDataFile { File = file };
            importData.Files.Add(idFile);
        }

        return View("DataImport", importData);
    }

このアクションは、ファイルのリストを含むviewmodelインスタンスとともに、DataImportページのビューを返します。

これは特定のポイントまでうまく機能し、ファイルを参照してアップロードでき、アクション内のビューモデルデータを確認できます。また、ビュー内にブレークポイントを置いて「this.Model」をデバッグすると、すべてがいいよ.

しかし、その後、AddImportFileアクション内にブレークポイントを配置するときに別のファイルをアップロードしようとすると、importDataパラメーターが空になります。したがって、ビューは明らかに、モデルの現在のインスタンスをアクションに渡していません。

私が経験したMVCサンプルでは、​​モデルインスタンスはパラメーターとしてアクションメソッドに「魔法のように」渡されます。

私の本当の問題はMVCの私の限られた理解であり、おそらくこれに対する非常に簡単な解決策があると思います。とにかく、誰かが私を正しい方向に向けてくれたらとても感謝しています。

51
TMan

私が質問を投稿してからしばらく経ちましたが、MVCについての私の小さな経験と知識によって色付けされました。それでも、私はいくつかの非常に有用な情報を受け取りました。その結果、最終的には解決策を見つけ、MVCの洞察を得ました。

そもそも私を驚かせたのは、次のように、パラメーターとして強く型付けされたオブジェクトを持つコントローラーを使用できることです。

public ActionResult DoSomething(MyClass myObject)...

このオブジェクトは同じコントローラーから発生しました:

...
return View(myObject);
...

これにより、オブジェクトはこれら2つのステップ全体に渡って存在し、ビューに送信して何かを実行し、「魔法のように」再びコントローラーに戻すことができると期待できます。

モデルのバインディングについて読んだ後、これはもちろんそうではないことを理解しました。ビューは完全に無効で静的であり、情報をどこかに保存しない限り消えます。

クライアントからファイルを選択してアップロードし、表示するこれらのファイルのリストを作成するという問題に戻って、MVCのリクエスト間で情報を保存する一般的な方法が3つあることに気付きました。

  1. ビューのフォームフィールドに情報を保存し、後でコントローラーにポストバックできます。
  2. 何らかの種類のストレージに永続化できます。ファイルまたはデータベース
  3. リクエスト全体に存在するオブジェクトにアクセスすることで、サーバーメモリに保存できます。セッション変数

私の場合、基本的には2種類の情報を保持する必要がありました。1。ファイルメタデータ(ファイル名、ファイルサイズなど)2.ファイルの内容

"by-the-book"アプローチは、おそらくフォームフィールドにメタデータを保存し、ファイルの内容をファイルまたはデータベースに保存することです。しかし、別の方法もあります。私のファイルは非常に小さく、そのうちのいくつかしか存在せず、このソリューションがサーバーファームなどにデプロイされることは決してないことを知っているため、セッション変数の#3オプションを調べたいと思いました。また、ファイルはセッションを超えて持続するのも面白くありません。処理されて破棄されるため、dbに保存したくありませんでした。

この優れた記事を読んだ後: Dynamicsを使用したASP.NETセッションデータへのアクセス

私は確信しました。この記事で説明されているように、セッションバッグクラスを作成しただけで、コントローラーで次のことができました。

    [HttpPost]
    public ActionResult AddImportFile(HttpPostedFileBase file)
    {

        ImportDataViewModel importData = SessionBag.Current.ImportData;
        if (importData == null) importData = new ImportDataViewModel();

        if (file == null)
            return RedirectToAction("DataImport");

        if (file.ContentLength > 0)
        {
            ImportDataFile idFile = new ImportDataFile { File = file };
            importData.Files.Add(idFile);
        }

        SessionBag.Current.ImportData = importData;

        return RedirectToAction("DataImport");
    }

私は、ほとんどの場合、これが悪い解決策であることを十分に認識しています。しかし、数キロバイトのサーバーメモリでファイルが占有し、すべてが単純であるため、私にとって非常にうまく機能したと思います。

SessionBagを使用することの追加のボーナスは、ユーザーが別のメニュー項目を入力してから戻った場合、ファイルのリストがまだ存在することです。これは当てはまりません。フォームフィールド/ファイルストレージオプションを選択するとき。

最後に、使いやすさを考えると、SessionBagは非常に簡単に悪用できることがわかりました。しかし、それを目的、つまりセッションデータに使用すると、強力なツールになり得ると思います。

48
TMan

アップロードについて

1)AJAXアップローダーを使用して、サーバーに送信される前に複数のファイルを選択できるようにする)を検討してください。このBlueImp Jquery = AJAXファイルアップローダーは、非常に優れたAPIで非常に素晴らしいです。 Blueimp Jqueryファイルアップロード 。ユーザーが複数のファイルをドラッグアンドドロップまたは複数選択してファイルを編集できるようにします。注文、包含/除外など。その後、彼らが満足しているとき、彼らはあなたのコントローラーまたはサーバー側で処理するためのアップロードハンドラーに送信するためにアップロードを押すことができます。

2)すべてのアップロードをデータベースに保持することができますが、ページ全体をリロードし、リスト効果を達成するために追加のビューモデルとカミソリコードを記述します。これはおそらく反応しないでしょう...

状態の保持についてWebForms/MVC

リクエスト間で状態を保持することは、やや魔術とブードゥー教です。 ASP.NET MVCを使用する場合は、Webアプリケーションが要求と応答を使用して通信することを理解してください。だから、ウェブをステートレスとして受け入れて、そこから発展してください!モデルがコントローラーを介してポストされると、コントローラー内の変数とともにgoneになります!ただし、削除する前に、後で取得するためにその内容をデータベースに保存できます。

Webアプリケーションは、デスクトップアプリケーションのような真の状態を維持できません。 ajaxフレームワークには多くの方法があり、HTTP環境で状態をシミュレートするために人々が使用するブードゥー教のツールもいくつかあります。状態のシミュレーションは、実際にはステートフルネスの誤った模倣にすぎません。 ASP.NET Web Formsは、HTTPのステートレスな性質を開発者から隠すことで、できる限り状態をシミュレートしようとします。独自のAJAXコードをWebフォームマークアップコードおよび独自のAjaxフレームワークと連携して使用しようとすると、多くの頭痛の種になります。

MVCを学んでいて本当にうれしいです

冗談はさておき、MVC/HTTP/Statelessnessメンタリティを取得すれば、パターンをRuby on Rails、SpringMVC(Java)、= Django(python)、CakePHPなど...この知識の簡単な移転は、はるかに優れた開発者になり、Ajaxを本当に上手く活用するのに役立ちます。

MVC 3を学んでいて本当にうれしい(-_- ')はさみを使って赤ちゃんの靴下を編んでいるように感じました。 1つの簡単な間違った動き、すべてが壊れました。 PHPで開発しているように感じたので、汗をかいて出てきて、何が何の行で起こったのか本当にわかりません。デバッグと更新はほとんど不可能でした。

11
Max Alexander