.net core 2.1 validation state: invalid - .net-core

The following simple .NET Core 2.1 MVC code reports "Validation State: Invalid" when I submit to create. Everything works fine without the Owner property; and it works if Owner property is not required.
The Owner is the current user which is in the context of the server side, and it shouldn't be submitted from a client side, so the Create.cshtml doesn't have a Owner input in the form.
The error:
info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1]
Executing action method AnnouncementApp.Controllers.AnnouncementsController.Create (AnnouncementApp) with arguments (AnnouncementApp.Models.Announcement) - Validation state: Invalid
The model:
using System;
using Microsoft.AspNetCore.Mvc;
using System.ComponentModel.DataAnnotations;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using AnnouncementApp.Models.Attributes;
using Microsoft.AspNetCore.Identity;
//using System.Security.Claims;
namespace AnnouncementApp.Models
{
public class Announcement
{
public int ID { get; set; }
[Required]
public string Content { get; set; }
[Display(Name = "Start Date and Time")]
public DateTime StartDate { get; set; }
[StartEndDate("End Date and Time must be after Start Date and Time")]
[Display(Name = "End Date and Time")]
public DateTime EndDate { get; set; }
[Required]
[BindNever]
public IdentityUser Owner { get; set; }
}
}
The controller method:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("ID,Content,StartDate,EndDate")] Announcement announcement)
{
if (ModelState.IsValid)
{
var user = await _userManager.GetUserAsync(this.User);
announcement.Owner = user;
_context.Add(announcement);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
return View(announcement);
}
The Create.cshtml
#model AnnouncementApp.Models.Announcement
#{
ViewData["Title"] = "Create";
}
<h2>Create</h2>
<h4>Announcement</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="Create">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="Content" class="control-label"></label>
<textarea asp-for="Content" class="form-control"></textarea>
<span asp-validation-for="Content" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="StartDate" class="control-label"></label>
<input asp-for="StartDate" class="form-control" />
<span asp-validation-for="StartDate" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="EndDate" class="control-label"></label>
<input asp-for="EndDate" class="form-control" />
<span asp-validation-for="EndDate" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="Index">Back to List</a>
</div>
#section Scripts {
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}

