Linq SelectMany query - c#

I have the following query:
DateTime cutoffDate = new DateTime(1997, 1, 1);
var orders =
from c in customers
where c.Region == "WA"
from o in c.Orders
where o.OrderDate >= cutoffDate
select new { c.CustomerID, o.OrderID };
How can this be written in Linq Lambda?
BTW, is this known as a SelectMany query?
Also this can be done with a join, what is the pros and cons with doing it as shown above.

Yes, this is a SelectMany. You use SelectMany to 'flatten' a nested or tiered collection (in this case, the orders are nested under customers) into a simple single-tier collection.
customers.Where(c => c.Region == "WA")
.SelectMany(c => c.Orders)
.Where(o => o.Orderdate >= cutoffDate)
.Select(x => new { x.OrderID, x.Customer.CustomerID });
If the Orders are a property of the Customer then there is no need to use a join.

Related

Getting 2 Top 5 within a single LINQ query in EFCore

I want to load 10 latest products containing 5 of category A and 5 of category B.
So the result contains 5 latest products with category A and 5 latest products of category B.
Normally I can do it using these two:
var listA = await (
from p in db.Set<Product>()
where p.Category == "A"
orderby p.ProductDate descending
select p
).Take(5).ToListAsync();
var listB = await (
from p in db.Set<Product>()
where p.Category == "B"
orderby p.ProductDate descending
select p
).Take(5).ToListAsync();
var result = listA.Concat(listB);
But as you see this piece of code requires 2 calls to the database.
How can I get the result, using just 1 database call?
With EF Core 3.0.0-preview7.19362.6, you can write it like this which produces just a single query and works perfectly fine :
IQueryable<Product> topA = context.Products
.Where(p => p.Category == "A")
.OrderByDescending(x => x.ProductDate)
.Take(5);
IQueryable<Product> topB = context.Products
.Where(p => p.Category == "B")
.OrderByDescending(x => x.ProductDate)
.Take(5);
List<Product> result = await topA
.Concat(topB)
.OrderBy(p => p.Category)
.ToListAsync();
Use Concat before await
var listA = (
from p in db.Set<Product>()
where p.Category == "A"
orderby p.ProductDate descending
select p
).Take(5);
var listB = (
from p in db.Set<Product>()
where p.Category == "B"
orderby p.ProductDate descending
select p
).Take(5);
var result= await listA.Concat(listB).ToListAsync();
Recently, The EF team has confirmed that translation of UNION, CONCAT, EXCEPT, and INTERSECT is going to be added to EF Core 3.
So, If you are using EF Core 3 Preview then good luck otherwise you have to use RAW SQL queries if you want to go with one shot.
In LINQ Query something like this should work:
var list = await(db.Product.Where(p => p.Category == "A" || p.Category == "B").OrderByDescending(p => p.ProductDate)
.ToList()
.GroupBy(p => p.Category)
.SelectMany(t => t.Select(b => b).Zip(Enumerable.Range(0, 5), (j, i) => j))).ToListAsync();

Can I use OrderBy<T> to order by a Join<T> entity?

I need to translate a Linq query into Dynamic Linq but I'm having a problem with the OrderBy part.
This is the Linq query:
var q = from pp in ctx.MyEntity
join c in sortedListString on pp.CountryId.ToString() equals c.Substring(9)
orderby c ascending
select pp;
As you can see, I'm sorting by the entinty in the join.
Now I need to write the same query but with dynamic linq and I have:
var q = from pp in ctx.MyEntity
select pp;
q = q.Join(
sortedListString,
o => o.CountryId.ToString(),
i => i.Substring(9), (o, i) => o
).OrderBy(???);
What should I put in the OrderBy, knowing that I want to order by sortedListString?
Not sure what you mean by "dynamic linq" here, but equivalent of your query using "full" LINQ syntax is:
var q = ctx.MyEntity
.Join(sortedListString,
pp => pp.CountryId.ToString(),
c => c.Substring(9),
(pp, c) => new { pp, c })
.OrderBy(t => t.c)
.Select(t => t.pp);

LinQ method syntax multiple tables with .where() and without joins

