Search code examples
htmlentity-framework-coreasp.net-core-mvc

How to display data from both sides of a 1 to 1 relationship in a view and update the correct table on Submit?


I have 2 model classes named Member & Envelope shown below where Member has a 1 to 1 relationship with Envelope:

namespace Mapping.Models
{
    public class Member
    {
        [Key]
        public int MemberId { get; set; }

        [Required]
        [Display(Name = "Mailing Name")]
        [StringLength(250)]
        public string? MemberMailingName { get; set; }

        [Required]
        [Display(Name = "Family Name")]
        [StringLength(250)]
        public string? MemberFamilyName { get; set; }

        [Required]
        [Display(Name = "Status")]
        [StringLength(10)]
        public string? MemberStatus { get; set; }

        // Navigation Properties
        public virtual Envelope? Envelope { get; set; }
    }

    public class Envelope
    {
        [Key]
        public int EnvelopeId { get; set; }

        [Required]
        [Display(Name = "Envelope Number")]
        public int EnvelopeNumber { get; set; }
    }
}

When I pass the Member model to the Edit view, it recognizes that there is an Envelope associated with it.

I have 2 problems:

  1. I can't display the EnvelopeNumber from the Envelope in Edit fields - it shows up blank
  2. I want to be able to update the Envelope table when the user clicks Submit (There is a separate and Submit button for the Member and the Envelope).

Below is the MembersController method for Edit and the Edit.cshtml that I'm using (Please don't judge the crappy HTML - I'll clean it all up once it works correctly!)

Thanks in Advance for any help with this!!

Edit Method in MembersController:

// GET: Members/Edit/5
public async Task<IActionResult> Edit(int? id)
{
    if (id == null)
    {
        return NotFound();
    }

    var member = await _context.Member.FindAsync(id);
    if (member == null)
    {
        return NotFound();
    }
    return View(member);
}

Edit.cshtml:

@model StJosephMapping.Models.Member

@{
    ViewData["Title"] = "Edit";
}

<h1>Edit</h1>

<h4>Member</h4>
<hr />
<div class="row">
    <div class="col-md-12">
        <form asp-action="Edit">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <input type="hidden" asp-for="MemberId" />
            <span>
                <table style="float: left;">
                    <tr>
                        <th style="width:25%;">
                            <label asp-for="MemberMailingName" class="control-label"></label>
                        </th>
                        <th style="width:25%;">
                            <label asp-for="MemberFamilyName" class="control-label"></label>
                        </th>
                        <th style="width:25%;">
                            <label asp-for="MemberStatus" class="control-label"></label>
                        </th>
                     </tr>
                     <tr>
                        <td>
                            <input asp-for="MemberMailingName" class="form-control" />
                        </td>
                        <td>
                            <input asp-for="MemberFamilyName" class="form-control" />
                        </td>
                        <td>
                            <input asp-for="MemberStatus" class="form-control" />
                        </td>
                    </tr>
                    <tr>
                        <td>
                            <span asp-validation-for="MemberMailingName" class="text-danger"></span>
                        </td>
                        <td>
                            <span asp-validation-for="MemberFamilyName" class="text-danger"></span>
                        </td>
                        <td>
                            <span asp-validation-for="MemberStatus" class="text-danger"></span>
                        </td>
                    </tr>
                </table>
                <table style="display: inline-block;">
                    <tr>
                        <td>
                            <div class="form-group" style="margin-top: 26px;">
                                <input type="submit" value="Save" class="btn btn-primary" />
                            </div>
                        </td>
                    </tr>
                </table>
            </span>
        </form>
    </div>
</div>
<br />
<h4>Envelope</h4>
<hr />
<div class="row">
    <div class="col-md-12">
        <form asp-action="Edit">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <input type="hidden" asp-for="MemberId" />
            <span>
                <table style="float: left;">
                    <tr>
                        <th style="width:25%;">
                            <label asp-for="Envelope.EnvelopeNumber" class="control-label"></label>
                        </th>
                    </tr>
                    <tr>
                        <td>
                            <input asp-for="Envelope.EnvelopeNumber" class="form-control" />
                        </td>
                    </tr>
                    <tr>
                        <td>
                            <span asp-validation-for="Envelope.EnvelopeNumber" class="text-danger"></span>
                        </td>
                    </tr>
                </table>
                <table style="display: inline-block;">
                    <tr>
                        <td>
                            <div class="form-group" style="margin-top: 26px;">
                                <input type="submit" value="Save" class="btn btn-primary" />
                            </div>
                        </td>
                    </tr>
                </table>
            </span>
        </form>
    </div>
</div>
<div>
    <a asp-action="Index">Back to List</a>
</div>

@section Scripts {
    @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}

