Why do I get this error when passing ViewModel to View? - c#

When passing ViewModel to View I get the error
The model item passed into the ViewDataDictionary is of type
'System.Collections.Generic.List'1[TraficAlert.Models.TaBarHeader]',
but this ViewDataDictionary instance requires a model item of type
'System.Collections.Generic.IEnumerable'1[TraficAlert.Models.ViewModels.HeaderTelegramaViewModel]'.
I have tried to use #model IEnumerable<TraficAlert.Models.ViewModels.HeaderTelegramaViewModel> in the Index.cshtml and it works, but I need to access a property from HeaderTelegramaViewModel.
Index.cshtml:
#model IEnumerable<TraficAlert.Models.ViewModels.HeaderTelegramaViewModel>
<table class="table">
<thead>
<tr>
<th>
#Html.DisplayNameFor(model => model.TaBarHeader.Id)
</th>
<th>
#Html.DisplayNameFor(model => model.TaBarHeader.ParentId)
</th>
<th>
#Html.DisplayNameFor(model => model.TaBarHeader.TStamp)
</th>
(...)
</thead>
<tbody>
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.TaBarHeader.Id)
</td>
<td>
#Html.DisplayFor(modelItem => item.TaBarHeader.ParentId)
</td>
<td>
#Html.DisplayFor(modelItem => item.TaBarHeader.TStamp)
</td>
(...)
<td>
<a asp-action="Edit" asp-route-id="#item.TaBarHeader.Id">Edit</a> |
<a asp-action="Details" asp-route-id="#item.TaBarHeader.Id">Details</a> |
<a asp-action="Delete" asp-route-id="#item.TaBarHeader.Id">Delete</a>
</td>
</tr>
}
</tbody>
</table>
HeaderTelegramaController:
(...)
public IActionResult Index()
{
var applicationDbContext = _unitofwork.BarHeader.GetAllBarH().ToList();
return View(applicationDbContext);
}
TaBarHeaderRepository:
public IEnumerable<TaBarHeader> GetAllBarH()
{
return _db.TaBarHeaders
.Include(t => t.CategoriaFk)
.Include(t => t.CauzaFk)
.Include(t => t.ClasaFk)
.Include(t => t.LucrareFk)
.Include(t => t.RegionalaFk)
.Include(t => t.UserFk);
}
HeaderTelegramaViewModel:
public TaBarHeader TaBarHeader { get; set; }
public IEnumerable<SelectListItem> Categoria { get; set; }
public IEnumerable<ViewOtf> ViewOTFCollection { get; set; }
(...)
Why do I get the above mentioned error?
Thank you.

use the model below in the cshtml.
#model TraficAlert.Models.ViewModels.HeaderTelegramaViewModel
And in the Index() create an instance of HeaderTelegramaViewModel:
var _HeaderTelegramaViewModel = new HeaderTelegramaViewModel();
_HeaderTelegramaViewModel.TaBarHeader = TaBarHeader;
And the class HeaderTelegramaViewModel must have:
public IEnumerable<TaBarHeader> TaBarHeader { get; set; }
public IEnumerable<SelectListItem> Categoria { get; set; }
public IEnumerable<ViewOtf> ViewOTFCollection { get; set; }

use :: #model TraficAlert.Models.ViewModels.HeaderTelegramaViewModel
instead of ::#model IEnumerable<TraficAlert.Models.ViewModels.HeaderTelegramaViewModel>
at the top of index.cshtml page

See the type of your model IEnumerable<TraficAlert.Models.ViewModels.HeaderTelegramaViewModel> and apply this:
public IActionResult Index()
{
var applicationDbContext = _unitofwork.BarHeader.GetAllBarH();
return View(applicationDbContext);
}

The error message explains the problem fairly clearly: you're passing in a different type than the view is expecting.
Specifically, you call GetAllBarH() to get the data for the view, and it returns IEnumerable<TaBarHeader>. Therefore the model declaration for the page should be:
#model IEnumerable<TraficAlert.Models.TaBarHeader>
If you really wanted HeaderTelegramaViewModel then you're going to have to convert the IEnumerable<TaBarHeader> somehow. I assume you missed that step in your controller.

