1

I'm having a weird one here. I'm doing Microsoft's Razor and EF tutorial and I don't know what's wrong with this code:

public class Student
{
    public int ID { get; set; }
    public string LastName { get; set; }
    public string FirstMidName { get; set; }
    public DateTime EnrollmentDate { get; set; }

    public ICollection<Enrollment> Enrollments { get; set; }
}
//CRUD Create Method
public async Task<IActionResult> OnPostAsync()
{
    var emptyStudent = new Student();

    if (await TryUpdateModelAsync<Student>(
        emptyStudent,
        "Student",
        s => s.FirstMidName,
        s => s.LastName,
        s => s.EnrollmentDate))
    {
        _context.Students.Add(emptyStudent);
        await _context.SaveChangesAsync();
        return RedirectToPage("./Index");
    }

    return Page();
}

The form is as follows:

<form method="post">
    <div asp-validation-summary="ModelOnly" class="text-danger"></div>

    <div class="form-group">
        <label asp-for="Student.LastName" class="control-label"></label>
        <input asp-for="Student.LastName" class="form-control" />
        <span asp-validation-for="Student.LastName" class="text-danger"></span>
    </div>

    <div class="form-group">
        <label asp-for="Student.FirstMidName" class="control-label"></label>
        <input asp-for="Student.FirstMidName" class="form-control" />
        <span asp-validation-for="Student.FirstMidName" class="text-danger"></span>
    </div>

    <div class="form-group">
        <label asp-for="Student.EnrollmentDate" class="control-label"></label>
        <input asp-for="Student.EnrollmentDate" class="form-control" />
        <span asp-validation-for="Student.EnrollmentDate" class="text-danger"></span>
    </div>

    <div class="form-group">
        <input type="submit" value="Create" class="btn btn-primary" />
    </div>
</form>

I have debugged and analyzed the traffic. I do know that I'm receiving the form with the values and actually, by using Request.Form["student.FirstMidName"] and so on, I can populate emptyStudent.

I still don't understand what's wrong, since the code, apparently, is fine. However, TryUpdateModelAsync is returning false.

Any idea why is this happening?

4
  • Is Student.Id marked as auto-generated on insert? If not, it would need a value to be valid. Commented Apr 2 at 20:48
  • 2
    Check ModelState. There should be errors set after TryUpdateModelAsync runs. ModelState is a property in Controller class. Please update your Answer if this helped or if you need further assistance. Commented Apr 2 at 23:00
  • 2
    For collection navigation properties, always initialize them to avoid possible issues with #null. public ICollection<Enrollment> Enrollments { get; } = []; Never expose a setter as with tracked entities you should also never reset a collection reference. Commented Apr 3 at 1:14
  • @pjs, actually yes. Student.Id is marked for auto-generation :). Thank you Commented Apr 3 at 9:06

2 Answers 2

2

Expanding on Basheer Jarrah's answer, Nullable context was enabled by default starting in .NET 6.

The method TryUpdateModelAsync invokes model binding manually, per Microsoft's documentation. A return value of false indicates model-binding failed.

include it in the form and params of TryUpdateModelAsync() method.

You should avoid assigning a complex type value with a lambda expression like: s => s.Enrollments

The following declaration sets the Enrollments property to nullable:

public ICollection<Enrollment>? Enrollments { get; set; }

Another option is to disable the Nullable context feature. In Program.cs, set the following option for Razor Pages:

builder.Services.AddRazorPages()
    .AddMvcOptions(x=> x.SuppressImplicitRequiredAttributeForNonNullableReferenceTypes = true);
Sign up to request clarification or add additional context in comments.

1 Comment

> Nullable context will be enabled by default since .net 6,you may see this document That URL seems broken. Did you mean this one? If so, please edit it, because in the tutorial there is no reference about that and I think this is going to be useful for many people. Thank you.
1

This issue occurs when the navigation property Enrollments is not nullable. Based on the ASP .NET Core docs for non-nullable types:

The validation system treats non-nullable parameters or bound properties as if they had a [Required(AllowEmptyStrings = true)] attribute.

So, either you make Enrollments collection nullable, or include it in the form and params of TryUpdateModelAsync() method.

1 Comment

if I added, even as a hidden field, Enrollments, It could become an overposting vulnerability, making possible some attacks. Finally, I have decided to make Enrollments nullable.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.