get the name of model and title of another model in view - asp.net-mvc

i want to get the name of model genre and title of model list in the partial view but #genre.Lists.Title doesn't work
this is my genre model
public class Genre
{
public int GenreId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public List<List> Lists { get; set; }
}
and this is my List model
[Bind(Exclude = "ListId")]
public class List
{
[ScaffoldColumn(false)]
public int ListId { get; set; }
[DisplayName("Genre")]
public int GenreId { get; set; }
[DisplayName("Maker")]
public int MakerId { get; set; }
[Required(ErrorMessage = "An List Title is required")]
[StringLength(160)]
public string Title { get; set; }
[Required(ErrorMessage = "Price is required")]
[Range(0.01, 100.00,ErrorMessage = "Price must be between 0.01 and 100.00")]
public decimal Price { get; set; }
[DisplayName("List URL")]
[StringLength(1024)]
public string ListUrl { get; set; }
public Genre Genre { get; set; }
public Maker Maker { get; set; }
public virtual List<OrderDetail> OrderDetails { get; set; }
}
and this my actionResult
public ActionResult Navbar()
{
var genres = storeDB.Genres.Include("Lists").ToList();
return PartialView("Navbar",genres);
}
and this is my PartialView
#model IEnumerable<Store.Models.Genre>
#foreach (var genre in Model)
{
#genre.Name
#genre.Lists.Title
}

#genre.Lists is of type List<List>, not List (by the way, I would rename your class somehow, it's easy to confuse with the standard library class of this name).
So you either need another foreach loop to iterate over #genre.Lists or you can get the first element with #genre.Lists[0].Title. It's up to you what you actually want to achieve. For example, you could use string.Join:
#model IEnumerable<Store.Models.Genre>
#foreach (var genre in Model)
{
<text>
#genre.Name
#string.Join(", ", genre.Lists.Select(x => x.Title))
</text>
}
Or write some real HTML. Again, depends what you want your output to be.

Related

ASP.Net MVC and using Linq to Entities

