ASP.NET MVC 2 RCモデルNhibernateおよびドロップダウンリストによるバインディング
-
20-09-2019 - |
質問
データアクセスに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
製品にはすでにカテゴリが割り当てられており、その既存のカテゴリの主要なキーのみが変更されているため、これは非常に理にかなっています。代わりに別のカテゴリインスタンスが割り当てられるはずです。
この問題を処理するためにカスタムモデルバインダーを作成できると思いますが、この作業を行うためのより簡単な方法はありますか?ドメインエンティティを変更してこれを処理するように変更できますか?
解決 3
当時私たちが選んだ解決策は、これに似たものでした。
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;
}
モデルバインダーに、関連性のプロパティを除外し、手動で処理するように指示するだけです。きれいではありませんが、当時働いていました。
他のヒント
次の行を変更することにより、編集ページのコンボボックスに関する同様の問題を解決しました
@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のみが含まれており、変更後、バインダーは従業員のすべての詳細をモデルにロードしました。
LINQからSQLクラスを使用して同様のテクニックを使用していました。これにはカスタムモデルバインダーが必要だとは思いません。 updateModelは、それに渡されている製品クラスを更新する必要があります - カテゴリのサブクラスはそれに接続されていません。ドロップダウンリストフォーヘルパーによって生成されたHTMLを確認してください。要素の名前は何ですか?それは、製品テーブルの外部キーフィールドの名前である必要があります(例:「categoryid」または「ford.categoryid」not "category.id")。 「category.id」の場合 - ドロップダウンリストの最初のパラメーターを「model => model.category」または「model => model.categoryid」(または外部キーフィールドが何であれ)に変更してみてください。これにより、updateModelは、カテゴリクラスIDではなく、製品クラスの外部キーフィールドのみを更新します。