(C# ASP.NET Core) Editing Records on the same Razor Page with EF Core

This tutorial discusses the most desired scenario where data entry form and the already existing records are shown on the same page. Each record has an edit link adjacent to it. When this link is clicked, the record appears in the same data-entry form, so that the user can make changes and submit it. See the image below for a quick understanding of the problem being addressed here. This tutorial also explains a general scheme for editing with EF Core.
(Rev. 19-Mar-2024)

Categories | About |     |  

Parveen,

General Scheme for Editing
(see the linked video for explanation)

First add a 2-way bind property, say, [BindProperty]public Product Prod { get; set; } to your backing class for the model you want to edit. For 2-way bindings see (C# ASP.NET Core) Getting Started with Database INSERT of Form data

  1. A user clicks an "edit" link and passes the primary key id of the record to a function called, say, OnGetEdit(int? id) in your backing class.
  2. Inside this function, the "id" is used to select the specific record from the database, and set the 2-way binding property (say, Prod). The response is, then, redirected to the same page.
  3. When the form is rendered, the current data of the record is now presented there, and the user can make changes and post it to a handler called, say, OnPost in your backing class.
  4. Inside OnPost, the DbContext is used to set the state of the record (called, say, Prod as above) to "Modified" as below:
    // set the state of the item as dirty 
    _ctx.Attach(Prod).State = EntityState.Modified;
    
    
    // it is assumed above that the DbContext 
    // is available as _ctx 
    
    // update the changes 
    _ctx.SaveChanges();
    
    

How to use the same form for INSERT and UPDATE

The screenshot shows a simple form that is used for INSERT-ing a new record. The records are displayed on the same page, just below the form. Each record has an "edit" link adjacent to it. When the link is clicked, the data is transferred to the form, and now the same form is used for UPDATE-ing the record.

Dual role of the form: When the form is POSTED and submitted, a 2-way binding property carries the data to the backing class. The handler OnPost differentiates an "insert" request from an "update" request on the basis of the primary key id of the record. If the id is un-specified, then it is an "insert" operation, but if the id contains some specific value, then it is an update operation.

Video Explanation with a Working Example

Please watch the following youtube video:

Pre-requisites for this Walkthrough

You should already have completed the tutorial on display of data - (C# ASP.NET Core) Displaying Data and OnGetAsync

Step 1 of 2: Index.cshtml Razor Page
(see the linked video for details)

As you can see below each record has an "edit" link attached to it. The handler connected to this link is also, co-incidentally, named Edit, but could be any convenient name.

@page "{handler?}/{id?}"

@model Modular.Pages.IndexModel

@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

<form method="post">

  Name: <input asp-for="Prod.Name" />

  <input asp-for="Prod.ID" type="hidden" />

  <input type="submit" />

</form>

<h1>Display of Data</h1>

@foreach (var prod in Model.Products)
{
  <div>
    @prod.Name
    |
    <a asp-route-id="@prod.ID"
       asp-page-handler="Delete">Delete</a>
    |
    <a asp-route-id="@prod.ID"
       asp-page-handler="Edit">Edit</a>
  </div>

  <hr />
}

Step 2 of 2: Index.cshtml.cs backing class file
(see the linked video for details)

Following is the code for the backing class. Notice the handler Edit is prefixed as OnGetEdit, because the request is a GET request, and ASP.NET Core requires that OnGet be prefixed to the name of the function.

using DBaseCon;

using Microsoft.AspNetCore.Mvc;

using Microsoft.AspNetCore.Mvc.RazorPages;

using Microsoft.EntityFrameworkCore;

using System.Collections.Generic;

using System.Threading.Tasks;

namespace Modular.Pages
{

  public class IndexModel : PageModel
  {

    private readonly ProductContext _ctx;

    // dependency injection of the ProductContext 
    public IndexModel(ProductContext ctx)
    {

      _ctx = ctx;

    }

    // for Display/ Sql SELECT 
    public IList<Product> Products { get; set; }

    // when the page loads 
    public async void OnGetAsync()
    {

      // using Microsoft.EntityFrameworkCore; 

      Products = await _ctx.Products.ToListAsync();

    }

    // 2-way binding for Sql INSERT/UPDATE 
    [BindProperty]
    public Product Prod { get; set; }

    // handler for each Edit link 
    public async Task<IActionResult> OnGetEdit(int? id)
    {

      if (null == id)
      {

        // sends 404 error 
        return NotFound();

      }

      Product p = await _ctx.Products.FindAsync(id);

      // concurrency check if someone deleted the record 
      if (p != null)
      {

        // set the bind property 
        Prod = p;

      }

      // fill the products collection 
      Products = await _ctx.Products.ToListAsync();

      // Model and bind properties are filled 
      // render the page now 
      return Page();

    }

    // handler for form submit 
    public ActionResult OnPost()
    {

      // new record if ID un-specified 
      if (default == Prod.ID)
      {

        _ctx.Products.Add(Prod);

      }

      // updation 
      else 
      {

        // mark dirty 
        _ctx.Attach(Prod).State = EntityState.Modified;

      }

      _ctx.SaveChanges();

      // redirect and show the 
      // form again 
      return RedirectToPage();

    }

    // Delete the record Sql DELETE 
    public async Task<IActionResult> OnGetDelete(int? id)
    {

      if (null == id)
      {

        // sends 404 error 
        return NotFound();

      }

      // query the DbSet for the item to delete 
      var prod = await _ctx.Products.FindAsync(id);

      // mark from removal from DbSet 
      _ctx.Products.Remove(prod);

      // cause the deletion 
      await _ctx.SaveChangesAsync();

      return RedirectToPage();

    }

  }

}


This Blog Post/Article "(C# ASP.NET Core) Editing Records on the same Razor Page with EF Core" by Parveen is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.