Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing Multiple Parameter Values via a Razor Page

I'm trying to get a delete operation to work on an entity with a composite key on a Razor page using ASP.NET Core 3.0 and Entity Framework 6. The entity in question is a CourseAssignment whose composite key is composed of an InstructorID value and a CourseID value.

For an entity with an ordinary, single-field key—say, a Course entity with a CourseID primary key—the code on the razor page is this:

<form method="post">
  <input type="hidden" asp-for="Course.CourseID" />
  <input type="submit" value="Delete" class="btn btn-danger" /> |
  <a asp-page="./Index">Back to List</a>
</form>

Which produces a URL like this: https://localhost:44388/Courses/Delete?id=1045

The C# code that acts on this is:

public async Task<IActionResult> OnPostAsync(int? id) {

  if (id == null) {
    return NotFound();
  }

  Course = await _context.Courses.FindAsync(id);

  if (Course != null) {
    _context.Courses.Remove(Course);
    await _context.SaveChangesAsync();
  }

  return RedirectToPage("./Index");

}

How do I change both the razor page and the C# OnPostAsync action to handle the entity with a composite key requiring two values?

Thanks!

like image 745
manonash Avatar asked Jan 02 '20 21:01

manonash


People also ask

How do you pass multiple parameters in route attribute?

you could add a route like: routes. MapRoute( "ArtistImages", // Route name "{controller}/{action}/{artistName}/{apikey}", // URL with parameters new { controller = "Home", action = "Index", artistName = "", apikey = "" } // Parameter defaults );


3 Answers

You can pass multiple values from the form by adding additional hidden fields. So, let us say the secondary field in your composite primary key is Course.Instructor; in that case, you might add:

<input type="hidden" asp-for="Course.Instructor" />

You would then extend the signature for your OnPostAsync() action to include that field:

public async Task<IActionResult> OnPostAsync(int? id, string instructor) { … }

Note: If you’re using C# 8.0’s nullable annotation context, then you’ll likely want string? for your parameter type.

At that point, you should be able pass the value on as a second parameter to FindAsync():

Course = await _context.Courses.FindAsync(id, instructor);

Note: This works because the FindAsync() method accepts a params Object[] keyValues which correspond to the primary key constraint.

Obviously, you’ll need to confirm that the name generated for your hidden field matches your action parameter name, or otherwise provide a binding hint.

like image 156
Jeremy Caney Avatar answered Nov 09 '22 13:11

Jeremy Caney


Update your HTML form to something like the following:

<form method="post">
    <input type="hidden" asp-for="Course.CourseID" />
    <input type="hidden" asp-for="Course.InstructorID" />
    <input type="submit" value="Delete" class="btn btn-danger" /> |
    <a asp-page="./Index">Back to List</a>
</form>

Create a new class for handling the composite key. You can use your existing view model class too.

public class DeleteCourseRequest {
  public int CourseId { get; set; }
  public int InstructorId { get; set; }
}

Then update your controller's OnPostAsync action to something like this:

public async Task<IActionResult> OnPostAsync(DeleteCourseRequest request) {
  if (request == null) {
    return NotFound();
  }

  Course = await _context.Courses.FirstOrDefaultAsync(c => c.CourseID == request.CourseID && c.InstructorID == request.InstructorID);

  if (Course != null) {
    _context.Courses.Remove(Course);
    await _context.SaveChangesAsync();
  }

  return RedirectToPage("./Index");
}

The issue is that when doing a Post with more than one parameter, you would either have to use [FromBody] or [FromUri] bindings on your parameters. This tells ASP.NET Web API where to parse the parameters. Another option is to use a view model to pass your information from your form to your controller.

like image 31
Cameron Tinker Avatar answered Nov 09 '22 13:11

Cameron Tinker


Well, as it so happens, my problem was in the POST code of Index.cshtml, which calls the Delete page, rather than in the POST code of the Delete page itself. There was also an issue in the OnGetAsync() method of the Delete.cshtml.cs page, rather than its OnPostAsnc() method (although that had to be adjusted to accept two parameters as well).

With the hints provided in Cameron Tinker and Jeremy Caney answers, I got it figured out. The link to the delete page for an entity with a non-composite primary key looks like this:

 <a asp-page="./Delete" asp-route-id="@item.CourseID">Delete</a>-->

For an entity with a composite key, it needs to look something like this:

<a asp-page="./Delete" asp-route-courseID="@item.CourseID" asp-route-instructorID="@item.InstructorID">Delete</a>

The last part of the asp-route helper is the name of the parameter that will be passed. (That probably doesn't matter except for clarity in the Razor page itself, which would be an issue if you were just passing a constant as the value, instead of the value of a named property like I'm doing.)

The C# code that acts on this is in the OnGetAsync() method of the called page, Delete.cshtml.cs:

public async Task<IActionResult> OnGetAsync(int? courseID, int? instructorID) {
  if (courseID == null || instructorID == null) {
    return NotFound();
  }

  CourseAssignment = await _context.CourseAssignments
      .AsNoTracking()
      .Include(c => c.Course)
      .Include(c => c.Instructor)
      .FirstOrDefaultAsync(m => m.CourseID == courseID && m.InstructorID == instructorID);

  if (CourseAssignment == null) {
    return NotFound();
  }
  return Page();
}
like image 25
manonash Avatar answered Nov 09 '22 15:11

manonash