For Announcement, it will apply [Required] for both client validation and database table.
As the comments indicates, you could consider split Announcement to Db Model and ViewModel, you could define a new AnnouncementViewModel for client validation.
For another option, you could try configure the [Required] in the fluent api instead of attribute.
Here are the detail steps.
Change Announcement
public class Announcement
{
public int ID { get; set; }
[Required]
public string Content { get; set; }
[Display(Name = "Start Date and Time")]
public DateTime StartDate { get; set; }
public string OwnerId { get; set; }
//[Required]
[BindNever]
[ForeignKey("OwnerId")]
public IdentityUser Owner { get; set; }
}
Fluent api in ApplicationDbContext
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<Announcement>()
.Property(a => a.OwnerId)
.IsRequired();
}
Controller
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("ID,Content,StartDate")] Announcement announcement)
{
if (ModelState.IsValid)
{
var user = await _userManager.GetUserAsync(User);
announcement.Owner = user;
_context.Add(announcement);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
return View(announcement);
}

I am not 100 % sure, what you define as the issue, but if you want to supress the "Model Invalid" error, since you are always setting the Owner property through the HttpContext, you can use the following before validating the model:
ModelState["Owner"].ValidationState = ModelValidationState.Valid
I think your issue is that you tell the router to never bind "Owner", but you still tells it is required, and therefore the ModelState would potentially invalidate it.
As long as the "Required" annotation is used, I do not think the ModelState will validate without it being set correctly.
Example:
ModelState["Owner"].ValidationState = ModelValidationState.Valid
if (ModelState.IsValid)
{
var user = await _userManager.GetUserAsync(this.User);
announcement.Owner = user;
_context.Add(announcement);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
return View(announcement);

Related

Custom validation attribute does not show error message

tring to compare two date input in asp.net core, i dont get any error message.
other built-in validators are working. also tried to create custom validator too.
(using tuple model. and named it as 'e')
my Model:
public DateTime? SchoolStart { get; set; }
public DateTime? SchoolEnd { get; set; }
Model Validator (IValidatableObject)
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (SchoolEnd.Value <= SchoolStart.Value)
{
yield return new ValidationResult("End date must be greater than the start date.", new[] { nameof(SchoolEnd) });
}
}
my view
<input asp-for="e.SchoolStart" type="date" class="form-control" data-provider="flatpickr" data-date-format="d F Y" placeholder="Başlangıç Tarihi" />
<input asp-for="e.SchoolEnd" type="date" class="form-control" data-provider="flatpickr" data-date-format="d F Y" placeholder="Bitiş Tarihi" />
This is backend validation, The error message will show after ModelState.IsValid, Please refer to this simple demo:
Model
public class DataModel
{
[Required]
public string Test { get; set; }
public DateTime? SchoolStart { get; set; }
[CustomAdmissionDate]
public DateTime? SchoolEnd { get; set; }
}
validate class
public class CustomAdmissionDate : ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var model = (DataModel)validationContext.ObjectInstance;
if (model.SchoolEnd <= model.SchoolStart)
{
return new ValidationResult("End date must be greater than the start date.");
}
return ValidationResult.Success;
}
}
Backend post method
[HttpPost]
public IActionResult validate(DataModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
return View();
}
View
#model DataModel
<form asp-action="Validate">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="Test" class="control-label"></label>
<input asp-for="Test" class="form-control" />
<span asp-validation-for="Test" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="SchoolStart" class="control-label"></label>
<input asp-for="SchoolStart" class="form-control" />
<span asp-validation-for="SchoolStart" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="SchoolEnd" class="control-label"></label>
<input asp-for="SchoolEnd" class="form-control" />
<span asp-validation-for="SchoolEnd" class="text-danger"></span>
</div>
<button type="submit">Submit</button>
</form>
#section Scripts {
#{
await Html.RenderPartialAsync("_ValidationScriptsPartial");
}
}
Demo:
=========Update=========
If you wanna achieve client validation, you need to create a client script, Please update CustomAdmissionDate class to:
public class CustomAdmissionDate : ValidationAttribute, IClientModelValidator
{
public void AddValidation(ClientModelValidationContext context)
{
context.Attributes.Add("data-val", "true");
context.Attributes.Add("data-val-date", "End date must be greater than the start date.");
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
//........
}
}
Then create js file named Date.js in wwwroot/js/Date.js
Date.js
jQuery.validator.addMethod("date",
function (value, element, param) {
var starttime = document.getElementById("SchoolStart").value;
var start = Date.parse(starttime);
var end = Date.parse(value)
if (end <= start) {
return false;
}
else {
return true;
}
});
Then refer this js file in your page:
<script src="~/js/Date.js"></script>
Now it can be validated in client site.

Validation failed: InvalidRoleName

