Как сделать форму бритвы для сложной модели EF - PullRequest
0 голосов
/ 06 сентября 2018

Я начинаю задумываться, возможно ли то, что я собираюсь спросить ... Но это ДОЛЖНО быть.

Примеры MS очень не соответствуют моим ожиданиям, поэтому, возможно, EF не спроектирован так, как я думал. (BTW Scaffold для CRUD для каждой отдельной таблицы прекрасно работает. Но он только создает уровень таблицы. Я хочу что-то, что управляет отношением как объектом.)

public class ContactPhone
{
    public ContactPhone(ContactPhone contactPhone)
    {
        Id = contactPhone.Id;
        ContactId = contactPhone.ContactId;
        ContactPhoneTypeId = contactPhone.ContactPhoneTypeId;
        Number = contactPhone.Number;
    }

    //private ContactPhone contactPhone;

    public ContactPhone()
    {
    }

    //public ContactPhone(ContactPhone contactPhone)
    //{
    //    this.contactPhone = contactPhone;
    //}

    public int Id { get; set; }

    public int ContactId { get; set; }
    public virtual Contact Contact { get; set; }

    public int ContactPhoneTypeId { get; set; }
    public virtual ContactPhoneType ContactPhoneType { get; set; }

    public string Number { get; set; }
}


public class Contact
{
   public  Contact()
    {
        Phones = new List<ContactPhone>();
    }
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Email { get; set; }

    public int LocationAddressId { get; set; }
    public LocationAddress LocationAddress { get; set; }

    public int UserId { get; set; }
    [ForeignKey("UserId")]
    public virtual SCPI_site_User User { get; set; }

    //public ICollection

    public List<ContactPhone> Phones { get; set; }
}

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

            public class CreateModel : PhoneTypesPageModel
            {
                private readonly SCPI_Site.Areas.SCPI_SiteContext _context;

                public CreateModel(SCPI_Site.Areas.SCPI_SiteContext context)
                {
                    _context = context;
                }
                [TempData]
                public string Message { get; set; }

                [BindProperty]
                public Contact Contact { get; set; }

                [BindProperty]
                public ContactPhone ContactPhone { get; set; }

                //public bool HasPhones => Contact.Phones.Count > 0;

                public IActionResult OnGet()
                {
                    PopulatePhoneTypeDropDownList(_context);
                    int userID = User.Identity.GetUserId<int>();

                    if (Contact == null)
                    {
                        Contact = new Contact();
                    }
                    if (ContactPhone == null)
                    {
                        Contact = new Contact();
                    }

                    Contact.UserId = userID;
                    ViewData["UserId"] = userID;          
                    return Page();
                }

                public async Task<IActionResult> OnPostAsync(string submit)
                {
                    int userID = User.Identity.GetUserId<int>();
                    Contact.UserId = userID;        

                    switch (submit)
                    {
                        case "AddPhone":
                            // (failed attempt) _context.ContactPhone.Add(ContactPhone);
                            Contact.Phones.Add(new ContactPhone(ContactPhone));
                            ContactPhone.Number = "";
                            return Page();


                        default:
                            //if (!ModelState.IsValid)
                            //{
                            //    return Page();
                            //}

                            _context.Contact.Add(Contact);
                            await _context.SaveChangesAsync();
                            Message = "Contact Created!";

                            return RedirectToPage("./Index");
                    }

                }
            }
        }

