I have several different solutions that work out different aspects of multiforms, the question is really quite voluminous, so I recommend that you do the same: before you make the real application do a few test cases to play around and see some aspects.
First, you can start with the application in which your email model will represent a simple line:
public class Customer { public int Id { get; set; } public string Title { get; set; } public string[] Phones { get; set; } }
This is sometimes very convenient, in this case I recommend to look at how to store such structures in the database using the Entity Framework.
We will start with two full-fledged models (i.e., the phone needs to store not only the number itself, but also let's say type - work / home / fax)
Contact model (you can mentally replace with Customer):
public class Contact { public Contact() { this.Phones = new List<Phone>(); } public int Id { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public virtual ICollection<Phone> Phones { get; set; } }
Phone model (only key fields are shown):
public class Phone { public int Id { get; set; } public int ContactId { get; set; } public virtual Contact Contact { get; set; } public string Number { get; set; } }
Next, we stupidly create a CRUD controller using scaffolding. You have probably seen such preparations many times, there is nothing difficult in them, but alas, they do not know how to multi-elements.
What should be done. First, add a block to the form to display a div with phone numbers:
<div id="phoneList"> @Html.EditorFor(x => x.Phones) <p> <a href="javascript:void(0);" class="addRowPhone"><span class="glyphicon glyphicon-plus"></span>Добавить номер</a> </p> </div>
Entire file form Create:
@model WebApplication1.Models.Contact @{ ViewBag.Title = "Create"; } <h2>Create</h2> @using (Html.BeginForm()) { @Html.AntiForgeryToken() <div class="form-horizontal"> <h4>Contact</h4> <hr /> @Html.ValidationSummary(true, "", new { @class = "text-danger" }) <div class="form-group"> @Html.LabelFor(model => model.FirstName, htmlAttributes: new { @class = "control-label col-md-2" }) <div class="col-md-10"> @Html.EditorFor(model => model.FirstName, new { htmlAttributes = new { @class = "form-control" } }) @Html.ValidationMessageFor(model => model.FirstName, "", new { @class = "text-danger" }) </div> </div> <div class="form-group"> @Html.LabelFor(model => model.LastName, htmlAttributes: new { @class = "control-label col-md-2" }) <div class="col-md-10"> @Html.EditorFor(model => model.LastName, new { htmlAttributes = new { @class = "form-control" } }) @Html.ValidationMessageFor(model => model.LastName, "", new { @class = "text-danger" }) </div> </div> <div id="phoneNumbers"> @Html.EditorFor(x => x.Phones) </div> <div class="form-group"> <div class="col-md-offset-2 col-md-10"> <input type="submit" value="Create" class="btn btn-default" /> </div> </div> </div> } <div> @Html.ActionLink("Back to List", "Index") </div> @section Scripts { @Scripts.Render("~/bundles/jqueryval") }
For this construction to work, you need to add the Views \ Contacts \ EditorTemplates folder and place the Phone.cshtml file in it (which will determine how to display our editing element:
@model WebApplication1.Models.Phone <div class="phoneRow"> @Html.HiddenFor(x => x.Id) @Html.HiddenFor(x => x.ContactId) <p> <label>Phone Number</label> @Html.TextBoxFor(x => x.Number) </p> <a href="javascript:void(0);" class="remRowPhone"><span class="glyphicon glyphicon glyphicon-trash"></span>Удалить</a> </div>
Total, something turns out like:

And it looks something like this (it didn’t decorate with css styles, as the project is educational, so it looks ugly):

Read the primer on this topic to understand how it works: Overriding display and editing templates
In asp.net core, it is customary to create forms using tag-helpers and analogs for Html. Editor I don’t know, but when I mixed two styles and wrote Html. Editor, it worked. Well, it's just ugly, too dissimilar style.
But the main problem is related to the fact that the DisplayTemplates / EditorTemplates templates for razor pages do not work , even in 2.1 — at least when I tried various options in / Pages and / Views / Shared; none, alas; (
That's why I started to show on classic asp, especially as the projects are ready;)
What pitfalls are still waiting for you. When you receive data from the form - everything is fine with you, but when you start saving phones while editing - you need to determine which of the phones were deleted, which were added and which changed - and accordingly, the deleted ones - removed from the database, new ones added, and change others.
This is a separate big topic that drinks lots of blood. I will send you to this and this issue here, but be prepared to spend a few days on this insidious topic, in this thread I simply denote the problem.
With GraphDiff, you can reduce the code to something like this:
// 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"); }
And on vanilla c # code will be four times more.
Another point will be how to take your classes to MediatR, see for yourself on a specific project.
And one more thing I missed - javascript on the add / delete buttons. I did not do it in a test project, and it differs for vanilla js / jQuery / vue / etc - so write yours in place.