How do you convert a List query result to view model? - asp.net-mvc

I have an MVC controller method List<DataRow> GetUserCriteria() which runs a standard SQL Query, no EF or automapper. After running the query it does:
DataTable dt = new DataTable();
SqlDataAdapter sdp = new SqlDataAdapter(objCommand)
conn.Open();
sdp.Fill(dt)
list = dt.AsEnumerable().ToList();
return list;
Question is in my ActionResultMethod which returns the relevant view, how can I convert that list to the right type for the view model to consume and use in the view?
public ActionResult ClientProfile() {
var rawData = GetUserCriteria();
//Convert to type for view model here and pass into the view below.
return View()
}

You can avoid converting, and make it simple:
public class aDataTableView
{
public DataTable aTable { get; set; }
}
public class HomeController : Controller
{
//use any action or code that goes here
public ActionResult Index63()
{
DataTable dt = new DataTable();
SqlConnection conn = new SqlConnection(#"data source=.\sqlexpress;initial catalog=Breaz;integrated security=True");
//conn.Open();
SqlCommand objCommand = new SqlCommand("Select * from dbo.Example", conn);
SqlDataAdapter sdp = new SqlDataAdapter(objCommand);
sdp.Fill(dt);
//you are not explicitely disposing of dt
//dt.Dispose();
aDataTableView dtw = new aDataTableView { aTable = dt };
objCommand.Dispose();
sdp.Dispose();
conn.Close();
conn.Dispose();
return View(dtw);
}
#model Testy20161006.Controllers.aDataTableView
#{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Index63</title>
</head>
<body>
#using (Html.BeginForm())
{
<table border="1">
<thead>
<tr>
#foreach (System.Data.DataColumn col in Model.aTable.Columns) {
<th>#col.Caption</th>
}
</tr>
</thead>
<tbody>
#foreach(System.Data.DataRow row in Model.aTable.Rows) {
<tr>
#foreach (var cell in row.ItemArray) {
<td>#cell.ToString() </td>
}
</tr>
}
</tbody>
</table>
<input type="submit" value="click" />
}
</body>
</html>

The above answer worked for the data part. I will just add that in the case of what I am doing, I was able to achieve it by doing the following in my controller:
public ActionResult Index63() {
DataTable dt = new DataTable();
SqlConnection conn = new SqlConnection(#"data source=.\sqlexpress;initial catalog=Breaz;integrated security=True");
//conn.Open();
SqlCommand objCommand = new SqlCommand("Select * from dbo.Example", conn);
SqlDataAdapter sdp = new SqlDataAdapter(objCommand);
sdp.Fill(dt);
aDataTableView dtw = new aDataTableView { aTable = dt };
//Cast Each Row Element to an object
object FirstNameField = dt.Row[0][3]
//Map user values to model backing view
Index63ViewModel userViewModel = new Index63ViewModel();
userViewModel.FirstName = FirstNameField.ToString();
return(userViewModel)
}
This approach works for passing a single user profile data to the view which is what I needed in this case. Credit is also given to the above link as well. Thank you!

Related

First row first column data in the table is missing in mvc view

View
#model SimpleEmpCRUD.Models.CommonViewModel
#{
ViewBag.Title = "AddEmployeeDetails";
}
<table id="empTable" class="table table-bordered table-condensed table-hover table-striped">
<thead>
<tr>
<th>Id</th>
<th>First Name</th>
<th>Last Name</th>
</tr>
</thead>
<tbody>
#foreach (var Emp in Model.EmpResultSet)
{
<tr>
<td>#Emp.EId</td>
<td>#Emp.FirstName</td>
<td>#Emp.LastName</td>
</tr>
}
</tbody>
</table>
Controller
public class EmployeesController : Controller
{
//Add Employee
[HttpGet]
public ActionResult AddEmployeeDetails()
{
EmployeeDBHandler emp = new EmployeeDBHandler();
CommonViewModel model = new CommonViewModel();
model.EmpResultSet = new List<Employee>();
model.EmpResultSet = emp.GetEmployees();
model.CitySet = new Cities();
model.CitySet.CityList = emp.FetchAllCities();
return View(model);
}
}
Handler
public class EmployeeDBHandler
{
public List<Employee> GetEmployees()
{
List<Employee> empList = new List<Employee>();
string sqlConnstr = Utils.GetDBConnection();
SqlConnection sqlConn = new SqlConnection(sqlConnstr);
SqlCommand sqlCmd = new SqlCommand("GetAllEmployee", sqlConn);
sqlCmd.CommandType = CommandType.StoredProcedure;
sqlConn.Open();
using (SqlDataReader reader = sqlCmd.ExecuteReader())
{
while (reader.Read())
{
var emp = new Employee()
{
EId = (int)(reader["EmpID"]),
FirstName = (reader["FirstName"].ToString()),
LastName = (reader["LastName"].ToString())
};
empList.Add(emp);
}
}
return empList;
}
Why the value 1 fail to appear in the table as seen in the attached image. Id,First Name and Last Name values brought into the view and every datas shown in the table except the first row, first column.
I think I'm missing something obvious here..Thanks for any help you can provide.
We have to validate 2 types of code by which this problem is occuring :
1)Code Side
2)Design Side (css)
1) Code Side: Always handle null ( .ToString() cannot handle null values . So use Convert.ToString() insterad of that)
Updated Code:
public class EmployeeDBHandler
{
public List<Employee> GetEmployees()
{
List<Employee> empList = new List<Employee>();
string sqlConnstr = Utils.GetDBConnection();
SqlConnection sqlConn = new SqlConnection(sqlConnstr);
SqlCommand sqlCmd = new SqlCommand("GetAllEmployee", sqlConn);
sqlCmd.CommandType = CommandType.StoredProcedure;
sqlConn.Open();
using (SqlDataReader reader = sqlCmd.ExecuteReader())
{
while (reader.Read())
{
var emp = new Employee()
{
EId = (int)(reader["EmpID"]),
FirstName = (reader["FirstName"] != DBNull.Value && string.IsNullOrWhiteSpace(Convert.ToString(reader["FirstName"]))) ? Convert.ToString(reader["FirstName"]) : string.Empty,
LastName = (reader["LastName"] != DBNull.Value && string.IsNullOrWhiteSpace(Convert.ToString(reader["LastName"]))) ? Convert.ToString(reader["LastName"]) : string.Empty,
};
empList.Add(emp);
}
}
return empList;
}
}
Check that you are getting proper data in empList or not.if you are getting proper data in empList then That is css issue.

