Home > database >  How to view model binding exceptions locally
How to view model binding exceptions locally

Time:11-08

I have an ASP.NET web app running in an Azure app service.

After doing a profiler trace, I noticed these three .NET exceptions:

Requested value 'Asc' was not found.

Asc is not a valid value for SortOrder.

The parameter conversion from type 'System.String' to type 'Enums.SortOrder' failed. See the inner exception for more information.

They all have this stack trace:

mscorlib.ni![COLD] System.Enum EnumResult.SetFailure
mscorlib.ni!System.Enum.Parse
system.ni!
system.web.http!System.Web.Http.ValueProviders.ValueProviderResult.ConvertSimpleType
system.web.http!System.Web.Http.ValueProviders.ValueProviderResult.UnwrapPossibleListType
system.web.http!System.Web.Http.ValueProviders.ValueProviderResult.ConvertTo
system.web.http!System.Web.Http.ModelBinding.Binders.TypeConverterModelBinder.BindModel
system.web.http!System.Web.Http.Controllers.HttpActionContextExtensions.Bind
system.web.http!System.Web.Http.ModelBinding.Binders.CompositeModelBinder.BindModel
system.web.http!System.Web.Http.ModelBinding.ModelBinderParameterBinding.ExecuteBindingAsync
system.web.http!System.Web.Http.Controllers.HttpActionBinding <ExecuteBindingAsyncCore>d__12.MoveNext
mscorlib!System.Runtime.CompilerServices.AsyncTaskMethodBuilder.Start
system.web.http!System.Web.Http.Controllers.HttpActionBinding.ExecuteBindingAsyncCore
system.web.http!System.Web.Http.Controllers.HttpActionBinding.ExecuteBindingAsync
system.web.http!System.Web.Http.Controllers.ActionFilterResult <ExecuteAsync>d__5.MoveNext
mscorlib!System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1[System.__Canon].Start
system.web.http!System.Web.Http.Controllers.ActionFilterResult.ExecuteAsync
system.web.http!System.Web.Http.ApiController.ExecuteAsync
system.web.http!System.Web.Http.Dispatcher.HttpControllerDispatcher <SendAsync>d__15.MoveNext
mscorlib!System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1[System.__Canon].Start
system.web.http!System.Web.Http.Dispatcher.HttpControllerDispatcher.SendAsync
system.net.http.ni!
system.web.http!System.Web.Http.Dispatcher.HttpRoutingDispatcher.SendAsync
system.net.http.ni!
autofac.integration.webapi!Autofac.Integration.WebApi.CurrentRequestHandler.SendAsync
system.net.http.ni!
Rend.invgen.invoicegateway.api!Rend.invgen.InvoiceGateway.Api.Handlers.RequestResponseLogHandler.SendNextAsync
Rend.invgen.invoicegateway.api!Rend.invgen.InvoiceGateway.Api.Handlers.RequestResponseLogHandler <>c__DisplayClass0_0.<SendAsync>b__0
mscorlib.ni!System.Threading.Tasks.Task.Execute
mscorlib.ni!System.Threading.Tasks.Task.ExecutionContextCallback
mscorlib.ni!System.Threading.ExecutionContext.Run
mscorlib.ni!System.Threading.Tasks.Task.ExecuteWithThreadLocal
mscorlib.ni!System.Threading.Tasks.Task.ExecuteEntry
mscorlib.ni!System.Threading.Tasks.SynchronizationContextTaskScheduler.PostCallback
system.web.ni!
mscorlib.ni!System.Threading.Tasks.Task.Execute
mscorlib.ni!System.Threading.Tasks.Task.ExecutionContextCallback
mscorlib.ni!System.Threading.ExecutionContext.Run
mscorlib.ni!System.Threading.Tasks.Task.ExecuteWithThreadLocal
mscorlib.ni!System.Threading.Tasks.Task.ExecuteEntry
mscorlib.ni!System.Threading.Tasks.Task.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem
mscorlib.ni!System.Threading._ThreadPoolWaitCallback.PerformWaitCallback

My action method which is causing this issue looks like this:

public async Task<IHttpActionResult> GetAsync(SortOrder sort = SortOrder.Ascending)
{
    // sort something
}

This action method is called using arguments such as Asc.

