Search code examples
c#asp.net-core-mvcentity-framework-coremany-to-many

many to many relationship update data mvc .net core


I have a many to many relationship in .net core mvc, but I have no idea how to implement a edit view.

The models are Studios

public class Studio
    {
        public int StudioID { get; set; }
        public string Name { get; set; }
        public ICollection<StudioAddress>StudioAddresses { get; set; }
    }

Addresses

public class Address
    {
        public int AddressID { get; set; }
        public string Street { get; set; }
        public ICollection<StudioAddress> StudioAddresses { get; set; }
    }

StudioAddress

public class StudioAddress
    {        
        public int StudioID { get; set; }
        public Studio Studio { get; set; }
        public int? AddressID { get; set; }
        public Address Address { get; set; }
    }

My databasecontext

modelBuilder.Entity<StudioAddress>()
     .HasKey(sa => new { sa.StudioID, sa.AddressID });
modelBuilder.Entity<StudioAddress>()
     .HasOne(sa => sa.Studio)
     .WithMany(s => s.StudioAddresses)
     .HasForeignKey(sa => sa.StudioID);
modelBuilder.Entity<StudioAddress>()
     .HasOne(sa => sa.Address)
     .WithMany(a => a.StudioAddresses)
     .HasForeignKey(sa => sa.AddressID);

Now, I have created the edit Get method in my studioscontroller

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

        var studio = await _context.Studios
             .Include(s => s.StudioAddresses).ThenInclude(s => s.Address)
                 .Where(s => s.StudioID == id)
             .AsNoTracking()
             .FirstOrDefaultAsync();

        if (studio == null)
        {
            return NotFound();
        }
        return View(studio);
    }

But I have no idea how to update the related data for studio and address? Bot are forms with textfields. The original microsoft docs are confusing (they work with tickboxes) and weird methods to whitelist fields. Is there a simpler, more intuitive way of doing this?


Solution

  • Based on your model definition, you could try to design the Edit view and the Post method like below :

    Here is the “Edit” view:

    @model SOMVCDemo.Models.Studio
    
    <div class="row">
    <div class="col-md-4">
        <form asp-action="Edit">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <input type="hidden" asp-for="StudioID" />
            <div class="form-group">
                <label asp-for="Name" class="control-label"></label>
                <input asp-for="Name" class="form-control" />
                <span asp-validation-for="Name" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label class="control-label">StudioAddresses</label>
                <table>
                    <tbody>
                        @{ int i = 0;}
                        <tr>
                            @foreach (var StudioAddress in @Model.StudioAddresses)
                            {
                                <td>
                                    <input type="hidden" name="studioAddresses[@i].AddressID" asp-for="@StudioAddress.AddressID" class="form-control" />
                                    <input type="text" name="studioAddresses[@i].Address.Street" asp-for="@StudioAddress.Address.Street" class="form-control" />
                                </td>
                                i++;
                            }
                        </tr>
                    </tbody>
                </table>
            </div>
    
            <div class="form-group">
                <input type="submit" value="Save" class="btn btn-default" />
            </div>
        </form>
    </div>
    </div>
    <div>
       <a asp-action="Index">Back to List</a>
    </div>
    

    Here is the POST method:

        [HttpPost]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> Edit(int id, Studio studio)
        {
            if (id != studio.StudioID)
            {
                return NotFound();
            }
    
            if (ModelState.IsValid)
            {
                try
                {
                    var st = _context.Studios.FirstOrDefault(n => n.StudioID == studio.StudioID);
                    st.Name = studio.Name;
                    _context.Update(st);
                    foreach(var i in studio.StudioAddresses)
                    {
                        var address = _context.Addresses.FirstOrDefault(n=>n.AddressID == i.AddressID);
                        address.Street = i.Address.Street;
                        _context.Update(address);
                    }
                    await _context.SaveChangesAsync();
                }
                catch (DbUpdateConcurrencyException)
                {
                    if (!StudioExists(studio.StudioID))
                    {
                        return NotFound();
                    }
                    else
                    {
                        throw;
                    }
                }
                return RedirectToAction(nameof(Index));
            }
            return View(studio);
        }