HTML:

    @page

    @model SCPI_Site.Areas.ContactModel.Pages.Contacts.CreateModel

    @{
        //Layout = "~/Views/Shared/_AdminLayout.cshtml";
        ViewData["Title"] = "Create";
    }
    <div class="col">
        <h4>Contact</h4>
        <hr />
        <div class="row">
            <div class="col-md-4">
                <form method="post">
                    <div asp-validation-summary="ModelOnly" class="text-danger"></div>
                    <input type="hidden" asp-for="Contact.UserId" />
                    <div class="row">
                        <div class="form-group col-md-6">
                            <label asp-for="Contact.FirstName" class="control-label"></label>
                            <input asp-for="Contact.FirstName" class="form-control" />
                            <span asp-validation-for="Contact.FirstName" class="text-danger"></span>
                        </div>
                        <div class="form-group col-md-6">
                            <label asp-for="Contact.LastName" class="control-label"></label>
                            <input asp-for="Contact.LastName" class="form-control" />
                            <span asp-validation-for="Contact.LastName" class="text-danger"></span>
                        </div>
                    </div>

                    <div class="form-group">
                        <label asp-for="Contact.Email" class="control-label"></label>
                        <input asp-for="Contact.Email" class="form-control" />
                        <span asp-validation-for="Contact.Email" class="text-danger"></span>
                    </div>

                    <div class="form-group">
                        <label asp-for="Contact.LocationAddress.Address" class="control-label"></label>
                        <input asp-for="Contact.LocationAddress.Address" class="form-control" />
                        <span asp-validation-for="Contact.LocationAddress.Address" class="text-danger"></span>
                    </div>
                    <div class="row">
                        <div class="form-group col-md-4">
                            <label asp-for="Contact.LocationAddress.City" class="control-label"></label>
                            <input asp-for="Contact.LocationAddress.City" class="form-control" />
                            <span asp-validation-for="Contact.LocationAddress.City" class="text-danger"></span>
                        </div>
                        <div class="form-group col-md-4">
                            <label asp-for="Contact.LocationAddress.State" class="control-label"></label>
                            <input asp-for="Contact.LocationAddress.State" class="form-control" />
                            <span asp-validation-for="Contact.LocationAddress.State" class="text-danger"></span>
                        </div>
                        <div class="form-group col-md-4">
                            <label asp-for="Contact.LocationAddress.PostalCode" class="control-label"></label>
                            <input asp-for="Contact.LocationAddress.PostalCode" class="form-control" />
                            <span asp-validation-for="Contact.LocationAddress.PostalCode" class="text-danger"></span>
                        </div>
                    </div>

                    @if (Model.Contact.Phones != null)
                    {
                        <hr />
                        <table class="table">
                            <thead>
                                <tr>
                                    <th>
                                        Type
                                        @*@Html.DisplayNameFor(model => model.Contact.Phones[0].ContactPhoneType)*@
                                    </th>
                                    <th>
                                        Number
                                        @*@Html.DisplayNameFor(model => model.Contact.Phones[0].Number)*@
                                    </th>
                                    <th></th>
                                </tr>
                            </thead>
                            <tbody>
                                @if (Model.Contact.Phones != null)
                                {
                                    @foreach (var item in Model.Contact.Phones)
                                    {
                                        <tr>
                                            <td>
                                                @Html.DisplayFor(modelItem => item.ContactPhoneType.Id)
                                            </td>
                                            <td>
                                                @Html.DisplayFor(modelItem => item.Number)
                                            </td>
                                            <td>
                                                <a asp-page="../ContactPhones/Edit" asp-route-id="@item.Id">Edit</a> |
                                                <a asp-page="../ContactPhones/Details" asp-route-id="@item.Id">Details</a> |
                                                <a asp-page="../ContactPhones/Delete" asp-route-id="@item.Id">Delete</a>
                                            </td>
                                        </tr>
                                    }
                                }
                            </tbody>
                        </table>
                    }
                    <div class="form-group">
                        <input type="submit" value="Create" class="btn btn-default" />
                    </div>

                    <div id="pay-invoice" class="card">
                        <div class="card-body">
                            <div class="card-title">
                                <input type="hidden" id="x_card_num" name="x_card_num" value="">
                                @*<partial name="../ContactPhones/Index" model=@Model />*@


                                @*First name:
                                <input type="text" name="A" value="<%= ViewData[" A"] %>" />
                                <br />
                                Last name:
                                <input type="text" name="B" value="<%= ViewData[" B"] %>" />
                                <br />
                                <input type="submit" value="Insert" />
                                <button type="submit" name="submit" value="add"><span class="glyphicon glyphicon-plus"></span>Add another</button>*@



                                <input type="hidden" asp-for="ContactPhone.ContactId" />
                                <div class="row">
                                    <div class="form-group col-md-7">
                                        <label asp-for="ContactPhone.Number" class="control-label"></label>
                                        <input asp-for="ContactPhone.Number" class="form-control" />
                                        <span asp-validation-for="ContactPhone.Number" class="text-danger"></span>
                                    </div>
                                    <div class="form-group col-md-5">
                                        <label asp-for="ContactPhone.ContactPhoneType" class="control-label"></label>
                                        <select asp-for="ContactPhone.ContactPhoneTypeId" class="form-control"
                                                asp-items="@Model.PhoneTypeSL">
                                            @*<option value="">-- Select Type --</option>*@
                                        </select>
                                        <span asp-validation-for="ContactPhone.ContactPhoneTypeId" class="text-danger" />
                                    </div>
                                </div>

                                <button type="submit" name="submit" value="AddPhone" class="btn btn-primary"><span class="fa fa-plus"> </span>  Add Phone Number</button>

                                @*<a asp-area="ContactsModel" asp-page="/ContactPhones/Create"> Add Phone Number</a>*@
                            </div>
                        </div>
                    </div>

                    <p>

                        @*    <a asp-page="Create">Create New</a>*@
                    </p>

                    @*@Html.Hidden("Contact.UserId")   This breaks the validation*@
                    @*<div class="form-group">
                            <label asp-for="Contact.UserId" class="control-label"></label>
                            <select asp-for="Contact.UserId" class="form-control" asp-items="ViewBag.UserId"></select>
                        </div>*@




                </form>
            </div>
        </div>

        <div>
            <a asp-page="Index">Back to List</a>
        </div>
    </div>
    @section Scripts {
        @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}