I have some problem with the code. I'm not able to create a role. The code seems fine to me, but I'm not able to find the error. With WriteLine I'm getting the following message:
Microsoft.AspNetCore.Identity.RoleManager: Warning: Role 38676182-f211-47d7-b69b-f190e4338123 validation failed: InvalidRoleName.
Why InvalidRoleName is invalid? It's just a string. Any help is appreciated.
c#:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace Sarpinos.Pages.Admin
{
public class CreateRoleModel : PageModel
{
private readonly RoleManager<IdentityRole> _roleManager;
public CreateRoleModel(RoleManager<IdentityRole> roleManager)
{
_roleManager = roleManager;
}
[BindProperty]
public List<IdentityRole> Roles { get; set; }
public void OnGet()
{
}
public async Task<IActionResult> OnPost()
{
if (ModelState.IsValid)
{
var newRole = new IdentityRole();
newRole.Id = Guid.NewGuid().ToString();
await _roleManager.CreateAsync(newRole);
Console.WriteLine($"{newRole.Id} {newRole.Name}");
}
return RedirectToPage("Dashboard");
}
}
}
cshtml:
#page
#model Sarpinos.Pages.Admin.CreateRoleModel
#{
ViewData["Title"] = "Register";
}
<div class="container">
<form asp-page="CreateRole" method="POST">
<div class="form-group">
<label asp-for="#Model.Roles">Role name:</label>
<input type="text" asp-for="#Model.Roles" class="form-control" placeholder="Enter role name" />
<span asp-validation-for="#Model.Roles" class="text-danger"></span>
</div>
<button type="submit" class="btn btn-success">Create</button>
</form>
</div>
[BindProperty]
public List<IdentityRole> Roles { get; set; }
public void OnGet()
{
}
public async Task<IActionResult> OnPost()
{
if (ModelState.IsValid)
{
var newRole = new IdentityRole();
newRole.Id = Guid.NewGuid().ToString();
await _roleManager.CreateAsync(newRole);
Console.WriteLine($"{newRole.Id} {newRole.Name}");
}
return RedirectToPage("Dashboard");
}
According to your description, it seems that you want to create a new Role, but by using the above code, it looks that you are using a List<IdentityRole> to display the roles and in the post method, you didn't set the role name for the newRole. So, it might cause the issue.
To Create new Role, in the CreateRoleModel, you could create a new class which contains the Role related properties. For example:
public class CreateRoleModel : PageModel
{
private readonly RoleManager<IdentityRole> _roleManager;
public CreateRoleModel(RoleManager<IdentityRole> roleManager) { _roleManager = roleManager; }
public class NewRole
{
public string RoleName { get; set; }
}
[BindProperty]
public NewRole Input { get; set; }
public void OnGet()
{
Input = new NewRole(); //
}
public async Task<IActionResult> OnPostAsync()
{
if (ModelState.IsValid)
{
var newRole = new IdentityRole();
newRole.Id = Guid.NewGuid().ToString();
newRole.Name = Input.RoleName;
await _roleManager.CreateAsync(newRole);
Console.WriteLine($"{newRole.Id} {newRole.Name}");
}
return RedirectToPage("Home/Index");
}
}
Then, in the CreateRole.cshtml page, based on the NewRole class to insert a new role (you could change the namespace to yours).
#page
#addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
#model IdentitySample.Pages.CreateRoleModel
<div class="container">
<form method="POST">
#Html.AntiForgeryToken()
<div class="form-group">
<label asp-for="#Model.Input.RoleName">Role name:</label>
<input asp-for="#Model.Input.RoleName" class="form-control" placeholder="Enter role name" />
<span asp-validation-for="#Model.Input.RoleName" class="text-danger"></span>
</div>
<button type="submit" class="btn btn-success">Create</button>
</form>
</div>

Dynamic Property and Child Model Not Binding

