Passing Data from Controllers to Views - c#

I have spent the past day and a half trying to find this answer and sometimes it seems like I am so close but so far no luck.
I have a controller where I defined a LINQ query and am trying to figure out how to pass the results onto a listing view. the following is the Controller code:
namespace CMS.Controllers
{
public class SurveyController : Controller
{
private SupportEntities supdb = new SupportEntities();
private BuisnessEntities bsdb = new BuisnessEntities();
//
// GET: /Survey/BizSurveyC
public ViewResult BizSurveyC(string nipaKey, string bOrg)
{
// use the next two lines for testing and then either delete or comment them out.
nipaKey = "22";
bOrg = "MPC";
var Cquery = from Mstr in bsdb.BizOrgInsts
join Dat in bsdb.BizSurveyQ on Mstr.ID equals Dat.MASTERID
where Mstr.NIPAKEY == nipaKey & Mstr.FULCIRCORG == bOrg
orderby Mstr.STREETSUFX, Dat.ADDRESS, Mstr.NUMBER
select new { MasterId = Mstr.ID, Name = Mstr.OLDNAME, Mstr.ADDRESS, Mstr.NIPAKEY, Dat.SURVEYDATE, SurveyId = Dat.ID, Dat.RESURVEYOF, Dat.STAMP };
//ViewBag.BizQuery = Cquery;
ViewData["BizQuery"] = new SelectList(Cquery);
return View();
}
}
}
As you can tell by looking I have tried ViewData and Viewbag but so far with no luck
Here are the way things are now appearing:
ViewModel Class
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace CMS.Models
{
public class BizSurveyCVM
{
public long? MasterId { get; set; }
public string Name { get; set; }
public string Address { get; set; }
public string NipaKey { get; set; }
public string Date { get; set; }
public long? SurveyId { get; set; }
public long? Resurvey { get; set; }
public string DateStamp { get; set; }
}
}
Modified Action
var Cquery = (from Mstr in bsdb.BizOrgInsts
join Dat in bsdb.BizSurveyQ on Mstr.ID equals Dat.MASTERID
where Mstr.NIPAKEY == nipaKey & Mstr.FULCIRCORG == bOrg
orderby Mstr.STREETSUFX, Dat.ADDRESS, Mstr.NUMBER
select new BizSurveyCVM
{
MasterId = Mstr.ID,
Name = Mstr.OLDNAME,
Address = Mstr.ADDRESS,
NipaKey = Mstr.NIPAKEY,
Date = Dat.SURVEYDATE,
SurveyId = Dat.ID,
Resurvey = Dat.RESURVEYOF,
DateStamp = Dat.STAMP
}).ToList();
return View(Cquery);
}
BizSurveyC View
#model List<CMS.Models.BizSurveyCVM>
<table>
<tr>
<th>
MasterId
</th>
<th>
Name
</th>
<th>
Address
</th>
<th>
NipaKey
</th>
<th>
Date
</th>
<th>
SurveyId
</th>
<th>
Resurvey
</th>
<th>
DateStamp
</th>
<th></th>
</tr>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.MasterId)
</td>
<td>
#Html.DisplayFor(modelItem => item.Name)
</td>
<td>
#Html.DisplayFor(modelItem => item.Address)
</td>
<td>
#Html.DisplayFor(modelItem => item.NipaKey)
</td>
<td>
#Html.DisplayFor(modelItem => item.Date)
</td>
<td>
#Html.DisplayFor(modelItem => item.SurveyId)
</td>
<td>
#Html.DisplayFor(modelItem => item.Resurvey)
</td>
<td>
#Html.DisplayFor(modelItem => item.DateStamp)
</td>
</table>
here is the resulting view:
SORRY NOT ALLOWED TO SAVE IMAGE YET BUT IN THE VIEW I HAVE HEADERS BUT NO DATA.
I obviously have some work to do in the view or maybe the query but things are looking much better thanks so everyone's help. Thank you very much

