MVC5 Linq to ViewModel to Razor View code improvement? - c#

I want to fetch data from two EF tables (Menus and MenuItems). Then I would like to place the result into a ViewModel. So I can easily loop through the data in the Razor View.
I've got it working. But I'm new to .Net MVC so I was wondering if my code could be improved. Maybe the queries can be merged and a lot shorter. And I'm using AsEnumerable(), I'm not sure if that is the best approach.
I think my attempt is okay. But I would like to hear what you think. Any suggestion for code improvement is welcome. Thanks.
The controller class:
public ActionResult Index(int? id)
{
if (id == null) return RedirectToAction("Index", new { controller = "Menu" });
var menu =
(from m in _db.Menus
join mi in _db.MenuItems on m.Id equals mi.MenuId
where m.Id == id
select m).First();
var menuItems =
(from mi in _db.MenuItems.AsEnumerable()
join m in _db.Menus on mi.MenuId equals m.Id
where m.Id == id
select new MenuItem
{
Id = mi.Id,
Name = mi.Name,
Href = mi.Href,
CssClass = mi.CssClass,
CssId = mi.CssId,
Title = mi.Title,
Weight = mi.Weight
});
var model = new MenuModelView
{
Id = menu.Id,
Name = menu.Name,
CssClass = menu.CssClass,
CssId = menu.CssId,
Deleted = menu.Deleted,
MenuItems = menuItems
};
return View(model);
}
The ViewModel class:
using System.Collections.Generic;
namespace DesignCrew.Areas.Admin.Models
{
public class MenuModelView
{
public int Id { get; set; }
public string Name { get; set; }
public string CssClass { get; set; }
public string CssId { get; set; }
public bool Deleted { get; set; }
public IEnumerable<MenuItem> MenuItems { get; set; }
}
}
The Razor View:
#model DesignCrew.Areas.Admin.Models.MenuModelView
#{
ViewBag.Title = "Index";
}
<h2>Menu - #Html.DisplayFor(model => model.Name, new { #class = "control-label col-md-2" })</h2>
<p>
#Html.ActionLink("Create New Item", "Create")
</p>
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.MenuItems.First().Id)
</th>
<th>
#Html.DisplayNameFor(model => model.MenuItems.First().Name)
</th>
<th>
#Html.DisplayNameFor(model => model.MenuItems.First().Href)
</th>
<th>
#Html.DisplayNameFor(model => model.MenuItems.First().Title)
</th>
<th>
#Html.DisplayNameFor(model => model.MenuItems.First().CssClass)
</th>
<th>
#Html.DisplayNameFor(model => model.MenuItems.First().CssId)
</th>
<th>
#Html.DisplayNameFor(model => model.MenuItems.First().ParentId)
</th>
<th>
#Html.DisplayNameFor(model => model.MenuItems.First().Weight)
</th>
<th>Options</th>
</tr>
#foreach (var item in Model.MenuItems)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.Name)
</td>
<td>
#Html.DisplayFor(modelItem => item.Name)
</td>
<td>
#Html.DisplayFor(modelItem => item.Href)
</td>
<td>
#Html.DisplayFor(modelItem => item.Title)
</td>
<td>
#Html.DisplayFor(modelItem => item.CssClass)
</td>
<td>
#Html.DisplayFor(modelItem => item.CssId)
</td>
<td>
#Html.DisplayFor(modelItem => item.ParentId)
</td>
<td>
#Html.DisplayFor(modelItem => item.Weight)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id = item.Id }, new { #class = "link-menu" }) |
#Html.ActionLink("Delete", "Delete", new { id = item.Id }, new { #class = "link-menu" })
</td>
</tr>
}
</table>

