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
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
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.