You could create a ViewModel class to hold whatever your query results are and then strongly type your view for the new ViewModel class:
ViewModel Class:
public class BizSurveyCVM
{
public long MasterId { get; set; }
public string Name { get; set; }
...
}
Modified Action:
var Cquery = (from Mstr in bsdb.BizOrgInsts
join Dat in bsdb.BizSurveyQ on Mstr.ID equals Dat.MASTERID
where Mstr.NIPAKEY == nipaKey & Mstr.FULCIRCORG == bOrg
orderby Mstr.STREETSUFX, Dat.ADDRESS, Mstr.NUMBER
select new BizSurveyCVM { MasterId = Mstr.ID, Name = Mstr.OLDNAME, ...}
).ToList();
return View(Cquery);
BizSurveyC View
#model List<{namespace}.BizSurveyCVM>
#foreach(var item in Model) {
{HTML Mark-up}
}
EDIT: Here's an updated example of the view based on the updated question:
#model List<{namespace}.BizSurveyCVM>
#foreach(var item in Model) {
<tr>
<td>
#item.MasterId
</td>
<td>
#item.Name
</td>
<td>
#item.Address
</td>
...
</tr>
}

return View(Cquery);
or
return View("ViewName",Cquery);
and then in your view, the model type should match the type of Cquery. But I find that its easier (assuming you're using VS) to just right click in the method body somewhere and click "add view", and select the model from the list of model types.

Are you sure that you want do?
it's more useful the collection in the view, so you can build anything you need, like a select list.
don't forget define who is your datavalue and displayvalue in the select list, like..
ViewData["BizQuery"] = new SelectList(Cquery, null, "MasterId", "Name");
Ok, your code works for me, so check your view, you must do the correct cast of your object in the viewdata
you need have something like this
#foreach (SelectListItem item in (SelectList)ViewData["BizQuery"])
{
<p>#item.Text</p>
}

Related

How do I display a list from SQL database when my table is not enumerable? I can use details view fine, but not an index view

I am having difficulty getting my Index view to display the values I have from the SQL database.
The main issue is that I cannot use #foreach (var item in Model) {... because my table is not created as Enumerable (I think). I run into the error message System.NullReferenceException: 'Object reference not set to an instance of an object.' pointing at Model in the foreach statement expression.
I am wondering if I need to set my table up as an Enumerable list, then display each item. Or if I am missing something with regards to my Index view. Or maybe I need to pass something through the Index return View()?
Here is the Image Model:
namespace Uploadimage.Models
{
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Web;
public partial class Image
{
public int Id { get; set; }
[Required]
public string FirstName { get; set; }
[Required]
public string LastName { get; set; }
public string City { get; set; }
public string Province { get; set; }
public string Phone { get; set; }
[Required]
[RegularExpression(#"[A-Za-z0-9._%+-]+#[A-Za-z0-9.-]+\.[A-Za-z]{2,4}",
ErrorMessage = "Email doesn't look like a valid email address.")]
public string Email { get; set; }
[Compare("Email", ErrorMessage = "Emails do not match.")]
public string ConfirmEmail { get; set; }
[DisplayName("Upload File")]
public string ImagePath { get; set; }
public HttpPostedFileBase ImageFile { get; set; }
}
}
Here is the controller:
public class ImageController : Controller
{
[HttpGet]
public ActionResult Add()
{
return View();
}
[HttpPost]
public ActionResult Add(Image imageModel)
{
string fileName = Path.GetFileNameWithoutExtension(imageModel.ImageFile.FileName);
string extension = Path.GetExtension(imageModel.ImageFile.FileName);
fileName = fileName + DateTime.Now.ToString("yymmssfff") + extension;
imageModel.ImagePath = "~/Image/" + fileName;
fileName = Path.Combine(Server.MapPath("~/Image/"), fileName);
imageModel.ImageFile.SaveAs(fileName);
using(LoginDBEntities db = new LoginDBEntities())
{
db.Images.Add(imageModel);
db.SaveChanges();
}
ModelState.Clear();
return View();
}
[HttpGet]
public ActionResult View(int id)
{
Image imageModel = new Image();
using (LoginDBEntities db = new LoginDBEntities())
{
imageModel = db.Images.Where(x => x.Id == id).FirstOrDefault();
}
return View(imageModel);
}
public ActionResult Index()
{
return View();
}
}
And lastly, my Index View:
#model IEnumerable<Uploadimage.Models.Image>
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
<p>
#Html.ActionLink("Create New", "Create")
</p>
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.FirstName)
</th>
<th>
#Html.DisplayNameFor(model => model.LastName)
</th>
<th>
#Html.DisplayNameFor(model => model.City)
</th>
<th>
#Html.DisplayNameFor(model => model.Province)
</th>
<th>
#Html.DisplayNameFor(model => model.Phone)
</th>
<th>
#Html.DisplayNameFor(model => model.Email)
</th>
<th>
#Html.DisplayNameFor(model => model.ImagePath)
</th>
<th></th>
</tr>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.FirstName)
</td>
<td>
#Html.DisplayFor(modelItem => item.LastName)
</td>
<td>
#Html.DisplayFor(modelItem => item.City)
</td>
<td>
#Html.DisplayFor(modelItem => item.Province)
</td>
<td>
#Html.DisplayFor(modelItem => item.Phone)
</td>
<td>
#Html.DisplayFor(modelItem => item.Email)
</td>
<td>
#Html.DisplayFor(modelItem => item.ImagePath)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id=item.Id }) |
#Html.ActionLink("Details", "Details", new { id=item.Id }) |
#Html.ActionLink("Delete", "Delete", new { id=item.Id })
</td>
</tr>
}
</table>
I have tried thoroughly looking this up but have found things that don't specifically apply to me.
Or I don't know what to look up.
Two things:
Your Index controller action will need to load the images to pass to the view. At a minimum to serve a collection of images as the view's "model":
public ActionResult Index()
{
using (LoginDBEntities db = new LoginDBEntities())
{
var images = db.Images.ToList();
return View(images);
}
}
Then in the view you will need to check whether you actually get any results, then extract your labels from the first result if there are any, or display a suitable message if there are no images:
<!-- ... -->
#if (Model.Any())
{
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.ElementAt(0).FirstName)
</th>
<th>
#Html.DisplayNameFor(model => model.ElementAt(0).LastName)
</th>
...
</tr>
#for (int count = 0; count < Model.Count; count++) {
<tr>
<td>
#Html.DisplayFor(model => model.ElementAt(count).FirstName)
</td>
<td>
#Html.DisplayFor(model => model.ElementAt(count).LastName)
</td>
...
<td>
#Html.ActionLink("Edit", "Edit", new { id=#Model.ElementAt(count).Id }) |
#Html.ActionLink("Details", "Details", new { id=#Model.ElementAt(count).Id }) |
#Html.ActionLink("Delete", "Delete", new { id=#Model.ElementAt(count).Id })
</td>
</tr>
}
</table>
}
else
{
<p> No Images. </p>
}
The above is written from memory so it will likely have some syntactic issues but it should point you in the right direction.
Typically when working with collections like search results I define a view model class for the results page that itself contains the collection of results. The page's model becomes that wrapper view model which can contain details about the page (such as things like current search criteria, lookup values for searches, etc.) along with the collection of results. (typically a PagedList to support pagination)
I.e.
[Serializable]
public class ImageIndexViewModel
{
public string NameSearchString { get; set; }
public ICollection<ImageViewModel> Results { get; set; } = new List<ImageViewModel>();
}
Where ImageViewModel is a serializable POCO view model to represent just the details about images that the view will display. Often the views don't need everything about a data row, and in cases where the data row has navigation properties and extra fields we don't need to display, serializing entities results in lazy load calls or simply sending a lot of extra data that isn't needed.
You don't have anything in your Index method which gets a list of entities.
Your Index method in your controller should look something like:
public ActionResult Index()
{
var model = db.Images.ToList();
return View(model);
}

