Two-Way-Binding Possible In ASP.NET MVC? - asp.net-mvc

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

Related

Entity Framework Database First Property Validation

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

How To: Use MVC and Ajax to add / remove a row in grid for data entry + model binding?

I'm new to Ajax, but I think I know how to reasonably use MVC + model binding.
What I'm trying to do is to create an Add button (or Ajax.ActionLink) to add a new row in my grid for data entry. Example: Think of a typical Order entry system with Order (header) and Product (items). My OrderViewModel contains an "Order" object, and the Order object contains a collection List.
The way I plan to do this is that my View render the grid in a PartialView, and the PartialView is a simple for-loop to create the table tags from the List. I will use the default model binder (for collections).
Anyone have suggestions on how to do this?
I've already figured out how to do this using jQuery, but I want (i think I want) to try and use Ajax so that I can add my custom business logic (e.g. like setting defaults, translations, etc.)as opposed to do this client-side.
In other words, I want to do do something similar to what the Telerik grid does with its Ajax Editing with the Add/Remove link/buttons.
Tips and sample code would be greatly appreciated.
One of my challenges, and not sure if I'm going down the wrong way, is that I don't know how to pass back the model back to the Controller Action from the Ajax submit. When I look at Telerik's code, it looks like they store the persisted items in HttpContext.Session, and this is exactly the reason why I don't want to use their grid.
Thanks.
They might choose the session repository storage for demonstration purposes. If you transform the logic from their SessionProductRepository class for your model and implement identical Update/Insert/Delete methods for it, you'll probably get what you want.

Is it okay to hit the database from a custom model binder?

Say I have an object that gets some data from HttpPost and some from the database. I think I want to allow the ModelBinder to go to the database/repository for the that data missing from the post. In practice, is this a good or bad idea?
I've decided to edit my original answer given my thinking on these types of things has evolved since early 2010.
In my original answer, I basically expressed that, while my instincts told me you shouldn't do this, I was uncomfortable saying we shouldn't without being able to articulate why.
Now, I'd recommend against this on the grounds that the responsibility of a Model Binder is to translate a user request into a Request Model and that retrieving data outside of what can be derived from the request goes beyond this scope of responsibility.
I would say a bad idea. The idea of the model binder is that it takes the parameters from the request and creates your model from those. If your model binder, behind the scenes, fills in some of the details from the database this breaks the paradigm. I'd much rather expose the database call in my controller by explicitly fetching the required extra data from the database directly. Note that this could be refactored into a method if used frequently.
I think this is perfectly fine and use this technique all the time.
The only arguments against are very pedantic and amount to arguing over philosophy. IMHO you can put "fill in missing posted data" code into you MVC app as a method in your base controller vs. method in you ActionFilter vs method in you ModelBinder. It all depends on who get what responsibility. To me the model binder can do a lot more than simply wire up some properties from posted values.
The reason I love doing database calls in my modelbinder is because it helps clean up your action methods.
//logic not in modelbinder
public ActionResult Edit( KittyCat cat )
{
DoSomeOrthagonalDatabaseCall( cat );
return View( new MODEL() );
}
vs.
//logic in model binder
public ActionResult Add( KittyCat cat )
{
return View( new MODEL() );
}
It violates the way MVC is supposed to work. ModelBinder is for binging Models from the data that comes from the view. Populating missing info from the database is something that is supposed to be handled by the controller. Ideally, it would have same data layer/repository class that it uses to do this.
The reason it should be in the controller is because this code is business logic. The business rules dictate that some data may be missing and thus it must be handled by the brains of the operation, the controller.
Take it a step further, say you want to log in the DB what info the user isn't posting, or catch an exception when getting the missing data and email admins about it. You have to put these in your model binder this way and it gets more and more ugly with the ModelBinder becoming more and more warped from its original purpose.
Basically you want everything but the controller to be as dumb and as specialized as possible, only knowing out how to carry out its specific area of expertise which is purely to assist the controller.
I would say, no.
Here's why: It would create a dependency on your database for testing your controller actions that would not be easy to abstract out.
I would say it is ok. The argument that creates dependency to database is a false argument for 2 reasons:
1- Database access should be abstracted via repository interfaces. Repository interfaces are part of the domain model and their implementation is part of the infrastructure/data access layer. So there is no dependency to the database.
2- Model Binders and Controllers are both part of presentation layer implemented using ASP.NET MVC Framework. If Controllers are allowed to access database using repository interfaces, how come Model Binders are not allowed?
Also, there are situations that you'd "better" fill the missing data in your model from Model Binders. Consider the scenario where you have a drop-down list on your view. The first time the view is loaded, the drop-down list is populated. The user submits the form but the validation fails. So you'll need to return the form again. At this stage, you'll have to re-populate the list in the Model for the drop-down list. Doing this in Controller looks ugly:
public ActionResult Save(DocumentViewModel viewModel)
{
if (!ModelState.IsValid)
{
viewModel.Categories = _repository.GetAll();
return View(viewModel);
}
}
I believe the initialization of Categories here is ugly and like a code smell. What if you had a few properties that needed to be filled out from database? What if you had more than 1 action that had DocumentViewModel as an argument? You'd have to repeat this ugly step over and over. A better approach is to fill all the properties of the model using the Model Binder and pass it to the Controller. So the object that is passed to the controller is in a "consistent" state.

