.Net Core 3.1 to .Net 6 breaks model validation

Michael Mastro II 51 Reputation points
2022-03-11T16:00:38.73+00:00

Good morning, I have a MVC view that was working fine in .Net Core 3.1. The view is essentially three sections. An input box for the name, a row of radio buttons, and field of checkboxes. This worked perfectly fine. I made no changes to my ViewModels, View, or Controller. The only change I made was on the project xml file, changing the TargetFramework from netcoreapp3.1 to net6.0. Now when I go to the view and make a change, whether the name or a radio button or a checkbox, I hit the submit button, I get validation returns that is essentially saying each Checkbox and Radio button must be checked. I put a breakpoint in the post controller right at ModelState.IsValid and it is not hitting the break point. Was there a breaking change I missed in the documentation? How do I make this work correctly?

Here is information from controller, views, and ViewModels. Again no changes to any of this, which worked in .Net Core 3.1.
Controller:

// GET: Factions/Edit/5
    public async Task<IActionResult> Edit( int? id )
    {
        //If the FactionID is null then return the error, else get the databse information and display it
        if ( id==null )
        {
            return BadRequest();
        }
        //get the information from the database for the Faction
        FactionViewModel eFaction = await _dts.GetFactionViewModelByIdAsync(id.Value);

        //If the database information does not exist, return not found
        if ( eFaction==null )
        {
            return NotFound();
        }
        //Display the View
        return View( eFaction );
    }

    // POST: Factions/Edit/5
    [HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> Edit( FactionViewModel returnedView )
    {
        //If the returnedView model state is valid, save the changes to the database
        if ( ModelState.IsValid )
        {
            await _dts.UpdateFactionAsync(returnedView);
            return RedirectToAction( "Index" );
        }

        //generate the view if something is wrong, use the current ID
        var id = returnedView.FactionNames.FactionID;

        FactionViewModel newView = await _dts.GetFactionViewModelByIdAsync(id);
        //Use the information the user typed
        newView.FactionNames = new FactionNamesListViewModel()
        {
            FactionID = returnedView.FactionNames.FactionID,
            FactionName = returnedView.FactionNames.FactionName
        };
        //Place the selected Reputation Type in the View
        newView.ReputationTypesID = returnedView.ReputationTypesID;
        //Display the View
        return View( newView );
    }

View Models
FactionViewModel:

public FactionViewModel()
    {
        this.FactionNames = new FactionNamesListViewModel();
        this.ReputationTypes = new List<ReputationTypeNamesListViewModel>();
        this.ReputationLevels = new List<ReputationLevelNamesListViewModel>();
    }
    public FactionNamesListViewModel FactionNames { get; set; }
    [Required]
    [Display(Name = "Reputation Type to be associated with Faction")]
    public int ReputationTypesID { get; set; }
    public List<ReputationTypeNamesListViewModel> ReputationTypes { get; set; }
    [Required]
    [Display(Name = "Reputation Levels that apply to the Faction")]
    public List<ReputationLevelNamesListViewModel> ReputationLevels { get; set; }

FactionsNamesListViewModel:

[HiddenInput]
    public int FactionID { get; set; }
    [Required]
    [Display(Name = "Name")]
    public string FactionName { get; set; }
    public bool IsSelected { get; set; }

ReputationTypeNamesListViewModel:

public class ReputationTypeNamesListViewModel
{
    public int ReputationTypeID { get; set; }

    [Display(Name = "Reputation Type Name")]
    public string ReputationTypeName { get; set; }

    public bool IsSelected { get; set; }
}

ReputationLevelNamesListViewModel:

public class ReputationLevelNamesListViewModel
{        
    public int ReputationLevelID { get; set; }
    public string ReputationLevelName { get; set; }
    public bool IsChecked { get; set; }
}

Data Calls:

public async Task<FactionViewModel> GetFactionViewModelByIdAsync(int id)
    {
        FactionViewModel output = new()
        {
            FactionNames = await GetFactionNamesListViewModelsAsync(id),
            ReputationTypes = await _repTypes.GetReputationTypesNamesListViewModelByIdAsync(id),
            ReputationLevels = await _repLevels.GetReputationLevelNamesViewModelByIdAsync(id)
        };
        return output;
    }

public async Task<FactionNamesListViewModel> GetFactionNamesListViewModelsAsync(int id)
    {
        return await _db.Set<FactionNamesListViewModel>().FromSqlRaw(sql: "SELECT Faction_ID AS FactionID, Faction_Name AS FactionName, CAST (1 AS bit) AS IsSelected  FROM Faction WHERE Faction_ID = @p0", parameters: new object[] { id }).FirstOrDefaultAsync();
    }

public async Task<List<ReputationTypeNamesListViewModel>> GetReputationTypesNamesListViewModelByIdAsync(int id)
    {
        List<ReputationTypeNamesListViewModel> output = await _db.Set<ReputationTypeNamesListViewModel>().FromSqlRaw(sql: "SELECT Reputation_Type_ID AS ReputationTypeID, Reputation_Type_Name AS ReputationTypeName, CAST (CASE WHEN  Reputation_Type_ID IN (SELECT ReputationTypeReputation_Type_ID FROM Faction WHERE Faction_ID = @p0) THEN 1 ELSE 0 END AS bit) AS IsSelected FROM ReputationType WHERE ActiveRecord = 1", parameters: new object[] { id }).ToListAsync();
        return output;
    }

public async Task<List<ReputationLevelNamesListViewModel>> GetReputationLevelNamesViewModelByIdAsync(int id)
    {
        List<ReputationLevelNamesListViewModel> output = await _db.Set<ReputationLevelNamesListViewModel>().FromSqlRaw(sql: "GetRepLevelList @p0", parameters: new object[] { id }).ToListAsync();
        return output;
    } 

View:

@{
    ViewData[ "Title" ] = "Edit";
    Layout = "~/Views/Shared/_GameDataLayout.cshtml";
}
@model Lotro.ViewModelClass.Dtl.Models.FactionViewModels.FactionViewModel

<h2>Edit</h2>

<form asp-controller="Factions" asp-action="Edit" method="post">
    <div class="form-horizontal">
        <h4>Factions</h4>
        <hr />
        <div asp-validation-summary="All" class="text-danger"></div>
        <div class="form-group row">
            <label asp-for="@Model.FactionNames.FactionName" class="control-label col-lg-2"></label>
            <div class="col-lg-10">
                <input type="hidden" asp-for="@Model.FactionNames.FactionID" />
                <input asp-for="@Model.FactionNames.FactionName" class="form-control" />
            </div>
        </div>
        <div class="form-group row">
            <label asp-for="@Model.ReputationTypesID" class="control-label col-lg-12" style="text-align:center"></label>
            <div class="col-lg-12 row">
                @for ( var i = 0; i < @Model.ReputationTypes.Count; i++ )
                {
                    <div class="col-lg-2">
                        <input type="hidden" asp-for="@Model.ReputationTypes[i].ReputationTypeID" />
                        <input type="radio" asp-for="@Model.ReputationTypesID" value="@Model.ReputationTypes[i].ReputationTypeID" checked="@Model.ReputationTypes[i].IsSelected" />
                        <b>@Html.DisplayFor( model => model.ReputationTypes[ i ].ReputationTypeName )</b>
                    </div>
                }
            </div>
        </div>
        <div class="form-group row">
            <label asp-for="@Model.ReputationLevels" class="control-label col-lg-12" style="text-align:center"></label>
            <div class="col-lg-12 row">
                @{ int j = 0; }
                @for ( var i = 0; i < Model.ReputationLevels.Count; i++ )
                {
                    <input type="hidden" asp-for="@Model.ReputationLevels[i].ReputationLevelID" />
                    <div class="col-lg-1">
                        <input type="checkbox" asp-for="@Model.ReputationLevels[i].IsChecked" />
                    </div>
                    <div class="col-lg-2">
                        @Html.DisplayFor( model => model.ReputationLevels[ i ].ReputationLevelName )
                    </div>
                    j++;
                    if ( j == 4 )
                    {
                        <div class="col-lg-12"><br /></div>
                        j = 0;
                    }
                    else
                    {
                        continue;
                    }
                }
            </div>
        </div>
        <div class="form-group row">
            <div class="col-lg-offset-2 col-lg-10">
                <input type="submit" value="Save" class="btn btn-secondary" />
            </div>
        </div>
    </div>
    <div>
        <a asp-action="Index">Back to List</a>
    </div>
</form>
ASP.NET Core
ASP.NET Core
A set of technologies in the .NET Framework for building web applications and XML web services.
4,612 questions
{count} votes

1 answer

Sort by: Most helpful
  1. Bruce (SqlWork.com) 66,706 Reputation points
    2022-03-11T16:25:51.65+00:00

    if you change the names in the form, you need to make the same changes in post back model. if you remove a form field, remove the matching property from the post back model or use a binding list on the action (a proper model is a better choice).

    you can also make the model fields nullable so they are optional.


Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.