Why do I get the above mentioned error?
Because the data type returned in your action is not the same as the data type required on the view.
You can modify your HeaderTelegramaController like this:
public IActionResult Index()
{
var applicationDbContext = _unitofwork.BarHeader.GetAllBarH().Select(m => new HeaderTelegramaViewModel { TaBarHeader = m }).ToList();
return View(applicationDbContext);
}

A possible error I assume may come from your table head
try specifying an index considering your model is an IEnumerable.
So change
#Html.DisplayFor(modelItem => item.TaBarHeader.Id)
to something like this
#Html.DisplayFor(modelItem => item[0].TaBarHeader.Id)

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

LAMBDA Troubles

Why is this not working?
I feel like I am losing my mind.
I am using asp.net core with entity frameworks. This code in my controller is not working. I just want to return the ChildLans where the ParentOrgLevel contains 0001 at the beginning.
Model
public class CORP_MatrixPositionOLDWay
{
public int ID { get; set; }
public string ParentLAN { get; set; }
public string ChildLAN { get; set; }
public string ChildOrgLevel { get; set; }
public string ParentOrgLevel { get; set; }
}
View
model IEnumerable<Certifications.Models.CORP_MatrixPositionOLDWay>
#{
ViewData["Title"] = "Index";
}
<h2>Index</h2>
<p>
<a asp-action="Create">Create New</a>
</p>
<table class="table">
<thead>
<tr>
<th>
#Html.DisplayNameFor(model => model.ParentOrgLevel)
</th>
<th></th>
</tr>
</thead>
<tbody>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.ParentOrgLevel)
</td>
<td>
<a asp-action="Edit" asp-route-id="#item.ID">Edit</a> |
<a asp-action="Details" asp-route-id="#item.ID">Details</a> |
<a asp-action="Delete" asp-route-id="#item.ID">Delete</a>
</td>
</tr>
}
</tbody>
</table>
Controller
public async Task<IActionResult> Index()
{
var ParentORG = _context.CORP_MatrixPositionOLDWay
.Where(i => i.ParentLAN == UserInformation.Globals.LANID)
.Select(i => i.ParentOrgLevel);
return View(ParentORG);
}
UserInformation.Globals.LANID is a string variable for the machines LANID
You are missing a lambda in your Where(), you can also just use StartsWith() instead of Contains() if you want to be more specific:
var list = _context.CORP_MatrixPositionOLDWay
.Where(x => x.ParentOrgLevel.StartsWith("0001"))
.Select(x => x.ChildLAN);
Given your latest edit
Your view expects a model of IEnumerable<Certifications.Models.CORP_MatrixPositionOLDWay>
Your action must return a View with that type as a model:
public async Task<IActionResult> Index()
{
var ParentORG = _context.CORP_MatrixPositionOLDWay
.Where(i => i.ParentLAN == UserInformation.Globals.LANID);
return View(ParentORG);
}
If you want your view to have only the ChildLAN properties (as you indicated in your comment below) then you'll have to adjust your view's model to be of type IEnumerable. However keep in mind that by doing this you will lose all the other functionality of your view (since its dependent on other properties of the CORP_MatrixPositionOLDWay class. What you likely want to do is create a new class called IndexViewModel or something like that. and have it contain both things you need:
public class IndexViewModel
{
//Name these properties better than I have
public IEnumerable<string> ChildLans {get;set;}
public IEnumerable<CORP_MatrixPositionOLDWay> DataList {get;set;}
}

Error CS0411 in.NET while using #Html.DisplayNameFor()

I am beginner in .NET and I am learning MVC pattern. I am building a demo application which demonstrate CRUD Operations on any product. Before I use any database connectivity, I decided to test it with in memory variable. My code is as below:
Model Class
public class Product
{
[Key]
public int Id { get; set; }
[Required]
[Display(Name="Name")]
public String ProductName { get; set; }
[Required]
[Display(Name="Price")]
public float Price { get; set; }
[Display(Name="Discription")]
public String ProductDiscription { get; set; }
}
Controller Class
public class ProductController : Controller
{
//
// GET: /Product/
public ActionResult Index()
{
return View(new Product{Id = 01, Price=100, ProductName="MyProduct", ProductDiscription="30 TeaBags in a pack"});
}
}
View
#model CrudAppDemo.Models.Product
#{
ViewBag.Title = "Products";
}
<h2>Products</h2>
<div class="row">
<div class="col-md-4 ">
<table class="table">
<thead>
<tr>
<td>#Html.DisplayNameFor(model => model.Name)</td>
<td>#Html.DisplayNameFor(model => model.Price)</td>
<td>#Html.DisplayNameFor(model => model.Description)</td>
</tr>
</thead>
<tbody>
<tr>
<td>#Html.DisplayFor(model => model.Name)</td>
<td>#Html.DisplayFor(model => model.Price)</td>
<td>#Html.DisplayFor(model => model.Description)</td>
</tr>
</tbody>
</table>
</div>
</div>
When I am running this code, I am getting CS0411 Error:
Your parameter doesn't match the ones in model.
you have ProductName in model and you are trying to access Name which is not in model, same goes for description.
Write this instead.
<thead>
<tr>
<td>#Html.DisplayNameFor(model => model.ProductName)</td>
<td>#Html.DisplayNameFor(model => model.Price)</td>
<td>#Html.DisplayNameFor(model => model. ProductDiscription)</td>
</tr>
</thead>
<tbody>
<tr>
<td>#Html.DisplayFor(model => model.ProductName)</td>
<td>#Html.DisplayFor(model => model.Price)</td>
<td>#Html.DisplayFor(model => model. ProductDiscription)</td>
</tr>
</tbody>
It looks like you are using the display name [Display(Name="Name")] as your property rather than the property itself public String ProductName { get; set; }. Try changing to use the property name.
<td>#Html.DisplayNameFor(model => model.ProductName)</td>
<td>#Html.DisplayNameFor(model => model.Price)</td>
<td>#Html.DisplayNameFor(model => model.ProductDiscription)</td>

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.

