Вопрос

У меня есть упрощенный тестовый сценарий, полезный для ответа на этот вопрос:Продукт может иметь множество Компонентов, Компонент может принадлежать многим Продуктам.EF сгенерировал классы, я сократил их следующим образом:

public partial class Product
{
    public int Id { get; set; }
    public string Name { get; set; }
    public virtual ICollection<Component> Components { get; set; }
}
public partial class Component
{
    public int Id { get; set; }
    public string Name { get; set; }
    public virtual ICollection<Product> Products { get; set; }
}

Создание компонента осуществляется с помощью следующих действий контроллера:

public ActionResult Create(int ProductId)
{
    Product p = db.Products.Find(ProductId);
    Component c = new Component();
    c.Products.Add(p);
    return PartialView(c);
} 

[HttpPost]
public ActionResult Create(Component model)
{
    db.Components.Add(model);
    db.SaveChanges();
}

и представление, возвращаемое методом GET, выглядит следующим образом:

@model Test.Models.Product

<fieldset>
    <legend>Product</legend>
    <div class="display-label">Name</div>
    <div class="display-field">@Model.Name</div>
</fieldset>

@Html.Action("Create", "Component", new {ProductId = Model.Id}) 
<p>
    @Html.ActionLink("Edit", "Edit", new { id=Model.Id }) |
    @Html.ActionLink("Back to List", "Index")
</p>

Из чего видно, что создание компонента выполняется на той же странице с помощью приведенного выше Html.Action - код для этого представления следующий:

@model Test.Models.Component
@using Test.Models

<script type="text/javascript">
    function Success() {
        alert('ok');
    }
    function Failure() {
        alert('err');
    }
</script>
@using (Ajax.BeginForm("Create", "Component", new AjaxOptions
{
    HttpMethod = "Post",
    OnSuccess = "Success",
    OnFailure = "Failure"
}))
{
    <fieldset>
        <legend>Components</legend>

        <div class="editor-label">
            @Html.LabelFor(model => model.Name)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Name)
            @Html.ValidationMessageFor(model => model.Name)
        </div>
        @Html.HiddenFor(x => x.Products.First().Id)
        @Html.HiddenFor(x => x.Products)
        @foreach (Product p in Model.Products)
        {
            @Html.Hidden("Products[0].Id", p.Id)
        }
        @foreach (Product p in Model.Products)
        {
            @Html.Hidden("[0].Id", p.Id)
        }
    </fieldset>
    <input type="submit" value="go" />
}

хорошо.так вот с чем я борюсь:мне нужно model параметр [HttpPost]back для правильного заполнения, т.е.он должен содержать продукт, поскольку я не могу создать новый компонент с нулевым продуктом.Чтобы получить продукт, мне нужно найти его по идентификатору продукта.Я ожидаю, что смогу сделать:

model.Products.Add(db.Products.Find(model.Products.First().Id));

или что-то в этом роде, которое зависит от model получение идентификатора.Это означает, что представление должно поместить идентификатор туда, предположительно в скрытое поле, и, как видно из моего кода представления, я предпринял несколько попыток заполнить это поле, но все они потерпели неудачу.

Обычно я предпочитаю методы *For, поскольку они отвечают за создание правильной номенклатуры.Если бы .Products было в единственном числе (.Product), я мог бы ссылаться на него как x => x.Product.Id и все бы ничего, но так как во множественном числе, я не могу сделать x => x.Products.Id поэтому я попробовал x => x.Products.First().Id который компилируется и выдает правильное значение, но получает имя Id (что неверно, поскольку связыватель модели считает, что это Component.Id и не Component.Products[0].Id.

Моя вторая попытка заключалась в том, чтобы позволить HiddenFor повторять (как я бы сделал с EditorFor):

@Html.HiddenFor(x => x.Products)

но это ничего не дает - я читал, что этот помощник не выполняет итерации.Я пытался x => x.Products.First() но это даже не компилируется.Наконец, я решил отказаться от *For и закодировать имя самостоятельно:

@foreach (Product p in Model.Products)
{
    @Html.Hidden("Products[0].Id", p.Id)

и хотя это выглядит правильно, обратная передача не видит моей ценности (Products.Count == 0).Я видел в некоторых сообщениях, что формат должен выглядеть так [0].Id но это тоже не работает.грр...

Я так понимаю, я мог бы закодировать это так:

@Html.Hidden("ProductId", p.Id)

а затем переобъявите действие моего контроллера следующим образом:

[HttpPost] ActionResult Create(Component model, int ProductId)

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

  • е

п.с.У меня есть проект, который я могу сделать доступным для скачивания, если кому-то интересно.

Это было полезно?

Решение

Вместо того, чтобы писать эти foreach циклы попробуйте использовать шаблоны редактора:

<fieldset>
    <legend>Components</legend>

    <div class="editor-label">
        @Html.LabelFor(model => model.Name)
    </div>

    <div class="editor-field">
        @Html.EditorFor(model => model.Name)
        @Html.ValidationMessageFor(model => model.Name)
    </div>

    @Html.EditorFor(x => x.Products)
</fieldset>

и внутри соответствующего шаблона редактора (~/Views/Shared/EditorTemplates/Product.cshtml)

@model Product
@Html.HiddenFor(x => x.Id)
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top