Partial Record Updates with Linq to SQL and MVC - asp.net-mvc

Let's say I have a DB table with columns A and B and I've used the Visual Studio designer to create a Linq objects for this table. All fields are marked NOT NULL.
Now I'm trying to edit this record using typical MVC form editing and model binding, but field B doesn't need to be editable, so I don't include it in the form.
When the post handler binds the object it doesn't populate field B, leaving it null. Now when I save the object I get a DB error saying field B can't be NULL.
The code to save looks something like:
m_DataContext.MyObjects.Attach(myObject);
m_DataContext.Refresh(RefreshMode.KeepCurrentValues, myObject);
m_DataContext.SubmitChanges();
How do I get this to work? Do I need to include field B as a hidden field on the form - I don't really want to do this as it may be updated by other users at the same time and I don't want to stomp over it.
I've found the solution to this problem revolves around getting the entity object associated with the data context before applying the changes. There's a couple of ways of doing this which I've described in separate answers below.

Descend into SQL
This approach ditches LINQ in favour of straight SQL:
public override void SaveMyObject(MyObject o)
{
// Submit
m_DataContext.ExecuteCommand("UPDATE MyObjects SET A={0} WHERE ID={1}", o.ID, o.A);
}
I like this approach the best because of it's simplicity. As much as I like LINQ I just can't justify it's messiness with this problem.

Use a Custom Model Binder
This approach uses a custom model binder to create the entity object and associate with the data context, before the binding takes place.
public class MyObjectBinder : DefaultModelBinder
{
protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
{
MyObject a = ((MyObjectController)controllerContext.Controller).Repository.GetMyObjectForUpdate(bindingContext.ValueProvider["ID"].AttemptedValue.ToString());
return a;
}
}
The repository then creates the object and associates it with the data context:
public Object GetMyObjectForUpdate(string id)
{
MyObject o=new MyObject();
o.ID=id;
m_DataContext.Articles.Attach(o);
m_DataContext.Refresh(RefreshMode.KeepCurrentValues);
return o;
}
The action handler needs to be attributed to use the model binder...
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult EditMyObject([ModelBinder(typeof(MyObjectBinder))] MyObject o)
{
if (!ModelState.IsValid)
return View("EditMyObject", a);
Repository.SaveMyObject(a);
return RedirectToAction("Index");
}
and finally SaveMyObject simply calls datacontext.SubmitChanges().
For this to work I also needed to set the update check attributes on all columns to Never (in the dbml file).
Overall, this approach works but is messy.

Use Two Entity Objects
This approach uses two entity objects, one for the model binding and one LINQ:
public override void SaveMyObject(MyObject o)
{
// Create a second object for use with linq and attach to the data context
MyObject o2 = new MyObject();
o2.ID = o.ID;
m_DataContext.Articles.Attach(o2);
m_DataContext.Refresh(RefreshMode.KeepCurrentValues);
// Apply fields edited by the form
o2.A = o.A;
// Submit
m_DataContext.SubmitChanges();
}
This approeach doesn't require any special handling in the controller (ie: no custom model binding) but still requires
the Update Check property to be set to Never in the dbml file.

You could add a timestamp field and check one on the page with the one in the DB (hiding the timestamp field as well). If a user has updated the record, a concurrency error is returned and the page is refreshed, or left the same iwth the users changes.

Related

Revalidating a modified ViewModel within a controller method?