If your EF model has a navigable foreign key between Menu and MenuItem, you won't need to explicitly join the two tables, and you can eager load the child MenuItems at the same time you fetch the parent Menu, and simply navigate from parent to child:
var menu = _db.Menus
.Include("MenuItems") // Or, use the typed version on newer EF's
.First(m => m.id == id); // Many LINQ expressions allow predicates
return new MenuModelView
{
Menu = menu.Name,
CssClass = menu.CssClass,
CssId = menu.CssId,
Deleted = menu.Deleted,
MenuItems = menu.MenuItems
}
And, since there seems to be much commonality in the MenuModelView vs the Menu EF entity, you could look at using AutoMapper. Once configured, this would allow you to replace the manual mapping step with something like:
return Mapper.Map<Menu, MenuModelView>(menu);
Edit
Here's a cheap and nasty ViewModel, which wraps your EF entities, and augments it with presentation tier data. Purists would probably note that you should create new classes for the wrapped EF Model, although then again, your EF model seems to model Html :)
public class MenuModelView
{
// Presentation tier stuff
public string PageTitle { get; set; }
public string MetaTagsForSEO { get; set; }
public bool IsThisARegisteredUserSoSkipTheAdverts { get; set; }
// Your EF / Domain Model
public Menu Menu { get; set; }
}
Your razor #Model is the MenuModelView.

Related

How to return values from a LINQ query and display them in a table using C#, ASP.NET MVC and Entity Framework

I have written a linq query from which I would like to obtain the values from that query and display the results in a table. I keep getting an error that reads as shown below
The model item passed into the dictionary is of type 'System.Data.Entity.Infrastructure.DbQuery1[<>f__AnonymousType56[System.Int32,System.String,System.String,System.String,System.String,System.Nullable1[System.DateTime]]]', but this dictionary requires a model item of type 'System.Collections.Generic.IEnumerable1[App.Web.marketingdbclients_invalidEmails]'
When I scaffolded the view from the controller I picked one of my tables as the model and I don't think that is the right way to do it. What is the right type of model to pick if I want to display results from a linq query and or stored procedure because I also have a stored procedure that does the same thing as the linq query so perhaps I could use the sp as an alternative
public class InvalidEmailsController : Controller
{
private MarketingDBEntitiesModel db = new MarketingDBEntitiesModel();
public ActionResult InvalidEmails(int UploadId)
{
//var emails = db.sp_marketing_getInvalidEmailsByUploadId(UploadId);
var emailTable = (from mie in db.marketingdbclients_invalidEmails
join mdt in db.marketingdbclients_dataTable on mie.ClientId equals mdt.ClientId
where mdt.UploadId == 88
select new
{
mie.ClientId, mie.Email1, mie.Email2,
mie.Email3, mie.Email4, mie.DateStamp
});
return View(emailTable);
}
}
This is my view which is bound to a a table as model (which I think is the wrong method)
#model IEnumerable<App.Web.marketingdbclients_invalidEmails>
<p>
#Html.ActionLink("Create New", "Create")
</p>
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.ClientId)
</th>
<th>
#Html.DisplayNameFor(model => model.Email1)
</th>
<th>
#Html.DisplayNameFor(model => model.Email2)
</th>
<th>
#Html.DisplayNameFor(model => model.Email3)
</th>
<th>
#Html.DisplayNameFor(model => model.Email4)
</th>
<th>
#Html.DisplayNameFor(model => model.DateStamp)
</th>
<th></th>
</tr>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.ClientId)
</td>
<td>
#Html.DisplayFor(modelItem => item.Email1)
</td>
<td>
#Html.DisplayFor(modelItem => item.Email2)
</td>
<td>
#Html.DisplayFor(modelItem => item.Email3)
</td>
<td>
#Html.DisplayFor(modelItem => item.Email4)
</td>
<td>
#Html.DisplayFor(modelItem => item.DateStamp)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id=item.invalidEmailId }) |
#Html.ActionLink("Details", "Details", new { id=item.invalidEmailId }) |
#Html.ActionLink("Delete", "Delete", new { id=item.invalidEmailId })
</td>
</tr>
}
</table>
You are trying to pass an anonymous type to 'App.Web.marketingdbclients_invalidEmails' so that's the way it produces an error.
You need to create a new View Model with required properties that you want to project from LINQ and show in your view.
public class marketingdbclients_invalidEmailsVM
{
public int ClientId { get; set; }
public string Email1 { get; set; }
public string Email2 { get; set; }
public string Email3 { get; set; }
public string Email4 { get; set; }
public DateTime DateStamp { get; set; }
}
Now use this view model for your LINQ
public ActionResult InvalidEmails(int UploadId)
{
//var emails = db.sp_marketing_getInvalidEmailsByUploadId(UploadId);
var emailTable = (from mie in db.marketingdbclients_invalidEmails
join mdt in db.marketingdbclients_dataTable on mie.ClientId equals mdt.ClientId
where mdt.UploadId == 88
select new marketingdbclients_invalidEmailsVM
{
ClientId = mie.ClientId,
Email1=mie.Email1,
Email2=mie.Email2,
Email3=mie.Email3,
Email4=mie.Email4,
DateStamp=mie.DateStamp
});
return View(emailTable);
}
And bind your model using a new created View Model.
#model IEnumerable<App.Web.marketingdbclients_invalidEmailsVM>
Hopefully, it will resolve your problem.
If you have a model in view:
#model IEnumerable<App.Web.marketingdbclients_invalidEmails>
You must return the same type from the controller action.
Change this:
... select new {...});
to this:
... select new App.Web.marketingdbclients_invalidEmails {...}).ToList();
and model to:
#model List<App.Web.marketingdbclients_invalidEmails>
Good luck

