Home > front end >  ASP.NET Core MVC - Data binding from form data to view model
ASP.NET Core MVC - Data binding from form data to view model

Time:06-14

I’m working on an ASP.NET Core MVC application based on .NET 6 framework. I want to retrieve a value from my form that matches the property of a class member of my view model.

My abstract model for all my specific database entities :

public abstract class MyAbstractDatabaseEntity
{
    [Column(TypeName = "bit"), Required, MaxLength(1), Unicode(false)]
    public bool SafeDelete { get; set; } = false;

    [Column(TypeName = "datetime")]
    public DateTime CreationDate { get; set; }

    [Required, MaxLength(12), Unicode(false)]
    public string CreationUserID { get; set; }

    [Column(TypeName = "datetime")]
    public DateTime UpdateDate { get; set; }

    [Required, MaxLength(12), Unicode(false)]
    public string UpdateUserID { get; set; }
}

One of my specific database entities :

[Table("MyDatabaseTable")]
public class MyEntity : MyAbstractDatabaseEntity
{
    [Key]
    [Required]
    public int ID { get; set; }

    [Display(Name = "Account number"), Required, Unicode(false)]
    [RegularExpression(@"^(\d )$")]
    [MaxLength(11)]
    public string AccountNumber { get; set; }

    [Column(TypeName = "bit")]
    [MaxLength(1)]
    [Required]
    public bool IsDraft { get; set; } = false;
}

My view model

public class MyViewModel
{
    public MyEntity Entity { get; set; } = new();

    public string Title { get; set; }
}

My controller

public class MyHomeController : Controller
{
    // GET: MyHome/Index
    public IActionResult Index()
    {
        MyViewModel model = new()
        {
            Title = "My page - Search account details"
        };
        return View(model);
    }

    // POST: MyHome/FindAccountDetails
    [HttpPost, ValidateAntiForgeryToken]
    public ActionResult FindBankAccountDetails(MyViewModel model)
    {
        if (!ModelState.IsValid)
        {
            return View(model);
        }
        return PartialView("_Details", model);
    }
}

My view :

@using MyApplication
@model MyViewModel

<div >
    <div >
        <h2>@Model.Title</h2>
    </div>
    <div >
        @using (Html.AjaxBeginForm("FindBankAccountDetails", "MyHome", new AjaxOptions
         {
             HttpMethod = "POST",
             UpdateTargetId = "request-general-information",
             InsertionMode = InsertionMode.Replace
         }))
        {
            <div >
                <label asp-for="Entity.AccountNumber" >Account number</label>
                <input asp-for="Entity.AccountNumber"  />
                <span asp-validation-for="Entity.AccountNumber" ></span>
            </div>

            <button type="submit" >Search details</button>
        }
    </div>
</div>

<div  id="request-general-information"></div>

@section Scripts {
    @{
        await Html.RenderPartialAsync("_ValidationScriptsPartial");
    }
}

My problem: on validation, the view model is not recognized by the program. The program returns the following message in console :

Microsoft.AspNetCore.Hosting.Diagnostics: Information: Request starting HTTP/1.1 POST https://localhost:44323/MyHome/FindBankAccountDetails application/x-www-form-urlencoded; charset=UTF-8 283
Microsoft.AspNetCore.Routing.EndpointMiddleware: Information: Executing endpoint 'MyApplication.MyHomeController.FindBankAccountDetails (‘MyApplication)'
Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker: Information: Route matched with {action = "FindBankAccountDetails", controller = "MyHome"}. Executing controller action with signature Microsoft.AspNetCore.Mvc.ActionResult FindBankAccountDetails(MyApplication.MyViewModel) on controller MyApplication.MyHomeController (‘MyApplication).
Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker: Information: Executed action MyApplication.MyHomeController.FindBankAccountDetails (‘MyApplication) in 3.8675ms
Microsoft.AspNetCore.Routing.EndpointMiddleware: Information: Executed endpoint 'MyApplication.MyHomeController.FindBankAccountDetails (‘MyApplication)'
Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware: Error: An unhandled exception has occurred while executing the request.