I want to build dynamic form using Blazor.
Here is my sample component.
#page "/customform"
#using System.Dynamic
#using System.Text.Json
#inject IJSRuntime JSRuntime;
<div class="card m-3">
<h4 class="card-header">Blazor WebAssembly Form Validation Example</h4>
<div class="card-body">
<EditForm EditContext="#editContext"
OnValidSubmit="HandleValidSubmit">
<DataAnnotationsValidator></DataAnnotationsValidator>
#foreach (var field in Model.Fields)
{
<div class="form-group">
<label>#field.Name</label>
<input #bind-value="field.Value" class="form-control" />
<ValidationMessage For="(()=> field.Value)" />
<ValidationMessage For="(()=> field.Name)" />
<ValidationMessage For="(()=> field)" />
</div>
}
<div class="form-group">
<label>Address</label>
<input #bind-value="Model.Address" class="form-control" />
<ValidationMessage For="()=> Model.Address" />
</div>
<div class="form-group">
<label>Child</label>
<input #bind-value="Model.ChildModel.ChildName" class="form-control" />
<ValidationMessage For="()=> Model.ChildModel.ChildName" />
</div>
<div class="text-left">
<button class="btn btn-primary" type="submit">Submit</button>
</div>
</EditForm>
</div>
</div>
#code{
private SampleModel Model = new SampleModel();
private EditContext editContext;
private ValidationMessageStore _messageStore;
protected override void OnInitialized()
{
editContext = new EditContext(Model);
editContext.OnValidationRequested += ValidationRequested;
_messageStore = new ValidationMessageStore(editContext);
}
private void HandleValidSubmit(EditContext context)
{
var modelJson = JsonSerializer.Serialize(context.Model, new JsonSerializerOptions { WriteIndented = true });
JSRuntime.InvokeVoidAsync("alert", $"SUCCESS!! :-)\n\n{modelJson}");
}
async void ValidationRequested(object sender, ValidationRequestedEventArgs args)
{
_messageStore.Add(editContext.Field("FirstName"), "Test");
_messageStore.Add(editContext.Field("Address"), "Invalid Address");
_messageStore.Add(editContext.Field("ChildModel.ChildName"), "Invalid Child Name");
editContext.NotifyValidationStateChanged();
}
public class SampleModel
{
public string Address { get; set; }
public ChildModel ChildModel { get; set; }
public List<Field> Fields { get; set; }
public SampleModel()
{
this.ChildModel = new ChildModel();
this.Fields = new List<Field>();
this.Fields.Add(new Field()
{
Name = "FirstName",
Value = "",
ControlType = ControlType.Input
});
this.Fields.Add(new Field()
{
Name = "LastName",
Value = "",
ControlType = ControlType.Input
});
}
}
public class ChildModel
{
public string ChildName { get; set; }
}
public enum ControlType
{
Input
}
public class Field
{
public string Value { get; set; }
public string Name { get; set; }
public string DisplayName { get; set; }
public ControlType ControlType { get; set; }
}
}
Currently I am facing too many issues.
If I use For lookup instead of For each it is not working
ChildModel seems to be bind but its validation is not working
Dynamically generated based on Fields collection control does not display validation.
Only address in SimpleModel display validation.
Is there any suggestion or help around this ?
Your profile suggests you know what you're doing, so I'll keep this succinct.
Your for loop needs to look something like this. Set a local "index" variable within the loop to link the controls to. If you don't they point to the last value of i - in this case 2 which is out of range! The razor code is converted to a cs file by the razor builder. You can see the c# file generated in the obj folder structure - obj\Debug\net5.0\Razor\Pages. Note, the linkage of the Validation Message
#for(var i = 0; i < Model.Fields.Count; i++)
{
var index = i;
<div class="form-group">
<label>#Model.Fields[index].Name</label>
<input #bind-value="Model.Fields[index].Value" class="form-control" />
<ValidationMessage For="(()=> Model.Fields[index].Value)" />
</div>
}
Now the message validation store. Here's my rewritten ValidationRequested. Note I'm creating a FieldIdentifier which is the correct way to do it. "Address" works because it's a property of EditContext.Model. If a ValidationMessage doesn't display the message you anticipate, then it's either not being generated, or it's FieldIdentifier doesn't match the field the ValidationMessage is For. This should get you going in whatever project you're involved in - if not add a comment for clarification :-).
void ValidationRequested(object sender, ValidationRequestedEventArgs args)
{
_messageStore.Clear();
_messageStore.Add(new FieldIdentifier(Model.Fields[0], "Value"), "FirstName Validation Message");
_messageStore.Add(new FieldIdentifier(Model.Fields[1], "Value"), "Surname Validation Message");
_messageStore.Add(editContext.Field("FirstName"), "Test");
_messageStore.Add(editContext.Field("Address"), "Invalid Address");
_messageStore.Add(editContext.Field("ChildModel.ChildName"), "Invalid Child Name");
editContext.NotifyValidationStateChanged();
}
If you interested in Validation and want something more that the basic out-of-the-box validation, there's a couple of my articles that might give you info Validation Form State Control or there's a version of Fluent Validation by Chris Sainty out there if you search.