Populate View with 2 Lists (Group by LINQ queries)

I am new to ASP.net MVC and I have looked at other questions and tried multiple methods to populate a view with 2 IEnumerables using a ViewModel. It seems using the groupby methods in the controller and passing them to the View has created some problems.
The overall problem is that the view doesn't accept ListCatViewModel. It gives the error: IEnumerable does not contain a definition for 'Category' and no extension method 'Category' accepting a first argument of type 'IEnumerable
In the multiple questions that I have seen on Stackoverflow and other sources we are supposed to combine the two lists with a variable and then pass that variable to the View. After that we are supposed to reference the ViewModel. When I reference the ViewModel I cannot access the properties in each IEnumerable (NotesList and RelList). I am looking for direction on how to change the controller and/or reference in the View to accept the 2 separate collections to ultimately create two tables.
Summary of Code: CatViewModel and RelViewModel hold different properties. ListCatViewModel contains the IEnumerable collections.
public class CatViewModel
{
public string Category { get; set; }
public decimal CountOfLoans { get; set; }
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:c}")]
public decimal TotalVolume { get; set; }
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:c}")]
public decimal UnfundedCommitment { get; set; }
}
public class RelViewModel
{
public string RelationshipName { get; set; }
public decimal CountOfLoans { get; set; }
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:c}")]
public decimal TotalVolume { get; set; }
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:c}")]
public decimal UnfundedCommitment { get; set; }
}
public class ListCatViewModel
{
public IEnumerable<CatViewModel> NotesList { get; set; }
public IEnumerable<RelViewModel> RelList { get; set; }
}
Controller:
public ActionResult Index()
{
ApplicationDbContext db = new ApplicationDbContext();
ListCatViewModel LCVM = new ListCatViewModel();
LCVM.NotesList = GetCatList();
LCVM.RelList = GetRelList();
return View(LCVM);
}
public IEnumerable<CatViewModel> GetCatList() //Category group by query
{
ApplicationDbContext db = new ApplicationDbContext();
IEnumerable<CatViewModel> CatVM = db.LoanPortfolio.GroupBy(i => i.Category)
.Select(g => new CatViewModel
{
Category = g.Key,
CountOfLoans = g.Count(),
TotalVolume = g.Sum(i => i.Volume),
UnfundedCommitment = g.Sum(i => i.Unfunded),
//Average = g.Average(i => i.Volume)
})
.OrderBy(c => c.Category)
.AsEnumerable();
return CatVM;
}
public IEnumerable<RelViewModel> GetRelList()
{
ApplicationDbContext db = new ApplicationDbContext();
IEnumerable<RelViewModel> RelVM = db.LoanPortfolio.GroupBy(i => i.RelationshipName)
.Select(g => new RelViewModel
{
RelationshipName = g.Key,
CountOfLoans = g.Count(),
TotalVolume = g.Sum(i => i.Volume),
UnfundedCommitment = g.Sum(i => i.Unfunded),
//Average = g.Average(i => i.Volume)
})
.OrderBy(c => c.RelationshipName)
.AsEnumerable();
return RelVM;
}
View:
View:
#model IEnumerable<Risk.ViewModel.ListCatViewModel>
#{
ViewBag.Title = "GroupByLoanPAllowanceB";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Category</h2>
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.NotesList.Category)
</th>
<th>
#Html.DisplayNameFor(model => model.NotesList.CountOfLoans)
</th>
<th>
#Html.DisplayNameFor(model => model.NotesList.TotalVolume)
</th>
<th>
#Html.DisplayNameFor(model => model.NotesList.UnfundedCommitment)
</th>
</tr>
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.NotesList.Category)
</td>
<td>
#Html.DisplayFor(modelItem => item.CountOfLoans)
</td>
<td>
#Html.DisplayFor(modelItem => item.TotalVolume)
</td>
<td>
#Html.DisplayFor(modelItem => item.UnfundedCommitment)
</td>
</tr>
}
</table>
Your view is not IEnumerable<Risk.ViewModel.ListCatViewModel>, it is now ListCatViewModel
Try:
#model Risk.ViewModel.ListCatViewModel
#{
ViewBag.Title = "GroupByLoanPAllowanceB";
Layout = "~/Views/Shared/_Layout.cshtml";
}
And when iterating over the list, you need to access it off the Model
#foreach (var item in Model.NotesList)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.Category)
</td>
<td>
#Html.DisplayFor(modelItem => item.CountOfLoans)
</td>
<td>
#Html.DisplayFor(modelItem => item.TotalVolume)
</td>
<td>
#Html.DisplayFor(modelItem => item.UnfundedCommitment)
</td>
</tr>
}
Assuming you're just trying to display the data, create some templates in the Views -> Shared -> DisplayTemplates folder (you may need to create it), and then use DisplayFor. Here's an example for your CatViewModel:
CatViewModel.cshtml
#model Your.Fully.Qualified.Namespace.CatViewModel
<tr>
<td>
#Model.Category
</td>
<td>
#Model.CountOfLoans
</td>
<td>
#Model.TotalVolume
</td>
<td>
#Model.UnfundedCommitment
</td>
</tr>
Parent view:
#model Risk.ViewModel.ListCatViewModel
#{
ViewBag.Title = "GroupByLoanPAllowanceB";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Category</h2>
<table class="table">
<tr>
<th>
Category
</th>
<th>
Count Of Loans
</th>
<th>
Total Volume
</th>
<th>
Unfunded Commitment
</th>
</tr>
#Html.DisplayFor(m => m.NotesList)
</table>
The Razor engine is smart enough to look for a partial view with the same name as the model (in the aforementioned folder) when you call either EditorFor or DisplayFor. So, if the property was of type Foo, and you had a view called Foo.cshtml, it would be used for that property. If you supply a collection, it will also iterate through the collection and generate markup for each item.
There's also an overload that allows you to specify the template to use, if you don't want to follow the convention I mentioned above.

