I am getting an error in my MVC C# application. Although i am not getting following error in development environment, not even functionality is breaking, but when deploying it at IIS server, I am getting this error through elmah via emails with corresponding entry in the database
The parameters dictionary contains a null entry for parameter 'id' of non-nullable type 'System.Int32' for method 'System.Web.Mvc.ActionResult DetailSmallForeignWidget(System.String, Int32, System.String, System.Nullable`1[System.Int32], System.Nullable`1[System.DateTime], System.Nullable`1[System.Int32], System.String, System.String)' in 'BAR.Web.Controllers.ForeignRestaurantWidgetController'. An optional parameter must be a reference type, a nullable type, or be declared as an optional parameter.
After making id parameter nullable I got to know that the corresponding action method is getting called twice. Reviewing the code it looks fine but don't know why this is happening.
routes for the action method is as follows:
_routes.MapRoute(null, "widget/DetailWidget/{name}/{id}/{langID}/",
new { controller = "Widget", action = "DetailWidget" },
new[] { "BAR.Web.Controllers" });
Function signature is as follows:
public override ActionResult DetailWidget(
string name,
int id,
string refCode,
int? partySize,
DateTime? when,
int? sittingTimeId,
string time,
string langID)
<script type="text/javascript" src="http://localhost/widget/IncludeWidget/Street/205/iframe/ja-JP" ></script>
Corresponding route
_routes.MapRoute(null, "widget/IncludeWidget/{name}/{id}/{mode}/{langID}/",
new { controller = "Widget",
action = "IncludeWidget" },
new[] { "BAR.Web.Controllers" });
Inside Includewidget func. code is something like below:
ContentResult content = new ContentResult();
string url = Url.ToAbsoluteUrl(string.Format("widget/DetailWidget/{0}/{1}/{2}", name, id.Value, langID), true);
content.Content = #"document.write('<iframe src=""" + url + #""" width=""300"" height=""430"" style=""border: none;"" frameborder=""0""></iframe>')";
content.ContentType = "text/javascript";
return content
I tried to rename the route parameters and tried to script references from the view page, but still it is showing the same error based on new parameter name. How can I fix this issue?
Related
I have a web-based game built using .netCore 2 and entity framework.
The game works, but I just added a new feature. In one of the views, the user can press a button that creates a new character via a controller.
Here is that controller method:
public async Task<IActionResult> AddCharacter(Guid playerId)
{
ICharManagement charMgr = new CharManagementClient();
Character newCharacter = await charMgr.AddCharacterAsync(
"Test Character",
new Guid("1e957dca-3fe1-4214-b251-a96e0106997a")
);
var newCharacterObject = newCharacter;
return View(newCharacterObject);
}
The Character object is from an API I use and it looks like this:
public partial class Character : object
{
private System.Guid Id;
private string Name;
private System.Nullable<System.DateTime> DateCreated;
private int32 CharPlayer;
private bool Playable;
}
Stepping through the controller in Visual Studio, I can see that a new Character object is returned and the newCharacterObject looks like this:
Id "1e957dca-3fe1-4214-b251-a96e0106997a"
Name "Test Character"
DateCreated "2/12/2019"
CharPlayer 3821
Playable "true"
In my view (cshtml), I have the model set to this:
#model IEnumerable<Character>
However, when I run it, I get this error:
InvalidOperationException: The model item passed into the
ViewDataDictionary is of type 'Character', but this
ViewDataDictionary instance requires a model item of type
'System.Collections.Generic.IEnumerable`1[Character]'.
So how do I get my view to display a single Character object(newCharacterObject) or how would I convert "newCharacterObject" into what the view needs?
Thanks!
Edit: Formatting
If you only need one character model, you can just use #model Character at the top of your cshtml
IEnumerable is used for iterating through a collection of objects.
I have a jquery method which looks like this:
$.post("/api/amazon/signature", { "policy": policy }, function (data) {
console.log(data);
});
the api method looks like this~:
// POST api/amazon/signature
[HttpPost]
[Route("api/amazon/signature")]
public IHttpActionResult GetSignature([FromBody]string policy)
{
var bKey = Encoding.ASCII.GetBytes(ConfigurationManager.AppSettings["AWSSecretKey"]);
var hmacSha1 = new HMACSHA1(bKey);
var bPolicy = Encoding.ASCII.GetBytes(policy);
var hash = hmacSha1.ComputeHash(bPolicy);
var encoded = Convert.ToBase64String(hash);
return Ok(encoded);
}
but when I run this code policy is always null!
If I change my method to this:
public class Signature
{
public string Policy { get; set; }
}
// POST api/amazon/signature
[HttpPost]
[Route("api/amazon/signature")]
public IHttpActionResult GetSignature([FromBody]Signature model)
{
var bKey = Encoding.ASCII.GetBytes(ConfigurationManager.AppSettings["AWSSecretKey"]);
var hmacSha1 = new HMACSHA1(bKey);
var bPolicy = Encoding.ASCII.GetBytes(model.Policy);
var hash = hmacSha1.ComputeHash(bPolicy);
var encoded = Convert.ToBase64String(hash);
return Ok(encoded);
}
and modify my jquery to this:
$.post("/api/amazon/signature", { "Policy": policy }, function (data) {
console.log(data);
});
it works fine....
Can someone tell me why?
ASP.NET Web API binds the request body in its entirety to one parameter (one parameter only and not more). By default, body is bound to a complex type. So, when you change the parameter type to Policy which is a complex type, you don't need to actually specify FromBody. Also binding works correctly now because you are sending JSON Object which looks something like this { "policy": policy }. Web API has no trouble in binding JSON object to your complex type.
When it comes to a simple type, string in your case, you must specify FromBody, since by default Web API binds from URI path and query string. In that case however, you cannot send a JSON Object. Web API is going to bind the entire body to that parameter, which is string. So, the request body must be just a string like this - "ABC123" and not a JSON object. If you send just "ABC123" (including the quotes) in the request body, your string parameter will be populated with ABC123.
I have a generic DateTime editor template that should format all of my DateTime properties with the required format("dd/MM/yyyy"):
#Html.TextBox(string.Empty, Model.ToString("dd/MM/yyyy"), new { #class = "datepicker" })
but for some reason the format is not working for my child actions.
If I do #Html.EditorFor(x => x.MyDate) in MainPage.cshtml
I get the expected result of: "23/04/2012"
If I do #Html.EditorFor(x => x.MyDate) in ChildAction.cshtml
I get the unexpected result of: "2012-04-24"
I can confirm that the Editor Template is being used, because if I change it like so (notice the WTF string at the beginning):
WTF #Html.TextBox(string.Empty, Model.ToString("dd/MM/yyyy"), new { #class = "datepicker" })
Then if I do #Html.EditorFor(x => x.MyDate) in ChildAction.cshtml
I get: "WTF 2012-04-24"
Any ideas?
Update
In my quest to reproduce this error in the MVC sample app, I found that two other things are required:
public class DateTimeModelBinder : DefaultModelBinder
{
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var value = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
DateTime date;
if (DateTime.TryParseExact(value.AttemptedValue, "dd/MM/yyyy", DateTimeFormatInfo.CurrentInfo, DateTimeStyles.AssumeLocal, out date))
{
return date;
}
return base.BindModel(controllerContext, bindingContext);
}
}
I'm assuming everyone knows how to wire that up. Also in the MainPage.cshtml you must pass the model in as the second parameter to RenderAction
#{Html.RenderAction("Child", Model);}
When passing the model as the second parameter, MVC uses the model binder to call the child action (why?). However, the format of the date is MM/dd/yyyy so it doesn't work with my model binder which assumes all my dates will be in dd/MM/yyyy format. That's why this isn't working.
But how do I fix it? The only place where the dates don't match my default format of dd/MM/yyyy is when rendering a child action.
The problem
MVC uses modelstate values if possible but the values are different in the model state of the parent action vs the child action:
In the parent action, the model state contains a string for the date submitted to the action (RawValue in the valueproviderresult) or a default value.
When the parent action passes the information to the child action
(renderaction) it passes a dateTime in the model state (RawValue in
the valueproviderresult).
So in the parent action, if no value is submitted, the value provided in Textbox(...) is correctly used. If a valid value is submitted by the client in the same format, it appears to be ok too since it takes the string in the model state which is identical to the one generated in the displaytemplate.
But in the child action it uses the modelstate which contains a datetime instead of the submitted string, MVC then convert that datetime to a string using invariantculture so it will show MM/dd/yyyy (I'm 3 years later so the value may have changed in the framework since you are having yyyy-MM-dd)
The solution
You need to replace the values in the model state with what you want or remove it for the affected values.
public static void Fix(ModelStateDictionary dic, string key)
{
ModelState modelState = dic[key];
if (!modelState.Errors.Any() && modelState.Value.RawValue != null
&& (modelState.Value.RawValue is DateTime || modelState.Value.RawValue is DateTime?))
{
var sValue = ((DateTime)modelState.Value.RawValue).ToString("dd/MM/yyyy");
var value = new ValueProviderResult(sValue, sValue, CultureInfo.InvariantCulture);
dic.SetModelValue(key, value);
//or
//dic.Remove(key);
}
}
and then call that method in your child action:
Fix(ModelState, nameof(viewModel.DateMax));
I am using MEF to dynamically load controllers in an MVC3 app.
In the export metadata I am specifying two meta data constraints
EX:
[ExportMetadata("controllerName", "APSR")]
[ExportMetadata("controllerVersion", "1.0.0.0")]
In my "main" mvc app, I am using a RedirectToAction method (In reponse to a user click on a dropdown)
[HttpPost]
public ActionResult Index(Models.HomeViewModel selected)
{
//ViewData.Add("Version", selected.AvailableWorkflows[int.Parse(selected.SelectedWorkflow)].Version);
return RedirectToAction("Create", selected.AvailableWorkflows[int.Parse(selected.SelectedWorkflow)].Controller);
}
How can I pass the desired version number to my Controller factory? Since the IControllerFactory.CreateController method only excepts to paramters:
IController IControllerFactory.CreateController(System.Web.Routing.RequestContext requestContext, string controllerName)
I would imagine you need some additional route data, and reading that when creating your controller.
For instance, I could define a route as:
routes.MapRoute(
"APSR_Create",
"/apsr/{version}/create",
new {
controller = "APSR",
action = "Create",
version = "1.0.0.0"
});
Now, when I create an instance of my controller, I can grab that version item from the RequestContext.RouteData collection:
public IController IControllerFactory.CreateController(System.Web.Routing.RequestContext requestContext, string controllerName)
{
string version = requestContext.RouteData["version"];
// Create instance using metadata lookup...
}
You just need to ensure that you are passing the version as an argument to the route.
return RedirectToAction(
"Create",
new { version = selected.AvailableWorkflows[int.Parse(selected.SelectedWorkflow)].Version });
I'm going to simplify my route declaration around of my asp.net mvc project. So I have created several method to do this.
context.MapExtendedRoute("Home".MergeWithAreaName(context),
"www".MergeWithAppDomain(),
"123".MergeWithDefaultRouteKeys(),
new {Controller = "Home", Action = "Index"}.MergeWithDefaultRouteValues(),
new {}.MergeWithDefaultRouteConstraints());
There isn't any problem with first three methods. But method 4 and 5 returns invalid value. The initial signature of (for example MergeWithDefaultRouteValues) is something like this:
public static object MergeWithDefaultRouteValues(this object defaultValues) {
return new RouteValueDictionary(defaultValues) {{"Culture", "SomeValue"}};
}
Which returns output like this(from RouteDebugger):
Count = 3, Keys = System.Collections.Generic.Dictionary`2+KeyCollection[System.String,System.Object], Values = System.Collections.Generic.Dictionary`2+ValueCollection[System.String,System.Object]
You can see none of Keys/Values is correct! Can anybody tell me how i can figure it out?
Signature of MapExtendedRoute:
MapExtendedRoute(this AreaRegistrationContext context, string name, string domain, string url, object defaults, object constraints)
Thanks in advance ;)
I resolved this issue by adding new overload to MapExtendedRoute method and change signature to this:
MapExtendedRoute(this AreaRegistrationContext context, string name, string domain, string url, RouteValueDictionary defaults, RouteValueDictionary constraints)
The problem is from casting RouteValueDictionary to object, then casting back object to RouteValueDictionary! So Route Keys/Values come from first RouteValueDictionary Properties/Values.
var defaultValues = new {};
//this works fine
var x = new RouteValueDictionary(defaultValues) {{"Culture", null}};
//But when x casted to object
var obj = (object)x;
//And obj casted back to RouteValueDictionary
var x2 = new RouteValueDictionary(obj);
//Everything goes to be fail!