System.InvalidCastException: The field of type System.Boolean must be a string, array or ICollection type.
   at System.ComponentModel.DataAnnotations.MaxLengthAttribute.IsValid(Object value)
   at System.ComponentModel.DataAnnotations.ValidationAttribute.IsValid(Object value, ValidationContext validationContext)
   at System.ComponentModel.DataAnnotations.ValidationAttribute.GetValidationResult(Object value, ValidationContext validationContext)
   at Microsoft.AspNetCore.Mvc.DataAnnotations.DataAnnotationsModelValidator.Validate(ModelValidationContext validationContext)
   at Microsoft.AspNetCore.Mvc.ModelBinding.Validation.ValidationVisitor.ValidateNode()
   at Microsoft.AspNetCore.Mvc.ModelBinding.Validation.ValidationVisitor.VisitSimpleType()
   at Microsoft.AspNetCore.Mvc.ModelBinding.Validation.ValidationVisitor.VisitImplementation(ModelMetadata& metadata, String& key, Object model)
   at Microsoft.AspNetCore.Mvc.ModelBinding.Validation.ValidationVisitor.Visit(ModelMetadata metadata, String key, Object model)
   at Microsoft.AspNetCore.Mvc.ModelBinding.Validation.ValidationVisitor.VisitChildren(IValidationStrategy strategy)
   at Microsoft.AspNetCore.Mvc.ModelBinding.Validation.ValidationVisitor.VisitComplexType(IValidationStrategy defaultStrategy)
   at Microsoft.AspNetCore.Mvc.ModelBinding.Validation.ValidationVisitor.VisitImplementation(ModelMetadata& metadata, String& key, Object model)
   at Microsoft.AspNetCore.Mvc.ModelBinding.Validation.ValidationVisitor.Visit(ModelMetadata metadata, String key, Object model)
   at Microsoft.AspNetCore.Mvc.ModelBinding.Validation.ValidationVisitor.VisitChildren(IValidationStrategy strategy)
   at Microsoft.AspNetCore.Mvc.ModelBinding.Validation.ValidationVisitor.VisitComplexType(IValidationStrategy defaultStrategy)
   at Microsoft.AspNetCore.Mvc.ModelBinding.Validation.ValidationVisitor.VisitImplementation(ModelMetadata& metadata, String& key, Object model)
   at Microsoft.AspNetCore.Mvc.ModelBinding.Validation.ValidationVisitor.Visit(ModelMetadata metadata, String key, Object model)
   at Microsoft.AspNetCore.Mvc.ModelBinding.Validation.ValidationVisitor.Validate(ModelMetadata metadata, String key, Object model, Boolean alwaysValidateAtTopLevel, Object container)
   at Microsoft.AspNetCore.Mvc.ModelBinding.ObjectModelValidator.Validate(ActionContext actionContext, ValidationStateDictionary validationState, String prefix, Object model, ModelMetadata metadata, Object container)
   at Microsoft.AspNetCore.Mvc.ModelBinding.ParameterBinder.EnforceBindRequiredAndValidate(ObjectModelValidator baseObjectValidator, ActionContext actionContext, ParameterDescriptor parameter, ModelMetadata metadata, ModelBindingContext modelBindingContext, ModelBindingResult modelBindingResult, Object container)
   at Microsoft.AspNetCore.Mvc.ModelBinding.ParameterBinder.BindModelAsync(ActionContext actionContext, IModelBinder modelBinder, IValueProvider valueProvider, ParameterDescriptor parameter, ModelMetadata metadata, Object value, Object container)
   at Microsoft.AspNetCore.Mvc.Controllers.ControllerBinderDelegateProvider.<>c__DisplayClass0_0.<<CreateBinderDelegate>g__Bind|0>d.MoveNext()
--- End of stack trace from previous location ---
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeInnerFilterAsync>g__Awaited|13_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResourceFilter>g__Awaited|25_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed context)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeFilterPipelineAsync()
--- End of stack trace from previous location ---
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Logged|17_1(ResourceInvoker invoker)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Logged|17_1(ResourceInvoker invoker)
   at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
   at Microsoft.AspNetCore.Session.SessionMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.Session.SessionMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.Session.SessionMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.Session.SessionMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.Diagnostics.StatusCodePagesMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.Diagnostics.StatusCodePagesMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware.<Invoke>g__Awaited|6_0(ExceptionHandlerMiddleware middleware, HttpContext context, Task task)
Microsoft.AspNetCore.Routing.EndpointMiddleware: Information: Executing endpoint '405 HTTP Method Not Supported'
Microsoft.AspNetCore.Routing.EndpointMiddleware: Information: Executed endpoint '405 HTTP Method Not Supported'
Microsoft.AspNetCore.Hosting.Diagnostics: Information: Request finished HTTP/1.1 POST https://localhost:44323/MyHome/FindBankAccountDetails application/x-www-form-urlencoded; charset=UTF-8 283 - 405 - text/plain 34.4795ms

The browser then presents this message: https://localhost:44323/MyHome/FindBankAccountDetails 405 (Method Not Allowed)

I can't reach FindBankAccountDetails controller method by using breakpoints.

Why doesn't MyViewModel view model get retrieved from FindBankAccountDetails controller method?

Thanks for your help.

CodePudding user response:

According to documentation property [MaxLength()] specifies the maximum length of array or string. IsDraft is boolean, this is why you get InvalidCastException.

[Column(TypeName = "bit")]
[MaxLength(1)]
[Required]
public bool IsDraft { get; set; } = false;
  • Related