Passing data base info from controller to view in .NET MVC - asp.net-mvc

Hello I'm trying to pass some database info from my controller to my view, but don't find the best way to do it. I'm populating the model in my controller, but I need to populate those values from database. I have a class called DataAccess which is the one that contains all my queries but not sure where I should put the logic to populate. I would say a for loop in my controller to populate the values, but seems to fail since I'm declaring the SchedulerViewModel there
The idea is having my values next to a radio button, so when selecting a radio button, I can "detect" the value and do something with that option....any suggestion would be appreciated...
My model:
public class SchedulerViewModel
{
public string theValue { get; set; }
public SelectListItem[] Items { get; set; }
}
My Controller:
public ActionResult Scheduler()
{
//DataAccess dataAccess = new DataAccess();
//for loop here???
var model = new SchedulerViewModel
{
Items = new[]
{
new SelectListItem { Value = "U", Text = "USA" }
}
};
return View(model);
}
My view:
#using (Html.BeginForm())
{
for (int i = 0; i < Model.Items.Count(); i++)
{
#Html.RadioButtonFor(x => x. theValue, Model.Items[i].Value, new { id = "item_" + i })
#Html.Label("item_" + i, Model.Items[i].Text)
<br />
}
}

Ideally you would have a service class that handles your database access. You shouldn't directly invoke the data layer from the controller, although nothing prevents you from doing it. For simplicity, I'm just putting calling the data access directly in the controller. The idea is that you need to return a collection of data, here an IEnumerable, in the View at the controller level so that the View can display this data.
Controller:
[HttpGet]
public ActionResult Index()
{
KnowledgeBaseEntities context = new KnowledgeBaseEntities();
IEnumerable<ISSUE> issues = context.ISSUES;
if(issues == null)
{
return HttpNotFound();
}
return View(issues);
}
View:
As you can see I'm referencing the collection of data that I'm expecting from the controller.
#model IEnumerable<ISSUE>
In this case it's an IEnumerable just like I had in the controller. Then you'll notice I'm referencing a Model object when I iterate the model.
#foreach (var item in Model)
Then I'm looping through each row of the model in order to add table rows to the table. Because we're using Model Binding from the Entity Framework. We're using Razor Syntax. You also notice I'm using Action Links for each row in the last column. This allows me to Edit, Delete or provide Details for a row of data. However, I will need to invoke another Controller Action for that. For example, you'd have an Edit controller action method that returns a single ISSUE to an Edit View.
#model IEnumerable<ISSUE>
#{
ViewBag.Title = "Knowledge Base Issues";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2 class="line">All Issues</h2>
<p>
#Html.ActionLink("Create New", "Create")
</p>
<table class="flat">
<tr>
<th>#Html.DisplayNameFor(model => model.KEYWORDS)</th>
<th>#Html.DisplayNameFor(model => model.SUBJECT)</th>
<th>#Html.DisplayNameFor(model => model.DATE_ENTERED)</th>
<th></th>
</tr>
#foreach (var item in Model) {
<tr>
<td>#Html.DisplayFor(modelItem => item.KEYWORDS)</td>
<td>#Html.DisplayFor(modelItem => item.SUBJECT)</td>
<td>#Html.DisplayFor(modelItem => item.DATE_ENTERED)</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id=item.ISSUE_ID }) |
#Html.ActionLink("Details", "Details", new { id=item.ISSUE_ID }) |
#Html.ActionLink("Delete", "Delete", new { id=item.ISSUE_ID })
</td>
</tr>
}

Related

Model Not Submitting / Binding Correctly on POST