Passing a parameter to Html.ActionLink

I am trying to pass a parameter to the Create #Html.ActionLink in the Index view however I am having some difficulties.
I have an Address controller in which the user sees the addresses of a specific person when they access the Index view. I would like to pass this PersonID to the Create view so that they do not have to select or enter the person when they create a new address. My actionlink looks like this -
#Html.ActionLink("Create New", "Create", new { id = Model.[PersonID is not a choice]})
My problem is that after Model PersonID is not an option. I am not sure how to get PersonID to the Create function in the AddressController.
I tried following along with the post - Passing a parameter to Html.ActionLink when the model is IEnumerable<T> - where they are having the same issue. The first answers seems like a likely solution however I could not duplicate the model they created and when I put the pieces in my Address model I could not duplicate the code they had performed in the controller. Is there another solution?
My Address Model -
public partial class Address
{
public int AddressID { get; set; }
public int PersonID { get; set; }
[Display(Name="Address")]
public string Address1 { get; set; }
[Display(Name = "Address 2")]
public string Address2 { get; set; }
public string City { get; set; }
[Display(Name="State")]
public string StateAbbr { get; set; }
[Display(Name = "Zip Code")]
public string Zip { get; set; }
public virtual Person Person { get; set; }
public List<Address> Address { get; set; }
}
My AddressController Index
public ActionResult Index(int id)
{
var addresses = db.Addresses.Include(a => a.Person)
.Where(a => a.PersonID == id);
Person person = db.People.Find(id);
ViewBag.FullName = person.FirstName + " " + person.LastName;
person.PersonID = id;
return View(addresses.ToList());
}
Address Index view -
#model IEnumerable<OpenBurn.Models.Address>
#{
ViewBag.Title = "Address";
}
<h2>Address</h2>
<p>
#Html.ActionLink("Create New", "Create", new { id = .PerosnID })
</p>
<h3 style="color: #008cba;"> #ViewBag.FullName </h3>
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.Address1)
</th>
<th>
#Html.DisplayNameFor(model => model.Address2)
</th>
<th>
#Html.DisplayNameFor(model => model.City)
</th>
<th>
#Html.DisplayNameFor(model => model.StateAbbr)
</th>
<th>
#Html.DisplayNameFor(model => model.Zip)
</th>
<th></th>
</tr>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.Address1)
</td>
<td>
#Html.DisplayFor(modelItem => item.Address2)
</td>
<td>
#Html.DisplayFor(modelItem => item.City)
</td>
<td>
#Html.DisplayFor(modelItem => item.StateAbbr)
</td>
<td>
#Html.DisplayFor(modelItem => item.Zip)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id=item.AddressID }) |
#Html.ActionLink("Details", "Details", new { id=item.AddressID }) |
#Html.ActionLink("Delete", "Delete", new { id=item.AddressID })
</td>
</tr>
}
</table>
Since the #Html.ActionLink syntax isn't inside a foreach loop, you obviously can't use IEnumerable<OpenBurn.Models.Address> as the model. You need a new model class that contains a property that holds those Address records and a property that holds the PersonID value. You should also use the same model class to pass the FullName value instead of using ViewBag. I would suggest the below model class
public class AddressIndexModel
{
public AddressIndexModel()
{
this.Addresses = new List<Address>();
}
public int PersonID { get; set; }
public string FullName { get; set; }
public List<Address> Addresses { get; set; }
}
then change your controller to this
public ActionResult Index(int id)
{
var addresses = db.Addresses.Include(a => a.Person)
.Where(a => a.PersonID == id);
Person person = db.People.Find(id);
AddressIndexModel model = new AddressIndexModel();
model.PersonID = id;
model.FullName = person.FirstName + " " + person.LastName;
model.Addresses = addresses.ToList();
return View(model);
}
and change your view to below
#model AddressIndexModel
#{
ViewBag.Title = "Address";
}
<h2>Address</h2>
<p>
#Html.ActionLink("Create New", "Create", new { id = Model.PersonID })
</p>
<h3 style="color: #008cba;"> #Model.FullName </h3>
<table class="table">
<tr>
<th>
Address
</th>
<th>
Address 2
</th>
<th>
City
</th>
<th>
State
</th>
<th>
Zip Code
</th>
<th></th>
</tr>
#foreach (var item in Model.Addresses) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.Address1)
</td>
<td>
#Html.DisplayFor(modelItem => item.Address2)
</td>
<td>
#Html.DisplayFor(modelItem => item.City)
</td>
<td>
#Html.DisplayFor(modelItem => item.StateAbbr)
</td>
<td>
#Html.DisplayFor(modelItem => item.Zip)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id=item.AddressID }) |
#Html.ActionLink("Details", "Details", new { id=item.AddressID }) |
#Html.ActionLink("Delete", "Delete", new { id=item.AddressID })
</td>
</tr>
}
</table>