I'm following an MVC 5 tutorial: http://www.c-sharpcorner.com/article/dashboard-application-with-asp-net-mvc-5-and-jquery/
but the Author left out 1 of the features - the middle panel - "Orders" (which is the orders for all customers). It has a View Details link but no code and partial view is displayed when clicked.
So I'm trying to do create that partial view but having trouble with writing the Linq To Entities.
I am trying for just a partial view that is like a header/multi detail:
CustomerName CustomerImage
OrderDate
Quantity ProductType, ProductName, ProductImage
OrderDate
Quantity ProductType, ProductName, ProductImage
CustomerName CustomerImage
OrderDate
Quantity ProductType, ProductName, ProductImage
Here is the ViewModels I created to represent the above:
public class OrderDetailsViewModel
{
public int Quantity { get; set; }
public string ProductType { get; set; }
public string ProductName { get; set; }
public string ProductImage { get; set; }
}
public class CustomerOrdersViewModel
{
public string CustomerName { get; set; }
public string CustomerImage { get; set; }
public DateTime OrderDate { get; set; }
public ICollection<OrderDetailsViewModel> OrderDetailsViewModel{ get;
set; }
}
Here is the DbContext and the models that the Author created:
DbContext:
public class DashboardContext : DbContext
{
// Constructor - inherits the base constructor.
public DashboardContext() : base("DashboardOrder")
{
}
public IDbSet<Customer> CustomerSet { get; set; }
public IDbSet<Order> OrderSet { get; set; }
public IDbSet<Product> ProductSet { get; set; }
public IDbSet<OrderDetails> OrderDetailSet { get; set; }
}
Customer:
public class Customer : IEntity
{
public Customer()
{
Orders = new List<Order>();
}
public int ID { get; set; }
public string CustomerName { get; set; }
public string CustomerEmail { get; set; }
public string CustomerPhone { get; set; }
public string CustomerCountry { get; set; }
public string CustomerImage { get; set; }
public virtual ICollection<Order> Orders { get; set; }
}
IEntity:
public interface IEntity
{
int ID { get; set; }
}
Order:
public class Order : IEntity
{
public Order()
{
OrderDetail = new List<OrderDetails>();
}
public int ID { get; set; }
public DateTime OrderDate { get; set; }
public virtual Customer Customer { get; set; }
public virtual ICollection<OrderDetails> OrderDetail { get; set; }
}
OrderDetails:
public class OrderDetails : IEntity
{
public int ID { get; set; }
public int Quatity { get; set; }
public virtual Order Order { get; set; }
public virtual Product Product { get; set; }
}
Product:
public class Product : IEntity
{
public Product()
{
OrderDetails = new List<OrderDetails>();
}
// Auto-implemented properties.
public int ID { get; set; }
public string ProductName { get; set; }
public decimal UnitPrice { get; set; }
public int UnitsInStock { get; set; }
public string ProductImage { get; set; }
public string ProductType { get; set; }
public virtual ICollection<OrderDetails> OrderDetails { get; set; }
}
Here is the Action Method in the DashboardController I was attempting to write.
I believe I want to read the Order Model which in turn has reference to the Customer and a list of Orders which in turn has a reference to the list of OrderDetail which has reference to the Product.
But I'm having a hard time with the "Linq to SQL" to get the data in the lists in the models to create my ViewModel to flatten it out and pass to my partial view as a list.
public ActionResult GetCustomerOrdersDetails()
{
List<CustomerOrdersViewModel> customerOrders = null;
using (DashboardContext _context = new DashboardContext())
{
// Using LINQ TO SQL and deferred execution via the "ToList".
customerOrders = (from o in _context.OrderSet
select new CustomerOrdersViewModel
{
CustomerName = o.Customer.CustomerName,
CustomerImage = o.Customer.CustomerImage,
OrderDate = o.OrderDate,
-- Here I need to process the list of Orders which in turn has a reference to the list of OrderDetail which has
reference to the Product.
ProductType = ?,
ProductName = ?,
ProductImage = ?,
Quantity = ?,
}).ToList();
}
return PartialView("~/Views/Dashboard/GetCustomerOrdersDetails.cshtml", customerOrders);
}
You first need a .GroupBy() clause to group the records by CustomerName, CustomerImage and OrderDate. Then because OrderDetail is a collection proeprty, you need a .SelectMany() to 'flatten that collection before projecting the result to your OrderDetailsViewModel model.
List<CustomerOrdersViewModel> model = db.OrderSet
.GroupBy(x => new { Name = x.Customer.CustomerName, Image = x.Customer.CustomerImage, Date = x.Date })
.Select(x => new CustomerOrdersViewModel
{
CustomerName = x.Key.Name,
CustomerImage = x.Key.Image,
OrderDate = x.Key.Date,
OrderDetailsViewModel = x.SelectMany(y => y.OrderDetail).Select(y => new OrderDetailsViewModel
{
ProductName = y.Product.ProductName,
Quantity = y.Quantity,
ProductType = y.Product.ProductType,
ProductImage = y.Product.ProductImage
}).ToList()
}).ToList();
return PartialView(model);
Then in the view you can use nested loops to display the details of each order
#model IEnumerable<CustomerOrdersViewModel>
....
#foreach(var order in Model)
{
.... // display details of customer name, date etc
#order.CustomerName
foreach(var item in order.OrderDetailsViewModel)
{
.... // display details of product, quantity etc for each order
#item.ProductName

How do I pass column values from multiple tables to the view in MVC?

I'm displaying the data from a table called gigs, however it contains a couple of foreign keys to tables 'Bands' and 'Venues' so when using this code in my controller,
string user = User.Identity.GetUserId();
var yourgigs = (from g in dbg.gigs
from v in dbg.Venues
from b in dbg.Bands
from ga in g.gigsaccasses
where (ga.Id == user &&
v.venueid == g.venueid &&
b.bandid == g.bandid)
select g);
return View(yourgigs);
it's displaying bandid and venueid in the view which are meaningless integers. How would I replace those with what I suppose would be b.bandname, v.venuename and also add v.address1 and v.city? The SQL statement that does this is
SELECT bands.bandname, venues.venuename, venues.address1, venues.city, gigs.whatdate, gigs.starttime
FROM gigs INNER JOIN
bands ON gigs.bandid = bands.bandid INNER JOIN
gigsaccass ON gigs.gigid = gigsaccass.gigid INNER JOIN
dbo.AspNetUsers ON gigsaccass.Id = dbo.AspNetUsers.Id INNER JOIN
venues ON gigs.venueid = venues.venueid
WHERE dbo.AspNetUsers.Id = //some user//
I did try using anonymous types as such:
var yourgigs = (from g in dbg.gigs
from v in dbg.Venues
from b in dbg.Bands
from ga in g.gigsaccasses
where (ga.Id == user &&
v.venueid == g.venueid &&
b.bandid == g.bandid
select new
{
bandname = b.bandname,
venuename = v.venuename,
address1 = v.address1,
city = v.city,
whatdate = g.whatdate,
starttime = g.starttime
});
But this then threw an error:
The model item passed into the dictionary is of type 'System.Data.Entity.Infrastructure.DbQuery1[<>f__AnonymousType76[System.String,System.String,System.String,System.String,System.DateTime,System.TimeSpan]]', but this dictionary requires a model item of type 'System.Collections.Generic.IEnumerable`1[OnStageTonight_MVC.Models2.gigs]'.
The View is expecting type 'gigs'
#model IEnumerable<OnStageTonight_MVC.Models2.gigs>
#{
ViewBag.Title = "Gigs";
}
<h2>Gigs</h2>
<p>
#Html.ActionLink("Create New", "Create")
</p>
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.venueid)
</th>
<th>
#Html.DisplayNameFor(model => model.bandid)
</th>
What am I missing?
EDIT:
I should add that I do have a model, but I'm assuming this is what is at fault.
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity;
using System.Linq;
using System.Web;
namespace OnStageTonight_MVC.Models2
{
[Table("gigs")]
public partial class gigs
{
public gigs()
{
this.gigsaccasses = new HashSet<gigsaccass>();
}
[Key]
public int gigid { get; set; }
public int venueid { get; set; }
public int bandid { get; set; }
[Display(Name="Date")]
public System.DateTime whatdate { get; set; }
[Display(Name="Starts at")]
public System.TimeSpan starttime { get; set; }
public virtual ICollection<gigsaccass> gigsaccasses { get; set; }
}
[Table("gigsaccass")]
public partial class gigsaccass
{
[Key]
public int gigaccassid { get; set; }
public int gigid { get; set; }
public string Id { get; set; }
public virtual gigs gig { get; set; }
public virtual AspNetUsers AspNetUser { get; set; }
}
[Table("dbo.AspNetUsers")]
public class AspNetUsers
{
[Key]
public string Id { get; set; }
public string Email { get; set; }
public bool EmailConfirmed { get; set; }
public string PasswordHash { get; set; }
public string SecurityStamp { get; set; }
public string PhoneNumber { get; set; }
public bool PhoneNumberConfirmed { get; set; }
public bool TwoFactorEnabled { get; set; }
public Nullable<System.DateTime> LockoutEndDateUtc { get; set; }
public bool LockoutEnabled { get; set; }
public int AccessFailedCount { get; set; }
public string UserName { get; set; }
public string YourName { get; set; }
public List<gigsaccass> gigsaccasses { get; set; }
}
[Table("venues")]
public partial class venues
{
[Key]
public int venueid { get; set; }
[Required]
[Display(Name = "Venue")]
public string venuename { get; set; }
[Required]
[Display(Name = "Address")]
public string address1 { get; set; }
[Required]
[Display(Name = "City")]
public string city { get; set; }
public List<gigs> venuegigs { get; set; }
}
[Table("bands")]
public class bands
{
[Key]
public int bandid { get; set; }
[Required]
[Display(Name = "Name")]
public string bandname { get; set; }
public List<gigs> bandgigs { get; set; }
}
public partial class gigscontext : DbContext
{
public gigscontext()
: base("DefaultConnection")
{
}
public DbSet<gigs> gigs { get; set; }
public DbSet<gigsaccass> gigsaccass { get; set; }
public DbSet<AspNetUsers> AspNetUsers { get; set; }
public DbSet<venues> Venues { get; set; }
public DbSet<bands> Bands { get; set; }
}
}
You can't use anonymous classes here. Your view needs to know how to work with model, it needs type information, but: "The type name is generated by the compiler and is not available at the source code level"
You can't pass such objects around. http://www.codeproject.com/Articles/15624/Inside-C-Anonymous-Methods#5
You need to create class that represent row in your data set and return list of populated objects.
As less preferred alternative you can use dynamic: https://msdn.microsoft.com/en-us/library/dd264736.aspx
The M in MVC stands for model, and what you want is one of the 3 main tenants of MVC.
You want an object that encapsulates all the information to be displayed on the view.
It is considered best practice to create a model for the view. This is an additional layer which separates the storage of the item (your entity model) from it's presentation.
var yourgigs = (from g in dbg.gigs
from v in dbg.Venues
from b in dbg.Bands
from ga in g.gigsaccasses
where (ga.Id == user &&
v.venueid == g.venueid &&
b.bandid == g.bandid
select new GigViewModel
{
bandname = b.bandname,
venuename = v.venuename,
address1 = v.address1,
city = v.city,
whatdate = g.whatdate,
starttime = g.starttime
});
public class GigViewModel
{
public string bandname { get; set; }
public string venuename { get; set; }
public string address1 { get; set; }
public string city { get; set; }
public DateTime whatdate { get; set; }
public Timespan starttime { get; set; }
}
When persisting data from your view models, use AutoMapper or something similar to copy properties with matching names between your view models and your entity models.
Use the new view model in your view:
#model IEnumerable<OnStageTonight_MVC.Models2.GigModelView>

Model count = null

I'm rewriting this question:
I have 2 models. Entry and Topic.
public class Entry
{
public int EntryId { get; set; }
public int UserId { get; set; }
public int TopicId { get; set; }
public String EntryQuestion { get; set; }
public String EntryAnswer { get; set; }
public int EntryReview { get; set; }
public String QuestionValidationURL { get; set; }
public virtual ICollection<Topic> TopicList { get; set; }
}
public class Topic
{
public int TopicId { get; set; }
public String TopicName { get; set; }
}
I followed an example on ASP.Net/MVC to set up my models this way.
What I would like to do is for every entry item I have a TopicId, but then I'd like to convert that to a TopicName by accessing my TopicList.
My question is, how do I load TopicList?
In the examples I'm following I'm seeing something about LazyLoading and EagerLoading, but it doesn't seem to be working.
I tried doing the following from my Entry controller:
db.Entries.Include(x => x.TopicList).Load();
But that still gives me a TopicList of 0 (which is better than null)
How can I do this?
In my view I'm binding to the Entries like this:
#model IEnumerable<projectInterview.Models.Entry>
I would like to access the TopicList here:
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.TopicId)
</td>
...
</tr>
I'd like to use the TopicId in this loop and display the TopicName that is part of the object in the collection.
I'm assuming you're following an Entity Framework example. You're trying to create a one-to-many relationship, as far as I can tell, although I'm unsure about which end is which.
In the general case, to establish a one-to-many relationship, you have to do something like this:
public class One
{
[Key]
public int Id { get; set; }
public virtual ICollection<Many> Many { get; set; }
}
public class Many
{
[Key]
public int Id { get; set; }
[ForeignKey("One")]
public int OneId { get; set; }
public virtual One One { get; set; }
}
If what you're trying to do is have one Entry relating to many Topic objects, then you're almost there but you're lacking something.
For the ICollection<Topic> to actually contain anything, the (many) Topic objects need to have a foreign key to the (one) Entry. (It also doesn't hurt to explicitly mark the primary key on both sides, rather than relying on the EF conventions.)
public class Topic
{
[Key]
public int TopicId { get; set; }
public String TopicName { get; set; }
[ForeignKey("Entry")]
public int EntryId { get; set; }
public virtual Entry Entry { get; set; }
}
public class Entry
{
[Key]
public int EntryId { get; set; }
public int UserId { get; set; }
public int TopicId { get; set; }
public String EntryQuestion { get; set; }
public String EntryAnswer { get; set; }
public int EntryReview { get; set; }
public String QuestionValidationURL { get; set; }
public virtual ICollection<Topic> TopicList { get; set; }
}
Now TopicList should be an actual and populated collection, without the need to do an Include.
If, on the other hand, you want one Topic relating to many Entry objects, then you have it a little backwards. The correct way would be:
public class Topic
{
[Key]
public int TopicId { get; set; }
public String TopicName { get; set; }
public virtual ICollection <Entry> Entries { get; set; }
}
public class Entry
{
[Key]
public int EntryId { get; set; }
public int UserId { get; set; }
public String EntryQuestion { get; set; }
public String EntryAnswer { get; set; }
public int EntryReview { get; set; }
public String QuestionValidationURL { get; set; }
[ForeignKey("Topic")]
public int TopicId { get; set; }
public virtual Topic Topic { get; set; }
}
In this case, you may or may not use db.Entries.Include(x => x.Topic) depending on whether you want them loaded all at once or one-by-one on demand. Regardless of what you choose, the following expression should return the proper value:
myEntry.Topic.TopicName
If I understand you correctly you have added the list of Topics to the Entry just to get the name of the topic when displaying the entry. The best way to do this is to actually have a Topic property in your entry model. So your model would look like this:
public class Entry
{
public int EntryId { get; set; }
public int UserId { get; set; }
public int TopicId { get; set; }
public String EntryQuestion { get; set; }
public String EntryAnswer { get; set; }
public int EntryReview { get; set; }
public String QuestionValidationURL { get; set; }
//Change this.....
public virtual Topic Topic { get; set; }
}
Then in your view you would use (assuming the Model is an IEnumerable):
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => modelItem.Topic.TopicName )
</td>
...
</tr>
This link has a great example of how to do this:
http://weblogs.asp.net/manavi/archive/2011/03/28/associations-in-ef-4-1-code-first-part-2-complex-types.aspx
In my opinion problem is with casting. In view you have IEnumerable<projectInterview.Models.Entry> while Topics is ICollection<Topic>, which is a collection of different type
Topics = null means there are no Topics in the list to iterate over. How do you fill them? Your view expects IEnumerable how do you cast your topics to the entries?
Based on the original question I've added a small working example, maybe it helps you to find your bug.
Controller:
public class TestController : Controller
{
public ActionResult Index()
{
var viewModel = new ViewModel()
{
Topics = new List<Topic>()
};
viewModel.Topics.Add(new Topic() { header = "test" });
viewModel.Topics.Add(new Topic() { header = "test2" });
return View(viewModel);
}
}
Model:
public class ViewModel
{
public virtual ICollection<Topic> Topics { get; set; }
public int getCount()
{
return Topics.Count;
}
}
public class Topic
{
public string header { get; set; }
}
View:
#model testProject.Models.ViewModel
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
#Model.getCount()
#foreach(var item in Model.Topics)
{
<div>#item.header</div>
}
Output:
Index
2
test
test2
It seems that you are not initializing your Topics anywhere in the code. If the collection is null it means it is not initialized. If you instantiate it with
ICollection<Topic> Topics = new List<Topic>();
Once initialized you should receive zero when calling Topics.Count. If you do not make a call to a database it will stay zero.
In your case check whether you are instantiating the Topics.

EF code first: using Linq with many-to-many relationship

I am using MVC 4 with EF code first approach. I have two simple objects. These are their POCO classes:
public class Activity
{
//Primitive Properties
[HiddenInput]
public int Id { get; set; }
[Required]
public int LengthInMinutes { get; set; }
public string AdditionalInfo { get; set; }
[Required]
public bool Archive { get; set; }
//Navigation Properties
public virtual User User { get; set; }
public virtual ActivitySet ActivitySet { get; set; }
public virtual ICollection<Company> Companies { get; set; }
public virtual ICollection<Description> Descriptions { get; set; }
}
public class Company
{
//Primitive Properties
[HiddenInput]
public int Id { get; set; }
[Required]
public string Title { get; set; }
[Required]
public bool Archive { get; set; }
//Navigation Properties
public virtual ICollection<Activity> Activities { get; set; }
}
Now, I have generic list of activities which I iterate through using foreach loop. While looping I want to write a name for each Company related to the activity from the list. This is a code I came up with:
#foreach (Activity a in Model)
{
<p>#a.Companies.Where(d => d.Activities.FirstOrDefault(y => y.Id == a.Id)).Single()</p>
}
Unfortunately it gives me compilation error when I build the project. How can I then access details of the elements with many-to-many relationship
You could try it like so:
#foreach (Activity a in Model)
{
<p>#string.Join(",", a.Companies.Select(c => c.Title))</p>
}
It should give a list of all company titles of a given activity separated by a comma.

Help with displaying master detail data into mvc3 view

I have the following models:
public class Page
{
public int PageID { get; set; }
public string Name { get; set; }
public string Content { get; set; }
public DateTime? DateCreated { get; set; }
public bool IsPublished { get; set; }
public string ModifiedBy { get; set; }
public DateTime? DateModified { get; set; }
public int UserID { get; set; }
public int CategoryID { get; set; }
public virtual User User { get; set; }
public virtual Category Category { get; set; }
}
public class Category
{
public int CategoryID { get; set; }
[Required(ErrorMessage = "Category name is required.")]
[Display(Name = "Category Name")]
public string Name { get; set; }
public virtual ICollection<Page> Pages { get; set; }
}
and I want to populate this navigation list:
<div id="centeredmenu" class="nav-border nav-color">
<ul>
#foreach (var pages in Model)
{
<li>CATEGORY NAME GOES HERE
<ul>
#foreach (var pages in Model)
{
<li>PAGE NAMES GO HERE</li>
}
</ul>
</li>
}
</ul>
</div>
but I'm having problems implementing the controller. I tried this ViewModel:
public class MainPageModels
{
public Category Categories { get; set; }
public Page Pages { get; set; }
}
but it just confused me even more with this error message:
System.Data.Edm.EdmEntityType: : EntityType 'MainPageModels' has no key defined. Define the key for this EntityType.
System.Data.Edm.EdmEntitySet: EntityType: EntitySet �MainModels� is based on type �MainPageModels� that has no keys defined.
This is my controller:
public ActionResult Index()
{
var pages = db.MainModels.Select(p => p.Pages).Select(c => c.Category);
return View(pages);
}
I may be missing something simple here.
Posting this here for the code/syntax
public class Person
{
[Key]
public int PersonID { get; set; }
public string Name { get; set; }
public string LastName { get; set; }
}
public class DataContext : DbContext
{
EntitySet<Person> Persons { get; set; }
}
Your View Model can then do the following
public class PersonAddViewModel
{
public string Name { get; set; }
public string LastName { get; set; }
public void CreateViewModelFromDataModel(Person person)
{
this.Name = person.Name;
this.LastName = person.LastName ;
}
}
This is just an example, just to show the difference between a Data Model and a View Model
Your View would then be a strongly typed view of PersonAddViewModel
Here my solution to my parent-child list problem:
I created a ViewModel to house both my categories and pages:
public class HomeViewModels
{
[Key]
public int HomeViewKey { get; set; } //This is a MUST!
public IEnumerable<Category> ViewCategories { get; set; }
public IEnumerable<Page> ViewPages { get; set; }
public void CreateHomeViewModel(IEnumerable<Category> categories,
IEnumerable<Page> pages)
{
this.ViewCategories = categories;
this.ViewPages = pages;
}
}
Then edited my controller to populate the viewmodel:
public ActionResult Index()
{
HomeViewModels homePages = new HomeViewModels();
homePages.CreateHomeViewModel(db.Categories.ToList(),
db.Pages.ToList());
return View(homePages);
}
and finally creating the ul-li lists with the following:
#{var hvCategories = Model.ViewCategories;}
#foreach (var categories in hvCategories)
{
<li>#Html.ActionLink(categories.Name, "Index", "Home")
<ul>
#{var hvPages = Model.ViewPages
.Where(p => p.CategoryID == categories.CategoryID);}
#foreach (var pages in hvPages)
{
<li>#Html.ActionLink(pages.Name, "Index", "Home")</li>
}
</ul>
</li>
I hope this helps anyone who plans to build a nested list using a parent-child model. This took me two days to figure out. Cheers!

Resources