EDIT - We're using MVC4 Dev Preview....
I'm implementing an edit page for a FishingTrip class. FishingTrip contains a child collection of simple Crew objects (i.e. FishingTripID, CrewID, CrewPosition).
I'm using Jarrett Meyer's approach to add, edit and delete from the Crew collection.
I'm using unobtrusive validation to specify that the properties of Crew are all Required.
My problem: when I logically-delete an item from the list (as per Jarrett's method), I don't want that item to be validated.
I have successfully tweaked the "removeRow" method on the client-side to disable unobtrusive validation for the logically-deleted item, so that the form will post despite there being an item that contains blank fields.
In my controller method [HttpPost] Edit, ModelState.IsValid starts off as false (as expected - because of the logically-deleted item that contains blank fields.) So I remove that item from my ViewModel.... but ModelState.IsValid is still false.
In summary, I (think I) want to modify my ViewModel within the controller method to remove the offending item, then call some kind of "revalidate", and have ModelState.IsValid show up as true.
Any ideas?
Once you have removed the offending item(s), clear the ModelState and validate again, like so:
ModelState.Clear();
TryValidateModel(crew); // assumes the model being passed is named "crew"
Note: Be carefull when use TryValidateModel method because this method does not validate nested object of model (As mentioned by #Merenzo).
Late to the game, but still:
I was also looking for a way to validate model after doing some tweaks to it (more precisely - to the items of its nested collection) - and TryValidateModel didn't work for me, as it doesn't process nested objects.
Finally, I settled with custom model binder:
public class MyItemModelBinder : DefaultModelBinder
{
protected override void OnModelUpdated(
ControllerContext controllerContext,
ModelBindingContext bindingContext)
{
if (bindingContext.ModelType == typeof(MyItemModel))
{
MyItemModel item = (MyItemModel)bindingContext.Model;
//do required tweaks on model here
//(I needed to load some additional data from DB)
}
//validation code will be called here, in OnModelUpdated implementation
base.OnModelUpdated(controllerContext, bindingContext);
}
}
on the model class:
[ModelBinder(typeof(MyItemModelBinder))]
public class MyItemModel : IValidatableObject
{
//...
}

Model Binding With Entity Framework (ASP.NET MVC)

Earlier I created an AddClient page that (when posted) passed a client object and I used db.AddToClient(obj) in my repository to persist it. Easy stuff.
Now, I have a details page whose save submits a post to an action "UpdateClient". Before that action is hit, my custom model binder creates my Client object and it's conveniently handed to the action. The thing is, this client object is not connected to a EF context yet. Where is the correct place to do that? In the modelbinder, or maybe when we get it from the controller, or perhaps we wait until we do a repository call and link it up there? What's the recommended process?
From what I remember, you will either need to attach the object again to the context and set it as modified
Or reload the object from the database and apply your changes.
This article explains it better:
http://msdn.microsoft.com/en-us/magazine/ee321569.aspx#id0090022
What version of EF are you using ?
If an EF object has been created outside a context, you need to Attach the object to the context.
See: http://msdn.microsoft.com/en-us/library/bb896271.aspx
I know this is an old thread but in the interest of new readers:
Based on my observations using VS 2012, MVC 4 and EF 4.0 with a view that has an EF object for a model that submits a form back to the controller.
On the controller:
public ActionResult SubmitEFObject(tblData data, FormCollection col)
"data" will only have the properties used in the view (#Html.xxxFor) filled.
It appears that when "data" is created, the posted FormCollection is used to set data's properties. If you had a property that wasn't used, DataID for example, then data.DataID will have a null/default value. Add a "#Html.Hidden(m => m.DataID)" to your view and THEN DataID will be filled.
As a 'quick n dirty' way to work with this, I created a method that would merge the incoming 'data' with the 'data' in the database and return the merged object:
// Note: error handling removed
public tblData MergeWithDB(DBContext db, tblData data, params string[] fields)
{
tblData d = db.tblData.Where(aa => aa.DataID == data.DataID).Single();
if (fields.Contains("Field1")) d.Field1 = data.Field1;
if (fields.Contains("Field2")) d.Field2 = data.Field2;
if (fields.Contains("Field3")) d.Field3 = data.Field3;
// etc...
return d;
}
On the controller:
public ActionResult SubmitEFObject(tblData data, FormCollection col)
{
DataEntities db = new DataEntities();
tblData d = MergeWithDB(db, data, col.AllKeys);
db.SaveChanges();
}
You could make this more generic using reflection or maybe more efficient by looping through the string[] fields instead of all the ifs but for my purposes this was 'good enough'.
Database work should be put in the repository call.
Are you directly binding to an entity framework object in your model binding? If not, you should consider do some mapping between your custom object and entity framework object.

How does the ASP.NET MVC UpdateModel() method work?

I'm working on my first .NET MVC application and using the NerdDinner tutorial as a reference point. One point that is intriguing me at the moment is the UpdateModel() method. (I don't like using things I don't really understand.)
Taken from the NerdDinner tutorial -
//
// POST: /Dinners/Edit/2
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(int id, FormCollection formValues) {
Dinner dinner = dinnerRepository.GetDinner(id);
UpdateModel(dinner);
dinnerRepository.Save();
return RedirectToAction("Details", new { id = dinner.DinnerID });
}
My main question is how does the UpdateModel() get access to the formValues passed in the Edit method? Why is the collection not passed in explicitly as a parameter to the method?
UpdateModel() is a Controller helper method that attempts to bind a bunch of different input data sources (HTTP POST data coming from a View, QueryString values, Session variables/Cookies, etc.) to the explicit model object you indicate as a parameter. Essentially, it is only for model binding.
If you express the input parameters for your Action as a strongly-typed model (like a View Model), you've already taken all of the steps that are done behind the scenes when UpdateModel() is called. If you retrieve an object from the DataContext and edit its properties, SaveChanges() is all you need to push the updates back to the database (in this case, Save()).
Example:
//
// POST: /Dinners/Edit/2
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(DinnerViewModel incoming) {
var dinner = dinnerRepository.GetDinner(incoming.DinnerID);
dinner.Description = incoming.Description;
dinnerRepository.Save();
return RedirectToAction("Details", new { id = incoming.DinnerID });
}
However, there is a use-case for using UpdateModel() with a strongly-typed model: when you are passing in a strongly-typed model and want its values to directly replace those of an entity from the database (provided they are all named and typed the same). In this case, you would retrieve the object, use UpdateModel() on it, and its model binding operation will pull in any similarly-named and typed properties from the strongly-typed object to the retrieved object. In other words, it will perform reflection for you.
So, like your example, if you want all properties to update without specifying which to update, and your strongly-typed model and database model have similarly-named properties, you would still want to use UpdateModel() to take advantage of the reflection.
Example:
//
// POST: /Dinners/Edit/2
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(DinnerViewModel incoming) {
var dinner = dinnerRepository.GetDinner(incoming.DinnerID);
UpdateModel(dinner);
dinnerRepository.Save();
return RedirectToAction("Details", new { id = incoming.DinnerID });
}
The only advantage here (over using a FormCollection object) is that you'd have access to all other properties of the strongly-typed object (as shown by incoming.DinnerID).
Conclusion: if you're translating a strongly-typed object to a derived object, it's probably easiest to use UpdateModel(). However, it's largely unnecessary if you are simply updating a few properties of the derived object. Also, be aware that use of the Entity Framework (instead of something like Linq to SQL) makes all of this moot, as it can relate strongly-typed objects and derived objects with its own methods.
It does inspect all the HttpRequest inputs such as Form, QueryString, Cookies and Server variables. I think in this order.
Instead of passing Model object as a parameter to "Post()" action method, we are creating an instance of an Model object within the "Post()" function, and updating it using "UpdateModel()" function. "UpdateModel()" function inspects all the HttpRequest inputs such as posted Form data, QueryString, Cookies and Server variables and populate the employee object.
e.g.
[HttpPost]
[ActionName("Create")]
public ActionResult Create_Post()
{
EmployeeBusinessLayer employeeBusinessLayer =
new EmployeeBusinessLayer();
Employee employee = new Employee();
UpdateModel(employee);
employeeBusinessLayer.AddEmmployee(employee);
return RedirectToAction("Index");
}

asp.net mvc: how to create custom binding for models when using LINQ to Entities

I've got a number of tables in my db that share common cols: modified by, modified date, etc. Not every table has these cols. We're using LINQ to Enties to generate the
I'd like to create a custom binder class that can handle the automatic binding of these fields. Is there a way to do this without having a custom binding class for each entity class?
Here's the code I have:
In global.asax.cs, Application_Start():
ModelBinders.Binders.Add(typeof(Foo),new FooBinder());
Then in FooBinder.cs:
public override Object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var obj = (Foo)base.BindModel(controllerContext, bindingContext);
var user = controllerContext.HttpContext.User.Identity;
obj.modified_by = user.Name;
obj.modified_date = DateTime.Now;
return obj;
}
Is there a way to generalize this so it can handle multiple types?
We do this in the repository, not in the binder. We have an interface with the common fields (Modified on). We implement the interface in partial classes which we codegen for our entities using a T4 template.
You can use reflection to set fields by name, so that you can do foreach over property names. This is not a big deal since model binder uses reflection itself to get to the properties, anyway. Then you can just register binder for each of the "common" types - which is also OK to do using reflection, for example by checking that entity has all fields required present - so it works even if you add new such entities.
Just have your binder call default one and then update the fields.

Difference using UpdateModel and ModelBinding in Parameter

Why would I use UpdateModel here?
A.
public ActionResult SubmitPerson(Person person)
{ }
B.
public ActionResult SubmitPerson(FormCollection form)
{
Person person=new Person();
UpdateModel<IFilter>(person,form)
}
It ultimately depends on your implementation requirements.
In A., a new instance of a Person object will be created and the model binder will attempt update the properties from the form.
In B., the example you have provided will also create a new Person object and will attempt to update the properties via the IFilter interface, which is one of the ways to specify a whitelist in MVC.
Another reason you might use option B is to updated an existing object (for example one that was populated from data in a database) instead of creating a new object instance.

Resources