Access Model List Used in Another Model to view - asp.net-mvc-4

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..

Related

Asp Core Filter & Search Data Using Drop Downs & Search Bars

I would like users to search through pets using a search bar or drop down lists. I tried using viewbag and asp tag helpers but I keep getting errors. Below is a picture of what i'm going for. Any help is appreciated.
Model
public class Reptile
{
public int ReptileId { get; set; }
public string Name { get; set; }
public string Age { get; set; }
[Display(Name ="Reptile's Image")]
public byte[] Image { get; set; }
[Display(Name ="Food Requirements")]
public string FoodReq { get; set; }
[Display(Name="Habitat Requiremtns")]
public string HabitatReq { get; set; }
public string Gender { get; set; }
public string Type { get; set; }
public string Size { get; set; }
public string Color { get; set; }
[Display(Name="Recent Checkup")]
public bool RecentCheckup { get; set; }
public bool Trained { get; set; }
public bool Neutered { get; set; }
public bool Declawed { get; set; }
[Display(Name = "Good With Other Reptiles")]
public bool GoodWithRept { get; set; }
[Display(Name = "Good With Kids")]
public bool GoodWithKids { get; set; }
public ApplicationUser ApplicationUser { get; set; }
public int ApplicationUserId { get; set; }
}
Controller
public async Task<IActionResult> Index(string searchString)
{
var reptiles = from r in _context.Reptiles
select r;
if (!string.IsNullOrEmpty(searchString))
{
reptiles = reptiles.Where(r => r.Type.Contains(searchString));
}
return View(await reptiles.ToListAsync());
}
View
<form asp-controller="Reptiles" asp-action="Index" method="get">
<div class="form-actions no-color">
<p>
Search By Type: <input type="text" name="SearchString" />
<input type="submit" value="Filter" class="btn btn-default" /> |
<a asp-action="Index">Back to Full List</a>
</p>
</div>
</form>
I've been trying to follow the docs here Tutorial: Add sorting, filtering, and paging - ASP.NET MVC with EF Core. Not having any luck though.
Here is a simple demo to show how to use searchstring:
Controller:
public IActionResult Index(string searchString)
{
IEnumerable<Reptile> list = new List<Reptile> { new Reptile { Type = "t1", Name= "Reptile1" }, new Reptile { Type = "t2", Name = "Reptile2" }, new Reptile { Type = "t3", Name = "Reptile3" } };
ViewData["CurrentFilter"] = searchString;
if (!String.IsNullOrEmpty(searchString))
{
list = list.Where(s => s.Name.Contains(searchString));
}
return View(list);
}
View:
Find by name:
|
Back to Full List
<table>
<thead>
<tr>
<th>
#Html.DisplayNameFor(model => model.Name)
</th>
<th>
#Html.DisplayNameFor(model => model.Type)
</th>
<th></th>
</tr>
</thead>
<tbody>
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.Name)
</td>
<td>
<input type="text" asp-for="#item.Type">
</td>
</tr>
}
</tbody>
</table>
result:
Okay, I figured out how to use select to filter the reptile page by using the data users already added to the database from the properties in the model. I had to create a view model and add the Reptile model to it.
View Model
public class ReptileGenderViewModel
{
public Reptile Reptile { get; set; }
public List<Reptile> reptiles;
public SelectList genders;
public string reptileGender { get; set; }
}
Reptile Controller
public async Task<IActionResult> Index(string searchString, string reptileGender)
{
IQueryable<string> genderQuery = from g in _context.Reptiles
orderby g.Gender
select g.Gender;
var reptiles = from r in _context.Reptiles
select r;
if (!string.IsNullOrEmpty(searchString))
{
reptiles = reptiles.Where(r => r.Type.Contains(searchString));
}
if (!string.IsNullOrEmpty(reptileGender))
{
reptiles = reptiles.Where(g => g.Gender == reptileGender);
}
var reptileGenderVM = new ReptileGenderViewModel();
reptileGenderVM.genders = new SelectList(await genderQuery.Distinct().ToListAsync());
reptileGenderVM.reptiles = await reptiles.ToListAsync();
return View(reptileGenderVM);
}
View
<select asp-for="reptileGender" asp-items="Model.genders">
<option value="">All</option>
</select>

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?

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");

Two models in a view (with foreach)

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>

MVC ViewModel errors