ICollection<> inside ICollection<> in Razor - can't retrieve the model names

I'm wondering how to resolve the problem. I'm developing an ASP.NET MVC app
I have a models
public class SearcherString
{
public int ID { get; set; }
public string Description { get; set; }
public virtual ICollection<Path> Path { get; set;
public SearcherString()
{
Path = new List<Path>();
}
}
public class Path
{
public int ID { get; set; }
public int CategoryID { get; set; }
public string CategoryName { get; set; }
}
I'm passing it in my Controller (I'm writing my model into my database and then I'm retrieving it)
public ActionResult Index()
{
return View(db.SearchersString.ToList()
}
And I have a View with:
#model IEnumerable<App.Models.SearcherString>
The problem is in my View, I can't display the names from Path model
(CategoryID, CategoryName)
I tried to do:
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.Description)
</th>
#foreach (var item in Model)
{
foreach (var path in item.Path)
{
<th>
#Html.DisplayName(path.CategoryID.ToString())
</th>
<th>
#Html.DisplayName(path.CategoryName)
</th>
}
}
<th></th>
</tr>
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.Description)
</td>
#foreach (var path in item.Path)
{
<td>
#Html.DisplayFor(modelItem => path.CategoryID)
</td>
<td>
#Html.DisplayFor(modelItem => path.CategoryName)
</td>
}
<td>
#Html.ActionLink("Edit", "Edit", new { id = item.ID }) |
#Html.ActionLink("Details", "Details", new { id = item.ID }) |
#Html.ActionLink("Delete", "Delete", new { id = item.ID })
</td>
</tr>
}
</table>
But I have only:
Could someone help me with this problem?
Edit:
Is there a way to change the Html.DisplayName to Html.DisplayNameFor here?
#Html.DisplayName(path.CategoryID.ToString())
You should include navigation property to your query:
public ActionResult Index()
{
return View(db.SearchersString.Include(c=>c.Path).ToList()
}
Your Model is incorrect.
public SearcherString()
{
Path = new List<Path>();
}
This is not a property. This is treated as a Method, a special one. And it is a constructor. That Creates a Path object That is EMPTY.
If you want to have a one to many relationship. You would have to use the property for this and add a property of ID.
public int PathID {get; set;}
public virtual Path Path{get; set;}
Now you have a lazy loading property that will automatically save the ID property of the Path class into your SearchString model property PathID.
to get the PathID, you will specifically need to call the include function if you're using EntityFramework, which most likely the case.
public ActionResult Index(){
return View(db.SearchersString.Include(c=>c.Path).ToList();
}

View not working, Passing object to view error

I am working on a project utilizing MVC4 and EF Data First in VS2013. I am getting this error when trying to pass my object to the view.
The model item passed into the dictionary is of type 'System.Data.Entity.Infrastructure.DbQuery1[<>f__AnonymousType34[System.String,System.Nullable1[System.Int32],System.Nullable1[System.Int32],System.Nullable1[System.Int32]]]', but this dictionary requires a model item of type 'System.Collections.Generic.IEnumerable1[TitansMVCWithBootStrap.Models.Player_Game_Stats]'
I am not sure how to fix this, any suggest would help. Thanks
Controller
public class PlayerGameStatsController : Controller
{
private Context db = new Context();
// GET: PlayerGameStats/Details/5
public ActionResult Details(int gid=19, int sid = 11)
{
var playerGameStats = from pgs in db.PlayerGameStats
join p in db.Players on pgs.Player_id equals p.id_player
where pgs.SeasonId == 11 && pgs.GameNumber == 19
select new
{
p.PlayerName,
pgs.PlateAppearance,
pgs.Runs,
pgs.Hits
};
return View(playerGameStats);
}
Model
public class Player_Game_Stats
{
[Key]
[Column(Order = 0)]
public int Player_id { get; set; }
[Key]
[Column(Order = 1)]
public int GameNumber { get; set; }
public Nullable<int> PlateAppearance { get; set; }
public Nullable<int> Runs { get; set; }
public Nullable<int> HR { get; set; }
public int Hits{ get; set; }
//[ForeignKey("id_player")]
//public virtual Players Player { get; set; }
}
}
View
#model IEnumerable<TitansMVCWithBootStrap.Models.Player_Game_Stats>
#{
ViewBag.Title = "Details";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Details</h2>
<table class="table table-striped">
<tr>
<th>
#Html.DisplayNameFor(model => model.PlayerName)
</th>
<th>
#Html.DisplayNameFor(model => model.PlateAppearance)
</th>
<th>
#Html.DisplayNameFor(model => model.Runs)
</th>
<th>
#Html.DisplayNameFor(model => model.Hits)
</th>
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.PlayerName)
</td>
<td>
#Html.DisplayFor(modelItem => item.PlateAppearance)
</td>
<td>
#Html.DisplayFor(modelItem => item.Runs)
</td>
<td>
#Html.DisplayFor(modelItem => item.Hits)
</td>
</tr>
}
</table>
Also tried
var playerGameStats = (from pgs in db.PlayerGameStats
join p in db.Players on pgs.Player_id equals p.id_player
where pgs.SeasonId == 11 && pgs.GameNumber == 19
select new Player_Game_Stats
{
p.PlayerName,
pgs.PlateAppearance,
pgs.Runs,
pgs.Hits
}).ToList();
return View(playerGameStats);
Try this:
public ActionResult Details(int gid=19, int sid = 11)
{
var playerGameStats = from pgs in db.PlayerGameStats
join p in db.Players on pgs.Player_id equals p.id_player
where pgs.SeasonId == 11 && pgs.GameNumber == 19
select new TitansMVCWithBootStrap.Models.Player_Game_Stats
{
PlayerName = p.PlayerName,
PlateAppearance = pgs.PlateAppearance,
Runs = pgs.Runs,
Hits = pgs.Hits
};
return View(playerGameStats);
}

