I have a form which posts these values:
survey[0].Key 75
survey[0].Value 4
survey[1].Key 76
survey[1].Value 4
I'm trying to use a default model binder to map it onto Dictionary type:
[HttpPost]
public ActionResult CompleteSurvey(Dictionary<int, int> answers)
{
...
}
but I get InvalidCastException: Specified cast is not valid.
Why??
Im just guessing here but i think It's because your argument is called 'answers' not 'survey'. You should look at what is being posted to your method by looking in the post headers easy to do in firebug or chrome. It makes more sense when you inspect the stuff being transmitted 'over the wire'
The cast not valid thing is probably because in your scenario answers is null when the model binder wants to do its job. If you used formscollection instead of dictionary you'd find everything you post is there.
For some strange reason when I removed "survey" and left iteration by itself (i.e. [0].Key) binding works fine.
I was basing my knowledge on this article:
http://www.hanselman.com/blog/ASPNETWireFormatForModelBindingToArraysListsCollectionsDictionaries.aspx
which appears to be wrong then??? or does it refer to the previous versions of MVC???
Related
This question already has answers here:
Setting the default JSON serializer in ASP.NET MVC
(2 answers)
Closed 8 years ago.
I just solved a recent problem where Json.Net allows me to include the 'type' of an object when it serializes it going to the Client. The reason for this is that the class that I'm sending back and forth has a property that is typed as a BaseClass to 4 Subclasses. The great thing now is that my client is showing each one correctly with the type attribute on the Json.
Now I need to send the object back to my Controller, not to an API controller. I've seen a bunch of posts about the api controller using Json.Net as the default JsonFormatter, but this doesn't work for a regular MVC Controller.
So my question is....How can I get my Controller to use the Json.Net Serializer by default? And...will this cause an issue for other items potentially making ajax calls?
I was able to send my json back to my controller as a string and then use JsonConvert to Deserialize my object successfully, so I know it can be done.
Any thoughts are appreciated.
EDIT:
Ok, so I followed the instructions here to change out the JsonValueProviderFactory and that seems to be working, however its not helping the original issue.
The problem is that the JsonFormatter is still the old one and not using Json.Net. So the ProviderFactory is building the dictionary correctly, but when it tries to Deserialize my object, I'm only getting my properties cast as the BaseClass, and not the Derived Class that I'm expecting.
Thoughts on that part?
EDIT:
Simply...how do I create my own JsonNetMediaTypeFormatter in MVC4? I've changed the ProviderFactory, but that only changes how each individual parameter is serialized to its corresponding value. What I need to do is to use Json.Net so that my Parameters that are typed as a Hierarchy is serialized to the correct derived class...not typed as the Baseclass.
Edit - Last:
Ok...so I did verify that if I call an ApiController, it maps the incoming parameter with the correct subclass. So, I can go this route, but I'd really like to find a way to have the same result with my regular Controller.
I had the same issue and resolved it by simply overriding the Json method on my base controller class.
EX:
protected override JsonResult Json(object data, string contentType, System.Text.Encoding contentEncoding, JsonRequestBehavior behavior)
{
var result = new JsonNetResult
{
Data = data,
ContentEncoding = contentEncoding,
JsonRequestBehavior = behavior
};
return result;
}
I'm trying to use Infragistics' grid to display a list of items from my Database. I'm using code-first method with Entity Framework in an MVC application with Razor engine. Every thing is working fine in the view except the Infragistics grid.
Here is my home view:
#using Infragistics.Web.Mvc
#model IEnumerable<BusinessModel.Models.TestPlan>
#{
ViewBag.Title = "Home";
}
#( Html.Infragistics().Grid<BusinessModel.Models.TestPlan>(Model)
.AutoGenerateColumns(true)
.DataSourceUrl(Url.Action("igListTestPlan"))
.DataBind()
.Render())
Here is my controller:
[GridDataSourceAction]
public ActionResult igListTestPlan()
{
return View(service.getListTestPlan());
}
Using firebug I can clearly see that the request is sent with a status code "200 OK", but the response tab is empty. It also causes an error in the console (in infragistics.js):
Uncaught TypeError: Cannot read property 'length' of undefined
I guess it's because of the empty response.
What I tried:
Debugging my controller showed me that return View(service.getListTestPlan()); doesn't return an empty list: I have 3 valid items in.
I also tried Html.Infragistics().Grid<BusinessModel.Models.TestPlan>(Model__.ToList()) but nothing changed. Also Html.Infragistics().Grid(Model) tells me I've got invalid parameters
Thanks in advance.
I think I have a pretty good idea why you are getting this, happened to me as well.
The MVC wrappers provide defaults for the way the grid model handles data on the server (serializing the data source to an object with 'Records' of your data and supportive 'Metadata'). However, if you do that yourself since you don't define a key of your own you are stuck with a default key 'Records' that is used to filter the response and since it's not there..well you get 'undefined' data fed to the grid :)
So solutions:
1) Wrap your response and define matching key using the "ResponseDataKey" property of the grid. I am suggesting this because as far as I recall it's a good practice to wrap responses in a single object - think there were some security implications.
2) If you don't feel like doing this and just want to get it working now then set the "ResponseDataKey" to empty string ("" will do) so your response will be properly filtered(or rather not).
On the second part of binding the grid directly to model data in the view - you are correctly getting the error as far as I see. The DataSource property explicitly states the source must implement IQueryable instead of IEnumerable. Slap a .AsQueryable() in there and that should work fine as well.
Let me know if this helps :)
so, I've seen this working on a previous project in MVC3, so wondering if a) i've screwed it up, or b) MVC4 is doing something different (can't see it would be).
I have a model bound Razor view which submits to a controller action method (as is the way with MVC)
post action method:
[HttpPost]
[AutoMap(typeof(MyViewModel), typeof(MyDomainObj))]
public void PostAction(MyDomainObj obj)
{... etc.. etc..
The action filter eventually does something like this:
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var model = filterContext.Controller.ViewData.Model;
NOTE: In Jimmy Bogard's example he used OnActionExecuted which I also tried..
The key issue I'm having is that at the pint where we get the "model" variable from the context, it's null. If I look at the filterContext.ActionParameters whilst debugging I can see a MyDomainObj instance!! which appears to (because it happens to have a prop name in common with the MyViewModel type) have been mapped from my form data!
So.. yes if I had MyViewModel type as the parameter to this method, then the param would be properly populated from the submitted form. But. I don't want to do that, I want to (and have done before based on JB's succinct how-to) converted the view model to domain model as part of the action executed/ing and then been able to just hit save on my domain model.
Summary - why is my ViewData.Model null on the post action filter?
Jimmmy also has/had a couple of ideas on how to implement post actions, along with another guy Matt Honeycutt who shares his view on how to do posts. I also believe Jimmy has gone in the direction of using explicit mapping in his get requests instead of attributes, as it can be hard to inject any extra code you need after mapping.
http://lostechies.com/jimmybogard/2011/06/22/cleaning-up-posts-in-asp-net-mvc/
http://trycatchfail.com/blog/post/Cleaning-up-POSTs-in-ASPNET-MVC-the-Fail-Tracker-Way.aspx
For a post you really want a couple of things imo, the original Entity and the Form Data. You could load the entity like you do for the GET request and do normal model binding to get the form data (remember you can accept a different model for post backs than you spit out in your view) then make the changes in the action.
Of course this would require using AutoMapper in your action, which you seem to be trying to avoid. But unless you write a custom model binder then you're not going to magically get the formdata in a model as the built in one looks at the action paramters to figure out what to bind. For this reason i'd recommend not using a domain model as a parameter for an action, as it may fill out fields that you don't wish it to.
I've also seen Jimmy using a ModelBinder to do something similar to your AutoMapGet, which may be another alternative method, but I'm guessing you've seen that post.
My standard post takes Matt's approach, moving the validation out into a global attribute so it can't be forgotten (there are some downsides to this) then accepting a new view model explicity for the form data, then I explicity load the entity, use automapper to update it and call save. Most the actions only end up being about 5 lines long.
I do avoid using the static methods on AutoMapper and pass a IMapper in via constructor injection. This allows me to make it a bit more testable if I really need to, however the methods are normally so simple the tests don't add all that much value.
I have uploaded my code to pastebin, this is the link:
http://pastebin.com/wBu9PP2x
When i submit a form, the Lists that i use are not bound to my ViewModel.
But when i send the ViewModel to the view, it renders fine using EditorFor. I have read that when using EditorTemplates, it is supposed to name the List appropriately so that they are bound to the ViewModel automatically upon postback.
The HTML output can be seen here:
http://pastebin.com/5KeyNXWC
Notice that the ViewModel derives from ShowQuestionViewModel, which contains some strings. These strings get bound perfectly.
This is the tutorial i have been following:
http://jarrettmeyer.com/post/2995732471/nested-collection-models-in-asp-net-mvc-3
In the tutorial, the MVC framework knows how to bind lists inside of a ViewModel.
Here are some debugger outputs:
Controller takes ShowQuestionViewModel as parameter:
http://imageshack.us/photo/my-images/803/debug.jpg
Controller takes FormCollection as parameter:
http://imageshack.us/photo/my-images/542/formcollection.png
Different Controller that takes a List and FormCollection as parameter:
http://imageshack.us/photo/my-images/685/listtest.png
Dont give up on me guys!!
Thanks!
Solution
I have found this solutin myself. I forgot to use Properties for the rows and columns list in the ShowMatrixQuestionViewModel. Also, the ActionController wont bind without TryUpdateModel() so thanks to #Adam Tuliper as well as the rest.
Since you mentioned lists are you sure your model Upon postback contains all of the expected items? Also remembe the HTML helpers will use modelstate to bind data from as well if you are showing data after a post and not redirecting.
I'm facing a really strange problem that has me smoked.
I have a fairly simple scenario where I have a strongly typed view that is correctly populated from the controller on the GET, but then when it POSTS the form to the controller, the Reqeust is full of all the right values and right key names for the default model binder to correctly populate one of my model objects, and the DMB creates the correct opject, but it never populates any of the properties, they're all in their default state.
This was working before, the only changes I can think of were that I tried a custom modelbinder (then removed it; double checked to make sure I'm not still using that), and I refactored the model to have a base class with some of the props.
Any thoughts?
A very similar scenario - that the DefaultModelBinder is - essentially - not binding to your model, arrise if you would give your bound model object the same name as one of its properties:
Model
Public Property ArbitraryName1 As Integer
Public Property Answer As String
Public Property ArbitraryName2 As Boolean
View
<p/> ... #Model.ArbitraryName1
<p/> Answer: #Html.TextBoxFor(Function(model) model.Answer)
<p/> ... #Html.CheckBoxFor(Function(model) model.ArbitraryName2)
Controller
<HttpPost()>
Function Index(answer As Model) As ActionResult
' answer is Nothing
End Function
(Using ASP.NET MVC 3)
Got it. The model had been refactored in a way which naturally affected the ability of the mdoel binder to populate it.
The name of your input param do not have to be equal to some property name of the object. Remember that all data coming as an array of name -> value and the default binding use the names for make the relation work.
I had this behaviour arise by moving two properties from the top of the class to further down. I still can't work out why this stopped the binding of a third property from working (so this isn't a solution so much as a 'watch out for') but I repeated the change multiple times and each time the binding went from working to not working.
I also found that after making this change I sometimes had to 'Clean' the solution for the binding to start working again.