MVC3 Razor Passing Model from View to Controller - asp.net-mvc

I have a MVC3 application using Entity Framework as the Model layer.
In the EmployeeController, I have:
public ActionResult GetEmployeeEdit(String id)
{
// Get the desired Employee
var model = GetEmployees().FirstOrDefault(o=>o.EFolderid == id);
return View("EmployeeEdit", model);
}
private IQueryable<Employee> GetEmployees()
{
// Returns IQueryable<Employee>
return _employeeService.GetTable();
}
In EmployeeEdit I have:
#model Metastorm.Domain.Models.Employee
#{
ViewBag.Title = "Employee Edit";
}
#using (Html.BeginForm("SaveEmployee", "Employee", FormMethod.Get, Model))
{
<fieldset>
<legend>Edit Employee</legend>
<br />
#Html.Label("firstName", "First Name: ")
#Html.EditorFor(o => #Model.NameFirst)
<br />
#Html.Label("lastName", "Last Name: ")
#Html.EditorFor(o => #Model.NameLast)
</fieldset>
<br />
<input class="button" id="submit" type="submit" value = "Save Employee" />
}
Now back to the EmployeeController:
[HttpGet]
public ActionResult SaveEmployee(Employee employee)
{
if (ModelState.IsValid)
{
// Get the Employee Model again from Entity, Update and save
// Unfortunately, the employee object's FolderId value is null
}
// Just getting a model to satisfy the function
var model = GetEmployees().FirstOrDefault();
return View("EmployeeEdit", model);
}
The problem I'm having is that all properties on the employee object are null, except for Employee.NameFirst and Employee.NameLast, which happen to be the properties that were exposed in the View with Html.EditorFor.
In summary, I get an Employee model object, which is fully hydrated. I pass this model from the Controller to the view. In the view, selected fields are allowed to be updated. The Employee model is then passed back to the Controller where updates are persisted.
My question is how do I keep the Employee model that was originally passed from the Controller to the View intact. In other words, I want to have the model

Try to use this code:
[HttpGet]
public ActionResult SaveEmployee([Bind(Include = "NameFirst,NameLast,EFolderid")] Employee employee)
{
if (ModelState.IsValid)
{
// Do your staff
// The employee object's FolderId value will be not null
}
// Just getting a model to satisfy the function
var model = GetEmployees().FirstOrDefault();
return View("EmployeeEdit", model);
}
This will force the data members "NameFirst, NameLast, EFolderid" of the object employee to be persistent. You need to declare in the include statement all the members that you want to be preserved during the Http request.
You should also add this code in your view, inside the Form, or the EFolderid data will be lost:
#Html.HiddenFor(o=> #Model.EFolderid)
Add an "HiddenFor" declaration foreach member that you want to preserve.

To obtain model properties in a Post action, you need include all the properties inside the form.
It is necessary for a correct model serialization.

I went a different route. I created a view model, then us AutoMapper to map the Employee object's properties to the view model. I pass the view model to the view, make changes, then pass the view model back to the Action. Then I can then user AutoMapper to map the view model back to a new instance of the Employee Object, then persist the changes.

Related

The model item passed into the dictionary is of type '', but this dictionary requires a model item of type '...EmployeeSearchModel'."} [duplicate]