Count how many elements of a type and return for a view MVC C#

here is my problem:
I have a Database where I have a table device that has some devices, and each devices has a type.
I want to make a view to see how many devices of each type I have in the table.
What I have till now is, in controller:
public ActionResult Stock()
{
var device = db.Device.Where(s => s.Status.ToUpper().Contains("Stock")).GroupBy(d => d.DeviceTypeName);
return View(device.ToList());
}
In the view I have:
#model IEnumerable<System.Linq.IGrouping<System.String, Mobile_Inventory.Models.Device>>
<table>
<tr>
<th>
DeviceType
</th>
<th>
Qt
</th>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.Key)
</td>
<td>
</td>
</tr>
}
</table>
But this way I can only see the Type of device and not the quantity.
I tried to change the var device to have the quantity:
var device = db.Device.Where(s => s.Status.ToUpper().Contains("Stock")).GroupBy(d => d.DeviceTypeName).Select( d => new
{
Type = d.Key,
Count = d.Count()
} );
But this way I return an anonymous type to the view and get the error:
The model item passed into the dictionary is of type 'System.Collections.Generic.List1[<>f__AnonymousType22[System.String,System.Int32]]', but this dictionary requires a model item of type 'System.Collections.Generic.IEnumerable1[System.Linq.IGrouping2[System.String,Mobile_Inventory.Models.Device]]'.
Don't know how I can change the Model type for the view accept the Anonymous type, and don't know if it is even possible. Anyone can help, with a solution?
Create a ViewModel like this:
public class DeviceGroupViewModel
{
public string Type { get; set; }
public int Count { get; set; }
}
Then, change your Action to this:
public ActionResult Stock()
{
var devices = db.Device.Where(s => s.Status.ToUpper().Contains("Stock"))
.GroupBy(d => d.DeviceTypeName)
.Select(d => new DeviceGroupViewModel
{
Type = d.Key,
Count = d.Count()
}).ToList();
return View(devices);
}
And, in your View, you'll have:
#model IEnumerable<Mobile_Inventory.ViewModels.DeviceGroupViewModel>
<table>
<tr>
<th>
DeviceType
</th>
<th>
Qt
</th>
</tr>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(item => item.Type)
</td>
<td>
#Html.DisplayFor(item => item.Count)
</td>
</tr>
}