asp.net core - login page doesn't show error message

I have a problem, i new with asp.net core and i try to build Login page (after i finish to build sign up page that add new row to db with the correct information).
my problem is when i click on login button with the wrong details so i don't see the error message (LoginError) only after i click on login button again i see the LoginError message (from TempData), why?
is the right way? if not i will happy to try another way.
Login cshtml page:
#page
#model AutomationTool.Pages.Login.IndexModel
#{
ViewData["Title"] = "Index";
}
<h1>Login</h1>
<form method="post">
<div class="form-group">
<label asp-for="user.UserEmail"></label>
<input asp-for="user.UserEmail" class="form-control" />
<span class="text-danger" asp-validation-for="user.UserEmail"></span>
</div>
<div class="form-group">
<label asp-for="user.Password"></label>
<input asp-for="user.Password" class="form-control" />
<span class="text-danger" asp-validation-for="user.Password"></span>
</div>
<div>
#Model.LoginError
</div>
<div>
#Model.Message
</div>
<button type="submit" class="btn btn-primary">Login</button>
</form>
Login cshtml.cs page:
namespace AutomationTool.Pages.Login
{
public class IndexModel : PageModel
{
private readonly IUserData userData;
[BindProperty]
public User user { get; set; }
[TempData]
public string Message { get; set; }
[TempData]
public string LoginError { get; set; }
public IndexModel(IUserData userData)
{
this.userData = userData;
}
public IActionResult OnGet()
{
return Page();
}
public IActionResult OnPost()
{
ModelState.Remove("User.Id");
ModelState.Remove("User.FirstName");
ModelState.Remove("User.LastName");
if (!ModelState.IsValid)
{
TempData["Message"] = "All fields required!";
return Page();
}
if (!userData.VerifyLogin(user.UserEmail, user.Password))
{
TempData["LoginError"] = "Something wrong try again!";
return Page();
}
else
{
return RedirectToPage("/Index");
}
}
}
}
thanks!
Here your Message and LoginError parameters have been read many times instead of once, so you should use [BindProperty] or [ViewData] instead of [TempData] to bind these two parameters.
The difference between them, you can refer to this.
Chang your code like this:
public class IndexModel : PageModel
{
private readonly IUserData userData;
[BindProperty]
public User user { get; set; }
[BindProperty]//[ViewData]
public string Message { get; set; }
[BindProperty]//[ViewData]
public string LoginError { get; set; }
public IndexModel(IUserData userData)
{
this.userData = userData;
}
public IActionResult OnGet()
{
return Page();
}
public IActionResult OnPost()
{
ModelState.Remove("User.Id");
ModelState.Remove("User.FirstName");
ModelState.Remove("User.LastName");
if (!ModelState.IsValid)
{
Message = "All fields required!";
return Page();
}
if (!userData.VerifyLogin(user.UserEmail, user.Password))
{
LoginError = "Something wrong try again!";
return Page();
}
else
{
return RedirectToPage("/Index");
}
}
}
Here is the test result:

Populating data from last row in form when creating a new entry