This question and community wiki answer has been added to assist in closing out numerous unanswered questions as discussed in this meta post.
I have some code and when it executes, it throws an exception saying:
The model item passed into the dictionary is of type Bar but this dictionary requires a model item of type Foo
What does this mean, and how do I fix it?
The error means that you're navigating to a view whose model is declared as typeof Foo (by using #model Foo), but you actually passed it a model which is typeof Bar (note the term dictionary is used because a model is passed to the view via a ViewDataDictionary).
The error can be caused by
Passing the wrong model from a controller method to a view (or partial view)
Common examples include using a query that creates an anonymous object (or collection of anonymous objects) and passing it to the view
var model = db.Foos.Select(x => new
{
ID = x.ID,
Name = x.Name
};
return View(model); // passes an anonymous object to a view declared with #model Foo
or passing a collection of objects to a view that expect a single object
var model = db.Foos.Where(x => x.ID == id);
return View(model); // passes IEnumerable<Foo> to a view declared with #model Foo
The error can be easily identified at compile time by explicitly declaring the model type in the controller to match the model in the view rather than using var.
Passing the wrong model from a view to a partial view
Given the following model
public class Foo
{
public Bar MyBar { get; set; }
}
and a main view declared with #model Foo and a partial view declared with #model Bar, then
Foo model = db.Foos.Where(x => x.ID == id).Include(x => x.Bar).FirstOrDefault();
return View(model);
will return the correct model to the main view. However the exception will be thrown if the view includes
#Html.Partial("_Bar") // or #{ Html.RenderPartial("_Bar"); }
By default, the model passed to the partial view is the model declared in the main view and you need to use
#Html.Partial("_Bar", Model.MyBar) // or #{ Html.RenderPartial("_Bar", Model.MyBar); }
to pass the instance of Bar to the partial view. Note also that if the value of MyBar is null (has not been initialized), then by default Foo will be passed to the partial, in which case, it needs to be
#Html.Partial("_Bar", new Bar())
Declaring a model in a layout
If a layout file includes a model declaration, then all views that use that layout must declare the same model, or a model that derives from that model.
If you want to include the html for a separate model in a Layout, then in the Layout, use #Html.Action(...) to call a [ChildActionOnly] method initializes that model and returns a partial view for it.
This question already has a great answer, but I ran into the same error, in a different scenario: displaying a List in an EditorTemplate.
I have a model like this:
public class Foo
{
public string FooName { get; set; }
public List<Bar> Bars { get; set; }
}
public class Bar
{
public string BarName { get; set; }
}
And this is my main view:
#model Foo
#Html.TextBoxFor(m => m.Name, new { #class = "form-control" })
#Html.EditorFor(m => m.Bars)
And this is my Bar EditorTemplate (Bar.cshtml)
#model List<Bar>
<div class="some-style">
#foreach (var item in Model)
{
<label>#item.BarName</label>
}
</div>
And I got this error:
The model item passed into the dictionary is of type 'Bar', but this
dictionary requires a model item of type
'System.Collections.Generic.List`1[Bar]
The reason for this error is that EditorFor already iterates the List for you, so if you pass a collection to it, it would display the editor template once for each item in the collection.
This is how I fixed this problem:
Brought the styles outside of the editor template, and into the main view:
#model Foo
#Html.TextBoxFor(m => m.Name, new { #class = "form-control" })
<div class="some-style">
#Html.EditorFor(m => m.Bars)
</div>
And changed the EditorTemplate (Bar.cshtml) to this:
#model Bar
<label>#Model.BarName</label>
Observe if the view has the model required:
View
#model IEnumerable<WFAccess.Models.ViewModels.SiteViewModel>
<div class="row">
<table class="table table-striped table-hover table-width-custom">
<thead>
<tr>
....
Controller
[HttpGet]
public ActionResult ListItems()
{
SiteStore site = new SiteStore();
site.GetSites();
IEnumerable<SiteViewModel> sites =
site.SitesList.Select(s => new SiteViewModel
{
Id = s.Id,
Type = s.Type
});
return PartialView("_ListItems", sites);
}
In my case I Use a partial view but runs in normal views
Consider the partial map.cshtml at Partials/Map.cshtml. This can be called from the Page where the partial is to be rendered, simply by using the <partial> tag:
<partial name="Partials/Map" model="new Pages.Partials.MapModel()" />
This is one of the easiest methods I encountered (although I am using razor pages, I am sure same is for MVC too)
First you need to return an IEnumerable version of your model to the list view.
#model IEnumerable<IdentityManager.Models.MerchantDetail>
Second, you need to return a list from the database. I am doing it via SQL Server, so this is code I got working.
public IActionResult Merchant_Boarding_List()
List<MerchantDetail> merchList = new List<MerchantDetail>();
var model = new MerchantDetail();
try
{
using (var con = new SqlConnection(Common.DB_CONNECTION_STRING_BOARDING))
{
con.Open();
using (var command = new SqlCommand("select * from MerchantDetail md where md.UserGUID = '" + UserGUID + "'", con))
{
using (SqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
model.biz_dbaBusinessName = reader["biz_dbaBusinessName"].ToString();
merchList.Add(model);
}
}
}
}
}
catch (Exception ex)
{
}
return View(merchList);
Passing the model value that is populated from a controller method to a view
public async Task<IActionResult> Index()
{
//Getting Data from Database
var model= await _context.GetData();
//Selecting Populated Data from the Model and passing to view
return View(model.Value);
}
one more thing.
if your view is a partial/sub page and the model for that partial view is null for some reason (e.g no data) you will get this error. Just need to handle the null partial view model

Form post not working on submit. Form object shows null