Solution

  • I can't display the EnvelopeNumber from the Envelope in Edit fields - it shows up blank

    You can do this by using the Include method in Entity Framework Core:

    public async Task<IActionResult> Edit(int? id)
    {
        if (id == null || _context.Member == null)
        {
            return NotFound();
        }
    
        //var member = await _context.Member.FindAsync(id);
        var member = await _context.Member.Include(a => a.Envelope).FirstOrDefaultAsync(a=>a.MemberId==id);
        if (member == null)
        {
            return NotFound();
        }
        return View(member);
    }
    

    I want to be able to update the Envelope table when the user clicks Submit (There is a separate and Submit button for the Member and the Envelope).

    You need to ensure that the EnvelopeId is included in the form so that the controller can identify which Envelope needs to be updated.

    Change your Edit.cshtml like below:

    <div class="row">
        <div class="col-md-12">
            <form asp-action="Edit">
                <div asp-validation-summary="ModelOnly" class="text-danger"></div>
                <input type="hidden" asp-for="MemberId" />
                <span>
                    <table style="float: left;">
                        <tr>
                            <th style="width:25%;">
                                <label asp-for="MemberMailingName" class="control-label"></label>
                            </th>
                            <th style="width:25%;">
                                <label asp-for="MemberFamilyName" class="control-label"></label>
                            </th>
                            <th style="width:25%;">
                                <label asp-for="MemberStatus" class="control-label"></label>
                            </th>
                         </tr>
                         <tr>
                            <td>
                                <input asp-for="MemberMailingName" class="form-control" />
                            </td>
                            <td>
                                <input asp-for="MemberFamilyName" class="form-control" />
                            </td>
                            <td>
                                <input asp-for="MemberStatus" class="form-control" />
                            </td>
                        </tr>
                        <tr>
                            <td>
                                <span asp-validation-for="MemberMailingName" class="text-danger"></span>
                            </td>
                            <td>
                                <span asp-validation-for="MemberFamilyName" class="text-danger"></span>
                            </td>
                            <td>
                                <span asp-validation-for="MemberStatus" class="text-danger"></span>
                            </td>
                        </tr>
                    </table>
                    <table style="display: inline-block;">
                        <tr>
                            <td>
                                <div class="form-group" style="margin-top: 26px;">
                                    <input type="submit" value="Save" class="btn btn-primary" />
                                </div>
                            </td>
                        </tr>
                    </table>
                </span>
            </form>
        </div>
    </div>
    <br />
    <h4>Envelope</h4>
    <hr />
    <div class="row">
        <div class="col-md-12">
            <form asp-action="Edit">
                <div asp-validation-summary="ModelOnly" class="text-danger"></div>
                <!--   change here  -->           
                <input type="hidden" asp-for="Envelope.EnvelopeId" />
                <span>
                    <table style="float: left;">
                        <tr>
                            <th style="width:25%;">
                                <label asp-for="Envelope.EnvelopeNumber" class="control-label"></label>
                            </th>
                        </tr>
                        <tr>
                            <td>
                                <input asp-for="Envelope.EnvelopeNumber" class="form-control" />
                            </td>
                        </tr>
                        <tr>
                            <td>
                                <span asp-validation-for="Envelope.EnvelopeNumber" class="text-danger"></span>
                            </td>
                        </tr>
                    </table>
                    <table style="display: inline-block;">
                        <tr>
                            <td>
                                <div class="form-group" style="margin-top: 26px;">
                                    <input type="submit" value="Save" class="btn btn-primary" />
                                </div>
                            </td>
                        </tr>
                    </table>
                </span>
            </form>
        </div>
    </div>
    <div>
        <a asp-action="Index">Back to List</a>
    </div>
    
    @section Scripts {
        @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
    }
    

    Edit action:

    [HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> Edit(int id,Member member)
    {
        if(member.MemberId!=0)
        {
            if (id != member.MemberId)
            {
                return NotFound();
            }
            if (ModelState.IsValid)
            {
                //edit the member
                try
                {
                    _context.Update(member);
                    await _context.SaveChangesAsync();
                }
                catch (DbUpdateConcurrencyException)
                {
                    if (!MemberExists(member.MemberId))
                    {
                        return NotFound();
                    }
                    else
                    {
                        throw;
                    }
                }
                //do you stuff...
                return RedirectToAction(nameof(Index));
            }
        }
        else
        {
            //edit the envelope
            if(member.Envelope!=null)
            {
                try
                {
                    _context.Update(member.Envelope);
                    await _context.SaveChangesAsync();
                }
                catch (Exception)
                {
    
                    throw;
                }
                return RedirectToAction(nameof(Index));
                //do you stuff...
            }
        }
        return View(member);
    }