Создать:

Учитывая эту маленькую модель, как мне создать страницу бритвы, которая будет добавлять записи телефона в объект Contact, захватывать данные объекта Contact и создавать всю модель одним вызовом Save. (управление ключами и т. д.)

Теперь я могу быть далеко от базы, но такого типа сценария я бы ожидал от ORM. Честно говоря, я не уверен, почему кто-то будет создавать EF и не предоставлять такой тип функциональности. Но, вероятно, я что-то упустил, и я много раз поливал документы MS в поисках подсказок.

Но в Джанго это так работает;)

Если есть хотя бы один пример такого типа операций, который не является одним и тем же клиентским одномерным примером, я был бы признателен.

Спасибо CB

1 Ответ

0 голосов
/ 06 сентября 2018

Если вы хотите привязать элементы к свойству, которое является какой-либо коллекцией, вам нужно добавить индексаторы к имени полей формы или выражению, которое вы передаете атрибуту asp-for тэгелпера, начиная с 0.

Вот более простая модель:

public class Order
{
    public int OrderId { get; set; }
    public string Customer { get; set; }
    public List<OrderItem> Items { get; set; }
}

public class OrderItem
{
    public int OrderItemId { get; set; }
    public string Item { get; set; }
    public decimal Price { get; set; }
}

Вот PageModel:

public class CreateModel : PageModel
{
    [BindProperty]
    public Order Order { get; set; }

    public void OnPost()
    {

    }
}

Вот форма:

@page
@model CreateModel
@{
}

<form method="post">
    <input asp-for="Order.Customer" /><br />
    <input asp-for="Order.Items[0].Item" /><br/>
    <input asp-for="Order.Items[0].Price" />
    <input type="submit"/>
</form>

Это все, что вам нужно. Значения из формы будут автоматически привязаны к свойству Order, помеченному атрибутом [BindProperty]. Если вы передадите это в EF, он создаст и сохранит заказ, а затем использует идентификатор для создания и сохранения OrderItem. Если вы хотите позволить пользователю создавать больше элементов, просто увеличьте индексатор на 1 для каждого элемента:

<form method="post">
    <input asp-for="Order.Customer" /><br />
    @for (var i = 0; i < 5; i++)
    {
    <input asp-for="Order.Items[i].Item" /><br/>
    <input asp-for="Order.Items[i].Price" />
    }
    <input type="submit"/>
</form>

Ссылка: https://www.learnrazorpages.com/razor-pages/model-binding#binding-complex-collections

...