list depends on the change in the index of dropdownlist in mvc

I have two things in my view 'ListUrl.cshtml'
a dropdown list filled with the names of URLs.
a list where I 'URLs' as the first column capitals.
What I want is that when I select an item from the dropdown list, the list is automatically adjusted according to the selection. when I select a URL then I only see that url in the list.
In Model:
public class UrlItem
{
public int Id { get; set; }
public string urlll { get; set; }
public int toontijd { get; set; }
public int positie { get; set; }
}
In HomeController:
public ViewResult ListUrl()
{
var ws = new Service1();
localhost.Service1 s1 = new Service1();
localhost.UrlInfo[] ui = s1.GetOrders();
Models.ListUrl.UrlList = new List<OrderItem>();
for (int i = 0; i < ui.Length; i++)
{
var OrderItem = new OrderItem();
OrderItem.Id = Convert.ToInt32(ui[i].Id);
OrderItem.urlll = ui[i].url;
OrderItem.toontijd = ui[i].ToonTijd;
OrderItem.positie = Convert.ToInt32(ui[i].positie);
Models.ListUrl.UrlList.Add(OrderItem);
}
//return result;
var urlname = from url in s1.GetUrlInfo() select url ;
ViewData["url"] = new SelectList(urlname, "Id", "url");
return View();
}
And in the View:
<table>
<tr>
<th>
#Html.DropDownList("url", (SelectList)ViewData["url"], "---All")
</th>
<th></th>
<th></th>
<th></th>
</tr>
<tr>
<th>
Url
</th>
<th>
...
</th>
<th>
</th>
<th>
...
</th>
</tr>
#foreach (var item in ListUrl.UrlList)
{
<tr>
<td>
#item.urlll.ToString()
</td>
<td>
#item.toontijd.ToString()
</td>
<td>
</td>
<td>
#item.positie.ToString()
</td>
</tr>
}
I hope someone can help this mvc beginner :)
You need to either pull down the entire list (in your secondary drop down) first, then use Javascript to remove any unwanted entries after the first drop down selection. Or you can make an Ajax call to retrieve the list of secondary values after the first is selected. Either way, some client side stuff has to happen. See my post below, the example code does exactly what you are trying to do using ajax calls that return partial views.
MVC3 Unobtrusive Validation Not Working after Ajax Call

Categories

Resources