Scenario:
I have a table with rows that are populated from a ViewModel. There are checkboxes for each row that allow the user to check 1 or more of the rows and then choose from actions in a dropdown menu to make edits to properties on the selected rows.
Everything works fine to this point, and I can get the ViewModel to pass correctly and then use it and all it's properties in a POST Action method. I could make the changes based on the option the user picked.
However, since some of the options in the dropdown would make fairly substantial and irreversible changes, I am calling a new View with a GET and populating a new table with just the selected rows, and asking the user to confirm they want to make the changes. Everything is still good up to this point. The new View populates as expected with only the rows that were selected in the previous View.
Problem:
After the user confirms their intent, an Action method is called with POST. The ViewModel that correctly populated the current View is making its way into the controller correctly. I get the ViewModel, but not with the same properties as the one that populated the View.
ViewModel
public class ProjectIndexViewModel
{
public List<ProjectDetailsViewModel> Projects { get; set; }
public string FlagFormEditProjects { get; set; }
public string FlagFormNewProjectStatus { get; set; }
}
The List<ProjectDetailsViewModel> Projects is what is used to populate the rows in the table, and Projects are what are not binding correctly in the POST Action methods in the controller.
Initial View where the checkboxes are selected. Note the example of one of the javascript functions that is called when one of the dropdown options is selected, which is what submits the form.
#using (Html.BeginForm("EditProjectsTable", "Project", FormMethod.Get, new { name = "formEditProjects", id = "formEditProjects" }))
{
#Html.HiddenFor(item => item.FlagFormEditProjects)
#Html.HiddenFor(item => item.FlagFormNewProjectStatus)
....
<table>
<thead>
....
</thead>
<tbody>
#for (int i = 0; i < Model.Projects.Count; i++)
{
<tr>
<td>#Html.DisplayFor(x => x.Projects[i].ProjectNumber)</td>
<td>#Html.DisplayFor(x => x.Projects[i].ProjectWorkType)</td>
.... // more display properties
<td>
#Html.CheckBoxFor(x => x.Projects[i].Selected, new { #class = "big-checkbox" })
#Html.HiddenFor(x => x.Projects[i].ProjectModelId)
</td>
</tr>
}
</tbody>
</table>
}
function submitFormRemoveProjects() {
$("#FlagFormEditProjects").attr({
"value": "RemoveProjects"
});
$('#formEditProjects').submit();
}
Action method that returns the "confirmation" View (works fine)
[HttpGet]
[Authorize(Roles = "Sys Admin, Account Admin, User")]
public async Task<ActionResult> EditProjectsTable([Bind(Include = "Projects,FlagFormEditProjects,FlagformNewProjectStatus")]ProjectIndexViewModel projectIndexViewModel)
{
// Repopulate the Projects collection of ProjectIndexViewModel to
// include only those that have been selected
return View(projectIndexViewModel);
}
View that is returned from Action method above (works fine) Note that the Action method that gets called is set dynamically with the actionName variable in the Html.BeginForm call.
#using (Html.BeginForm(actionName, "Project", FormMethod.Post))
{
#Html.AntiForgeryToken()
#Html.HiddenFor(model => model.FlagFormNewProjectStatus)
....
<table>
<thead>
....
</thead>
<tbody>
#for (int i = 0; i < Model.Projects.Count; i++)
{
<tr>
<td>#Html.HiddenFor(x => x.Projects[i].ProjectModelId)</td>
<td>#Html.DisplayFor(x => x.Projects[i].ProjectNumber)</td>
<td>#Html.DisplayFor(x => x.Projects[i].ProjectWorkType)</td>
.... // more display properties
</tr>
}
</tbody>
</table>
<input type="submit" value="Delete Permanently" />
}
An example of one of the Controller Action methods that is called from this View, and that does not have the same Project that was in the View. Somehow, it has the same number of Projects that were originally selected, but if only one was selected, it has the Project with the lowest Model Id. I'm not sure how else to describe what's happening. But in summary, the correct ViewModel is not making it's way into the POST method example shown below.
[HttpPost]
[ValidateAntiForgeryToken]
[Authorize(Roles = "Sys Admin, Account Admin")]
public async Task<ActionResult> DeleteConfirmedMultipleProjects([Bind(Include = "Projects")] ProjectIndexViewModel projectIndexViewModel)
{
if (ModelState.IsValid)
{
// Remove Projects from db and save changes
return RedirectToAction("../Project/Index");
}
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Please help!
The issue is that when you submit to the EditProjectsTable() method from the first view, the values of all form controls are added to ModelState.
Repopulating your collection of ProjectDetailsViewModel does not update ModelState, and when you return the view, the DisplayFor() methods will display the correct values because DisplayFor() uses the values of the model, however your
#Html.HiddenFor(x => x.Projects[i].ProjectModelId)
will use the values from ModelState, as do all the HtmlHelper methods that generate form controls (except PasswordFor()).
One way to solve this is to call ModelState.Clear() before you return the view in the EditProjectsTable() method. The HiddenFor() method will now use the value of the model because there is no ModelState value.
[HttpGet]
[Authorize(Roles = "Sys Admin, Account Admin, User")]
public async Task<ActionResult> EditProjectsTable(ProjectIndexViewModel projectIndexViewModel)
{
// Repopulate the Projects collection of ProjectIndexViewModel to
// include only those that have been selected
ModelState.Clear(); // add this
return View(projectIndexViewModel);
}
For a explanation of why this is the default behavior, refer the second part of this answer.
Side note: Your using a view model, so there is no point including a [Bind] attribute in your methods.
I think your problems comed from this part:
#Html.CheckBoxFor(x => x.Projects[i].Selected, new { #class = "big-checkbox" })
#Html.HiddenFor(x => x.Projects[i].ProjectModelId)
I had this error before and what I did is adding a Boolean property to ProjectDetailsViewModel like IsSelected.
then you should have :
#Html.CheckBoxFor(x => x.Projects[i].IsSelected, new { #class = "big-checkbox" })
Then on method you should add:
foreach (var project in ProjectIndexViewModel.Projects )
{
if (project.IsSelected==true)
"put your logic here"
}

The model item passed into the dictionary is of type 'System.Data.Entity.DynamicProxies.Objt

The model item passed into the dictionary is of type 'System.Data.Entity.DynamicProxies.Donemler_8A34F5A0E6AB4F6429B22B8E4A5B0CDD80C5DCEA6C183E8068EDB99DF7FA8263', but this dictionary requires a model item of type 'System.Collections.Generic.IEnumerable`1[Anket.Data.DomainModel.Donemler]'.
Here is my code:
Controller part
public ActionResult _DonemGetir(int id)
{
var donem = donemService.Bul(id);
return PartialView(donem);
}
Service Class
public Donemler Bul(int id)
{
return db.Donem.Find(id);
}
View Part
#model IEnumerable<Anket.Data.DomainModel.Donemler>
#{
foreach (var item in Model)
{
#Html.DisplayFor(model => item.donem )
}
}
Id value in Controller( _DonemGetir(int id) ) comes from another view and those View Codes :
#model IEnumerable<Anket.Data.DomainModel.Anketler>
#foreach(var item in Model)
{
<tr>
<td class="nameRecord">
#Html.Action("_DonemGetir", "Ortak", new { id = item.ID })
</td>
</tr>
}
</table>
_DonemGetir View :
#model IEnumerable<Anket.Data.DomainModel.Donemler>
#{
foreach (var item in Model)
{
#Html.DisplayFor(model => item.donem )
}
}
I have two tables:
Anketler and Donemler
Anketler table has :
ID(PK) - name - and donem_id (FK)
Donemler table has :
ID(PK) - name
I want to fetch data from Anketler and I do not want to fetch donem_id as an integer. I need it's name from Donemler table. I tried to do this with these codes.
I fixed the problem
I changed _DonemGetir View like this
#model Anket.Data.DomainModel.Donemler
#{
#Html.DisplayFor(model => model.donem)
}

