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.
Related
I'm using EF database first and with MVC.
I'm wanting to add some validation on a property to compare its old value to its new one and report a validation error to the MVC ModelState if there is a problem.
This would be easy enough using code first and validating using 'set' on the property. However I can't do this using database first because its auto generated.
I've looked at using IValidatableObject and the validate() method however by then the value has already been changed on the property so I can't see the old one anymore to compare to.
Short of creating a method to pass the new value into first to check it, I can't think of another way.
Any suggestions?
Thanks
If you want to compare a new value to an old value then you are going to have to grab the values from the database first (before updating) and compare them:
[HttpPost]
public ActionResult Update(MyObject myObject)
{
var oldObject = db.Objects.FirstOrDefault(o => o.Id == myObject.Id);
//Compare oldObject.Value to myObject.Value
}
You could still use IValidatebleObject and pass in the objects that you need to keep that logic outside the controller.
Its not the ideal and this has started illustrating some of the weaknesses in Model and DB first but here's how I ended up doing it.
I decided to change the property in my model so that set was private and then create a separate method in the partial class to set the value. The validation is then all done in that method.
Thanks for your help anyway
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.
Having just spent two amazingly frustrating hours formatting some hidden fields to bind properly to action method parameters -- the second time I've had this experience in the last week -- I'm now very curious as to why the MVC architects chose the particular binding convention that they did for lists (and dictionaries) of objects.
Here's my painfully gathered understanding of the format expected by the default binding engine. In my example I want to bind to a List, where the CustomClass type exposes a public property called PropertyName:
<input type="hidden" name="prefix[idx].PropertyName" value="PropertyName[idx] Value" />
prefix is the ViewDataDictionary.TemplateInfo.HtmlPrefix, if one has been defined.
I find it deeply counter-intuitive to start the reference to something with the indexing information (i.e., the [idx] piece). I also find it disturbing that nowhere in this construct do I make reference to the name of the action method parameter to which I'm binding. That seems to be in direct contrast to what happens with, say, a primitive model property:
<input type="hidden" name="Text" value="something" />
public ActionResult SomeActionMethod( string Text )...
I understand I can roll my own model binder in MVC. That doesn't seem like a profitable use of my time, although spending hours trying to puzzle out the correct format for hidden fields isn't profitable either :), so maybe I'll try that. I also know that I can let MVC do all the heavy lifting by creating a type template using #Html.HiddenFor(), and then outputting instances of CustomClass though a simple partial view that has CustomClass as a model and the single line #Html.DisplayForModel(). But that seems like going the long way around the barn, too. Besides, there are limitations to using the #Html.Hidden helpers (e.g., they "helpfully" raid the cache after postback to fill in values, so writing #Html.Hidden("fieldname", value) doesn't guarantee value will end up being output -- you may get the older value in the cache instead, which was another hour-long annoyance today).
But I'm mostly just curious why this format was chosen. Why not something more C/C++/C#/VB like:
<input name="prefix.ParameterName.PropertyName[idx]" />
Edit:
Good point on where I put the index parameter in my example. You're right, the property isn't indexed, the containing class is.
However, that doesn't change the basic situation. Why isn't the syntax something like:
<input name="prefix.ParameterName[0].PropertyName" />
The standard syntax ignores the parameter name with collections of custom types, and "guesses" the custom type from the property names. That's bizarre...so there must be a story or choice behind it :).
Actually, it makes perfect sense. The index is into the collection, not the property. You don't have a collection of PropertyName, you have a Collection of CollectionName[] that has a PropertyName.
To put this another way:
public class Foo { public string Bar { get; set; } }
var foos = List<Foo>();
for (var i = 0; i < foos.Length; i++)
{
var prop = foos[i].Bar; // This is the important bit
}
That's exactly what happens in the model binder.
When the model binder deserializes the post values, it has to know which collection to insert the values into, and it has to know what index each item is for. So it has to know to create a Collection of Foos, of x number of items, and which indexes each Bar is associated with.
I've got a View where I use a naming-convention on my text-fields, to indicate what should be done with the content once it is posted back to my controller.
The format is similar to:
<input type="text" name="RegistrationLine#ID" />
for updates
<input type="text" name="CreateRegistrationLine#LineNumber" /> for create
Now since I'm using this Naming-convention, regular model-binding isn't possible. But I've been reading up a bit on the subject and did find a bit of an indication that it would be possible to write a custom model binder, that should be able to help parse and bind these form elements and instantiate the objects correctly.
Please read: Bind formValue to property of different name, ASP.NET MVC
This is a bit similar to what I am doing except, I have the additional complexity of having appended information in the formelement-name that I am trying to bind to.
Am I way off base here? and if not, can any of you drop a few lines of code to show how you would start this model-binder off..
If this is a very bad approach to what I am really trying to achieve, I would love to hear suggestions for better approaches. Just note that what I want to be able to do is post back both updates and creates in one go.
I kinda have to agree with #jfar and #omar. I don't think a custom model binder is where you want to be in this instance.
You can pass a complex type to your view and simply use the full stop like #jfar mentioned.
id="Model.Person.Name.FirstName" will happily bind to an object named Person that has a class in it called Name that has a property called FirstName.
Now if you want to do some special checks on the data you could implement a partial class which would do the validations etc and populate the ModelState errors.
public partial class Name
{
public void Validate(){ }
public int CreateRegistrationLine(){ }
public bool DoSpecialActions(){ }
}
It's a little unclear what your special actions are doing so my example above may not be what you want.
Let's say I have a product object (pretty much empty) and I bind it to a Product view. Then I click update in the view. In my CustomModelBinder my bindingContext.Model is always null on the update request. Is there a recommended way of me retrieving the prior model at this point or do I always have to recreate it?
You have to recreate it from the form fields. The values you bound to the model for the GET are long gone.
perhaps im not understanding your needs to use a CustomModelBinder, but did u concider Data Annotations Model Binder yet?
it even comes with (serverside) validation based on simple statements like [Required] which u can put right inside your model, see this