Goal: To create a re-usable drop down menu that lists my website's administrators, managers and agents. These types of users are defined by the .NET Simplemembership webpages_Roles and webpages_UsersInRoles tables.
So Far:
I have a UserProfile table in my database which has 25 columns. I have a corresponding domain model of the same name which is accessed from my UsersContext() EF.
The drop down menu only needs to list the User's FirstName, LastName and UserId so instead of working with the complete domain model, I created the following ViewModel:
namespace MyModels.Models.ViewModels
{
public class AdminsAndAgentsListVM
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int UserId { get; set; }
}
}
I then added the following to my Account controller (notice I'm not working with partial view yet):
public ActionResult AdminsAndAgentsList()
{
UsersContext _db = new UsersContext(); //provides me access to UserProfiles data
var admins = Roles.GetUsersInRole("Admin"); //gets users with this role
var viewModel = _db.UserProfiles
.Where(x => admins.Contains(x.UserName)); //Selects users who match UserName list
return View(viewModel);
}
I then scaffold a list view and base it on the strongly typed ViewModel:
#model IEnumerable<MyModels.Models.ViewModels.AdminsAndAgentsListVM>
#{
ViewBag.Title = "AdminsAndAgentsList";
}
<h2>AdminsAndAgentsList</h2>
<p>
#Html.ActionLink("Create New", "Create")
</p>
<table>
<tr>
<th>
#Html.DisplayNameFor(model => model.FirstName)
</th>
<th>
#Html.DisplayNameFor(model => model.LastName)
</th>
<th>
#Html.DisplayNameFor(model => model.UserId)
</th>
<th></th>
</tr>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.FirstName)
</td>
<td>
#Html.DisplayFor(modelItem => item.LastName)
</td>
<td>
#Html.DisplayFor(modelItem => item.UserId)
</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 do a successful build and when I run the web page I get the following error:
The model item passed into the dictionary is of type'System.Data.Entity.Infrastructure.DbQuery1[My.Models.UserProfile]',
but this dictionary requires a model item of type
'System.Collections.Generic.IEnumerable1[My.Models.ViewModels.AdminsAndAgentsListVM]'.
If I recreate the view but strongly type it agains the UserProfile, it works fine. So how to re work this so I can strongly type against my ViewModel instead? Please provide examples if possible. I am new to C# and MVC and really benefit from the seeing the code first hand. Much appreciate the help!
EDIT -----------------------------
Here is the object for the UserProfile:
public class UsersContext : DbContext
{
public UsersContext()
: base("DefaultConnection")
{
}
public DbSet<UserProfile> UserProfiles { get; set; }
}
[Table("UserProfile")]
public class UserProfile
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
public string UserName { get; set; }
[Required]
[ReadOnly(true)]
[DisplayName("SubscriberID")]
public int? SubscriberId { get; set; } //Foreign key
[StringLength(50, ErrorMessage = "The {0} must be at least {2} characters long.")]
[Display(Name = "First Name")]
public string FirstName { get; set; }
[StringLength(50, ErrorMessage = "The {0} must be at least {2} characters long.")]
[Display(Name = "Last Name")]
public string LastName { get; set; }
//public DateTime DOB { get; set; }
[DataType(DataType.Date)]
public DateTime? DOB { get; set; } //This allows null
public bool? Gender { get; set; }
[Required]
[MaxLength(250)]
[EmailAddress]
public string Email { get; set; }
[MaxLength(250)]
[EmailAddress]
[NotEqualTo("Email", ErrorMessage = "Alt Email and Email cannot be the same.")]
public string AltEmail { get; set; }
[MaxLength(250)]
[EmailAddress]
public string FormEmail { get; set; }
public Address Address { get; set; }
[MaxLength(20)]
public string Telephone { get; set; }
[MaxLength(20)]
public string Mobile { get; set; }
[Required]
[DataType(DataType.Date)]
public DateTime DateAdded { get; set; }
[DataType(DataType.DateTime)]
public DateTime? LastLoginDate { get; set; }
public bool? OffersOptIn { get; set; } //any offers we may have with us or partners
public bool? NewsOptIn { get; set; } //newsletter
public bool? SubscriptionOptIn { get; set; } //account, technical, renewal notices, pp invoices, pp receipts
public bool? OrderOptIn { get; set; } //orders - workflow notices
[DataType(DataType.DateTime)]
public DateTime? LastUpdatedAccountDate { get; set; } //Last time user updated contact info
}
Try this. It will cast your query into your view model.
var viewModel = _db.UserProfiles
.Where(x => admins.Contains(x.UserName))
.Select(x => new AdminsAndAgentsListVM {
FirstName = x.FirstName,
LastName = x.LastName,
UserId = x.UserId});
You're passing the view your query, not your model.
Execute the query as you have it
var query = _db.UserProfiles
.Where(x => admins.Contains(x.UserName));
Then instantiate and populate your view model
var viewModels = new List<AdminsAndAgentsListVM>();
foreach (var item in query)
{
var viewModel = new AdminsAndAgentsListVM();
viewodel.FirstName = item.FirstName;
viewodel.LastName = item.LastName;
viewodel.UserId = item.UserId;
viewModels.Add(viewModel);
}
return View(viewModels);
This assumes, of course, that a UserProfile and AdminsAndAgentsListVM have matching properties.
Change your return line to:
return View(viewModel.AsEnumerable());
You aren't selecting your ViewModel. You need to do a Select(x => new AdminsAndAgentsListVM on your query. I would also do ToList() on there.