Why is my ViewData list null? MVC 4

I have two models, question and answer. I want to insert a list of answers thru ViewModel to a question but it seems in my post method my list is getting null. That might be a bad implementation as well, because I am returning a model of my question back when I post something and I guess my List is just getting null. How could I fix this?
Edit: I remade the controller and the view based on comments you gave me: Thats how it looks now, but seems my Answer List to be Empty again.
ViewModel:
public class ViewModel
{
public IEnumerable<Answer> Answers { get; set; }
public Question Question { get; set; }
}
Controller:
[Authorize]
public ActionResult Create()
{
ViewModel vm = new ViewModel();
ViewBag.BelongToTest = new SelectList(db.Tests, "TestId" , "TestTitle").FirstOrDefault();
vm.Question = new Question { Question_Text = String.Empty };
vm.Answers = new List<Answer> { new Answer { CorrectOrNot = false, AnswerText = "", OpenAnswerText = "" } };
return View(vm);
}
//
// POST: /Question/Create
[HttpPost]
[Authorize]
public ActionResult Create(ViewModel vm)
{
if (ModelState.IsValid)
{
vm.Question.BelongToTest = (from t in db.Tests
join m in db.Members on t.AddedByUser equals m.MemberId
where m.UserID == WebSecurity.CurrentUserId &&
t.AddedByUser == m.MemberId
orderby t.TestId descending
select t.TestId).FirstOrDefault();
db.Questions.Add(vm.Question);
db.SaveChanges();
if (vm.Answers != null)
{
foreach (var i in vm.Answers)
{
i.BelongToQuestion = vm.Question.QuestionId;
db.Answers.Add(i);
}
}
db.SaveChanges();
ViewBag.Message = "Data successfully saved!";
ModelState.Clear();
}
ViewBag.BelongToTest = new SelectList(db.Tests, "TestId", "TestTitle", vm.Question.BelongToTest);
vm.Question = new Question { Question_Text = String.Empty };
vm.Answers = new List<Answer> { new Answer { CorrectOrNot = false, AnswerText = "", OpenAnswerText = "" } };
return View("Create" , vm);
}
View:
#model MvcTestApplication.Models.ViewModel
#using MvcTestApplication.Models
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
#{
ViewBag.Title = "Create";
}
#using (Html.BeginForm("Create", "Question", FormMethod.Post)) {
<h2>Create</h2>
<table>
<tr>
<th>Question Name</th>
</tr>
<tr>
<td>#Html.EditorFor(model=>model.Question.Question_Text)</td>
</tr>
</table>
<table id="dataTable">
<tr>
<th>Correct?</th>
<th>Answer text</th>
<th>Open Answer</th>
</tr>
#foreach(var i in Model.Answers)
{
<tr>
<td>#Html.CheckBoxFor(model=>i.CorrectOrNot)</td>
<td>#Html.EditorFor(model=>i.AnswerText)</td>
<td>#Html.EditorFor(model=>i.OpenAnswerText)</td>
</tr>
}
</table>
<input type="button" id="addNew" value="Add Answer"/>
<input type="submit" value="Create" />
}
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
<script lang="javascript">
$(document).ready(function () {
//1. Add new row
$("#addNew").click(function (e) {
e.preventDefault();
var $tableBody = $("#dataTable");
var $trLast = $tableBody.find("tr:last");
var $trNew = $trLast.clone();
var suffix = $trNew.find(':input:first').attr('name').match(/\d+/);
$trNew.find("td:last").html('Remove');
$.each($trNew.find(':input'), function (i, val) {
// Replaced Name
var oldN = $(this).attr('name');
var newN = oldN.replace('[' + suffix + ']', '[' + (parseInt(suffix) + 1) + ']');
$(this).attr('name', newN);
//Replaced value
var type = $(this).attr('type');
if (type.toLowerCase() == "text") {
$(this).attr('value', '');
}
// If you have another Type then replace with default value
$(this).removeClass("input-validation-error");
});
$trLast.after($trNew);
// Re-assign Validation
var form = $("form")
.removeData("validator")
.removeData("unobtrusiveValidation");
$.validator.unobtrusive.parse(form);
});
// 2. Remove
$('a.remove').live("click", function (e) {
e.preventDefault();
$(this).parent().parent().remove();
});
});
</script>
}
For the ModelBinder to bind to a List the HTML form must be sequentially indexed.
Your
<td>#Html.CheckBoxFor(model=>a.CorrectOrNot)</td>
<td>#Html.EditorFor(model=>a.AnswerText)</td>
<td>#Html.EditorFor(model=>a.OpenAnswerText)</td>
is creating something that will be bound to an individual answer. You need to render HTML that will be bound to a List, something like
#for (int i = 0; i < ((List<Answer>)ViewData["Answers"]).Count; i++)
{
<tr>
<td>#Html.CheckBoxFor(model=>((List<Answer>)ViewData["Answers"])[i].CorrectOrNot)</td>
<td>#Html.EditorFor(model=>((List<Answer>)ViewData["Answers"])[i].AnswerText)</td>
<td>#Html.EditorFor(model=>((List<Answer>)ViewData["Answers"])[i].OpenAnswerText)</td>
</tr>
}
Also, this looks pretty awful casting ViewData all over the place. It would generally be better, if you plan to keep this approach creating a real view model. You could pass that model to the view and it could wrapper both question and answer collections.
EDIT:
You still need to have a sequential index against your list which your edited implementation is not supplying. Something like
#for (int i = 0; i < Model.Answers.Count; i++)
{
<tr>
<td>#Html.CheckBoxFor(model=> Model.Answers[i].CorrectOrNot)</td>
<td>#Html.EditorFor(model=> Model.Answers[i].AnswerText)</td>
<td>#Html.EditorFor(model=> Model.Answers[i].OpenAnswerText)</td>
</tr>
}
ViewData is relevant when going from the controller to the view. It won't post back.
You should relay on the (model / parameter) binding that will take care of passing List<Answer> answerList for you
ViewData is only to transfer the data between the view and controller. You can use session to transfer the data between the controller
Thanks for the comments. They really helped me out. It was all correct that you say but there was something that was missing. My IEnumerable in the ViewModel simply does not allow me to index my values, instead using IList helped me out to index everything as it is supposed to be and everything works.