How do you ignore/persist values in MVC when your view-model doesn't have as many fields as your domain model?

I have a site where I'm using fluentNhibernate and Asp.net MVC. I have an Edit view that allows user to edit 8 of the 10 properties for that record (object). When you submit the form and the Model binds, the two un-editable fields come back in the view-model as Empty strings or as default DateTime values depending on the type of property.
Because I'm also using AutoMapper to map my view-model to my Domain Entity, I cannot just load a fresh copy of my object from the database and manually set the 2 missing properties. Whats the best way to persist those fields that I don't want edited?
One way that does work is to persist the values in hidden Input fields on my View. That works but feels gross. I appreciate any recommendations. Is there a way in my AutoMapper to configure this desired functionality?
UPDATE:
Ok, So I guess I'm not trying to ignore the fields, I'm trying to make sure that I don't persist null or empty string values. Ignoring the fields in AutoMapper does just that, they get ignored and are null when I attempt to map them before Saved to my repository.
The asp.net mvc DefaultModelBinder is extensible, and you can override it to create your own binding schema. But this will involve more work than two "hidden Input fields", which , in my point of view, is not that gross.
You can tell Automapper to ignore the 2 properties:
Mapper.CreateMap<Source, Destination>()
.ForMember(dest => dest.SomeValuefff, opt => opt.Ignore());
Possible related question.
Can you use the AutoMapper.Map overload that also accepts TEntity?!
entity = Mapper.Map(viewmodel, entity);
As long as you do not have the properties on your viewmodel, it won't change the values on your entity. It takes the entity being passed in and applies only the properties from the viewmodel back to the entity.

asp.net mvc how to save?

I have the following model:
Customer:
ID
Name
Address
Phone
Fax
I added an Edit view based on the above model from the controller. I modified the Edit view to only allow edit on the Phone and Fax field (deleted the rest). When I submit it I get an error. It works if I leave the Edit view untouched (5 fields). However I only want to allow change in the last 2 fields.
I am lost, please help. Thanks :)
If you are using the MVC ability to populate your entity/class i.e. your action sig looks like this:
ViewResult MyAction(MyObject object) {
...
Save(MyObject);
}
then you'll need to make sure you include the other field, non-editable, either as visible information or using Html.Hidden within the form scope to ensure you have a fully populated object. Remember, the web is stateless and the server has no idea which record you were editing unless it has the keys to do so retrospectively.
The other option would be to retrieve the original object (for which you'll still need the primary key) from the database, update the fields from your form data and then submit the changes. We'd need to know the specific error to be able to help further, the code you are using would also be a great help.
Without knowing more I would guess it has something to do with binding null to a non null property in your model. Can you give me more details on the model, the error.
If you are using the default mvc model binder then it will only bind the fields you submit. So either submit as hidden or dont use a model binder and manually map the variable from Request.Form into a copy of the model you pulled form the db.

Resources