All. more or less new to MVC so not sure how to handle the issue.
I have data model generated from a database table(approximately 100 columns). The application form is broken up into 6-7 views. The first view creates a new record and inserts it into the database. The rest of the views suppose to gather information from the form and update the created record.
[HttpPost]
public ActionResult GeneralInfo(ADP.Models.ADPRegistration _registration)
{
try
{
ADP.Models.IS_WEBEntities _test = new IS_WEBEntities();
_test.ADPRegistrations.Attach(_registration);
_test.ObjectStateManager.ChangeObjectState(_registration, System.Data.EntityState.Modified);
_test.SaveChanges();
return View("ReferralMethod",_registration);
}
catch(Exception er)
{
return View();
}
}
This is the general code that updates my table, it is similar across all of the views. The reason for passing the model back to the view is to preserve the ID across the views.
The problem that I have is that, my entity _registration contains all of the table fields, so when the view is posted only the fields in the views get updated, the previous columns are getting NULLs, since they don't exist in the view my guess. I can't seem to enumerate through the database first model as well.
You are correct in that when MVC reconstitutes your model in a POSTback, any fields that are not in the form will be NULL in your model.
Your choices are to include the field(s) as hidden in your view:
#Html.HiddenFor(model => model.SomeValue)
Or update the model after post, but prior to passing it to your validation & repository layers:
ModelState.SetModelValue("Name", new ValueProviderResult("Some string",string.Empty,new CultureInfo("en-US")));
Related
I've had a thorough search around but really can't find anything addressing the scenario I'm facing (oddly because I'd have thought it's quite a common thing to do).
Background
I'm creating an application with ASP.NET MVC 4 and Entity Framework 5 Code First. For the purpose of this question, think of it as a blogging application with posts and users.
Project
The post model requires that every post have a corresponding UserId.
With the ASP.NET MVC 4 Membership it is easy to find the username of the person logged in with
User.Identity.Name.
This isn't ideal, we want the ID, but a query such as this can search the db for the name and get the ID.
db.UserProfiles.Single(a => a.UserName == User.Identity.Name);
Problem
The problem arises when trying to create a post. Model.IsValid is false, as no UserId is being passed in from the view. Obviously, as the user isn't expected to enter their ID.
I've tried putting the ID value into the ViewBag and using a #Html.Hidden() field in the view, however I've had no success with this. Model.IsValid always returns false.
Should this information be input through the create view? Or should it be done directly in the controller? Its quite a frustrating problem as I have the information and just need to figure how to pass it into the model.
CONTROLLER CODE
This is basically just the default scaffolded code. The commented code is how I tried setting the model value directly from the controller, however that was little more than trial and error.
//
// POST: /Post/Create
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(Post post)
{
if (ModelState.IsValid)
{
//var userId = db.UserProfiles.Single(a => a.UserName == User.Identity.Name);
//post.User.UserId = userId.UserId;
db.Posts.Add(post);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(post);
}
Be careful with hidden fields. Anyone could put whatever value they want in that field (i.e. they could spoof another user). You'd be better off caching the ID in the session at login, and using that value.
This is a typical case where you want to create an EditModel as a data transfer object (DTO) between your view and controller layers.
Create a class BlogPostEditModel that has all properties you need the user to fill in when creating a new blog post. Then, map this type (e.g. using AutoMapper) to your BlogPost entity, and fill in the user ID as well.
To use built-in validation such as Model.IsValid(), put the data annotations attributes on the DTO instead.
Honestly, I would have the value assigned via the controller. If you had someone messing with your html via Firebug, they could actually change the id before it was passed and submitted to your form. I would remove it from your Create view and submit from the controller.
My application needs to do an HTTP post of a table with checkboxes like in the image above. On the controller side I will need to traverse the table and perform certain operations for each row that was checked.
The things that I need to do are:
Identify whether a row is checked
Get the cell values of a checked row
I have a good understanding on how this will be done in Razor in as far as posting the form is concerned. But I am clueless once I am in my controller's action method.
Please help. Thanks.
From what you've show, it appears that all you really need in your action method is a collection of ids to identify which "rows" to modify. I'd use a series of checkboxes with values set to the id of the row they represent. Presumably you have some sort of persistence mechanism in which these rows can be looked up or have them cached server side.
[HttpPost]
public ActionResult Update( List<int> rowIDs ) // where your checkboxes are named rowIDs
{
var messages = DB.Messages.Where( m => rowIDs.Contains( m.ID ) );
foreach (var message in messages)
{
// process the update
}
DB.SaveChanges();
return RedirectToAction( "index" ); // display the updated list
}
Note that it's more likely that you have a model with the collection of ids as well as some other data representing what "update" to perform. Posting collections can be tricky; you might need to play with the name of the input and/or with hidden indexes if you're not getting all the data posted back as expected.
I'm working on an ASP.NET MVC2 app. I've come to realize a very surprising, yet amazing thing that MVC does behind the scenes having to do with the ModelState and model binding. I have a ViewModel which has a whole bunch of data - some fields being part of a form while others are simply part of the UI. On HttpPost, my Action method uses the DefaultModelBinder which attempts to bind the whole model, but only fields which were part of the form are successfully deserialized - all others remain null. That's fine and understandable. If the ModelState is invalid, I need to refresh the model from the db and bind those particular form fields before returning to the same edit view to display those associated ModelState validation errors.
Here's where my amazement and curiosity comes. It was my assumption that in order for me to bind the form fields with the refreshed model, I needed to make a call to either UpdateModel() or TryUpdateModel<>(), passing in the newly refreshed model. For example:
[HttpPost]
public ActionResult EditDetail(EditDetailItemModel model)
{
if (model.IsValid)
{
// Save the results to the db
return RedirectToAction(...)
}
// Can't simply "return View(model)". Not all fields in EditDetailItemModel
// were part of the form - thus they returned null. Have to refresh
// model from the db.
var refreshedModel = RefreshModelFromDB();
// Is this line necessary?????
TryUpdateModel<EditDetailItemModel>(refreshedModel);
return View(refreshedModel);
}
But, what I found was that if I simply returned refreshedModel to the view WITHOUT making a call to TryUpdateModel<>(), the refreshed model was automatically bound with the form field values posted!! Hence, the TryUpdateModel<>() is not needed here!
The only way I can make any sense of it is that since the ModelState is in an invalid state, once I returned the view with the refreshed model, the "MVC rendering engine" looped through the ModelState errors and bound those property values with my refreshed model. That is simply AWESOME! But, I want proof as to this assumption. I can't find documentation regarding this anywhere on the web. Can anyone either confirm my hypothesis of WHY/HOW this AWESOME auto binding behavior is occuring and/or educate me as to why/how it's happening, hopefully backed up with some online documentation links so I understand more fully what's going on under the covers?
public ActionResult EditDetail(EditDetailItemModel model)
That line will perform model binding. Think of ActionMethod parameters as always being populated by a call to UpdateModel.
You are not seeing refreshedModel's values in the view, you are seeing the ModelState entries and values from EditDetailItemModel.
I have an asp.net mvc app with a form.
When you submit the form, it adds records to the sql database with linq-to-sql. After adding the records, the controller displays the form again, and should show those new values on the form. But, when it displays the form, the values are blank, until you refresh the page.
While tracing through the code, I can see the records being added to the database when they are submitted, but the view doesnt display them, unless I refresh. The view is not the problem, it just displays the view model, which is missing the new records immediately after the post.
I know this is kind of vague, but wasnt sure what parts of code to include here.
Could this have something to do with data context life cycle? Basically, there is a data context created when the form is posted, then a different data context is created in the method that displays the form.
Any suggestions on what might be causing this?
Update:
There's a whole lot of code I could post here, but I'll try to give you a simplified version:
This code maintains a schedule of volunteer assignments
The view uses a view model with a list of schedules, and displays a form of schedules and their associated assignments. (child records)
When the form is posted, with a new schedule & assignemnts, a schedule record is created, and the related assignment records are created.
// Controller
public class SchedulerController : Controller
{
ScheduleServices ScheduleSvc = new ScheduleServices(); // creates a new data context
public ActionResult Index()
{
return ShowSchedules();
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Index(FormCollection form)
{
ScheduleSvc.ProcessRequest(form);
return Index();
}
}
public ActionResult ShowSchedules()
{
SchedulerViewModel sched_vm = new SchedulerViewModel();
sched_vm.EventsAndSchedules = ScheduleSvc.GetEventSchedulesFromDate();
return View(sched_vm);
}
ProceessScheduleRequest(ScheduleRequest req)
{
CreateSchedule(req);
AssignmentServices AssignmentSvc = new AssignmentServices(); // creates it's own data context
AssignmentSvc.Assign(req);
}
I found the answer in this post (myth #10).
Apparentlly, using the same DataContext for multiple units of work, results in stale data, because objects tracked by a DataContext instance are not refreshed simply by requerying.
Instead of using the same DataCcontext for both adding the records, and then displaying the results, I used two separate ones and that fixed it. So instead of having one ScheduleServices instance for the whole controller class, I create one for the ProcessRequest() and a separate one for ShowSchedules().
After saving to the database, you should redirect to the controller method that displays the data. This will cause the controller method to look up the data from the database again, which will now display the newly-updated record.
Situation: In some project management software written in asp.net I have a create project page (working fine). I need to add to this the ability to add tasks from a list of templates to this project pre-creation BUT the list of available tasks is dependent on some values sitting in the create form.
My abstract solution is this:
I have a "Create" view and an "Add Tasks" View - both strongly typed to a composite viewModel defined in the controller
My Create method checks which button was used to call it - if the
button was "Add Tasks" it then renders the AddTasks view, passing the model in from the create view, again all in the same controller.
The AddTasks View posts to the Create view with one of two buttons, one loads the view and the other causes an actually DB save.
My Problem is this:
The different views use different properties of the same model, but in passing this model between them, the data is reset (in any case reload or save).
I am guessing this is happening from auto binding of data - though I thought fields not present on the form would not overwrite existing model data passed down.
There is hardly any code in the controller manipulating the model at present - It is only passed from view to view in these cases.
This is the controller code:
// POST: /Project/Create/<viewModel>
[Authorize, AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create([Bind(Exclude = "Id,id")] ProjectViewModel model)
{
if (model.SubmitValue == "Create")
{
try
{
model.Project.Id = Guid.NewGuid();
model.Save(this.User.Identity.Name);
return this.RedirectToAction("Details", new {id = model.Project.Id});
}
catch (Exception e)
{
this.ModelState.AddModelError(e.ToString(), e.ToString());
}
return View(model);
}
if(model.SubmitValue == "AddTasks")
{
return this.View("AddTasks",model);
}
return this.View(model);
}
//POST: /Project/AddTasks/ + model
[Authorize, AcceptVerbs(HttpVerbs.Post)]
public ActionResult AddTasks([Bind(Include = SelectedCarrierTasks")]ProjectViewModel model)
{
return View(model);
}
The Question is: How do I maintain the state of the model across these views until it finally save it?
I would prefer to avoid any hackish (TempData) or JS dependant solutions, but I am not closed to these if they are really the best solution.
Thanks,
Adam Tolley
One simple solution is to persist the ViewModel object in a Session variable and bind the View from this source.I ts certainly not the most elegant solution. Another option, and probably less elegant one is persist this model data in the database, with some temporary/unsaved flag.
The problem is that when you display the add tasks view you're not providing fields for your "Project" object therefore the ModelState loses the data related to the project, you will need to provide this fields to ensure you're not loosing that data.
You don't need to display this fields they can be of type hidden and they will preserve the value. Just make sure that if you will be binding to a view model you will need to name this fields correctly like this Model.Project.Property.
Perhaps I am trying to solve the wrong problem (ala Bruce Eckel). I am going to try to move to a structure that needs this sort of fuzzy boundary less. I don't want to adopt a REST paradigm only to shoe-horn it into a stateful application.
Possibly these controls belong on the same page, and I can use some JQuery goodness to put in a tab pane for easiness on the eyes.
Thanks to those who answered, I found each useful and will try to remember to up-vote them as soon as I have some more rep.
I can't comment on other peoples questions at the moment, but the only real option is the session if you want to persist an objects state during web requests, or serializing it and placing it in a hidden field.
Or a final option would be to change the way your pages work so you can save the object after each request...
If your using nHibernate then you might want look into the Conversations pattern, but this just essentially saves the nHibernate session into the asp.net session anyway...