4

データ アクセスに NHibernate を使用する ASP.NET MVC 2 RC アプリケーションのモデル バインドに問題があります。Ruby on Rails の方法でアプリケーションを構築しようとしており、ドメイン エンティティがデータベースからビューまでずっと使用される非常に単純なアーキテクチャを持っています。

アプリケーションには、次の 2 つのクラスで示すことができるドメイン エンティティがいくつかあります。

public class Product {
    ...

    public Category Category { get; set; }      
}

public class Category {
    public int Id { get; set; }
    public string Name { get; set; }
}

編集フォームをレンダリングするビューには、ドロップダウン リストを表示する次のステートメントがあります。

<%= Html.DropDownListFor(model => model.Category.Id, 
       new SelectList(ViewData["categories"] as IList<Category>, "Id", "Name"), 
       "-- Select Category --" ) %>

カテゴリ コレクションを保持するために「型指定されていない」ビュー データを使用することは無視してください。

フォーム ポストを受け取るアクション メソッドは次のようになります。TransactionFilter 属性は NHibernate トランザクション処理を追加し、例外が発生せず、検証が成功した場合にトランザクションをコミットすることに注意してください。

[HttpPost]
[TransactionFilter]
public ActionResult Edit(int id, FormCollection collection) {
    var product = _repository.Load(id);

    // Update the product except the Id
    UpdateModel(product, null, null, new[] {"Id"}, collection);

    if (ModelState.IsValid) {
      return RedirectToAction("Details", new {id});
    }
    return View(product);
}

私の問題は、product.Category.Id がフォームで選択された値で設定されていることです。たとえば、Category.Id = "2" です。デフォルトのモデル バインダーを使用すると、次のタイプの NHibernate 例外が発生します。

identifier of an instance of Name.Space.Entities.Category was altered from 4 to 2

製品にはすでにカテゴリが割り当てられており、その既存のカテゴリの主キーのみが変更されているため、これは非常に理にかなっています。代わりに、別のカテゴリ インスタンスを割り当てる必要があります。

この問題を処理するためにカスタム ModelBinder を作成できると思いますが、これを機能させる簡単な方法はありますか? これを処理するためにドメイン エンティティを変更できますか (また変更する必要がありますか)。

4

3 に答える 3

2

次の行を変更して、編集ページのコンボ ボックスで同様の問題を解決しました。

@Html.DropDownListFor(x => x.Employee.Id, new SelectList(ViewBag.Employees, "Id", "DisplayName"))

@Html.DropDownListFor(x => x.Employee, new SelectList(ViewBag.Employees, "Id", "DisplayName"))

そこで、ブライアンが提案したように「.Id」を削除しました。変更前、モデルには従業員の ID のみが含まれていましたが、変更後、Binder は従業員のすべての詳細もモデルにロードしました。

于 2012-10-03T19:34:25.400 に答える
0

当時私たちが選んだ解決策は、次のようなものでした。

TryUpdateModel(product, null, null, new[] {"Category"}, collection);
int categoryId;
if (int.TryParse(collection["Category.Id"], NumberStyles.Integer, CultureInfo.InvariantCulture, out categoryId) && categoryId > 0) {
    product.Category = _categoryRepository.Load(categoryId);
}
else {
    product.Category = null;
}

モデルバインダーに、関連付けプロパティを除外して手動で処理するように指示するだけです。きれいではありませんが、当時はうまくいきました。

于 2011-06-08T12:32:37.497 に答える
0

以前、Linq to SQL クラスで同様の手法を使用したことがありますが、問題はありませんでした。これにはカスタム ModelBinder は必要ないと思います。UpdateModel は、それにアタッチされた Category サブクラスではなく、渡した Product クラスを更新する必要があります。DropDownListFor ヘルパーによって生成された html を確認します。要素の名前は何ですか? Products テーブルの外部キー フィールドの名前にする必要があります (たとえば、「Category.Id」ではなく「CategoryID」または「Product.CategoryID」)。"Category.Id" の場合 - DropDownListFor の最初のパラメーターを "model => model.Category" または "model => model.CategoryID" (または外部キー フィールドが何であれ) に変更してみてください。

于 2010-01-18T02:37:57.143 に答える