View passing wrong viewmodel

I've created a website using ASP.Net MVC5 (VS 2013) but I guess the same problem would present itself in MVC3 or MVC4
I have the following view:
#model IEnumerable<WilhanWebsite.Models.TestimonialViewModel>
#{
ViewBag.Title = "Index";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Index</h2>
<p>
#Html.ActionLink("Create New", "Create")
</p>
<table class="table">
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.Testimonial.Description)
</td>
<td>
#Html.DisplayFor(modelItem => item.Testimonial.Author)
</td>
<td>
#Html.DisplayFor(modelItem => item.Testimonial.Timestamp)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id=item.Testimonial.TestimonialId }) |
#Html.ActionLink("Details", "Details", new { id=item.Testimonial.TestimonialId }) |
#Html.ActionLink("Delete", "Delete", new { id=item.Testimonial.TestimonialId })
</td>
</tr>
}
</table>
The Index action of my Testimonial controller sends back a List and the view displays existing testimonials correctly in the html table. My problem is that when I click the Edit hyperlink I get the following error:
The model item passed into the dictionary is of type
'WilhanWebsite.DomainClasses.Testimonial', but this dictionary
requires a model item of type
'WilhanWebsite.Models.TestimonialViewModel'
I was previously using DomainClasses.Testimonial as the model passed between controller an view but today I refactored to create the new dedicated view model. It seems strange that the view is happy to process the new viewmodel when displaying the data so why is it passing the old DomainClasses.Testimonial when I click the Edit link?
Any help greatly appreciated!
The View expects #model IEnumerable<WilhanWebsite.Models.TestimonialViewModel> so you have to return it from the Controller's Index action method. Without seeing your controller I would guess that your returning a List of type Testimonial rather than a List of Type TestimonialViewModel
// GET: /Testimonial/
public ActionResult Index()
{
List<TestimonialViewModel> testimonialViewModel = new List<TestimonialViewModel>();
// Add some testimonials to your list.
return View(testimonialViewModel);
//NOT THIS - IT WILL THROW THE ERROR YOUR GETTING
return View(db.Testimonial.ToList());
}
//Testimonial/Index
#model IEnumerable<WilhanWebsite.Models.TestimonialViewModel>
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
Alternativly if you are returning a System.Collection and TestimonialViewModel is specified. Make sure that its implements IEnumerable. The following types do.
ICollection
IDictionary
IDictionaryEnumerator
IEnumerable
IEnumerator
IHashCodeProvider
IList
I was able to fix by updating the Edit action to the following:
public ActionResult Edit(int id)
{
var query = from t in _context.Testimonials
where t.TestimonialId == id
select t;
TestimonialViewModel tvm = new TestimonialViewModel();
tvm.Testimonial = query.First();
return View(tvm);
}