Filtering by parameter & Routing

I have an MachineInfo view page which I am showing 60 different specifications of the related machine like processor info, ram info, disk info, db info etc.
ActionLink to this page is:
#Html.ActionLink("Machine Info", "MachineInfo", new { id = Model.LicenseKey }) |
Controller:
public ActionResult MachineInfo(string LicenseKey)
{
if (LicenseKey == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Farm farm = db.Farms.Find(LicenseKey);
if (farm == null)
{
return HttpNotFound();
}
return View(farm);
}
Farm Model:
public partial class Farm
{
public Farm()
{
this.FarmChanges = new HashSet<FarmChange>();
this.FarmDetails = new HashSet<FarmDetail>();
this.FarmTables = new HashSet<FarmTable>();
}
public int Id { get; set; }
public string LicenseKey { get; set; }
public string Name { get; set; }
public string CustomerName { get; set; }
public virtual ICollection<FarmChange> FarmChanges { get; set; }
public virtual ICollection<FarmDetail> FarmDetails { get; set; }
public virtual ICollection<FarmTable> FarmTables { get; set; }
}
FarmDetails Model:
public partial class FarmDetail
{
public System.Guid Id { get; set; }
public int FarmId { get; set; }
public int Type { get; set; }
public string Name { get; set; }
public string Value { get; set; }
public virtual Farm Farm { get; set; }
}
All the MachineInfo is coming from the "Value" in the FarmDetails table.
View:
#model IEnumerable<FarmManagement.Models.FarmDetail>
#{
ViewBag.Title = "Machine Info";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Machine Info</h2>
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.Type)
</th>
<th>
#Html.DisplayNameFor(model => model.Name)
</th>
<th>
#Html.DisplayNameFor(model => model.Value)
</th>
<th>
#Html.DisplayNameFor(model => model.Farm.LicenseKey)
</th>
<th></th>
</tr>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.Type)
</td>
<td>
#Html.DisplayFor(modelItem => item.Name)
</td>
<td>
#Html.DisplayFor(modelItem => item.Value)
</td>
<td>
#Html.DisplayFor(modelItem => item.Farm.LicenseKey)
</td>
<td>
</td>
</tr>
}
</table>
#Html.ActionLink("Back to Farms List", "Index")
I am trying to show MachineInfo of a specific machine (LicenseKey=ABXYZ-XYZAB) with this url: mydomain.com/MachineInfo/ABXYZ-XYZAB
I need to filter the view by LicenseKey.
After my all tries, I'm only getting 400 - Bad Request error (Because LicenseKey == null) or getting the MachineInfo of ALL machines, not the specific machine with LicenseKey=ABXYZ-XYZAB.
What I am doing wrong?
Change your actionlink code
#Html.ActionLink("Machine Info", "MachineInfo", new { id = Model.LicenseKey })
to
#Html.ActionLink("Machine Info", "MachineInfo", new { LicenseKey = Model.LicenseKey })
As the Action link parameter name should match with the controller action parameter name.
Solved the problem after making following changes:
New Controller After Change:
public ActionResult MachineInfo(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
FarmDetail farmdetail = db.FarmDetails.Where(x => x.FarmId == id).FirstOrDefault();
if (farmdetail == null)
{
return HttpNotFound();
}
return View(farmdetail);
}
New View After Change:
#model FarmManagement.Models.FarmDetail
#{
ViewBag.Title = "Machine Info";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Machine Info</h2>
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.Type)
</th>
<th>
#Html.DisplayNameFor(model => model.Name)
</th>
<th>
#Html.DisplayNameFor(model => model.Value)
</th>
<th>
#Html.DisplayNameFor(model => model.Farm.LicenseKey)
</th>
<th></th>
</tr>
#for (int i = 0; i < Model.Farm.FarmDetails.Count(); i++)
{
<tr>
<td>
#Html.DisplayFor(model => model.Farm.FarmDetails.ElementAt(i).Type)
</td>
<td>
#Html.DisplayFor(model => model.Farm.FarmDetails.ElementAt(i).Name)
</td>
<td>
#Html.DisplayFor(model => model.Farm.FarmDetails.ElementAt(i).Value)
</td>
<td>
#Html.DisplayFor(model => model.Farm.LicenseKey)
</td>
<td>
</tr>
}
</table>
#Html.ActionLink("Back to Farms List", "Index")
I needed to use a where sentence in Controller;
FarmDetail farmdetail = db.FarmDetails.Where(x => x.FarmId == id).FirstOrDefault();
And removed IEnumerable from the View and changed #foreach to #for with ElementAt(i).
#Html.DisplayFor(model => model.Farm.FarmDetails.ElementAt(i).Name)

