I'm trying to build a teacher recommendation web app using sessions for lab, and have gotten to a particular point where I need to view the recommendations that a particular teacher has.
app
When I click on the number of recommendations, it should take me to a view that lists all the recommendations that particular person has, but instead I get an error page saying
'Lab3Models.Models.Person' does not contain a definition for 'Rating'
Here's some of my code, hopefully someone can point me in the right direction.
Recommendation Controller
using Lab3Models.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace Lab3Models.Controllers
{
public class RecommendationController : Controller
{
private static IDictionary<string, Person> _people = null;
public ActionResult Add(string id)
{
if (Session["people"] != null)
{
_people = (Dictionary<string, Person>)Session["people"];
}
else
{
_people = new Dictionary<string, Person>();
Session["people"] = _people;
}
return View(_people[id]);
}
[HttpPost]
public ActionResult Create(string personId, Recommendation recommendation)
{
if (personId == null)
{
return HttpNotFound("Error, ID not found");
}
else
{ _people[personId].Recommendations.Add(recommendation);
return RedirectToAction("Index", "Home");
}
}
public ActionResult Show(string id)
{
if (Session["people"] != null)
{
_people = (Dictionary<string, Person>)Session["people"];
}
else
{
_people = new Dictionary<string, Person>();
Session["people"] = _people;
}
return View(_people);
}
}
}
Person & Recommendation Models
public class Person
{
public string Id { get; set; }
public string FirstName { get; set; }
public string MiddleName { get; set; }
public string LastName { get; set; }
public ICollection<Recommendation> Recommendations { get; set; }
public Person()
{
Recommendations = new List<Recommendation>();
}
public int NumberOfRecommendations
{
get
{
return Recommendations.Count;
}
}
public class Recommendation
{
public string Id { get; set; }
public int Rating { get; set; }
public string Narrative { get; set; }
public string RecommenderName { get; set; }
public Person ThePerson { get; set; }
}
}
When I put #model IDictionary<string, Lab3Models.Models.Person> in the top of my Show I get the error message 'Person' does not contain a definition for 'Rating' and no extension method 'Rating' accepting a first argument of type 'Person' could be found
If I put #model IDictionary<string, Lab3Models.Models.Recommendation> in the top of my view I get the error message ERROR
If anyone could help me out, it'd be greatly appreciated.
EDIT
#model IDictionary<string, Lab3Models.Models.Recommendation>
#{
ViewBag.Title = "Show";
}
<h2>Show</h2>
<table class="table">
<tr>
<th>
...
</th>
</tr>
#foreach (var item in Model)
{
<tr>
<td>
#item.Value.Id
</td>
<td>
#item.Value.Rating
</td>
<td>
#item.Value.Narrative
</td>
<td>
#item.Value.RecommenderName
</td>
<td>
Delete |
</td>
</tr>
}
</table>
EDIT 2
I have #model IDictionary<string, Lab3Models.Models.Recommendation> at the top of my view and have changed the code in my view to look like this:
#foreach (var item in Model)
{
foreach (var rec in item.Recommendations)
{
var rating = rec.Rating;
var narr = rec.Narrative;
...
<tr>
<td>#rating</td>
<td>#narr</td>
<td>#recName</td>
<td>
Delete
</td>
</tr>
}
}
But I'm getting errors in my code specifically on Model in this statement #foreach (var item in Model) and on Value in the delete link. #item.Value.Id When I load the view, I get an error saying
'KeyValuePair' does not contain a definition for 'Recommendations' and no extension method 'Recommendations' accepting a first argument of type 'KeyValuePair'
Did I goof up somewhere logically?
You do want to use #model IDictionary, as that's the type you are using. The issue is that you are getting a type Person out of the dictionary, and attempting to display rating directly from that type. Without seeing your front-end code I can't pinpoint exactly how the issue is presenting, but can tell you what your issue is. Essentially, you are attempting to get the Rating property from the person object, but the Rating property is part of the Person object's Recommendation Collection.
I'm assuming here that you are iterating through each Person in the dictionary to build out the display. You also need to iterate through each Recommendation for each person if you want to access the Rating.
roughly
foreach(var person in #model) {
//person specific display things
foreach(var recommendation in person.Recommendations) {
var rating = recommendation.Rating;
// display rating things
}
}
Related
I have an object that I am successfully passing to my view with a collection inside that is properly populated.
My Model.
conversion_rates is simply a collection of doubles:
public class Converter
{
public string result { get; set; }
public string documentation { get; set; }
public string terms_of_use { get; set; }
public string time_zone { get; set; }
public string time_last_update { get; set; }
public string time_next_update { get; set; }
public ConversionRate conversion_rates { get; set; }
}
My Controller.
currencyConverter is properly passed through to my view:
public class Rates
{
public static Converter Import()
{
try
{
string URLString = "https://v6.exchangerate-api.com/v6/APIKey/latest/gbp";
using (var webClient = new System.Net.WebClient())
{
var json = webClient.DownloadString(URLString);
Converter Test = JsonConvert.DeserializeObject<Converter>(json);
return Test;
}
}
catch (Exception e)
{
...
}
}
}
public IActionResult Index()
{
var currencyConverter = Rates.Import();
return View(currencyConverter);
}
My View:
#using System.Collections;
#model Converter
#foreach (var currency in (IEnumerable)Model.conversion_rates)
{
<tr>
<td>
#currency
</td>
</tr>
}
In the debugger, I can see that Model.conversion_rates is properly populated, but I can't get each individual element stored in my var currency for display in my table.
I can access each individual element and display them fine by just doing:
**#Successfully pulled data: #Model.conversion_rates.AED**
But for many reasons, this isn't practical. New values could be added dynamically etc. So I need to access the data in my Converter.conversion_rates, which is the collection I need to access.
This is an answer I came across, but it gives me the following error:
InvalidCastException: Unable to cast object of type 'Models.ConversionRate' to type 'System.Collections.IEnumerable'.
The most notable, similar answer I could find suggests turning my returned model itself to an IEnumerable, but it doesn't work. I'm assuming it's because I'm only returning one object, and the actual collection I want to access is inside the Converter object itself, so turning Converter into a collection of 1 doesn't help. Also if I do this, I can't access Model.conversion_rates directly either.
#model IEnumerable<Converter>
#foreach (var currency in Model.conversion_rates)
{
<tr>
<td>
#currency
</td>
</tr>
}
Appreciate any suggestions.
Edit: So an earlier fix I tried appears to be the correct one, my model is better off with:
public IEnumerable<ConversionRate> conversion_rates { get; set; }
But this messes up my json Deserialization. Will update if I find the fix.
Final code:
Just fix your class
public class Converter
{
......
public ConversionRate conversion_rates { get; set; }
public IEnumerable<ConversionRateItem> ConversionRateItems { get; set; }
}
public class ConversionRateItem
{
public string Name {get; set;}
public double Value {get; set;}
}
and action
var currencyConverter = Rates.Import();
var items = currencyConverter.conversion_rates.GetType()
.GetProperties()
.Select(p => new ConversionRateItem
{
Name=p.Name,
Value = (double) p.GetValue(currencyConverter .conversion_rates, null)
}).ToArray();
converter.ConversionRateItems=items;
converter.conversion_rates=null;
return View(currencyConverter);
and finaly view
#foreach (var currency in Model.ConversionRateItems)
{
<tr>
<td>
#currency.Name
</td>
<td>
#currency.Value
</td>
</tr>
}
I am new to C#, MVC, LINQ, Entity Framework, and everything else I'm doing. I have picked the stackoverflow brain-trust and other sites on this issue and do not see how to resolve it.
I am trying to implement the Repository pattern for a new application we are building and having problems returning the query results. The first and most pressing issue is that I am getting the error below, and the second is more a matter of figuring out how to handle empty results from the query.
For this issue, I am trying to get a list of requests from the database to be presented in the dashboard view. We have a working prototype that uses SQL queries, and I'm trying to replace it with repositories, which may be overkill, but it is something we feel we want to do.
Here is the view:
#using RAM.DAL.Models
#model RequestViewModel
#{
ViewBag.Title = "Dashboard";
}
<h1>Dashboard</h1>
<div class="col-md-4">
<h2>Resource Requests</h2>
<div class="panel">
<table class="table table-hover">
<thead>
<tr class="bg-primary">
<th>Number</th>
<th>Mission Title</th>
<th>Resource Requested</th>
<th>Person</th>
</tr>
</thead>
<tbody>
#if (Model.isEmpty)
{
<tr>
<td colspan="4">No requests pending</td>
</tr>
}
else
{
<tr onclick="location.href= '#Url.Action("Assignment", "Mission", new { id = Model.ID })'">
<td>#Model.ID</td>
<td>#Model.title</td>
<td>#Model.resourceTitle</td>
<td>#Model.userName</td>
</tr>
}
</tbody>
</table>
</div>
<!--<p><a class="btn btn-default" href="#">Content 1 Btn ยป</a></p>-->
</div>
Here is the ViewModel:
using System;
namespace RAM.DAL.Models
{
public class RequestViewModel
{
public int? ID { get; set; }
public string title { get; set; }
public string requestText { get; set; }
public string user_ID { get; set; } //The userID of the user being requested.
public string userName { get; set; } //Full name of the user being requested
public int? fromResourceID { get; set; } //The resource where the request was generated from
public int? toResourceID { get; set; } //The resource where the reassigned worker is requested to go to
public string resourceTitle { get; set; } //Title of the resource where the reassigned worker is requested to go to
public DateTime? requestDate { get; set; }//The date the request was made
public bool? isEmpty { get; set; }
}
}
And here is the repository up to the GetRequests method I'm having problems with (the rest is not implemented yet):
using RAM.DAL.Models;
using System;
using System.Linq;
using System.Data;
using System.Collections.Generic;
namespace RAM.DAL
{
public class RequestRepository : IRequestRepository<RequestViewModel>
{
private RAMcontext context;
public RequestRepository(RAMcontext context)
{
this.context = context;
}
public IEnumerable<RequestViewModel> GetRequests()
{
var requests = from r in context.RAM_Requests
join u in context.Users on r.user_ID equals u.User_ID
join res in context.RAM_Resources on r.toResourceID equals res.ID
where r.resolved == false
select new RequestViewModel()
{
title = r.title,
ID = r.ID,
fromResourceID = r.fromResourceID,
toResourceID = r.toResourceID,
user_ID = r.user_ID,
userName = u.First_Name + " " + u.Last_Name,
resourceTitle = res.title,
requestText = r.requestText,
requestDate = r.requestDate
};
/* }
catch
{
RequestViewModel empty = new RequestViewModel
{
isEmpty = true
};
return empty;
}*/
return requests.ToList().AsEnumerable();
}
The error I'm getting is:
The model item passed into the dictionary is of type
'System.Collections.Generic.List`1[RAM.DAL.Models.RequestViewModel]',
but this dictionary requires a model item of type
'RAM.DAL.Models.RequestViewModel'.
From the error message, I guess your action method is sending a collection of RequestViewModel to the view. But your view is strongly typed to a single instance of RequestViewModel , not a collection. That is the reason you are getting this error.
Since you want to show a collection of requests, you should change the view to be strongly typed to a collection. You can use the LINQ Any() method to determine whether you have more than one item in the collection passed to the view and show/hide a message/ table to display the data.
#model IEnumerable<RequestViewModel>
<h1>Dashboard</h1>
#if(!Model.Any())
{
<p>No records found </p>
}
else
{
<table>
<tr>
<th>Title</th>
<th>User name </th>
</tr>
#foreach(var req in Model)
{
<tr>
<td>#req.title</td>
<td>#req.userName</td>
</tr>
}
</table>
}
I'm still working on my asp.net app. I have a page where orders table is displayed and i would like to include "details"column, so someone can choose order and then look up it's details.
Here is my View code:
<table id="example" class="table table-striped table-bordered" cellspacing="0" width="100%">
<thead>
<tr>
<th>ID</th>
<th>Seat</th>
<th>Movie</th>
<th>Date</th>
<th>Details</th>
</tr>
</thead>
<tbody>
#foreach (var item in Model.OrderList.results)
{
<tr>
<td>#item.objectId</td>
<td>#item.Seat</td>
<td>LOTR</td>
<td>#item.createdAt</td>
<td>Details</td>
</tr>
}
</tbody>
</table>
Ofcourse Url.Action is just for testing. Here is my controller method:
public ActionResult Details(OrderModel model)
{
return View(model);
}
Results is a List of OrderModel objects. I'd like to pass one OrderModel object that corresponds with chosen table row. The whole point is to display OrderModel object contents on Details page. Can someone explain me how to do that?
EDIT: My model:
OrderModel
public class OrderModel
{
/*class representing Order data*/
public string Seat { get; set; }
public string objectId { get; set; }
public DateTime? createdAt { get; set; }
public DateTime? updatedAt { get; set; }
}
My root for ordermodel(needed for json deserialization to list of objects)
public class OrderRootModel
{
public List<OrderModel> results { get; set; }
}
My baseviewmodel orderlist line(viewmodel is shared along all sites - i'm using shared layout):
public OrderRootModel OrderList { get; set; }
EDIT2:
Alright so after looking at my code i modified it, so Details page recieves BaseViewModel instead of ordermodel(i'm using shared layout).
BaseViewModel:
public class BaseViewModel
{
public OrderModel Order { get; set; }
public OrderRootModel OrderList { get; set; }
}
OrdersController:
public ActionResult Details(OrderModel order)
{
BaseViewModel model = new BaseViewModel();
model.Order = order;
return View(model);
}
Alright guys. I think i didn't get enough sleep so my thought process was a little bit off. As #user1672994 suggested i can just pass order id to details view(if i remember right passing whole object between view and controller is not recommended).
So if anyone is interested here is a solution:
View:
#foreach (var item in Model.OrderList.results)
{
<tr>
<td>#item.objectId</td>
<td>#item.Seat</td>
<td>LOTR</td>
<td>#item.createdAt</td>
<td>Details</td>
</tr>
}
Controller:
public ActionResult Details(string id)
{
ApiModel data = new ApiModel();
BaseViewModel model = new BaseViewModel();
model.Order = data.GetOrderData(id);
return View(model);
}
Model:
public OrderModel GetOrderData(string id)
{
OrderModel model = new OrderModel();
string url = "https://api.parse.com/1/classes/Orders" + "/" + id;
model = JsonConvert.DeserializeObject<OrderModel>(getParseIdData(url));
return model;
}
It works perfect. Thank you guys.
I am having alot of difficult getting my viewmodel to work correctly. As a bit of context i am using a viewmodel to use two models from another project. These models contain the User information and thier chosen Device in a localdb. However i cannot currently use a view to display the data from both of those models on one view so i created a viewmodel.
However I am current recieving:
Error: 'System.Collections.Generic.IEnumerable' does not contain a definition for 'UserID' and no extension method 'UserID' accepting a first argument of type 'System.Collections.Generic.IEnumerable' could be found (are you missing a using directive or an assembly reference?)
This error is occurring for all of the model objects. If i can get around it it will be the first step to a functioning view model. Any help would be greatly appreciated.
User.cs - Model (in project: FaceToFace)
namespace FaceToFace.Model
{
public class User
{
public int UserID { get; set; }
public string CodeName { get; set; }
public bool UseBriefInstructions { get; set; }
public ICollection<RegimeItem> RegimeItems { get; set; }
public Device Device { get; set; }
public virtual ICollection<Grading> UserGradings { get; set; }
public User()
{
this.RegimeItems = new List<RegimeItem>();
Device = new Device();
}
}
public class RegimeItem
{
public int RegimeItemID { get; set; }
public Exercise RegimeExercise { get; set; }
}
}
Device.cs - Model (in project: FaceToFace)
namespace FaceToFace.Model
{
public class Device
{
public int DeviceID { get; set; }
public string Name { get; set; }
}
}
UserDeviceViewModel.cs (in project: FaceToFaceWebsite)
namespace FaceToFaceWebsite.Models
{
public class UserDeviceViewModel
{
public UserDeviceViewModel()
{
User = new User();
Devices = new List<SelectListItem>();
}
public User User { get; set; }
public IList<SelectListItem> Devices { get; set; }
}
}
PatientController.cs - Only a segment of the entire page to avoid spam (Project: FaceToFaceWebsite)
namespace FaceToFaceWebsite.Controllers
{
public class PatientController : Controller
{
private F2FData db = new F2FData();
public ActionResult Index()
{
var viewModel = new List<FaceToFaceWebsite.Models.UserDeviceViewModel>();
return View(viewModel);
}
}
}
Views/Patient/Index.cshtml (facetofacewebsite)
#model IEnumerable<FaceToFaceWebsite.Models.UserDeviceViewModel>
#*#model FaceToFaceWebsite.Models.UserDeviceViewModel*#
#*#model IEnumerable<FaceToFace.Model.User>*#
<h2>Your Patients</h2>
#*Showing #Model.Count() users*#
<p>#Html.ActionLink("Add New User", "Create")</p>
<table>
<tr>
<th>#Html.DisplayNameFor(model => model.UserID)</th>
<th>#Html.DisplayNameFor(model => model.CodeName)</th>
<th>#*#Html.DisplayNameFor(model => model.Device.Name)*#Device</th>
<th>#Html.DisplayNameFor(model => model.DeviceID)</th>
</tr>
#foreach (var item in Model)
{
<tr>
<td>#Html.DisplayFor(modelItem => item.UserID)</td>
<td>#Html.DisplayFor(modelItem => item.CodeName)</td>
<td>#Html.DisplayFor(modelItem => item.Name)</td>
<td>#Html.DisplayFor(modelItem => item.DeviceID)</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id = item.UserID }) |
#Html.ActionLink("Details", "Details", new { id = item.UserID }) |
#Html.ActionLink("Delete", "Delete", new { id = item.UserID })
</td>
</tr>
}
</table>
So what i REALLY need to know is that by using model properties from another project, what do i have to do differently. What am i currently doing wrong? what should i do so that the USER information and the DEVICE information can be show.
UPDATE
Thanks to Stephen Muecke, the solution to the issue of the index view not displaying the user db data was solved by changing the Action result in the index controller to:
public ActionResult Index()
{
var viewModel = db.Users.Select(u => new UserDeviceViewModel() { User = u, Device = u.Device }).ToList();
return View(viewModel);
}
UserDeviceViewModel contains a property named User not UserID (which is a property of User. Your loop needs to be
#foreach (var item in Model)
{
<tr>
<td>#Html.DisplayFor(m => item.User.UserID)</td>
<td>#Html.DisplayFor(m => item.User.CodeName)</td>
Note you table headers wont work in this case.
Note also you are not really using a true 'view model'. A view model contains only those properties which you need for display/edit in a view (not just for dumping other models). Based on the view code you have shown it should be something like
public class UserDeviceViewModel
{
public int UserID { get; set; }
public string CodeName { get; set; }
public int DeviceID { get; set; }
public IList<SelectListItem> Devices { get; set; }
}
Although you view contains a reference to property Name (not sure what this is - perhaps DeviceName?) and your view does not use Devices (have you omitted some of the view?)
Remove the Ienumerable!
#model FaceToFaceWebsite.Models.UserDeviceViewModel
Look in your controller:
public ActionResult Index()
{
var viewModel = new
FaceToFaceWebsite.Models.UserDeviceViewModel();
return View(viewModel);
}
You are passing List (IEnumerable<FaceToFaceWebsite.Models.UserDeviceViewModel>) , while your view code expected to be FaceToFaceWebsite.Models.UserDeviceViewModel
Well you could pass the Correct type of ViewModel to your View:
In your View you have:
#model IEnumerable<FaceToFaceWebsite.Models.UserDeviceViewModel>
And in Controller you have:
var viewModel = new FaceToFaceWebsite.Models.UserDeviceViewModel();
return View(viewModel);
Try passing a List of your ViewModel:
var viewModel = new List<FaceToFaceWebsite.Models.UserDeviceViewModel>();
return View(viewModel);
OR:
In your View change this:
#model IEnumerable<FaceToFaceWebsite.Models.UserDeviceViewModel>
To:
#model FaceToFaceWebsite.Models.UserDeviceViewModel
WHY are you getting that ERROR Message?
Because your ViewModel doesn't have UserId, CodeName etc.
BUT your User Class has UserId and CodeName
So In ViewModel you will access like this:
ViewModel.User.UserId and ViewModel.User.CodeName:
Like This:
<th>#Html.DisplayNameFor(model => model.User.UserID)</th>
<th>#Html.DisplayNameFor(model => model.User.CodeName)</th>
As per given code snippet, your View mapping and data model not sync up.
can you just follow below steps
clear all views.
first display only user level info
Verify user level info are you able to? next proceed further device level
put device level loop for your devices collection (make sure your Device collection model m not sure about your "SelectedListItem"
The goal
Iterating between two queries within a single view.
The problem
In my application there is a comparison list of a specific product. At the top of the page, there is details of this product as its name, min/max price, photo and some other details.
What I need is something like this:
#modelComparisonList List<MyApp.Models.Products>
#modelProduct MyApp.Models.Products
#{
ViewBag.Title = "Comparing " + modelProduct.name;
}
<h1>There is #modelProduct.quantity products to compare</h1>
<table>
#foreach (var item in modelComparisonList)
{
<tr>
<p>#item.productName</p>
</tr>
<tr>
<p>#item.productPrice</p>
</tr>
<tr>
<p>#item.marketName</p>
</tr>
}
</table>
Can you understand my case?
I don't know how to perform a solution to resolve this. Can someone give me an idea?
Just make a wrapper class that contains both, ie simply:
public class TheViewModel
{
public List<MyApp.Models.Products> Item1 { get; set; }
public MyApp.Models.Products Item2 { get; set; }
}
Yes you can do it by using a view model. View models are there to represent your data on the view. Your view model can be a "concatenation" from multiple models.
Below is a couple of tips as well regarding your classes and properties.
Your product domain model will look like this:
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public string MarketName { get; set; }
public decimal Price { get; set; }
// Other properties not mentioned above
}
Your view model could look something like this:
public class ProductViewModel
{
public IEnumerable<Product> Products { get; set; }
public Product Product { get; set; }
}
Your action method could look something like this:
public ActionResult Compare(int id)
{
ProductViewModel viewModel = new ProductViewModel
{
Product = productRepository.GetBuId(id),
Products = productRepository.GetAll()
};
return View(viewModel);
}
And your view could look something like this:
#model YourProject.ViewModels.Products.ProductViewModel
#{
ViewBag.Title = "Comparing " + #Model.Product.Name;
}
<h1>There is #Model.Product.Quantity products to compare</h1>
<table>
#foreach (var product in Model.Products)
{
<tr>
<td>#product.Name</td>
</tr>
<tr>
<td>#product.Price</td>
</tr>
<tr>
<td>#product.MarketName</td>
</tr>
}
</table>
I hope this helps.