Binding input model in partial view help needed

I want to pass an input model from a partial view to a controller. I'm rather new to MVC so still trying to understand how the default model binder works.
Via AJAX (listBox) a controller passes back a partial view and inserts into table id=searchResults.
#model ViewModels.LocationViewModel
#using (Ajax.BeginForm("ProcessSearch", "SearchR", new AjaxOptions
{
HttpMethod = "GET",
InsertionMode = InsertionMode.Replace,
UpdateTargetId = "searchResults",
}))
{
<div>#Html.ListBoxFor(xxx)</div>
<input id="Search" type="submit" value="Search" />
}
Here is the controller and ViewModel that populates the partial view
public class OrderViewModel
{
public string Description { get; set; }
public string Status { get; set; }
}
public ActionResult ProcessSearch(SearchViewModel search)
{
select new OrderViewModel{
Status=f.STATUS,
Description=f.DESCRIPTION}).ToList();
return PartialView(model);
}
In the same main view I have this form that I want I want to bind to yet another view model. I simply don't understand how to implement the default binder from the model of the partial view. I apologize if I didn't explain this correctly. I hope it makes sense.
#using (Html.BeginForm("Testing", "SearchR", FormMethod.Post))
{
<div>#Html.DropDownListFor(yyy)</div>
<input id="Reshop" type="submit" value="Reshop" />
}
<table id="searchResults"></table>
public ActionResult Testing(RSOrderViewModel rOrder)
{
return Content("hey");
}
public class RSOrderViewModel
{
public string yyy { get; set; }
public IEnumerable<OrderViewModel> sovm { get; set; }
}
#model List<ViewModels.OrderViewModel>
#{ViewData.TemplateInfo.HtmlFieldPrefix = "sovm";
}
<table id="searchResults">
<tr>
<th>Order Id</th>
<th>Order Detail</tr>
#for (int i = 0; i < Model.Count; i++)
{
<tr>
<td>
#Html.DisplayFor(x => x[i].OrderId)
</td>
<td>
#Html.DisplayFor(x => x[i].OrderDetail)
</td>
</tr>
}
</table>
The table is outside the second form. So when you POST to the Testing action all that is sent to the controller is the value of the dropdown list. If you want to send the collection that's stored in this table you will have to either use AJAX or put the table inside the form:
#using (Html.BeginForm("Testing", "SearchR", FormMethod.Post))
{
<div>#Html.DropDownListFor(yyy)</div>
<table id="searchResults"></table>
<input id="Reshop" type="submit" value="Reshop" />
}
Now of course putting the table inside the form doesn't mean that it will send anything to the server when you submit the form. You need to put input fields (hidden if you don't want them to be visible to the user) that will contain the values that will be POSTed back. Also those input field names must follow the standard convention for binding to a list.
You haven't actually shown how does the partial view look like but here's an example of how it might look so that the convention is respected. For example let's suppose that you have 2 properties in your OrderViewModel that you want to be bound: OrderId and OrderDetail:
#model IEnumerable<OrderViewModel>
#{
// We set the name prefix for input fields to "sovm"
// because inside your RSOrderViewModel the collection
// property you want to bind to this table is called sovm
// and in order to respect the convention the input names
// must be in the following form "sovm[0].OrderId", "sovm[1].OrderId", ...
ViewData.TemplateInfo.HtmlFieldPrefix = "sovm";
}
<thead>
<tr>
<th>Order Id</th>
<th>Order detail</th>
</tr>
</thead>
<tbody>
#Html.EditorForModel()
</tbody>
and then you could have an editor template which will be rendered for each element of the model (~/Views/Shared/EditorTemplates/OrderViewModel.cshtml):
#model OrderViewModel
<tr>
<td>#Html.EditorFor(x => x.OrderId)</td>
<td>#Html.EditorFor(x => x.OrderDetail)</td>
</tr>
The name of the template is the name of the type used in the collection you want to bind to (IEnumerable<OrderViewModel> sovm { get; set; } => OrderViewModel.cshtml). It must also be placed inside the ~/Views/Shared/EditorTemplates folder if it can be reused between multiple controllers or if it is specific to the current controller inside the ~/Views/XXX/EditorTemplates folder where XXX is the current controller.