View is returning FormCollection in Chrome but not IE

I have a View that has a DDL. Once a value is selected in the DDL the form can be submitted and the items for the vendor will be retrieved from a Web Api service and the page is reloaded. This form works as designed in Chrome but it fails to find the DDL value in the FormCollection object in IE. What did I miss?
View Code
#using AMCWeb.Models
#model AppointsViewModel
#{
ViewBag.Title = "Index";
}
<h2>Appraisal Appointment</h2>
#using (Html.BeginForm("GetAppointmentsByVendor", "AppraisalAppointment", FormMethod.Post, new {#id = "validationlist"}))
{
<br/>
<br/>
#Html.DropDownListFor(model => model.SelectedCompany, new SelectList(Model.Vendors.OrderBy(s => s.Company), "Id", "Company", Model.SelectedCompany), "- Select Vendor -")
<br/>
<input type="submit" value="Get Appointments" onclick="location.href='#Url.Action("GetAppointmentsByVendor", "AppraisalAppointment")'" />
<br/>
<table>
<tr>
<th>Id</th>
<th>Loan Number</th>
<th>State</th>
<th>Order Date</th>
<th>Apt Date</th>
<th>Est Due Date</th>
<th>Fees</th>
<th>MN Status</th>
</tr>
#foreach (AMCWeb.Models.AppraisalAppointment appraisalAppointment in Model.Appraisals)
{
//...load the table with data
}
</table>
}
Controller Code
public ActionResult GetAppointmentsByVendor(FormCollection formValues)
{
List<string> messages = new List<string>();
if (formValues["selectedCompany"].Trim() == string.Empty)
{
messages.Add("You must select a Vendor to begin.");
ViewBag.Messages = messages;
appointments.Vendors = _vendorRepository.Get();
return View("Index", appointments);
}
var vendorId = Convert.ToInt32(formValues["selectedCompany"]);
appointments.Appraisals = _appraisalAppointmentRepository.GetByVendor(vendorId);
appointments.Vendors = _vendorRepository.Get();
appointments.SelectedCompany = vendorId;
return View("Index", appointments);
}
Value from IE:
Value from Chrome:
UPDATE:
It appears that the vendorId was being passed. What I found was happening as I stepped through the code was it was indeed not in the FormCollection object, the code then broke on the first 'if statement' because it was not in the collection but if I continued to step through the code it jumped right back up the the first line var vendorId = Convert.ToInt32(formValues["selectedCompany"]); and the value was there. So I modified the code as follows
public ActionResult GetAppointmentsByVendor(FormCollection formValues)
{
// to set up the model for reload
IEnumerable<AppraisalAppointment> appointment = new[] { new AppraisalAppointment() };
appointments.Appraisals = appointment;
appointments.Vendors = _vendorRepository.Get();
// for use in the tryparse
var selectedCompany = formValues["selectedCompany"];
int vendorId;
// for message deliver to the user
List<string> messages = new List<string>();
// if the vendorId is not an int then they didn't select one return
if (!Int32.TryParse(selectedCompany, out vendorId))
{
appointments.SelectedCompany = 0;
messages.Add("You must select a Vendor to begin.");
ViewBag.Messages = messages;
return View("Index", appointments);
}
// get the data for the user
appointments.Appraisals = _appraisalAppointmentRepository.GetByVendor(vendorId);
appointments.SelectedCompany = vendorId;
return View("Index", appointments);
}