Even though this seems to cause .NET exceptions, the default value of Ascending gets used if it can't bind a value.

My question is, why am I unable to view the Requested value Asc was not found exceptions locally?

When I run the app locally and pass Asc and Desc, no Exceptions are thrown, and I can't see any Exceptions in the Debug window either.

CodePudding user response:

I have reproduced and able to resolve, please follow the below steps

  • Below is the folder structure

enter image description here

ActionModel.cs

  • To bind the data to the view we are using action model
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Data.SqlClient;
using System.Linq;
using System.Threading.Tasks;
using System.Web.Http;
using System.Web.Mvc;

namespace MVC_Sort
{
    public class ActionModel
    {
        public ActionModel()
        {
            ActionsList = new List<SelectListItem>();
        }
        [Display(Name="Names")]
        public int ActionId { get; set; }

        public IEnumerable<SelectListItem> ActionsList { get; set; }       
    }
}

ActionType.cs

  • A list of Enum to display in sorted order
namespace MVC_Sort
{
    public enum ActionType
    {
        MMM = 1,
        RRR = 2,
        AAA = 3,
        JJJ = 4,
        EEE = 5,
        SSS = 6,
        HHH = 7
    }

}

ActionTypeModel.cs

  • In view it is used to display the Enum values and label names in the output
using System.ComponentModel.DataAnnotations;
namespace MVC_Sort
{
    public class ActionTypeModel
    {
        [Display(Name = "Names")]
        public int ActionId { get; set; }
        public ActionType ActionTypeList { get; set; }
    }
}

HomeController.cs

  • The Action method in the HomeController is executed when requested
namespace MVC_Sort.Controllers
{
    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            ActionModel model = new ActionModel();
            IEnumerable<ActionType> actionTypes = Enum.GetValues(typeof(ActionType))
                                                       .Cast<ActionType>();

            var res= actionTypes.OrderBy(enm => enm.ToString()).ToArray();

            model.ActionsList = from action in res
                                select new SelectListItem
                                {
                                    Text = action.ToString(),
                                    Value = ((int)action).ToString()
                                };

            return View(model);
        }

        public ActionResult ActionTypes()
        {
            ActionTypeModel model = new ActionTypeModel();
            return View(model);
        }
    }
}

Index.cshtml

  • It is the Html page displayed when executed
model MVC_Sort.ActionModel
@{
    ViewBag.Title = "Index";
}

@Html.LabelFor(model=>model.ActionId)
@Html.DropDownListFor(model=>model.ActionId, Model.ActionsList)

Extension.cs

  • It is used to fetch or retrieve Enum values from ActionType
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Web.Mvc;
using System.Web.Mvc.Html;

namespace MVC_Sort
{
    public static class Extension
    {

        public static MvcHtmlString EnumDropDownListFor<TModel, TProperty, TEnum>(this HtmlHelper<TModel> htmlHelper,
                                                                                    Expression<Func<TModel, TProperty>> expression, 
                                                                                    TEnum selectedValue)
        {
            IEnumerable<TEnum> values = Enum.GetValues(typeof(TEnum))
                                        .Cast<TEnum>();

            IEnumerable<SelectListItem> items = from value in values
                                                select new SelectListItem()
                                                {
                                                    Text = value.ToString(),
                                                    Value = value.ToString(),
                                                    Selected = (value.Equals(selectedValue))
                                                };

            return SelectExtensions.DropDownListFor(htmlHelper,expression, items);
        }
    }
}
  • After the above steps Click on debug => Start debugging. After it will open the below one in the browser in Sorted order

enter image description here

CodePudding user response:

By default, Binding an Enum expects the integer value corresponding to the value.

If you want to supply a string value, you must tell the modelbinder that by adding an attribute to your Enum type declaration, like this:

[ModelBinder(BinderType = typeof(EnumModelBinder))]
public enum SortOrder
{
}

or you can add an attribute to your property in the model, like this:

[BindProperty(BinderType = typeof(EnumModelBinder))]
public SortOrder MySortOrder {get;set;}

Now the modelbinder should be able to handle strings. Of course the strings must match the enum values exact.

BTW: Modelbinding, that fails, does not throw exceptions, it simply ignores the values, meaning properties will get their default values.

  • Related