How do fetch data from multiple tables with method syntax without using joins, but only .where() methods?
I'm making a select against EF5 db context which maps to this legacy table structure where I have a persons detail table and another table which refers both to itself to create a hierarchy and to the person details table this way:
PersonSet
.Where(p => p.LastName.ToLower()=="surname")
.Join(SubsetSet, p => p.Id, ss => ss.SubsetLink, (p, ss) => new { PersonDetail = p, person = ss })
.Where(m => m.person.Frame=="a" && m.person.Purpose=="1")
.Join(SubsetSet, ss1 => ss1.person.Owner, person => person.SubsetLink, (ss1, ss2) => new { person = ss1, club = ss2 })
.Where(a => a.club.Frame=="b" && a.club.Purpose=="2")
.Join(SubsetSet, ss => ss.club.Owner, ss2 => ss2.SubsetLink, (ss, ss2) => new { club = ss, association = ss2 })
.Where(a => a.association.Frame=="b" && a.association.Purpose=="3")
.Join(SubsetSet, ss => ss.association.Owner, ss3 => ss3.SubsetLink, (ss, ss3) => new { association = ss, district = ss3})
.Where(d => d.district.Frame=="b" && d.district.Purpose=="4" && d.district.SubsetLink=="12345")
.Select(proj => new { proj.association.club.person, proj.association.club, proj.association, proj.district })
.OrderByDescending(a => a.association.club.person.phyperson.FirstName)
.Take(10).Dump();
The above query works at least in LinqPad but, it seems to me that If I could get rid of those joins the statement might look a bit nicer. Now I know, like in the Albahari example below, that this can be done with query syntax. But I couldn't find an example that would illustrate this situation with method syntax. The way I'm trying to approach this might of course be wrong and that's why I can't find suitable examples.
Here I found something similar, but couldn't make it work in LinQPad:
Is multiple .Where() statements in LINQ a performance issue?
Or this one, where the solution is again in query syntax:
Cross Join with Where clause
Or this example by Albahari: (http://www.linqpad.net/WhyLINQBeatsSQL.aspx)
from p in db.Purchases
where p.Customer.Address.State == "WA" || p.Customer == null
where p.PurchaseItems.Sum (pi => pi.SaleAmount) > 1000
select p
Consider this query:
var q = from order in orders
from orderline in order.Lines
where orderline.Count > 10
select order.Discount * orderline.Price;
this more or less corresponds to
var q = orders
.SelectMany(order => order.Lines, (order, orderline) => new { order, orderline})
.Where(item => item.orderline.Count > 10)
.Select(item => item.order.Discount * item.orderline.Price);
For more information on SelectMany, see the MSDN documentation.
If you don't have associations defined:
var q = from order in orders
from orderline in orderLines
where orderline.OrderId == order.Id
where orderline.Count > 10
select order.Discount * orderline.Price;
this more or less corresponds to
var q = orders
.SelectMany(order => orderLines, (order, orderline) => new { order, orderline})
.Where(item => item.orderline.OrderId == item.order.Id)
.Where(item => item.orderline.Count > 10)
.Select(item => item.order.Discount * item.orderline.Price);

Method Chaining equivalent?

This is working properly (from initial testing).
Since method chaining is my preferred format, I've tried to figure out what the method chaining equivalent is, but with no luck. Any ideas?
var data = (from p in db.Persons
from c in db.Companies
where c.CompanyName == companyName && p.CompanyId == c.CompanyId
select p)
.Select(p => new
{
Id = p.PersonId,
Name = string.Format("{0} {1}", p.FirstName, p.LastName)
});
Thanks,
--Ed
I would reorder the query a bit to filter out the companyName first, then perform the join. This would allow you to use this fluent syntax:
var query = db.Companies.Where(c => c.CompanyName == companyName)
.Join(db.Persons, c => c.CompanyId, p => p.CompanyId, (p, c) => p)
.Select(p => new
{
Id = p.PersonId,
Name = string.Format("{0} {1}", p.FirstName, p.LastName)
});
Having said that, some queries are a lot easier to write in query syntax, so why constrict yourself? Complicated joins are usually nicer in query syntax and you also get the benefit of using SelectMany join format with from ... from... instead of join p in ... on x equals y. See this question for more details: When to prefer joins expressed with SelectMany() over joins expressed with the join keyword in Linq.
Without changing the query, something like below, where oi is the opaque identifier:
var data = db.Persons.SelectMany(p => db.Companies, (p, c) => new {p,c})
.Where(oi => oi.c.CompanyName == companyName
&& oi.p.CompanyId == oi.c.CompanyId)
.Select(oi => oi.p)
.Select(p => new
{
Id = p.PersonId,
Name = string.Format("{0} {1}", p.FirstName, p.LastName)
});
However, you might also consider a few re-writes; maybe a join, or moving the company-name check earlier; and removing the double-select.
Non-toplevel from clauses translate to SelectMany calls:
db.Persons.SelectMany(p => db.Companies.Select(c => new { p, c }))
It is non-intuitive and a little hacky (although logical and reliable). I personally tend to use the query syntax for complex queries like this one because the SelectMany form is unreadable.

LINQ - SELECT DISTINCT with NOT IN

I'm have a SQL statement which I am trying to transform in a LINQ statement...
SELECT DISTINCT mc.*
FROM ManufractorCategories mc
WHERE mc.Active = 'true'
AND mc.Folder = 'false'
AND (mc.Id not in (SELECT Category_id FROM Manufractor_Category
WHERE Manufractor_id = 3));
That's my last, not working LINQ statement
(IQueryable<object>)db.ManufractorCategories
.Where(o => o.Active == active)
.Where(o => o.Folder == folder)
.Select(i => new { i.Id, i.Folder }).Except(db.Manufractor_Categories.Where(t => t.Manufractor_id == id).Select(t => new { t.Category_id })).Distinct();
I've tried the whole Sunday on that, but the Except statement won't work.
Thanks in advances for any help!
The Except method requires two sets of the same type - this means that you would have to select objects of type ManufractorCategory in the nested query as well as in the outer query - then it would select all categories that are in the first one and not in the second one.
An easier alternative is to use the Contains method to check whether the current ID is in a list of IDs that you want to filter. The following should work:
var q =
db.ManufractorCategories
.Where(o => o.Active == active)
.Where(o => o.Folder == folder)
.Select(i => new { i.Id, i.Folder })
.Where(o =>
!db.Manufractor_Categories
.Select(t => t.Manufractor_id)
.Contains(o.Id)
.Distinct();
And a simplified version using query syntax:
var q =
from o in db.ManufractorCategories
where o.Active == active && o.Folder == folder &&
db.Manufractor_Categories
.Select(t => t.Manufractor_id)
.Contains(o.Id)
select new { i.Id, i.Folder };
The Except statement is going to get a list of objects with the Category_id property. However, you're query has a result that contains objects with the Id and Folder properties. The query will most likely be unable to see where these objects are equal, and so, the Except clause won't take effect.

Categories