When I post the form by clicking on the save button, it hits the post method but the model parameter is always null.
Controller:
[HttpPost]
public ActionResult Edit(QuestionMaster question)
{
if (questionLogic.Update(model))
{
return RedirectToAction("List");
}
return View();
}
View:
#using (Html.BeginForm()) {
<fieldset>
<legend>QuestionMaster</legend>
<div class="editor-label">
#Html.LabelFor(model => model.Question)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Question)
#Html.ValidationMessageFor(model => model.Question)
</div>
</fieldset>
<p><input type="submit" value="Save" /></p>
}
You have not posted your model for QuestionMaster but from the view code it appears to contain a property named Question which is typeof string. The problem is that your POST method parameter is also named question which causes model binding to fail and the object is null.
Rename the parameter to anything but a name of a property in your model, for example
[HttpPost]
public ActionResult Edit(QuestionMaster model)
The reason why your model was null on postback is because of how model binding works
The DefaultModelBinder initializes a new instance of
QuestionMaster
The posted form's name/value pairs are then checked. If a matching
property name is found, the value of that property is set.
In your case your posting back Question="The text you entered" The
model binder finds the parameter named question (i.e. a match) and
sets it to "The text you entered", but question is typeof
QuestionMaster (a complex object, not a string) so binding fails
and the model becomes null.
Pass HtmlFieldPrefix in your EditorFor.
#Html.EditorFor(model => model.Question, new ViewDataDictionary() { TemplateInfo = new TemplateInfo { HtmlFieldPrefix = "QuestionMaster" }})
This way the names of the fields will be correct and the model binder will be able to bind them.
From the code you've given above, your View is missing a model.
You can add the model to the view as below:
#model QuestionMaster
This code is typically the first line in your view.
Other than that, can you explain the scope of model in your controller action? Where is model defined? If it isn't defined, you should understand that using #Model.someValue in the view is fine, but accessing model in your controller won't work unless your posted model parameter is called model.
Assuming that may be another reason for your form being "null", try changing your controller to:
[HttpPost]
public ActionResult Edit(QuestionMaster question)
{
if (questionLogic.Update(question))
{
return RedirectToAction("List");
}
return View();
}
You can do it what vortex told you or just change your parameter name to anything but "question"
[HttpPost]
public ActionResult Edit(QuestionMaster question2)
or
[HttpPost]
public ActionResult Edit(QuestionMaster model)
or
[HttpPost]
public ActionResult Edit(QuestionMaster anything)

Calling Function upon field change in MVC

I have a entity called WorkOrder which gets assigned to an Employee.
I want send an email notification when the workorder has been asigned. This can happen on my MVC Create or Edit Action (POST).
The problem i have is i have to do checks to see if the value has changed in the Edit to determine if i should send an email.
Is there a better place to call the SendEmail Function, like in the Entity Model itself?
If you are talking about posting from a view, you could create and bind the existing value to a hidden field in your form when loading the view. Then, on the POST to your action you can check to see if the value from the field is different from the one that is on the hidden field.
Example of View:
#using (Html.BeginForm("MyAction", "MyController")
{
#Html.HiddenFor(m => m.CurrentValue)
#Html.TextBoxFor(m => m.Value)
<input type="submit" value="submit" />
}
Example of Action GET
public ActionResult MyAction()
{
var viewModel = GetModelFromSomeWhere();
viewModel.CurrentValue = viewModel.Value;
return this.View(viewModel);
}
Example of Action POST
[HttpPost]
public ActionResult MyAction(ViewModel model)
{
if (model.Value != model.CurrentValue)
{
// It has changed! Send that email!
}
}

Creating a Child object in MVC4 - Parent's information not being passed to Create() controller

