0
votes

I have a Razor page in ASP.NET core meant for editing product data. It receives a productId and has input controls for editing the properties of said product. It has a binding to the product instance saved in the PageModel. I populate the product instance OnGet, which works fine, as it populates the fields properly. When I submit the form, and OnPost is called, I see that the product instance is now null.

After debugging I realised that when I submit the form, the PageModel is re-instantiated (the constructor is called again), so the product instance is reset and is now null.

Is this expected behaviour?

Should this binding between the property in my PageModel and it's Razor Page presentation not persist?

In The course I am following, this behaves differently, the product instance persists to the OnPost method call. My Page Model:

public class EditModel : PageModel
{
    private readonly IProductData productData;
    private readonly IHtmlHelper htmlHelper;

    [BindProperty]
    public Product Product { get; set; }
    public IEnumerable<SelectListItem> Category { get; set; }

    public EditModel(IProductData productData, IHtmlHelper htmlHelper)
    {
        this.productData = productData;
        this.htmlHelper = htmlHelper;
    }

    public IActionResult OnGet(int productId)
    {
        Category = htmlHelper.GetEnumSelectList<ProductType>();
        Product = productData.GetById(productId);
        if(Product == null) 
        {
            return RedirectToPage("./NotFound");
        }
        return Page();
    }

    public IActionResult OnPost() 
    {
        Product = productData.Update(Product);
        productData.Commit();
        return Page();
    }

}

My Razor Page:

@page "{productId:int}"
@model LearningASPdotNETCore.Pages.Products.EditModel
@{
    ViewData["Title"] = "Edit";
}

<h1>Editing @Model.Product.Name</h1>

<form method="post">

<input type="hidden" asp-for="Product.Id" />
<div class="form-group">
    <label asp-for="Product.Name"></label>
    <input asp-for="Product.Name" class="form-control" />
</div>

<div class="form-group">
    <label asp-for="Product.Country"></label>
    <input asp-for="Product.Country" class="form-control" />
</div>

<div class="form-group">
    <label asp-for="Product.Type"></label>
    <select class="form-control" asp-for="Product.Type" asp-items="Model.Category">

    </select>
</div>

<button type="submit" class="btn btn-primary">Save</button>

2

2 Answers

0
votes

Yes, this is expected behavior. Virtually everything in the request pipeline, including PageModel, controllers, etc. are request-scoped. They are instantiated at the beginning of the request and disposed when the request is finished. If you need something set, then that should be set for each request, regardless of method (GET, POST, etc.).

0
votes

I have found the solution. It seems like the product instance could not be instantiated because I have not implemented the property get/set. This confuses me as I thought these two were equal, but I guess not. Before (did't work):

public class Product
{
    public int Id;
    public string Name;
    public string Country;
    public ProductType Type;
}

After (works):

public class Product
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Country { get; set; }
    public ProductType Type { get; set; }
}