Привязка связанных объектов модели ASP.NET MVC на той же странице

Эта проблема уже несколько часов сводит меня с ума ...

В моем домене у меня есть 2 объекта, которые связаны друг с другом Sku и Item. В каждом артикуле может быть много предметов.

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; }
}

Я создаю страницу, которая позволит конечному пользователю одновременно обновлять некоторые поля в артикуле и некоторые поля для каждого элемента.

<% 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 ...
    }

Это работает, однако я заметил, что он совершает две поездки через DefaultModelBinder для каждого Item в дополнение к одной поездке для Sku. Когда Sku привязан, вызывается установщик для Items, и связующее даже передает гидратированную Items коллекцию с правильными значениями. Однако после вызова items.Assign Items.Count будет 0. Вот почему мне нужно переназначить элементы в коде контроллера. Я ожидал, что переплетчик перенесет предметы в коллекцию Items. Это должно исключить дополнительное отключение для каждого элемента, поскольку параметр items в моем методе контроллера может быть удален. Почему это не работает?


person Jeremy Durnell    schedule 13.08.2009    source источник
comment
Я не уверен, понимаю ли я, в чем ваша проблема, но проверьте эту статью и, что более важно, комментарии от Фила Хаака haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx   -  person Eduardo Molteni    schedule 16.09.2009


Ответы (3)


Для этого вам может потребоваться создать настраиваемую подшивку модели?

person jeef3    schedule 14.08.2009
comment
В конце концов я остановился на индивидуальном переплетчике. - person Jeremy Durnell; 19.11.2009

Надеюсь, я правильно понимаю вашу проблему ...

Вместо того, чтобы определять действие Сохранить с двумя параметрами, пробовали ли вы просто определить его с помощью одного параметра типа Sku?

Затем вы захотите переопределить элементы управления HTML, как в следующем примере ...

<%=
    Html.TextBox
    (
        string.Format("sku.Items[{0}].{1}", i, "ItemId"), 
        Model.Items[i].ItemId, 
        new { @readonly = "readonly", onfocus = "this.blur();" }
    )
%>


Таким образом, вы заполняете элементы непосредственно в самом объекте Sku.


Еще одно возможное решение - добавить к классу Item дополнительное поле, например ...

Int32 SkuId { get; set; }

Таким образом, вы можете определить дополнительное скрытое поле для каждого элемента в вашем представлении, которое будет автоматически привязано к SkuId каждого элемента обратно в контроллере.

<%=
    Html.Hidden
    (
        string.Format("sku.Items[{0}].{1}", i, "SkuId"), 
        Model.Items[i].SkuId
    )
%>

Затем вы можете просто обновить свою коллекцию элементов независимо от вашего объекта Sku. Независимо от того, какой путь вы выберете, две коллекции должны явно указать Linq to SQL все равно обновить номер SKU и элементы.

Вы также можете определить свой собственный класс связующего, но в данном случае это, вероятно, больше работы, чем того стоит. Просто следуйте соглашениям ASP.NET MVC, и я думаю, вы сможете найти что-то, что будет работать, не чувствуя себя взломанным.

person Justin Holzer    schedule 25.09.2009

У меня была аналогичная проблема, когда EntitySets не были привязаны должным образом в моей ViewModel.

Я создал еще одно свойство под названием Mvc [YourEntitySetPropertyName] в виде общего списка, который обернут вокруг частных полей EntitySet:

public List<InvoiceLineItem> MvcInvoiceLineItemList
    {
        get { return _invoiceLineItemList.ToList(); }
        set { _invoiceLineItemList.AddRange(value); }
    }

Затем я использовал это вместо свойства EntitySet в своей разметке представления.

Нет необходимости передавать элементы в сигнатуре метода вашего контроллера - просто передайте Sku в этот момент:

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Save(Sku sku)
{
    if (sku != null)
    {
       // save Sku to repository ...
       // return Details view ...
    }

}
person CitizenBane    schedule 01.10.2009