#c_sharp #aspnet_mvc #entity_framework #aspnet_core #razor
Есть простая база данных клиентов. В ней 2 таблицы, в одной из которых клиент с информацией и 2 таблица с его почтовыми ящиками. Внешний ключ связывает эти таблицы по id клиента. Есть такой код добавления на бекенде в базу данных: public async TaskHandle(CreateCommand request, CancellationToken cancellationToken) { var entity = new Customer { CompanyName = request.CompanyName, Country = request.Country, City = request.City, Address = request.Address, ZipCode = request.ZipCode, }; _context.Customers.Add(entity); await _context.SaveChangesAsync(cancellationToken); foreach (var createCustEmail in request.Emails) { _context.CustomerEmails.Add(new CustomerEmail() { CustomerId = entity.CustomerId, Email = createCustEmail.Email, }); } await _context.SaveChangesAsync(cancellationToken); return Unit.Value; } Вопрос заключается в том,как сделать добавление емейлов на стороне клиента в Razor Pages?
Ответы
Ответ 1
У меня есть несколько разных солюшенов, которые отрабатывают разные аспекты мультиформ, вопрос реально достаточно объёмный, поэтому вам рекомендую сделать так же: прежде, чем делать реальное приложение сделать несколько тестовых, чтобы поиграться и посмотреть отдельные аспекты. Во-первых, вы можете начать с приложения в котором у вас модель емейла будет представлять простую строку: public class Customer { public int Id { get; set; } public string Title { get; set; } public string[] Phones { get; set; } } Это иногда очень удобно, в этом случае рекомендую посмотреть как хранить подобные структуры в базе используя Entity Framework. Мы же будем начинать с двух полноценных моделей (т.е. телефону нужно хранить не только сам номер, но и допустим тип - рабочий/домашний/факс) Модель контакта (мысленно можете заменять на Customer): public class Contact { public Contact() { this.Phones = new List(); } public int Id { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public virtual ICollection Phones { get; set; } } Модель телефона (показаны только ключевые поля): public class Phone { public int Id { get; set; } public int ContactId { get; set; } public virtual Contact Contact { get; set; } public string Number { get; set; } } Далее тупо создаём CRUD-контроллер, используя scaffolding. Вы наверное многократно видели подобные заготовки, в них нет ничего сложного, но увы, они не умеют в мультиэлементы. Что нужно сделать. Во-первых, добавить в форму блок для отображения div'а с телефонными номерами: @Html.EditorFor(x => x.Phones)Целиком файл формы Create: @model WebApplication1.Models.Contact @{ ViewBag.Title = "Create"; }Create
@using (Html.BeginForm()) { @Html.AntiForgeryToken()}Contact
@Html.ValidationSummary(true, "", new { @class = "text-danger" })@Html.LabelFor(model => model.FirstName, htmlAttributes: new { @class = "control-label col-md-2" })@Html.EditorFor(model => model.FirstName, new { htmlAttributes = new { @class = "form-control" } }) @Html.ValidationMessageFor(model => model.FirstName, "", new { @class = "text-danger" })@Html.LabelFor(model => model.LastName, htmlAttributes: new { @class = "control-label col-md-2" })@Html.EditorFor(model => model.LastName, new { htmlAttributes = new { @class = "form-control" } }) @Html.ValidationMessageFor(model => model.LastName, "", new { @class = "text-danger" })@Html.EditorFor(x => x.Phones)@Html.ActionLink("Back to List", "Index")@section Scripts { @Scripts.Render("~/bundles/jqueryval") } Чтобы эта конструкция заработала - нужно добавить папку Views\Contacts\EditorTemplates и в ней расположить файл Phone.cshtml (который и будет определять как отобразить наш элемент редактирования: @model WebApplication1.Models.Phone@Html.HiddenFor(x => x.Id) @Html.HiddenFor(x => x.ContactId)Итого, получается что-то типа: И выглядит как-то так (стилями css не украшал, так как проект учебный, поэтому выглядит страшненько): Почитайте букварь по этой теме, чтобы понимать, как это работает: Переопределение шаблонов отображения и редактирования В asp.net core принято создавать формы используя тэг-хелперы и аналога для Html.EditorFor я не знаю, но когда я смешал два стиля и прописал Html.EditorFor - оно сработало. Ну это просто некрасиво, слишком разнородный стиль. Но основная проблема связана с тем, что шаблоны DisplayTemplates/EditorTemplates для razor pages не работают, даже в 2.1 - по крайней мере когда я пробовал и в /Pages и в /Views/Shared разные варианты не сработал ни один, увы ;( Поэтому я и показывать стал на классическом asp'е, тем более что и проекты есть готовые ;) Какие подводные камни вас ещё ждут. Когда вы получаете данные с формы - всё у вас хорошо, но когда вы начинаете сохранять телефоны при редактировании - вам нужно определять какие из телефонов были удалены, какие добавлены и какие изменились -- и соответственно, удалённые - удалить из базы, новые добавить, а остальные изменить. Это отдельная большая тема, которая выпивает множество крови. Я вас отправлю к вот этому и этому вопросу, но будьте готовы потратить несколько дней на эту коварную тему, в этой ветке я просто обозначаю проблему. При помощи GraphDiff у вас получится сократить код до примерно такого: // POST: Contacts/Edit/5 [HttpPost] [ValidateAntiForgeryToken] public ActionResult Edit(Contact contact) { if (!this.ModelState.IsValid) return this.View(contact); var entity = db.Contacts.Include(x=>x.Phones).SingleOrDefault(x => x.Id == contact.Id); if (entity == null) return this.HttpNotFound(); this.db.UpdateGraph(contact, map => map.OwnedCollection(p => p.Phones)); this.db.SaveChanges(); return this.RedirectToAction("Index"); } А на ванильном c# код будет раза в четыре больше. Другой момент будет как смапить ваши классы на MediatR, это смотрите сами по конкретному проекту. И ещё один момент я упустил - javascript на кнопочках добавления/удаления записей. Я его в тестовом проекте не делал, да и он различается для ванильного js/jQuery/vue/etc - поэтому пишите свой по месту.@Html.TextBoxFor(x => x.Number)
Удалить
Комментариев нет:
Отправить комментарий