I have a form to create new data entries for comments. Creating completely new entries works fine. However, when I have already created one entry for my entity I want to populate the data from the last entry in my form.
I have tried to modify the OnGet action to include the data from the last entry. I copied the OnGet code from the Edit view into the Create view. However, if I do this, the Create page is not displayed anymore.
I have the following model:
public class ProjectComment
{
public int Id { get; set; }
public int? ProjectId { get; set; }
public Project Project { get; set; }
public int RAGStatusId { get; set; }
public RAGStatus RAGStatus { get; set; }
public string StatusComment { get; set; }
public string EscalationComment { get; set; }
public string GeneralComment { get; set; }
public double? EOQ { get; set; }
public DateTime LastUpdateDate { get; set; }
public ProjectComment ()
{
this.LastUpdateDate = DateTime.UtcNow;
}
The create form Create.cshtml:
#page
#model SimpleProjectReporting.Pages.ClientDetails.CreateModel
#{
ViewData["Title"] = "Create";
}
<h1>Create</h1>
<h4>ProjectComment</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form method="post">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="ProjectComment.ProjectId" class="control-label"></label>
<select asp-for="ProjectComment.ProjectId" class="form-control" asp-items="ViewBag.ProjectId"><option value="" default="" selected="">-- Select --</option></select>
</div>
<div class="form-group">
<label asp-for="ProjectComment.RAGStatusId" class="control-label"></label>
<select asp-for="ProjectComment.RAGStatusId" class="form-control" asp-items="ViewBag.RAGStatusId"><option value="" default="" selected="">-- Select --</option></select>
</div>
<div class="form-group">
<label asp-for="ProjectComment.StatusComment" class="control-label"></label>
<input asp-for="ProjectComment.StatusComment" class="form-control" />
<span asp-validation-for="ProjectComment.StatusComment" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="ProjectComment.EOQ" class="control-label"></label>
<input asp-for="ProjectComment.EOQ" class="form-control" />
<span asp-validation-for="ProjectComment.EOQ" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</form>
</div>
</div>
The original Create.cshtml.cs action:
[BindProperty]
public ProjectComment ProjectComment { get; set; }
public IActionResult OnGet()
{
ViewData["ProjectId"] = new SelectList(_context.Project.Where(a => a.IsArchived == false), "Id", "ProjectName");
ViewData["RAGStatusId"] = new SelectList(_context.RAGStatus.Where(a => a.IsActive == true), "Id", "RAGStatusName");
return Page();
}
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see https://aka.ms/RazorPagesCRUD.
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
_context.ProjectComment.Add(ProjectComment);
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
The modified Create.cshtml.cs OnGet action:
public async Task<IActionResult> OnGetAsync(int? id)
{
if (id == null)
{
return NotFound();
}
ProjectComment = await _context.ProjectComment
.Include(p => p.Project)
.Include(p => p.RAGStatus).FirstOrDefaultAsync(m => m.Id == id);
if (ProjectComment == null)
{
return NotFound();
}
When modifying the action the way I did it, the page is not displayed anymore (404 error).
I would like to populate the create form with the data from the last entry in the database. If there is no comment, the create page would only populate the name of the project.
You are not sending the "id" parameter to your post action I guess.
So could you please try to adding this line under your form tag:
<form method="post">
<input type="hidden" id="ProjectComment.Id" name="id" value="ProjectComment.Id" />
You are trying to reach the last record of your ProjectComment table.
There are more than one methods to find the last record of your data table. But lets keep it simple.
You have an integer based identity column, which is Auto Increment. So you can simply use below methods to reach out the last created data of your table.
In your OnGetAsync() method:
//maxId will be the maximum value of "Id" columns. Which means that the maximum value is the last recorded value.
int maxId = _context.ProjectComment.Max(i => i.Id);
//And this line will bring you the last recorded "ProjectComment" object.
var projectComment = _context.ProjectComment.Find(maxId);
//You can assign it to your above 'ProjectComment' property if you want to..
ProjectComment = projectComment
Now, since you've find the last recorded data in your database, you can use that object.
Firstly, thanks to Burak for providing the above solution, which works when you want to display the last row in the table. This helped me solving my issue by using the same approach and finding the record based on the Id of the record.
I have amended the code from the Create.cshtml.cs file as follows:
public async Task<IActionResult> OnGetAsync(int? id, int projectid)
{
//This will find the "ProjectComment" object.
var projectComment = _context.ProjectComment.Find(id);
//This will display the 'ProjectComment' on the page
ProjectComment = projectComment;
if (id == null)
{
ProjectComment = projectComment;
ViewData["ProjectId"] = new SelectList(_context.Project, "Id", "ProjectName", projectid);
return Page();
}
ViewData["ProjectId"] = new SelectList(_context.Project, "Id", "ProjectName");
return Page();
}
I am using the int projectid to populate the drop down menu of the project when there is no comment create yet.

Resources