ASP.NET MVCモデルバインディング関連エンティティが同じページにあります
-
16-09-2019 - |
質問
この問題は今数時間私を夢中にさせてきました...
私のドメインには、お互いに関連する2つのエンティティがあります Sku
と Item
. 。各SKUには多くのアイテムがあります。
public class Sku
{
private readonly EntitySet<Item> items;
public Sku()
{
items = new EntitySet<Item>(AttachItems, DetachItems);
}
public int SkuId { get; set; }
public string LongDescription { get; set; }
public EntitySet<Item> Items
{
get { return items; }
set{ items.Assign(value);}
}
private void AttachItems(Item entity)
{
entity.Sku = this;
}
private static void DetachItems(Item entity)
{
entity.Sku = null;
}
}
public class Item
{
public Sku Sku { get; set; }
public int ItemId { get; set; }
public string Category { get; set; }
public string Description { get; set; }
}
エンドユーザーがSKUの一部のフィールドと各アイテムの一部のフィールドを同時に更新できるようにするページを構築しています。
<% using (Html.BeginForm("Save", "Merchant", FormMethod.Post,
new { enctype = "multipart/form-data" })) { %>
<fieldset>
<legend>Sku</legend>
<p><label for="SkuId">SkuId:</label>
<%= Html.TextBox("SkuId", Model.SkuId,
new{@readonly="readonly",onfocus="this.blur();"}) %></p>
<p><label for="LongDescription">LongDescription:</label>
<%= Html.TextBox("LongDescription", Model.LongDescription) %></p>
</fieldset>
<% for (int i = 0; i < Model.Items.Count; i++) { %>
<fieldset>
<legend>Item</legend>
<p><label for="ItemId">ItemId:</label>
<%= Html.TextBox(string.Format("items[{0}].{1}", i, "ItemId"),
Model.Items[i].ItemId,
new { @readonly = "readonly", onfocus = "this.blur();" })%></p>
<p><label for="Category">Category:</label>
<%= Html.TextBox(string.Format("items[{0}].{1}", i, "Category"),
Model.Items[i].Category)%></p>
<p><label for="Description">Description:</label>
<%= Html.TextBox(string.Format("items[{0}].{1}", i, "Description"),
Model.Items[i].Description)%></p>
</fieldset>
<%} // for-loop %>
<p><input type="submit" value="Save" /></p>
<%} // form %>
両方を受け入れることで機能するコントローラーコードがいくつかあります Sku
と EntitySet
の Item
そして、を割り当てます Items
に Sku
.
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Save(Sku sku, EntitySet<Item> items)
{
if (sku != null)
{
if (items != null)
{
sku.Items.Assign(items);
}
}
// save Sku to repository ...
// return Details view ...
}
これは機能しますが、私はそれが2回の旅行をしていることに気づきました DefaultModelBinder
それぞれのため Item
のための1つの旅行に加えて Sku
. 。いつ Sku
バインドされています、セッターです Items
呼び出され、バインダーは水分補給さえ通過します Items
正しい値のコレクション。ただし、呼び出しの後 items.Assign
, Items.Count
は 0
. 。これが、コントローラーコードのアイテムを再割り当てする必要がある理由です。私はアイテムがに転送されることを期待していました Items
バインダーによるコレクション。これにより、アイテムごとの余分な旅行が排除されるはずです。 items
コントローラーメソッドのパラメーターを削除できます。なぜこれが機能しないのですか?
解決
これのためにカスタムモデルバインダーを作成する必要があるかもしれませんか?
他のヒント
うまくいけば、私はあなたの問題を正しく理解しています...
あなたを定義するのではなく 保存 2つのパラメーターを使用したアクション、タイプの単一のパラメーターで定義してみましたか Sku?
次に、次の例と同様のアイテムHTMLコントロールを再定義する必要があります...
<%=
Html.TextBox
(
string.Format("sku.Items[{0}].{1}", i, "ItemId"),
Model.Items[i].ItemId,
new { @readonly = "readonly", onfocus = "this.blur();" }
)
%>
これにより、SKUオブジェクト自体にアイテムを直接浸透させています。
別の潜在的な解決策は、...などの...
Int32 SkuId { get; set; }
このようにして、コントローラーに戻って各アイテムのSKUIDに自動するように、ビュー内の各アイテムの追加の非表示フィールドを定義できます。
<%=
Html.Hidden
(
string.Format("sku.Items[{0}].{1}", i, "SkuId"),
Model.Items[i].SkuId
)
%>
その後、SKUオブジェクトとは独立してアイテムコレクションを更新することができます。どちらの道に関係なく、2つのコレクションは、SKUとアイテムを更新するためにLINQをSQLに明示的に指示する必要があります。
独自のバインダークラスを定義することもできますが、この場合、それはおそらくそれが価値があるよりも多くの仕事です。 ASP.NET MVCコンベンションに従ってください。ハッキングのように感じずに機能するものを見つけることができるはずです。
エンティティセットが私のviewmodelで適切にバインドされていないという同様の問題がありました。
私がしたことは、エンティティセットのプライベートフィールドに包まれた一般的なリストとして、MVC [YourEntitySetPropertyName]と呼ばれる別のプロパティを作成することでした。
public List<InvoiceLineItem> MvcInvoiceLineItemList
{
get { return _invoiceLineItemList.ToList(); }
set { _invoiceLineItemList.AddRange(value); }
}
次に、ビューマークアップでエンティティセットプロパティの代わりにそれを使用しました。
コントローラーメソッドの署名にアイテムを渡す必要はありません。その時点でSKUを渡すだけです。
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Save(Sku sku)
{
if (sku != null)
{
// save Sku to repository ...
// return Details view ...
}
}