I have the following model:
public Class Category{
public int Id {get;set;}
public string Name {get;set;}
public ICollection<SubCategory> SubCategories {get;set;}
}
public Class SubCategory{
public int Id {get;set;}
public string Name {get;set;}
public int CategoryId { get; set; }
public virtual Category Category{ get; set; }
public ICollection<Ticket> Tickets { get; set; }
}
public class Ticket {
public Ticket();
public int Id { get; set; }
public virtual SubCategory SubCategory{ get; set; }
public int SubCategoryId{ get; set; }
}
I want to get data groupBy Category and get the count of tickets in each subcategory using this query:
Entities
.Include(h => h.SubCategories )
.ThenInclude(s => s.Tickets)
.GroupBy(s => s.Id)
.Select(t => new Cata {
Name = t.FirstOrDefault().Name,
Children = GetChildern(t.FirstOrDefault().SubCategories )
});
public List<SubCat> GetChildern(IEnumerable<SubCategories> subs)
{
var output = new List<SubCat>();
foreach (var sub in subs) {
var subcat = new SubCat();
subcat.Name = sub.Name;
if (sub.Tickets != null) {
subcat.Size = sub.Tickets.Count;
}
output.Add(subcat);
}
return output;
}
With the Query above the ticket is always zero for all, but tickets exists.
I don't see why you need to do a group by if you start your query in Categories
var result= Entities
.Include(h => h.TicketSubCategories)
.ThenInclude(s => s.Tickets)
.Select(t => new Cata {
Name = t.Name,
Children= t.TicketSubCategories
.Select(ts=>new SubCat{
Name=ts.Name,
Count=ts.Tickets.Count()})
};
Agreed with #Ivan about he commented above, here you don't need to use a custom method, using it you will force the projection of your query to be executed on the client side and not on the server (your DB)
So each Category has zero or more SubCategories, and each SubCategory has zero or more Tickets. Each Ticket belongs to exactly one SubCategory and each SubCategory belongs to exactly one Category
And you want a query, that results in groups of SubCategories that have the same Category. You want some (or all) properties of each SubCategory, but above all, you want the number of Tickets each SubCategory has.
All elements in every group of SubCategories belong to the same Category. You also want some (if not all) properties of this Category.
The solution is to group all SubCategories into groups of same Category (for efficiency use CategoryId). Then use a Select to get the properties you want.
var result = SubCategories
// group them into groups with same CategoryId
.GroupBy(subCategory => subCategory.CategoryId
// from every group take the properties you want:
.Select(group => new
{
// All SubCategories in one group belong to the same Category.
// For efficiency, take only the Category properties you plan to use,
CommonCategory = group.Key.Select(category => new
{
// take the category properties you want to use
}
// The group has a lot of SubCategories.
// For each subcategory select only the properties you want to use
SubCategories = group.Select(subCategory => new
{
// one of the properties you want is the number of Tickets of this SubCategory:
TicketCount = subCategory.Tickets.Count(),
// for efficiency: select only SubCategory properties you plan to use:
Property1 = subCategory.Property1,
Property2 = subCategory.Property2,
...
}),
});
So the result is a sequence of objects. Each object has two Properties:
SubCategories: a sequence of some properties of all SubCategories that belong to the same Category.
CommonCategory. Several properties of the Category that all SubCategories belong to.
The SubCategories is a sequence. Each element of the sequence is an object with several properties:
TicketCount: the number of tickets in the SubCategory
other properties: several other properties of the SubCategory
From this it is easy to construct the code to GetChildren
Related
How can the following be accomplished using LINQ
SELECT r.BrandID
FROM dbo.Items AS r
JOIN Brands AS d ON r.BrandID = d.BrandID
WHERE CategoryID IN (SELECT CategoryID
FROM dbo.Categories
WHERE Name = 'Bread - Bakery')
Code for Brand class:
public class Brand
{
public int BrandID { get; set; }
[DisplayName("Brand Name")]
public string Name { get; set; }
public virtual List<Category> Categories { get; set; }
public virtual List<Item> Items { get; set; }
}
Code for Item class:
public class Item
{
[Key]
public int ItemID { get; set; }
public virtual Category Category { get; set; }
public virtual Brand Brand { get; set; }
public int CategoryID { get; set; }
public int BrandID { get; set; }
}
code for Category class:
public class Category
{
[Key]
public int CategoryID { get; set; }
[DisplayName("Category Name")]
public virtual string Name { get; set; }
public virtual List<Brand> Brands { get; set; }
public virtual List<Item> Items { get; set; }
}
dbContext.Items
.Where(x => x.Category.Name.Equals("Bread - Bakery"))
.Select(x => x.BrandID);
I am not sure why you need to use below join. It seems that it is not needed (unless intentionally inner joined with brands to remove non-matching records from items)
JOIN Brands AS d ON r.BrandID = d.BrandID
Hm, pity you didn't write your requirements, now I have to guess what they are from your SQL code.
So you have a database with Brands, Items and Categories. Every Item has a Category, every Category can be used by zero or more Items: a one-to-many relation
Every Item is of a certain Brand, Every Brand can have zero or more items: also a straightforward one-to-many relation.
Finally every Brand has zero or more Categories, every Category has zero or more Brands: many-to-many
Now you take your collection of Items, you only want to keep those Items that have a Category with a Name that equals Bread - Bakery. From the remaining items you want all their BrandIds.
The requirement would be: "Give me the BrandIds of all Items that have a Category with a Name that equals 'Bread - Bakery`.
If you use entity framework, it is usually easier if you use the virtual ICollection instead of doing the join yourself. Entity framework knows the relations between the tables and will compose the proper joins for it.
var result = myDbContext.Items // from the collection of Items
.Where(item => item.Category.Name == "Bread - Bakery") // keep only those with a Category
// that has a Name equal to ...
.Select(item.BrandId); // from the remaining items select the BrandId
If you really want, and you can convince your project leader that entity framework can't be trusted to do the proper joins you can do the join yourself:
// get the sequence of categories that I'm interested in:
var breadBakeryCategories = myDbContext.Categories
.Where(category => category.Name == "Bread - Bakery");
// join the items with these categories
// and select the BrandIds
var requestedBrandIds= myDbContext.Items
.Join(breadBakeryCategories,
item => item.CategoryId, // from every Item take the CategoryId,
category => category.CategoryId, // from every Category take the CategoryId
(item, category) => item.BrandId); // when they match, select the BrandId
TODO: consider concatenating this into one big ugly LINQ statement.
Remark 1
You do realize that your result might have the same BrandIds several times, don't you?
If you don't want that, start with the Brands:
var result = myDbContext.Brands
.Where(brand => brand.Items.Select(item => item.Category.Name)
.Where(name => name == "Bread - Bakery")
.Any())
.Select(brand => brand.brandId);
In words: from the collection of Brands, keep only those Brands that have at least one Category with a name equal to "Bread - Bakery". From the remaining Brands select the BrandId.
** Remark 2 **
Why are your one-to-many Lists instead of ICollections? Are you sure that brand.Categories[4] has a proper meaning?
var result = myDbContext.Brands
.Where(brand => brand.Category[4].Name == "Bread - Bakeries");
Your compiler won't complain, but you'll get runtime errors.
Consider using virtual ICollection<...> for your one-to-many and many-to-many relations. This way you'll have exactly the functionality you expect with a database table, and your compiler will complain if you try to use functionality that can't be translated into SQL
Need help in writing LINQ Query to get count of employee based on department.
Department Model
public class Department
{
public int Id { get; set; }
public string Name { get; set; }
public List<Employee> Employees { get; set; }
}
Employee Model
public class Employee
{
public int Id { get; set; }
public string Name { get; set; }
public int DepartmentId { get; set; }
public Department Department { get; set; }
}
MyViewModel
public class MyViewModel
{
public string Department { get; set; }
public int count { get; set; }
}
LINQ Query
public ActionResult shows()
{
//one department can have many employees and one employee can only be parrt of one department
// below LINQ query fetches the count of employees belonging to each department
var x = _context.Employees.Include("Department").GroupBy(e => e.DepartmentId)
.Select(y=> new MyViewModel{
Department= y.Key, // ERROR HERE AS Cannot type cast string to integer
count = y.Count()
}).ToList();
// code removed for brevity
return Content("x");
}
Output Expected
Department Count (or employees)
Human Resource 10
Information Tech 5
Question
How to write a LINQ Query to get the output as above. Please guide me.
You have 2 options for modifying your query. I am assuming your Department model contains a property string Name which you want to assign to the Department property of your view model.
Get the first Department in the grouped collection and assign its
Name property to your view models Department property. Note that
you need to add .ToList() before .GroupBy() so that the
first part of the query is executed before grouping
var x = _context.Employees
.Include("Department")
.ToList()
.GroupBy(e => e.DepartmentId)
.Select(y => new MyViewModel
{
Department = y.First().Department.Name,
count = y.Count()
}).ToList();
Change the .GroupBy() expression to group by the Name property
of the Department property
var x = _context.Employees
.Include("Department")
.GroupBy(e => e.Department.Name)
.Select(y=> new MyViewModel
{
Department = y.Key,
count = y.Count()
}).ToList();
public ActionResult shows()
{
//one department can have many employees and one employee can only be parrt of one department
// below LINQ query fetches the count of employees belonging to each department
var x = _context.Employees.Include("Department").GroupBy(e => new { e.DepartmentId, e.Department.Name})
.Select(y=> new MyViewModel{
Department= y.Key.Name
count = y.Count()
}).ToList();
// code removed for brevity
return Content("x");
}
the key here is to group by DepartmentId AND Name
Try it like this:
public ActionResult shows()
{
//one department can have many employees and one employee can only be parrt of one department
// below LINQ query fetches the count of employees belonging to each department
var x = _context.Employees.Include("Department").GroupBy(e => e.DepartmentId)
.Select(y=> new MyViewModel{
Department= y.Key.DepartmentName, // or whatever property you have for storing the name
count = y.Count()
}).ToList();
// code removed for brevity
return Content("x");
}
you can use this code :
using System.Linq;
using System.Web.Mvc;
using MvcApplication1.Models;
namespace MvcApplication1.Controllers
{
public class HomeController : Controller
{
public ActionResult Index()
{
var dataContext = new MovieDataContext();
int count = (from row in dataContext.Movies
where row.IsEnquiry == true
select row).Count();
ViewBag.ItemCount = count;
return View();
}
}
}
My data base is made of two Models: Products and Categories.
The Category model itself is made of parent and children categories as for a specific 'ParentID' field relating one to the others.
The relation Parent to Children category is one to many. Children could be any number from zero to 'n'.
The relation Category (or SubCategory) to Products is one to many, the CategoryID being a FK in Products table.
I started writing a search among products' name and description and categories' name:
IQueryable<Product> products =
from p in db.Products
where (p.Name.Contains(searchString)
|| p.Description.Contains(searchString)
|| p.Category.Name.Contains(searchString))
select p;
As the form allows user to select a specific Category from a DDL,in case a category has been selected I need now to extend my query to its subcategories as follows:
looking for the search string into the children categories' name too
filtering results to those belonging to Categories and Subcategories only
adding new items to previous result
How am I supposed to cycle inside children categories name in order to fulfill points 1.2.3, adding needed result to previously selected ?
Adding models (only relevant fields):
public partial class Product
{
public int ID { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public int CategoryID { get; set; }
}
public partial class Category
{
public int ID { get; set; }
public string Name { get; set; }
public int? ParentID { get; set; }
public virtual ICollection<Product> Products { get; set; }
}
Trying to better explain it. Given:
a search string (no search string returns ALL products)
a selected Parent Category (optional, no selection returns ALL categories)
I want to retrieve all products where:
CASE 'A': no parent category selected
products' name contains the search string
all categories contains the search string
CASE 'B': parent category selected
products' name contains the search string (only those products
pertaining to selected parent category AND to its children categories,
if any)
selected parent category name contains the search string
selected parent's children categories (if any) name contains the search string
Assuming your child categories are exposed as an enumerable on the parent category and probably called Categories, then your original query can be extended to search on them by using the Any() operator, e.g. p.Category.Categories.Any(x => x.Name.Contains(searchString)), so:
var products =
from p in Products
where (p.Name.Contains(searchString)
|| p.Description.Contains(searchString)
|| p.Category.Name.Contains(searchString)
|| p.Category.Categories.Any(x => x.Name.Contains(searchString))
) && p.IsDeleted == false && p.IsApproved == true
select p;
Your filter on the parent category can continue to be used as is.
IQueryable<Product> products =
from p in db.Products
where (p.Name.Contains(searchString)
|| p.Description.Contains(searchString)
|| p.Category.Name.Contains(searchString))
select p;
if (selectedCategoryID.HasValue) // filter category AND subcategories
{
List<int> categories = new List<int>();
categories.Add(selectedCategoryID.Value);
foreach(Category child in GetCategories.activeChildrenCategories(db,selectedCategoryID.Value))
{
categories.Add(child.ID);
}
products = products .Where(p => (categories.Contains(p.Category.ID)));
i have a Class:
public class Company
{
public int id { get; set; }
public string title { get; set; }
public string address { get; set; }
public string city { get; set; }
public string zip { get; set; }
public List<string> phones { get; set; }
public List<string> categories { get; set; }
}
and i have a Generic List which contains that Class:
public List<Company> Companies = new List<Company>();
i want to do two things:
get a distinct list of the categories
get a total count of companies per category
i think i managed to the the first thing:
Companies.SelectMany(c => c.categories).Distinct()
please tell me if you think anything is wrong with that.
i tried the second step as so:
Companies.SelectMany(c => c.categories).Where(c=>c == Category).Count()
but im not sure that is really right.
Correct
You need to flatten the list into (company, category) pairs, then group by category:
from company in Companies
from category in company.Categories
group company by category into g
select new { Category = g.Key, Count = g.Count() }
EDIT: If you want to find out how many companies are in a single given category, you can write
Companies.Count(c => c.Categories.Contains(categoryName))
Companies
.SelectMany(c => c.categories)
.GroupBy(c => c)
.Select(g => new
{
Cateogry = g.Key,
Count = g.Count()
});
If you want it for specific category then your query is correct already.
Below are my classes. I have a product that contains list of days. Each day has a city property.
I need to create a linq query that will give me the distinct cities that are used on all my products in the system.
I tried something like this but it does not work:
var cities = from product in NHibernateSession.Linq<Product>() select new { city = product.Days.Where(d => d.City != null).Distinct() }; //This returns the day items but i need distinct cities
public class Product : EntityBase
{
public virtual string Name { get; set; }
public virtual IList<ProductDayDefinition> Days { get; set; }
}
public class ProductDayDefinition : EntityBase
{
public virtual Product Product { get; set; }
public virtual City City { get; set; }
}
You need to call the SelectMany function, which takes a single item and lets you get multiple items from it.
For example:
var cities = NHibernateSession.Linq<Product>()
.SelectMany(p => p.Days)
.Select(p => p.City)
.Where(c => c != null)
.Distinct();
Note that if the City class doesn't implement Equals and GetHashCode correctly, this will return duplicates.
You can do this using query comprehension syntax like this: (Untested)
var cities = (from product in NHibernateSession.Linq<Product>()
from day in product.Days
where day.City != null
select day).Distinct();