linq select items from child collection - c#

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

Related

Get and filter multiple classes inside of iqueryable object

I can't figure out how to get this query to work!
I need to create iquerialble list of Orders and select only the orders which have StateId from class OrderState equal to desired number
Here are my model classes!
public class Order
{
public virtual ICollection<OrderState> OrderState { get; set; }
}
public class OrderState
{
public int OrderStateId {get;set;}
public int OrderId{get;set;}
public int StateId{get;set;}
public virtual ICollection<State> State { get; set; }
}
public partial class State
{
public int StateId{get;set;}
public string Name{ get; set; }
}
And here is my code
var query = _context.Set<Database.Order>().AsQueryable();
if (search.UserId!= 0)
{
query = query.Where(x => x.UserId == search.UserId);
}
if (search.OrderId!=0)
{
query = query.Where(x => x.OrderId == search.OrderId);
}
if(search.StateId!=0)
{
//query =get every Order where StateId from Orderstate == search.StateId
}
var entities = query.ToList();
return _mapper.Map<List<Model.Order>>(entities);
Any help is appreciated!!
Thanks for trying to help!!!
So the orderstate list is a history of states the order has been in? It would perhaps be more useful if the orderstate table had more columns, like the datetime the order transitioned to that state, by the way.. Also the orderstate collection in Order should be called OrderStates plural, because it is a collection. Doing so makes code more readable/self describing; plurals essentially say "you can call LINQ stuff on me"
Anyway, you probably want "where any of the states is X"
if(search.StateId!=0)
{
query = query.Where(o => o.OrderStates.Any(os => os.StateId == search.StateId));
}
On the DB this will translate to something like FROM Orders o WHERE EXISTS(SELECT null FROM OrderStates os WHERE os.Id = o.Id AND os.StateId = #p1)

conversion - SQL to LINQ

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

get parent name and child count from model using LINQ

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

C# - All values where ID is 1 from List

I got a List that contains all the employees, now I need to dig in to a specific employee on a new page. I want to get all the values from the employee where the ID is 1 for example. Is there a sollution for this in LINQ?
It's practically a Query SELECT * FROM Employee WHERE id = 1;
class Employee
{
public int EmployeeID { get; set; }
public string LastName { get; set; }
public string FirstName { get; set; }
public string Title { get; set; }
public string TitleOfCourtesy { get; set; }
public DateTime BirthDate { get; set; }
public DateTime HireDate { get; set; }
public string Address { get; set; }
public string City { get; set; }
public string Region { get; set; }
public string PostalCode { get; set; }
public string Country { get; set; }
public string HomePhone { get; set; }
public string Extension { get; set; }
//public Image Photo { get; set; }
public string Notes { get; set; }
public int ReportsTo { get; set; }
public string PhotoPath { get; set; }
}
I tried it like this but it doesn't work:
List<Employee> employees = Database.getEmployees();
var uniqUsers = employees.Where(x => employees.Contains(x.EmployeeID == 1)).ToList();
Where employee is type of IEnumerable<Employee>
If you are expecting 1 record:
var result = employee.FirstOrDefault(x => x.EmployeeID == 1); // Returns Employee
If you are expecting more than 1 record:
var result = employee.Where(x => x.EmployeeID == 1); // Return IEnumerable<Employee>
Please note, when using FirstOrDefault if there is no items in your collection (or doesn't match your lambda) then it will return default T which in your case will be Employee and it will be null.
If you want a "single" item that meets that critera use the Single Linq statement:
Employee employee = employees.Single(e => e.EmployeeID == 1);
or
Employee employee = employees.SingleOrDefault(e => e.EmployeeID == 1);
if you want the query to return null instead of throwing an exception if there is not an item in the list that meets that criteria.
Let EmployeeList is the current List of Employees. You can use LINQ to filter the required details as like the specified query by using this(IT will give you all sublist satisfies the specified condition):
int empIdToSearch=1;
List<Employee> FilteredList=EmployeeList.Where(x=>x.EmployeeID ==empIdToSearch).ToList();
If the EmployeeID is unique then there will be one item in the list with particular ID, You can use FirstOrDefault to get the First item from the collection that satisfies the condition.ie.,
Employee EmployeeObject= FilteredList.FirstOrDefault(x => x.EmployeeID == empIdToSearch);
The concept that you need to get is how most linq queries operate.
When you say .Where(x => x.EmployeeID == 1) then x is a single empolyee as if you said:
foreach(Employee x in employees)
{
if(x.EmployeeID == 1)
// take it
}
So the correct syntax would be:
List<Employee> uniqUsers = employees.Where(x => x.EmployeeID == 1).ToList();
Single Optional Result:
Employee uniqUser = employees.SingleOrDefault(x => x.EmployeeID == 1);
Single Mandatory Result:
Employee uniqUser = employees.Single(x => x.EmployeeID == 1);
First Optional Result:
Employee uniqUser = employees.FirstOrDefault(x => x.EmployeeID == 1);
First Mandatory Result:
Employee uniqUser = employees.First(x => x.EmployeeID == 1);
We can fetch the records from collection in two ways.
Linq to sql like query
var employee= from emp in employees where emp.ID==1;
Linq to extension methods.
var employee = employees.Where(emp=>emp.ID==1);
Linq supports a query syntax that is closer to SQL.
var employee1 = from employee in employees where employee.EmployeeID == 1 select employee;
foreach (var x in employee1)
{
Console.WriteLine(x.EmployeeID);
}
The compiler converts all query syntax to method syntax. Not all things can be done with query syntax. The 'from' comes before the 'select' so auto-complete is more useful. It is important to note the linq query is not executed until it is used. The foreach loop is where it is first used in this example.

LINQ - get total count of values from nested list

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.

Categories

Resources