I'm using ASP.NET Core 2.2. I have 2 models and a viewmodel which injects data to a view. I want to order results based on their productType. Let's make it clear
This is Product model
public class Product
{
public int ProductID { get; set; }
public int ProductName { get; set; }
public string ProductImage { get; set; }
public int ProductTypeID { get; set; }
[ForeignKey("ProductTypeID")]
public virtual ProductType ProductType{ get; set; }
}
This is ProductType model
public class ProductType
{
public int ProductTypeID { get; set; }
public int ProductTypeName { get; set; }
public string ProductTypeImage { get; set; }
public string ProductTypeDescription { get; set;
}
And finally this is DishesViewModel
public class DishesVM
{
public IEnumerable<ProductType> ProductType { get; set; }
public IEnumerable<Product> Product { get; set; }
}
In MyController I get data from DB then with automapper, map them to DishViewModel
public class HomeController : Controller
{
public IActionResult Dishes()
{
var productTypes= _context.ProductType.OrderBy(p =>p.ProductTypeID).ToList();
var products= _context.Products.OrderBy(p => p.ProductID).ToList();
var DishesVM = new DishesVM();
DishesVM.ProductType = _mapper.Map<IEnumerable<ProductType>>(productTypes);
DishesVM.Product = _mapper.Map<IEnumerable<Product>>(products);
}
}
Now in Dishes View I can have nested foreach
#model DishesViewModel
<div>
foreach(var pt in Model.ProductType)
{
<h1>pt.ProductTypeName</h1>
foreach(var p in Model.Product)
{
p.ProductName
}
}
</div>
This works fine but the only problem it has, is it returns all products. but I want each Product Category has its Products In front of its header. This is visual representation of what I want and what I have now.
This is what I want
But this is what I have
You have to filter your products by product type in each iteration. At the moment you just display all products for each product type:
<div>
foreach(var type in Model.ProductType)
{
//products which belong to the particular type
var productsForType = Model.Product.Where(x => x.ProductTypeID == type.ProductTypeID);
<h1>pt.ProductTypeName</h1>
foreach(var product in productsForType)
{
product.ProductName
}
}
</div>
This will give you a jump on the linq statement:
var list = productTypes.Where(x => x.ProductTypeID == 1).Select(x => new Product()
{
ProductImage = x.ProductTypeImage,
}).ToList();
You will need to decide on what to put into the where clause, I'm using ProductTypeID
Related
I have developed a Site using asp net mvc 6, I have used enom as categories, now I need to get product count under each enom value,
Here is my enum look like,
namespace ecom.Data
{
public enum BookCategory
{
Action,
Comedy,
Drama,
Others
}
}
What I want is get products count under this enoms,
See here, Front view without product count
This is my Controller Looks like,
using ecom.Data.Services;
namespace ecom.Controllers;
public class HomeController : Controller
{
private readonly IBooksService _service;
public async Task <IActionResult> BookView(string slug)
{
var data = await _service.GetBookBySlugAsync(slug);
if(data == null) return View("NotFound");
return View("BookView", data);
}
This is model which i use as product model,
namespace ecom.Models
{
public class Book:IEntityBase
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public string Slug { get; set; }
public string Description { get; set; }
public double Price { get; set; }
public string Image { get; set; }
public string PublishDate { get; set; }
//enom
public BookCategory BookCategory { get; set; }
//Relationships
public List<Writter_Book> Writter_Books { get; set; }
//Publisher
public int PublisherId { get; set; }
[ForeignKey("PublisherId")]
public Publisher Publisher { get; set; }
}
}
My cshtml view right now,
#foreach (var cat in Html.GetEnumSelectList<BookCategory>())
{
<li>#cat.Text <span>()</span></li>
}
I want to get product count to above inside foreach's span tag,
Anyone can help me with that??
You can use GroupBy and Count to achieve your goal.
Something like
myBooksDataSource
.GroupBy(book => book.BookCategory)
.Select(group => new { Category = group.Key, Count = group.Count() });
Which will output a collection of (BookCategory, Count).
{ BookCategory.Action, 20 }
{ BookCategory.Comedy, 204 }
You can create a class to hold that data if you need to pass it around.
public class BookCategoryCountData
{
public BookCategory Category { get; set; }
public int Count { get; set; }
}
and select using that object.
public IEnumerable<BookCategoryCountData> GetBookCountByCategory()
{
return _context.Books
.GroupBy(book => book.BookCategory)
.Select(group => new BookCategoryCountData() {
Category = group.Key,
Count = group.Count()
});
}
I have three level (Category- Subcategory - Nestedcategory) dropdown navigation menu on my website for which data must come dynamically from database. My main problem in generation of InvokeAsync() method to make it work. I can write two levels which work fine as I checked, but confused in defining Nestedcategories - need to get it from subcategories which derived from categories.
Here is my Controller
public class MenuViewComponent: ViewComponent
{
private readonly SamirDbContext _samirDbContext;
public MenuViewComponent(SamirDbContext samirDbContext)
{
_samirDbContext = samirDbContext;
}
public async Task<IViewComponentResult> InvokeAsync()
{
var menu = await _samirDbContext.Categories.Include(x => x.Subcategories).ThenInclude(y => y.NestedCategories).
Select(x => new MenusModel()
{
Category = x,
Id = x.Id,
Subcategories = x.Subcategories,
**NestedCategories = ...**
}).ToListAsync();
return View(menu);
}
}
Here are models:
public class Category
{
public Category()
{
Subcategories = new HashSet<Subcategory>();
}
public int Id { get; set; }
public string Name { get; set; }
public ICollection<Subcategory> Subcategories { get; set; }
}
public class Subcategory
{
public Subcategory()
{
Posts = new HashSet<Post>();
NestedCategories = new HashSet<NestedCategory>();
}
public int Id { get; set; }
public string Name { get; set; }
public Category Category { get; set; }
public int CategoryId { get; set; }
public ICollection<Post> Posts { get; set; }
public ICollection<NestedCategory> NestedCategories { get; set; }
}
public class NestedCategory
{
public NestedCategory()
{
Posts = new HashSet<Post>();
}
public int Id { get; set; }
public string Name { get; set; }
public ICollection<Post> Posts { get; set; }
public Subcategory Subcategory { get; set; }
public int SubcategoryId { get; set; }
}
Menu ViewModel
public class MenusModel
{
public int Id { get; set; }
public Category Category { get; set; }
public IEnumerable<Category> Categories { get; set; }
public IEnumerable<Subcategory> Subcategories { get; set; }
public Subcategory Subcategory { get; set; }
public IEnumerable<NestedCategory> NestedCategories { get; set; }
public NestedCategory NestedCategory { get; set; }
}
Please, help in completion InvokeAsyinc() method in order to get work for 3 level menu.
You can use SelectMany() method, change the linq like below:
var menu = await _samirDbContext.Categories
.Select(x => new MenusModel()
{
Category = x,
Id = x.Id,
Subcategories = x.Subcategories,
NestedCategories = x.Subcategories.SelectMany(s => s.NestedCategories).ToList()
}).ToListAsync();
Looking at your models for Category, Subcategory, and NestedCategory, I'm asking myself why you actually could need to have a separate output property (**NestedCategories = ...**) in your final Select statement.
Let's think this way if the NestedCategory is defined inside the Subcategory collection, then every Subcategory element should have its own list of NestedCategory-ies, which will be available when you will check some Subcategory from the dropdown.
So, my advice here is to leave the result as follows:
var menu = await _samirDbContext.Categories
.Include(x => x.Subcategories)
.ThenInclude(y => y.NestedCategories)
.Select(x => new MenusModel()
{
Category = x,
Id = x.Id,
Subcategories = x.Subcategories
.Select(sb => new SubcategoryDTO
{
sb.Id,
sb.Name,
...
NestedCategories = sb.NestedCategories
.Select(nst => new NestedCategoriesDTO
{
nst.Id,
nst.Name,
...
})
}),
}).ToListAsync();
Then you can use the above model in your UI.
Hope this will help ))
I have the following model;
public class Object
{
public int Id { get; set; }
public string Name { get; set; }
public Nullable<double> Price { get; set; }
public string Description { get; set; }
public string Image { get; set; }
public Nullable<int> Type { get; set; }
}
and i create a view model;
public class PlanBaseTypedObjects
{
public int? Id { get; set; }
public IEnumerable<Object> ObjectDetails { get; set; }
}
in my controller i did grouping as follows;
var model = model1.GroupBy(t => t.Type).Select(g => new PlanBaseTypedObjects
{
Id = g.Key,
ObjectDetails = g
});
How can i get the number of records that belong to a particular "Type"??
for an example for type 1 how many records under ObjectDetails??
and can i access the "objectDetails" directly??
for an example if i want to know the "Name" of Id=3 of Type 2.how can i get it??
You need to iterate on the grouping result to find the count for each one.
foreach(var group in model)
{
int groupCount = group.ObjectDetails.Count();
}
If you want to access the items in ObjectDetails you need another foreach:
foreach(var group in model
{
int groupCount = group.ObjectDetails.Count();
foreach(var item in group.ObjectDetails))
{
//do something with item
}
}
I used scaffolding to create the Index, Details, Create, Edit and Delete views and the controller. I have two view models (Parent / Child) relation. In my Index view I want to display the list of Teams as well as some information on the players (Parent / child). For example I want to display in the Index view the teams with the players count per team and last players that was modified. I am not sure where to begin.
Example:
(Team) Red -- (Last Modified) 01/02/2015 -- (Number Players) 10 and so on.
Team ViewModel
public class TeamVM
{
public int ID { get; set; }
public string Name { get; set; }
public DateTime? LastActivity { get; set; }
public string NumberPlayers { get; set; }
public IList<PLayerVM> PlayerVM { get; set; }
}
Player ViewModel
public class PlayerVM
{
public int ID { get; set; }
public int TeamID { get; set; }
public string PlayerInfo { get; set; }
public DateTime? CreateDate { get; set; }
}
Other ViewModel
public class TeamViewModel
{
public List<Team> Teams{ get; set; }
}
Controller
public ActionResult Index()
{
TeamViewModelviewModel = new TeamViewModel();
viewModel.Teams= db.Teams.ToList();
return View(viewModel);
}
db.Products.ToList()?? I assume that is where you mean db.Teams.ToList()?
You are using viewmodels, so you should map the db data to your viewmodels first:
public ActionResult Index()
{
var teams = db
.Teams
.Include("Players") // Assuming your Team entity has a collection of Players
.SelectMany(t => new TeamVM {
ID = t.ID,
// etc..
})
.ToList();
return View(new TeamViewModel { Teams = teams });
}
model:
public class TeamVM
{
public int ID { get; set; }
public string Name { get; set; }
public DateTime? LastActivity { get; set; }
public IList<PLayerVM> PlayerVM { get; set; }
public int NumberPlayers {
get { return PlayerVM.Count(); }
}
}
Then in your view:
#model MyProject.Models.TeamViewModel
<table>
#foreach(var team in Model.Teams.ToList()) {
<tr>
<td>#team.Name</td> // Name
<td>#team.NumberPlayers</td> // Playercount
<td>#team.PlayerVM.Max(p => p.LastActivity).LastActivity</td> // Last edited
</tr>
}
</table>
Title says it all, right now I have a single table that is populated from a linq query and I want to add more tables with different linq queries. I'm kinda lost on how would I do that..
I could probably do it if I create different views for each table but I want to have just one view for all. :D
Here's my code: (It's a table for "on going" projects)
Controller:
public ActionResult Index()
{
var project = from x in db.Projects
where x.Project_Status == "Ongoing"
select x;
return View(project);
}
Model:
public class Project
{
[Key]
public int Project_Id { get; set; }
public string Project_Name { get; set; }
public string Project_Detail { get; set; }
public string Project_Status { get; set; }
public int Employee_Id { get; set; }
}
View Model:
public class AdminHomeViewModel
{
public Project Ongoing { get; set; } //table for ongoing projects
public Project NYA { get; set; } //another table for Not Yet Assigned projects
public Employee Free { get; set; } //another table for free employees
public List<Project> OngoingList { get; set; }
public List<Employee> NYAList { get; set; }
public List<Employee> FreeList { get; set; }
}
You are confusing yourself with the different types of models. You should have a clear understanding between View-Model and Data-Model. You should always return View-Model to the View, and not the Data-Model. Data-Model are just the POCO classes which represents your data framework (in this case, each tables). There should be different Data Models for each of your table, which you must be having already based on your entity-framework approach (Code first, Model first or Database first). Then, prepare a single model for your view (as we can bind only one model to one view). Keep all the fields from different Data-Models that you need in that View and pass it along. See the approach below:
Data-Models
public class Project
{
[Key]
public int Project_Id { get; set; }
public string Project_Name { get; set; }
public string Project_Detail { get; set; }
public string Project_Status { get; set; }
public int Employee_Id { get; set; }
}
public class Employee
{
[Key]
public int Employee_Id { get; set; }
public string Employee_Name { get; set; }
public string Employee_Detail { get; set; }
}
View-Model
public class MyViewModel
{
public int Project_Id { get; set; }
public string Project_Name { get; set; }
public string Project_Detail { get; set; }
public string Project_Status { get; set; }
public int Employee_Id { get; set; }
public string Employee_Name { get; set; }
public string Employee_Detail { get; set; }
}
Or
public class MyViewModel
{
public Project proj { get; set; }
public Employee emp { get; set; }
}
Pass it to view as:
public ActionResult Index()
{
MyViewModel model = new MyViewModel();
// You linq query to populate model goes here
return View(model);
}
Update:
From my understanding, you need something like this:
View-Model:
public class AdminHomeViewModel
{
public AdminHomeViewModel()
{
Ongoing = new List<Project>();
NYA = new List<Project>();
Free = new List<Employee>();
}
public List<Project> Ongoing { get; set; } //table for ongoing projects
public List<Project> NYA { get; set; } //another table for Not Yet Assigned projects
public List<Employee> Free { get; set; } //another table for free employees
}
Controller:
public ActionResult Index()
{
AdminHomeViewModel model = new AdminHomeViewModel();
var result1 = (from x in db.Projects
where x.Project_Status == "Ongoing"
select new Project(){
Project_Id = x.Project_Id ,
Project_Name = x.Project_Name,
... //all other assignments goes here
}).ToList();
var result2 = (from x in db.Projects
where x.Project_Status == "blah blah"
select new Project(){
Project_Id = x.Project_Id ,
Project_Name = x.Project_Name,
... //all other assignments goes here
}).ToList();
var result3 = (from x in db.Employee
where x.AnyCondition == "blah blah"
select new Employee(){
Employee_Id = x.Employee_Id ,
Employee_Name = x.Employee_Name,
... //all other assignments goes here
}).ToList();
model.Ongoing = result1;
model.NYA = result2;
model.Free = result3;
return View(model);
}