In ASP.NET MVC, why does new input get persisted on postback?

This question is a bit different than most. My code works but I don't understand why it works.
I am trying to understand why changes made in the form get persisted after posting to the server.
Model:
public class TestUpdateModel
{
public int Id { get; set; }
public List<CarrierPrice> Prices { get; set; }
public TestUpdateModel() { } // parameterless constructor for the modelbinder
public TestUpdateModel(int id)
{
Id = id;
Prices = new List<CarrierPrice>();
using (ProjectDb db = new ProjectDb())
{
var carriers = (from c in db.Carriers
select c);
foreach (var item in carriers)
{
var thesePrices = item.Prices.Where(x => x.Parent.ParentId == Id);
if (thesePrices.Count() <= 0)
{
Prices.Add(new CarrierPrice
{
Carrier = item
});
}
else
Prices.Add(thesePrices.OrderByDescending(x => x.DateCreated).First());
}
}
}
}
Controller:
public ViewResult Test(int id = 1)
{
TestUpdateModel model = new TestUpdateModel(id);
return View(model);
}
[HttpPost]
public ViewResult Test(TestUpdateModel model)
{
model = new TestUpdateModel(model.Id);
return View(model); // when model is sent back to view, it keeps the posted changes... why?
}
View
#model Namespace.TestUpdateModel
#{ ViewBag.Title = "Test"; }
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
#Html.HiddenFor(model => model.Id)
#Html.EditorFor(x => x.Prices)
<input type="submit" value="Save" />
}
EditorTemplate
#model Namespace.CarrierPrice
<tr>
<th>
#Html.HiddenFor(model => model.CarrierPriceId)
#Html.HiddenFor(model => model.DateCreated)
#Model.Carrier.Name
</th>
<td>
$#Html.TextBoxFor(model => model.Fee)
</td>
</tr>
The Source of My Confusion
1) Load the page in the browser
2) Change the value of the model.fee TextBox
3) Submit
At this point I would expect that model = new TestUpdateModel(model.Id); would create a new TestUpdateModel object and wipe out my changes so the original values re-appear when the view is returned. But what actually happens is my changes in the form are persisted to the postback.
Why does this happen?
Thanks for any help.
No. The reason is that the View Engine looks in the ModelState to fill your values before it looks at the model when it renders your view. If there are posted values, then it will find those first and use them.
If you want to override this behavior, then you need to clear the ModelState first with:
ModelState.Clear();

Resources