I have the following [HttpGet] Create() method:
public ActionResult Create(int? parentId)
{
var model = new CreatePersonViewModel();
// pull parent from db
var parent = _db.Persons.FirstOrDefault(s => s.Id == parentId);
model.Parent = parentSet;
return View("Create", model);
}
If I'm creating a new Person from another person's Details page, I pass in the ID of that parent Person and then construct a viewModel with the Parent included.
The POST looks like this:
[HttpPost]
public ActionResult Create(CreatePersonViewModel viewModel)
{
if (ModelState.IsValid)
{
var parent = viewModel.Parent; // This is always null for some reason
var person = new Person() { Name = viewModel.Name };
// if it has a parent, build new relationship
if (parent != null)
{
person.Parent = parent;
parent.Children.Add(person);
};
_db.Save();
return RedirectToAction("detail", "person", new { personId = person.Id });
}
return View(viewModel);
}
For some reason the viewModel getting pushed back to the POST method never contains the Parent that was defined in the GET controller method. How can I tell MVC to push the parent from GET to POST, without muddling the View with a hidden field for Parent?
In case it helps, my view is here:
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>CreatePersonViewModel</legend>
<div class="editor-label">
#Html.LabelFor(model => model.Name)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Name)
#Html.ValidationMessageFor(model => model.Name)
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
The controller action will only receive data that is explicitly included in the HTML form.
If you want to receive the Parent object, you'll need to put it in hidden <input> tags.
Note that any data that comes from the client is under complete control of your attacker and cannot be trusted.
The web is stateless so how would the server know what data you wanted to pass behind the scenes?
You could use TempData to pass data and it will remain on the server until it is read by your next request. However I don't believe you need that here. Since you aren't using anything but the parent id and the name, store that on the client - ie your viewmodel contains only those fields- no entity in your viewmodel.
When you post to the server load your parent, assign a new child and save it. No sense sending the entire object to the client.
Also I would verify on te server that the current user has access to those records - if applicable here unless your application allows all users access to all persons.

Binding to a SelectList in MVC

Once again I'm confronted with a "This shouldn't be this ?*!# hard" situation.
Problem: I want to use a form in MVC for creation of an object. One of the elements of the object is a set of limited choices - a perfect candidate for a drop down list.
But if I use a SelectList in my model, and a drop down list in my View, and then try to post the Model back to my Create method, I get the error "Missing Method Exception:No Parameterless constructor for this object". Exploring the MVC source code, it appears that in order to bind to a model, the Binder has to be able to create it first, and it can't create a SelectList because there is no default constructor for it.
Here's the simplified code:
For the model:
public class DemoCreateViewModel
{
public SelectList Choice { get; set; }
}
For the controller:
//
// GET: /Demo/Create
public ActionResult Create()
{
DemoCreateViewModel data = new DemoCreateViewModel();
data.Choice = new SelectList(new string[] { "Choice1", "Choice2", "Choice3" });
ViewData.Model = data;
return View();
}
//
// POST: /Demo/Create
[HttpPost]
public ActionResult Create(DemoCreateViewModel form)
{
try
{
// TODO: Add insert logic here
return RedirectToAction("Index");
}
catch
{
return View();
}
}
And for the View:
<fieldset>
<legend>Fields</legend>
<%= Html.LabelFor(model => model.Choice) %>
<%= Html.DropDownListFor(model => model.Choice, Model.Choice) %>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
Now, I know I can MAKE this work by dropping back 10 yards and punting: bypass model binding and drop back to the FormCollection and validate and bind all the fields myself, but there's got to be a simpler way. I mean, this is about as simple a requirement as it gets. Is there a way to make this work within the MVC ModelBinding architecture? If so, what is it? And if not, how come?
Edit: Well, I have egg on my face, but maybe this will help someone else. I did some more experimenting and found a simple solution that seems to work.
Provide a simple value (string or integer, depending on what your select list value type is), and name that as the model element that you bind to. Then provide a second element as the select list of choices, and name it something else. So my model became:
public class DemoCreateViewModel
{
public string Choice { get; set; }
public SelectList Choices { get; set; }
}
And then the DropDownListFor statement in the View becomes:
<%= Html.DropDownListFor(model => model.Choice, Model.Choices) %>
When I do this, the submit button correctly binds the choice made in the form to the string Choice, and submits the model back to the second Create method.
Here is one approach:
#Html.DropDownListFor(model => model.Choice,
ViewBag.Choices as SelectList,
"-- Select an option--",
new { #class = "editor-textbox" })
Notice that I use ViewBag to contain my SelectList. This way when you post back, the client doesn't send the entire select list up to the server as part of the model.
In your controller code, you just need to set the view bag:
ViewBag.Choices = new SelectList(....
Consider creating a different view model for your post action without the SelectList property:
public class DemoCreateViewModelForUpdate
{
public string Choice { get; set; }
}
Then you can always map from the DemoCreateViewModelPost instance to an DemoCreateViewModel instance if the model state is invalid and you want to re-show the view. I tend to prefer everything needed by the view to be in my display view model class, so using a separate update only view model let's me keep things slim and trim for the trip back to the server.
In your view, you'd do:
#Html.DropDownListFor(m => m.Choice, Model.Choices)
as in the previous answer, so no unnecessary data would round trip.

Resources