Two models in a view (with foreach) - asp.net-mvc-4

I have two classes - Student.cs and Lecturer.cs placed under Models. Now that in my razor view I have to place two classes together.
I know there's a method Tuple to solve the problem. But I do not know what to do next. What should I do with my #foreach?
Here's my code in cshtml.
#model Tuple<MVCApp1.Models.Student, MVCApp1.Models.Lecturer>
#{
ViewBag.Title = "MainPage";
Layout = "~/Views/Shared/_Layout.cshtml";
}
I'm using a table, below is my #foreach code section.
#foreach (var j in Model)
{
<tr>
<td">#j.FirstName
</td>
<td>#j.MiddleName
</td>
<td>#j.LastName
</td>
I need to have 2 tables each with different attributes. First table from Student.cs and second table will be Lecturer.cs.
I know there is something wrong with the #foreach but I just can't find any solution online. Please help.

A tuple does not expose an iterator.
public class Tuple<T1> : IStructuralEquatable, IStructuralComparable, IComparable, ITuple
What you are after is a ViewModel.
public class ViewModel
{
public List<Student> Students { get; set; }
public List<Teacher> Teachers { get; set; }
}
public ActionResult Index()
{
ViewModel model = new ViewModel();
// retreive from database
model.Students = new List<Student>() { new Student()};
model.Teachers = new List<Teacher>() { new Teacher()};
return View(model);
}
Then you can structure your table
<table>
<tr>
<th>First</th>
<th>Middle</th>
<th>Last</th>
</tr>
#foreach (var student in Model.Students)
{
<tr>
<td>#student.First</td>
<td>#student.Middle</td>
<td>#student.Last</td>
</tr>
}
#foreach (var teacher in Model.Teachers)
{
<tr>
<td>#teacher.First</td>
<td>#teacher.Middle</td>
<td>#teacher.Last</td>
</tr>
}
</table>
Once you are comfortable with this, you can explore inheritance and Entity Framework TPH Table per hierarchy.
You could end up with something like this:
public abstract class Person
{
public int Id { get; set; }
public string First { get; set; }
public string Middle { get; set; }
public string Last { get; set; }
}
public class Teacher : Person
{
public string Class { get; set; }
public DateTime HireDate { get; set; }
}
public class Student : Person
{
public int Grade { get; set; }
public DateTime EnrolledDate { get; set; }
}
public class ViewModel
{
public List<Student> StudentsOnly { get; set; }
public List<Person> StudentsAndTeachers { get; set; }
}
public ActionResult Index()
{
Context db = new Context();
ViewModel model = new ViewModel();
// You could collect just the students
model.StudentsOnly = db.People.OfType<Student>().ToList();
// Or all of them
model.StudentsAndTeachers = db.People.ToList();
return View(model);
}
Then you would only have to iterate through the single list of people, if you only needed to display their names.
<table>
...
#foreach (var person in Model.StudentsAndTeachers)
{
<tr>
<td>#person.First</td>
<td>#person.Middle</td>
<td>#person.Last</td>
</tr>
}
</table>

Related

How to deal with this decimal error for a price?

I'm working on this app that should show on "localhost/catalog" some data. I have a library for the models and for the services that the application might use. I am getting this error:
InvalidOperationException: The property 'Price' is not a navigation
property of entity type 'StoreAsset'. The 'Include(string)' method can
only be used with a '.' separated list of navigation property names.Microsoft.EntityFrameworkCore.Query.Internal.IncludeCompiler.WalkNavigations(IEntityType entityType, IReadOnlyList<string> navigationPropertyPaths, IncludeLoadTree includeLoadTree, bool shouldThrow)
Here is the code that I'm using (controller, models and view) and the service methods on bottom:
public class CatalogController : Controller
{
private IStoreAsset _assets;
public CatalogController(IStoreAsset assets)
{
_assets = assets;
}
public ActionResult Index()
{
var assetModels = _assets.GetAll();
var listingResult = assetModels
.Select(result => new AssetIndexListingModel
{
Id = result.Id,
Tipology = _assets.GetTipology(result.Id),
Size = _assets.GetSize(result.Id),
Price = decimal.Parse(_assets.GetPrice(result.Id))
});
var model = new AssetIndexModel()
{
Assets = listingResult
};
return View(model);
}
public class AssetIndexListingModel
{
public int Id { get; set; }
public string Size { get; set; }
public decimal Price { get; set; }
public string Tipology { get; set; }
public string ImageUrl { get; set; }
}
public abstract class StoreAsset
{
public int Id { get; set; }
[Required]
public Status Status { get; set; }
[Required]
public decimal Price { get; set; }
public string ImageUrl { get; set; }
}
public class Dress : StoreAsset
{
[Required]
public string Color { get; set; }
[Required]
public string Tipology { get; set; }
[Required]
public string Size { get; set; }
}
#model Models.Catalog.AssetIndexModel
<div id="assets">
<h3></h3>
<div id="assetsTable">
<table class="table table-condensed" id="catalogIndexTable">
<thead>
<tr>
<th>Size</th>
<th>Price</th>
<th>Tipology</th>
</tr>
</thead>
<tbody>
#foreach (var asset in Model.Assets)
{
<tr class="assetRow">
<td class="">
<a asp-controller="Catalog" asp-action="Detail" asp-route-id="#asset.Id">
<img src="#asset.ImageUrl" class="imageCell" />
</a>
</td>
<td class="">#asset.Price</td>
<td class="">#asset.Size</td>
<td class="">#asset.Tipology</td>
</tr>
}
</tbody>
</table>
</div>
public class StoreAssetService : IStoreAsset
{
private Context _context;
public StoreAssetService(Context context)
{
_context = context;
}
public void Add(StoreAsset newAsset)
{
_context.Add(newAsset);
_context.SaveChanges();
}
public IEnumerable<StoreAsset> GetAll()
{
return _context.StoreAssets
.Include(asset => asset.Status)
.Include(asset => asset.Price);
}
public StoreAsset GetById(int id)
{
// Return a query (same as returning GetAll().FirstOrDefault(...))
return _context.StoreAssets
.Include(assets => assets.Status)
.Include(assets => assets.Price)
// So it can return null with no problem
.FirstOrDefault(asset => asset.Id == id);
}
public StoreBranch GetCurrentLocation(int id)
{
throw new NotImplementedException();
}
// To implement and test
public string GetPrice(int id)
{
return _context.Dresses.FirstOrDefault(p => p.Id == id).Price.ToString();
}
public string GetSize(int id)
{
return _context.Dresses.FirstOrDefault(s => s.Id == id).Size;
}
public string GetStatus(int id)
{
throw new NotImplementedException();
}
public string GetTipology(int id)
{
var dress = _context.StoreAssets.OfType<Dress>()
.Where(b => b.Id == id);
// For now return other if it's not a party dress
return dress.Any() ? "Party" : "Other";
}
}
Should I use some ForeignKey attribute or change Price to a string?
Any help would be great thanks
As pointed out in the error message, the Include is for the Navigation property only.
You need to change below:
return _context.StoreAssets
.Include(asset => asset.Status)
.Include(asset => asset.Price);
To:
return _context.StoreAssets
.Include(asset => asset.Status).ToList();
Reference: https://learn.microsoft.com/en-us/ef/core/modeling/relationships#definition-of-terms
https://learn.microsoft.com/en-us/ef/core/querying/related-data
I am having yet another problem. When I go to "localhost/catalog" the page should display all columns/entries that I have in the database but it only displays one column. Is there something wrong in the foreach cicle?

Reference model class in controller, so muliple data can be in one view

I am trying to get data from multiple SQL Server stored procedure to be accessible in 1 view so I can then run comparisons and create graphs/grids etc.
I got each section to work on their own, and I am now trying to get them to work together.
I have change my controller and put the field types into their own classes, and then created a "Ring of Rings" class, and put them all in.
namespace APP.Models
{
public class SP_RESULTS
{
public type Type { get; set; }
public status Status { get; set; }
public condition Condition { get; set; }
public rooms Rooms { get; set; }
}
public class type
{
[Key]
public decimal type_id { get; set; }
public string type_code { get; set; }
public string type_name { get; set; }
}
public class status
{
//status
public decimal status_id { get; set; }
public string status_code { get; set; }
public string status_name { get; set; }
public string rentable { get; set; }
}
public class condition
{
//condition
public decimal condition_id { get; set; }
public string condition_code { get; set; }
public string condition_name { get; set; }
public string rentable { get; set; }
public int colour_code { get; set; }
public int service_order { get; set; }
}
public class rooms
{
//rooms
public decimal room_id { get; set; }
public string room_no { get; set; }
public decimal type_id { get; set; }
public int floor { get; set; }
public decimal status_id { get; set; }
public decimal condition_id { get; set; }
}
}
I then amended each section of my controller that was running SQL Server stored procedures, to use the correct class name instead of the "ring of rings" name, IE:
var outputmodel4 = new List<rooms>();
var command4 = db.Database.Connection.CreateCommand();
command4.CommandText = "SELECT rooms.room_id , rooms.room_no , rooms.type_id , rooms.floor_no , rooms.status_id , rooms.condition_id FROM rooms";
using (var SPOutput4 = command4.ExecuteReader())
{
foreach (var row in SPOutput4)
{
outputmodel4.Add(new rooms()
{
room_id = (decimal)SPOutput4["room_id"],
room_no = (string)SPOutput4["room_no"],
type_id = (decimal)SPOutput4["type_id"],
floor_no = (int)SPOutput4["floor_no"],
status_id = (decimal)SPOutput4["status_id"],
condition_id = (decimal)SPOutput4["condition_id"],
});
}
db.Database.Connection.Close();
return View(outputmodel4);
}
...etc for other SQL stored procedures
..and the same with my view
#model IEnumerable<app.Models.SP_RESULTS >
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.Rooms.room_id)
</th>
<th>
#Html.DisplayNameFor(model => model.Rooms.room_no)
</th>
<th></th>
</tr>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.Rooms.room_id)
</td>
<td>
#Html.DisplayFor(modelItem => item.Rooms.room_no)
</td>
</tr>
}
</table>
…etc for the other models
Everything looks fine in VBS (no red swiggles), but when I access the view in the browser, I get an error:
Server Error in '/' Application.
The model item passed into the dictionary is of type 'System.Collections.Generic.List1[app.Models.rooms]', but this dictionary requires a model item of type 'System.Collections.Generic.IEnumerable1[app.Models.SP_RESULTS]'.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.InvalidOperationException: The model item passed into the dictionary is of type 'System.Collections.Generic.List1[app.Models.rooms]', but this dictionary requires a model item of type 'System.Collections.Generic.IEnumerable1[app.Models.SP_RESULTS]'.
If I go back to my "rings of rings" model, and change it to this :
public class SP_RESULTS
{
public IEnumerable<type> Type { get; set; }
public IEnumerable<status> Status { get; set; }
public IEnumerable<condition> Condition { get; set; }
public IEnumerable<rooms> Rooms { get; set; }
}
and change the following lines in in my controller:
var outputmodel4 = new List<rooms>();
outputmodel4.Add(new rooms()
to
var outputmodel4 = new List<SP_RESULTS>();
outputmodel4.Add(new SP_RESULTS()
VBS tells me that that
SP_RESULTS does not contain a definition for room_id
I've tried prefixing the definition with the class name, with no luck.
I've looked on SO, fourms.asp.net and google.. can cannot see a solution (there probably is, but I am unsure what solution I an looking for.... if that makes sense).
Would someone be able to tell me what I need to do to get all my model classes working in the same view ?
I apologise now, as I realize that this question has probably been asked several (million) times, but I cannot seem to fine out that jumps out says THIS is that way to do it.
For anyone else looking for an answer (and for future me), I got this work by putting the models like this :
namespace APP.Models
{
public class SP_RESULTS
{
public type Type { get; set; }
public status Status { get; set; }
public condition Condition { get; set; }
public rooms Rooms { get; set; }
public class type
{
[Key]
public decimal type_id { get; set; }
public string type_code { get; set; }
public string type_name { get; set; }
}
public class status
{
//status
public decimal status_id { get; set; }
public string status_code { get; set; }
public string status_name { get; set; }
public string rentable { get; set; }
}
public class condition
{
//condition
public decimal condition_id { get; set; }
public string condition_code { get; set; }
public string condition_name { get; set; }
public string rentable { get; set; }
public int colour_code { get; set; }
public int service_order { get; set; }
}
public class rooms
{
//rooms
public decimal room_id { get; set; }
public string room_no { get; set; }
public decimal type_id { get; set; }
public int floor { get; set; }
public decimal status_id { get; set; }
public decimal condition_id { get; set; }
}
}
}
Using VIEWDATA in the controller for each Store procedure:
var outputmodel3 = new List<SP_RESULTS.conditions>();
var command3 = db.Database.Connection.CreateCommand();
command3.CommandText = "dbo.pr_PROCEDUREONE";
command3.CommandType = System.Data.CommandType.StoredProcedure;
using (var SPOutput3 = command3.ExecuteReader())
{
foreach (var row in SPOutput3)
{
outputmodel3.Add(new SP_RESULTS.conditions()
{
condition_id = (decimal)SPOutput3["condition_id"],
condition_code = (string)SPOutput3["condition_code"],
condition_name = (string)SPOutput3["condition_name"],
});
}
ViewData["CONDITIONSOutput"] = outputmodel3;
}
var outputmodel4 = new List<SP_RESULTS.rooms>();
var command4 = db.Database.Connection.CreateCommand();
command4.CommandText = "SELECT rooms.room_id , etc FROM rooms";
using (var SPOutput4 = command4.ExecuteReader())
{
foreach (var row in SPOutput4)
{
outputmodel4.Add(new SP_RESULTS.rooms()
{
room_id = (decimal)SPOutput4["room_id"],
room_no = (string)SPOutput4["room_no"],
});
}
ViewData["ROOMSOutput"] = outputmodel4;
db.Database.Connection.Close();
return View();
}
..and then changing my view to read :-
table class="table">
<tr>
<th>
Condition ID
</th>
<th>
Condition code
</th>
<th>
Condition name
</th>
<th>
is it rentable?
</th>
<th>
Condition color code
</th>
<th>
Condition service order
</th>
<th></th>
</tr>
#foreach (var item in ViewData["CONDITIONSOutput"] as IEnumerable<Happ.Models.SP_RESULTS.conditions>)
{
<tr>
<td>
#item.condition_id
</td>
<td>
#item.condition_code
</td>
<td>
#item.condition_name
</td>
<td>
#item.rentable
</td>
<td>
#item.colour_code
</td>
<td>
#item.service_order
</td>
</tr>
}
</table>
<table class="table">
<tr>
<th>
Room ID
</th>
<th>
Room No
</th>
<th>
Rooms type_id
</th>
<th></th>
</tr>
#foreach (var item in ViewData["ROOMSOutput"] as IEnumerable<APP.Models.SP_RESULTS.rooms>)
{
<tr>
<td>
#item.room_id
</td>
<td>
#item.room_no
</td>
</tr>
}
</table>

get values from two tables in one single view by stored procedure in mvc

My model is
public partial class Device
{
[DisplayFormat(ConvertEmptyStringToNull = false)]
public string Device_Id { get; set; }
[DisplayFormat(ConvertEmptyStringToNull = false)]
public string Device_Name { get; set; }
}
public partial class Customer
{
[DisplayFormat(ConvertEmptyStringToNull = false)]
public string Customer_id { get; set; }
[DisplayFormat(ConvertEmptyStringToNull = false)]
public string Customer_Name { get; set; }
}
I want to display the values in both tables in one view i am using stored procedure to get the values..
I have written procedure to get the details from the 2 tables..I need the view to be like this.
<table >
<tr>
<th>Device </th>
<th>Customer Name</th>
</tr>
#foreach (var item in Model)
{
<tr>
<td>#Html.LabelForModel(item.Device_ModelNo)</td>
<td>#Html.LabelForModel(item.Customer_Name)</td>
</tr>
}
</table>
Create a Master class and include this two classes like this
public class Master
{
public Device device { get; set; }
public Customer customer { get; set; }
}
Now type your view with the master class
#model MasterModel

Access Model List Used in Another Model to view

I have the following code. I want to use the objects of List in my view where I have used DTOQuestions
//My Model Question
public class Question
{
public int Question_Id { get; set; }
public string Question_Title { get; set; }
public List<Reply> Reply_List { get; set; }
}
//My 2nd Model Answer
public class Reply
{
public int Reply_Msg { get; set; }
public int Attachment_Id { get; set; }
}
//View
#foreach(var item in Model)
{
foreach(var replylbl in item.Forum_Reply_List)
{
<th>#Html.DisplayNameFor(replymodel => replylbl.Reply_Msg)</th>
<th>#Html.DisplayNameFor(replymodel => replylbl.Attachment_Id)</th>
}
}
#foreach (var item in Model) {
<tr>
<td>
#foreach (var reply in item.Forum_Reply_List){#Html.DisplayFor(modelReply => reply.Reply_Msg)}
</td>
<td>
#foreach (var reply in item.Forum_Reply_List){#Html.DisplayFor(modelReply => reply.Attachment_Id)}
</td>
I have these two models and following view. Now I want to access Answer (reply) model's objects in my view which has model type Question
But in view I am unable to display headers for those columns..

How to make this razor view working?

in my MVC web apps, this is the models which uses icollection object,
public class EmpProfile
{
public int EmpProfileID { get; set; }
public string EmpName { get; set; }
public string EmpNum { get; set; }
public string ManagerEditor { get; set; }
public string DocCreatedBy { get; set; }
public virtual ICollection<PerfPlan> PerfPlans { get; set; }
public virtual ICollection<ProgReview> ProgReviews { get; set; }
}
and this is PerfPlan model, the other model ProgReviews is similar like this one.
public class PerfPlan
{
public int ID { get; set; }
public int EmpProfileID { get; set; }
public string EmpName { get; set; }
public string EmpNum { get; set; }
public string Title { get; set; }
....
public virtual EmpProfile EmpProfile { get; set; }
}
Basically, it builds one to many relationship between EmpProfile and PerfPlan, ProgReview. So one EmpProfile has 0 or many Performance plan and Progress Review data (model).Now, in my Index razor of EmpProfile, I want to list all PerfPlan and ProgReview which related to each EmpProfile, I build something like this:
#model IEnumerable<PerfM.Models.EmpProfile>
<table>
#foreach (var item in Model)
{
<tr class="#selectedRow">
<td >
#Html.ActionLink(#item.EmpName, "Index", new { id = item.EmpProfileID })
</td>
</tr>
//here I need to list all PerfPlan and ProgReview related to this EmpProfile and list under this row.
Can any expert help me to continue the codes below?
Thanks a lot,
Just use simple foreach loops like this (inside of your foreach loop) :
foreach(var plan in item.PerfPlans)
{
// here you can access your PerfPlan properties like:
<tr>
<td> #plan.Id </td>
<td> #plan.EmpName</td>
<td> #plan.EmpNum </td>
...
</tr>
}
foreach(var review in item.ProgReviews)
{
...
}
And in your Controller don't forget to include your collections:
var profiles = context.EmpProfiles.Include("PerfPlans").Include("ProgReviews");