.net strongly typed view Model not set to instance of object [duplicate]

This question already has answers here:
What is a NullReferenceException, and how do I fix it?
(27 answers)
Closed 6 years ago.
So I am creating a strongly-typed view. My model is called RestaurantReview.cs and looks like this:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace OdeToFood.Models
{
public class RestaurantReview
{
public int Id { get; set; }
public string Name { get; set; }
public string City { get; set; }
public string Country { get; set; }
public int Rating { get; set; }
}
}
I had Visual Studio create a strongly-typed List model based on this, which looks like this:
#model IEnumerable<OdeToFood.Models.RestaurantReview>
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
<p>
#Html.ActionLink("Create New", "Create")
</p>
<table>
<tr>
<th>
#Html.DisplayNameFor(model => model.Name)
</th>
<th>
#Html.DisplayNameFor(model => model.City)
</th>
<th>
#Html.DisplayNameFor(model => model.Country)
</th>
<th>
#Html.DisplayNameFor(model => model.Rating)
</th>
<th></th>
</tr>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.Name)
</td>
<td>
#Html.DisplayFor(modelItem => item.City)
</td>
<td>
#Html.DisplayFor(modelItem => item.Country)
</td>
<td>
#Html.DisplayFor(modelItem => item.Rating)
</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>
When I run the site, I get a null pointer exception in the line "#foreach (var item in Model)", highlighting Model object, and stating "Object reference not set to an instance of an object."
Not really understanding how this code can be wrong since I didn't even write it, Visual Studio did. What is happening here?
Your Controller show have the RestaurantReview IEnumerable passed. Example:
public class HomeController : Controller { //suppose this is your Home
public ActionResult Index() {
IEnumerable<OdeToFood.Models.RestaurantReview> model;
model = from m in db.RestaurantReviews
... //your query here
select m;
return View(model); //pass the model here
}
Then you will not get null exception
Sounds like you haven't instantiated your model correctly within the Controller.
As a test you could try this:
public ActionResult Reviews()
{
var model = new List<OdeToFood.Models.RestaurantReview>();
model.Add(new OdeToFood.Models.RestaurantReview { Name = "Test" });
model.Add(new OdeToFood.Models.RestaurantReview { Name = "Test2" });
return View(model);
}
However the model should be correctly populated from the DB. If you can paste your Controller code that would help.

Categories