model is null in MVC

i want to select * data in a table rows and columns in MVC. when i do so, my model is always null. i`m using ADO.NET EDM for this.
my Action Controller code:
public ActionResult SellerView1(string Seller)
{
KeywinTest_Project1.tblSeller sellerName = new tblSeller();
SqlConnection cn = new SqlConnection(#"Data Source=.;Initial Catalog=KeywinDB;Integrated Security=True;Pooling=False");
string query = "Select * from tblSeller";
SqlCommand cmd = new SqlCommand(query, cn);
SqlDataAdapter da = new SqlDataAdapter(cmd);
DataTable dt = new DataTable();
da.Fill(dt);
if (dt.Rows.Count > 0)
{
ViewBag.Name = dt.Rows[0]["sellerName"].ToString();
}
return View();
}
my View code:
#model IEnumerable<KeywinTest_Project1.tblSeller>
#using System.Web.UI.WebControls;
#{
ViewBag.Title = "SellerView1";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<body>
<table>
#Model = #ViewBag.Name
#foreach (var item in Model)
{
<tr>
<td>
#item.sellerName
</td>
</tr>
}
</table>
</body>
my Model code:
using System;
using System.Collections.Generic;
public partial class tblSeller
{
public int id { get; set; }
public string sellerName { get; set; }
}
what ive done is, ive declared my view as the model class that was generated by my EDM.
What i am doing wrong here? plz guide.
Thanks
Because you didn't pass your model when you returning your View from Controller
return View();
Here you should pass your ViewModel which is IEnumerable<KeywinTest_Project1.tblSeller>,
Like this (fro example):
var sellerList = dt.AsEnumerable()
.Select(d => new tblSeller { sellerName = d["sellerName"].ToString() })
.ToList();
return View(sellerList);
You didn't pass the model to the View, like this :
return View(sellerName);
But the sellerName object is not an IEnumerable, so you should rewrite a part of your code in order to make it work.

Displaying results of a stored procedure via a dataset in an MVC 4 View

I have spent a lot of time on this, but i can't seem to get it to work. I have a legacy web forms app that i am rewriting in MVC. It has all bussiness logic coming from stored procs and in the iterest of time, I decided to reuse the BAL(bussiness layer) and DAL. Here is what I have:
Model
public class GridDisplay
{
public List<string> columnNames { get; set; }
public List<string> fieldValues { get; set; }
}
Controller
public ActionResult PatientInfo()
{
var model = new PatientInfoViewmodel();
ViewBag.countries = model.FillCountryDropDown();
ViewBag.studies = model.FillStudyDropDown();
SearchElements SE = new SearchElements { StudyID = "PRMDEMO" };
List<string> fields = new List<string>();
DataSet ds = new DataSet();
ds = pibl.PopulateData(SE);
var display = new List<GridDisplay>();
display = model.BuildGridData(SE);
ViewBag.columns = display[0].columnNames.ToList();
ViewBag.fields = display;
return View();
}
List of GridDisplays:
public List<GridDisplay> BuildGridData(SearchElements SE) //(string country, string studyID, string patientid, string lastname)
{
DataSet ds = new DataSet();`enter code here`
ds = pibl.PopulateData(SE);
GridDisplay display = new GridDisplay();
List<GridDisplay> displayList = new List<GridDisplay>();
string strtemp = string.Empty;
display.columnNames = new List<string>();
foreach (DataRow row in ds.Tables[0].Rows)
{
display.columnNames.Add(row["label"].ToString());
}
display.fieldValues = new List<string>();
foreach (DataRow row in ds.Tables[1].Rows)
{
display.fieldValues = new List<string>();
foreach (DataColumn column in ds.Tables[1].Columns)
{
if (column.Caption != "STUDY" & column.Caption != "PATIENT_ID")
{
display.fieldValues.Add(row[column].ToString());
}
if (column.Caption == "PATIENT_ID")
{
strtemp = row[column].ToString();
}
}
display.fieldValues.Add(strtemp);
displayList.Add(new GridDisplay() { columnNames = display.columnNames, fieldValues = display.fieldValues});
}
return displayList;
}
The displayList has everything I need and I am able to display the columns, but not the fields. Below is the view portion that pertains to this(one of my versions)
<div id="dvDynamicPatient" style="overflow: auto;">
<table id="gvDynamicPatient">
<tr>
#foreach (var col in #ViewBag.columns)
{
<th id="gridHeader" style=" white-space: nowrap;">
#col
</th>
}
</tr>
<tr>
#foreach (var item in #ViewBag.fields.fieldvalues)
{
<td id="gridCell" style="border-style: solid; color: #000000; white-space: nowrap">
#item
</td>
}
</tr>
</table>
</div
Understanding models is a beautiful thing. I did figure this out. To recap, I have a stored procedure that returns two result sets via a dataset. Dataset.Tables[0] has header strings and Dataset.Tables[1] contains the information to be displayed.My task was to create an MVC 4 application to view this and then some.
Model
public class GridDisplay
{
public List<string> columnNames { get; set; }
public List<List<string>> fieldValues { get; set; }
}
Controller
public ActionResult PatientInfo()
{
var model = new PatientInfoViewmodel();
// build dropdown lists
ViewBag.countries = model.FillCountryDropDown();
ViewBag.studies = model.FillStudyDropDown();
SearchElements SE = new SearchElements { StudyID = "PRMDEMO" };
DataSet ds = new DataSet();
ds = pibl.PopulateData(SE);
var gridDisplay = new GridDisplay();
gridDisplay = model.BuildGridData(SE);
return View(gridDisplay);
}
View
<div id="dvDynamicPatient" style="overflow: auto;">
<table id="gvDynamicPatient">
<tr>
#foreach (var col in Model.columnNames)
{
<th id="gridHeader" style="white-space: nowrap;">
#col
</th>
}
</tr>
#for (int i = 0; i < Model.fieldValues.Count; ++i)
{
<tr id="row" style="white-space: nowrap;">
#foreach (var item in Model.fieldValues[i])
{
<td id="col" style="white-space: nowrap;">
#item
</td>
}
</tr>
}
</table>
</div>

Resources