MVC return list of items to the view

Can someone please help me out. I am trying to retrieve a list of storage details from the database and simply display the list in a view.
Storage Model:
public class StorageModel
{
[Required]
[Display(Name = "Storage Name")]
public string Name { get; set; }
[Required]
[Display(Name = "Date From")]
public string DateFrom { get; set; }
[Required]
[Display(Name = "Date To")]
public string DateTo { get; set; }
[Required]
[Display(Name = "Size")]
public string Size { get; set; }
}
Controller Method:
public ActionResult ViewStorage()
{
List<CommonLayer.TblNewsStorage> storageList = new BusinessLayer.Storage().getAllStorage().ToList();
return View(storageList);
}
Data being retrieved from the BusinessLayer above:
public IQueryable<CommonLayer.TblNewsStorage> getAllStorage()
{
return this.Entities.TblNewsStorage;
}
Now I created a strongly typed view with the StorageModel using view scaffold template, however it is not working. What exactly am I doing wrong? I tried passing a var instead of a List but still it is not working. I am new to MVC so I must be doing something wrong. Which is the proper way to pass and display a list of data to a view?
View code generated by MVC:
#model IEnumerable<NewsLibrary.Models.StorageModel>
#{
ViewBag.Title = "ViewStorage";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>ViewStorage</h2>
<p>
#Html.ActionLink("Create New", "Create")
</p>
<table>
<tr>
<th>
#Html.DisplayNameFor(model => model.Name)
</th>
<th>
#Html.DisplayNameFor(model => model.DateFrom)
</th>
<th>
#Html.DisplayNameFor(model => model.DateTo)
</th>
<th>
#Html.DisplayNameFor(model => model.Size)
</th>
<th></th>
</tr>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.Name)
</td>
<td>
#Html.DisplayFor(modelItem => item.DateFrom)
</td>
<td>
#Html.DisplayFor(modelItem => item.DateTo)
</td>
<td>
#Html.DisplayFor(modelItem => item.Size)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { /* id=item.PrimaryKey */ }) |
#Html.ActionLink("Details", "Details", new { /* id=item.PrimaryKey */ }) |
#Html.ActionLink("Delete", "Delete", new { /* id=item.PrimaryKey */ })
</td>
</tr>
}
</table>
I get the following error:
The model item passed into the dictionary is of type 'System.Collections.Generic.List1[CommonLayer.TblNewsStorage]', but this dictionary requires a model item of type 'System.Collections.Generic.IEnumerable1[NewsLibrary.Models.StorageModel]'.
The
#model IEnumerable<NewsLibrary.Models.StorageModel>
must have the same datatype passed in.
You are passing in
List<CommonLayer.TblNewsStorage>
If you take a closer look at the Model the View expects you see it's IEnumerable of NewsLibrary.Models.StorageModel. You are passing a list/IEnumerable of the type CommonLayer.TblNewsStorage. Make sure these two are the same datatype.
#foreach (var item in Model) {
item.Name
}
try like this. You need to remove #html.displayfor(model => item.name) to only item.name inside your tags

Categories