Efficient way to sub Queries in Linq - c#

here is my linq code:
BOOK entity = db.BOOKS
.Where(s => s.ID == (from p in db.LIBRARY
from b in db.BOOKS
where (p.ID == 123) && (p.idpage == b.idpage)
select b.fields));
My actual oracle code is:
SELECT DISTINCT BOOKS.ID
FROM LIBRARY,BOOKS
WHERE LIBRARY.ID = 123 AND LIBRARY.ID = BOOKS.ID
But its showing the error in s.ID that..
Delegate 'System.Func Project.Models.BOOKS,int,bool' does not take 1 arguments
Why does this happen? Are there any workarounds?

Your SQL is using a join, so you can do the same thing in LINQ. Either of these approaches will suffice:
// join
var query = (from b in db.BOOKS
join p in db.LIBRARY on b.IdPage equals p.IdPage
where p.ID == 123
select b.Id).Distinct();
// 2 from statements (SelectMany) can also be used as a join
var query = (from b in db.BOOKS
from p in db.LIBRARY
where p.ID == 123 && b.IdPage == p.IdPage
select b.Id).Distinct();
// fluent syntax
var query = db.BOOKS
.Where(b => db.LIBRARY.Any(p =>
p.ID == 123 && b.IdPage == p.IdPage))
.Select(b => b.Id)
.Distinct();

s.ID is comparing to an Enumerable, so you get the error.
At the end of the LINQ query, add a SingleOrDefault().

Your subquery returns a sequence of values, not a single values, so you can't compare it to a scalar property like ID. You should use First on the result of the subquery to get the first result (or Single if there should be only one)
BOOK entity = db.BOOKS
.Where(s => s.ID == (from p in db.LIBRARY
from b in db.BOOKS
where (p.ID == 123) && (p.idpage == b.idpage)
select b.fields).First());

You should be able to use the navigation properties on your BOOKS class to do something like this:
var bookIds = db.BOOKS.Where(b => b.LIBRARIES.Any(l => l.ID == 123))
.Select(b => b.ID)

Related

Add 'where' clausures dynamically to linq query

I have this linq query:
var query = (from dc in context.Table1.Include(d => d.Doc)
join u in _context.Table2 on dc.IDDoc equals u.IDDoc
where dc.ID == id && u.IDUser == user.IDUser
select dc)
.Union(from dc in context.Table1.Include(d => d.Doc)
join p in _context.Table3 on dc.IDDoc equals p.IDDoc
where dc.ID == id
select dc);
And I want to add more where conditions dynamically depends of a list (List ids)
What I want to achieve is something like this:
Imagine that I have a List ids = new(){1, 2, 5, 27);
What I want to do is to add that info into this part of the query to have something like this:
.Union(from dc in context.Table1.Include(d => d.Doc)
join p in _context.Table3 on dc.IDDoc equals p.IDDoc
where dc.ID == id && p.ID == 1 || p.ID == 2 || p.ID == 5 || p.ID = 27
select dc)
But if next time list is List ids = new(){4}, query should look like:
.Union(from dc in context.Table1.Include(d => d.Doc)
join p in _context.Table3 on dc.IDDoc equals p.IDDoc
where dc.ID == id && p.ID == 4
select dc)
Is it even possible? If not, what would be a possible solution?
Thank you
EDIT: I made up with this query because I have no clue about how to add it to my main query.
What I really have is this:
var mainQuery = _context.RootTable
.Include(i => i.Items).ThenInclude(dc => dc.Docs)
.Include(i => i.Items).ThenInclude(sg => sg.Signs)
.FirstOrDefault(m => m.ID== id);
And what I want to do is to filter the ".ThenInclude(dc => dc.Docs) with the other query. I can't figure out how to do it in a better and efficient way that with two sepparate queries.
Use Enumerable.Contains:
List<int> ids = new(){4};
....
.Union(from dc in context.Table1.Include(d => d.Doc)
join p in _context.Table3 on dc.IDDoc equals p.IDDoc
where dc.ID == id && ids.Contains(p.ID) // here
select dc)
....

Distinct record using linq in entity framework

According to this query i get all records which have same id but i want only one time that record
var query = (from c in CompObj.EmpInfoes
join d in CompObj.Leaves on c.EmpID equals d.ProjectManagerID
where c.RoleID == 4 || c.RoleID == 2
select new { c.EmpName, c.EmpID });
var query = (from c in CompObj.EmpInfoes
join d in CompObj.Leaves on c.EmpID equals d.ProjectManagerID
where c.RoleID == 4 || c.RoleID == 2
select new { c.EmpName, c.EmpID })
.Distinct();
Or was there something more you needed?
Just add .Distinct() at the end.

Lambda expression filtering included related data with Entity Framework

I need to filter only the visibles products from a category, but it's not working.
Category category = db.Categories
.Include(c => c.Products.Where(p => p.IsVisible))
.First(c => c.CategoryID == id);
Error:
The Include path expression must refer to a navigation property defined on the type. Use dotted paths for reference navigation properties and the Select operator for collection navigation properties.
UPDATE
var result = (from c in db.Categories
where c.CategoryID == id
select new
{
CategoryID = c.CategoryID,
Description = c.Description,
Products = (from p in db.Products
where p.IsVisible
&& p.CategoryID == c.CategoryID
orderby p.DateSent descending
select p)
}).FirstOrDefault();
but now i need to cast the anonymousType to Category
Your query doesn't make sense if you want:
the visibles products from a category
If you genuinely want the visible products, try this:
var visibleProducts = db.Categories
.Where(c => c.CategoryID == id)
.Select(c => c.Products.Where(p => p.IsVisible));
Note: untested
Maybe something like:
var category = db.Products.Where(p=>p.IsVisible && p.CategoryID == id).Include("Category").ToList().Select( p=> p.Category).Distinct();
It may not be ideal because of the ToList... but I can see no other way right now.
Maybe you could change the Distinct into a FirstOrDefault()...
var category = db.Products.Where(p=>p.IsVisible && p.CategoryID == id).Include("Category").ToList().FirstOrDefault().Category;
Not tested either...

How can I write a linq query for this?

I need to write following query in Linq to SQL but not sure what is the best way of doing, given it has two derived tables. Any suggestions.
SELECT A.ID
FROM
(
SELECT *
FROM Orders
WHERE ProductID = 5
) A
JOIN
(
SELECT CustomerID, MAX(Price) Price
FROM Orders
WHERE ProductID = 5
GROUP BY CustomerID
) B
ON A.CustomerID = B.CustomerID and A.Price = B.Price
var b = (
from o in db.Orders
where o.ProductID == 5
group o by o.CustomerID into og
select new {
CustomerID = og.Key
Price = Max(og.Price)
}
);
var a = (
from o in db.Orders
join p in b on new {a.CustomerID, a.Price} equals
new {b.CustomerID, b.Price}
where o.ProductID == 5
select a.ID
);
var r = a.ToString();
These two links are invaluable when forming things like this:
http://code.msdn.microsoft.com/101-LINQ-Samples-3fb9811b
http://msdn.microsoft.com/en-us/vstudio/bb688085
Can you simplify this with LINQ, especially if you use method syntax instead of query syntax.
orders.Where(o => o.ProductID == 5)
.GroupBy(o => o.CustomerID)
.SelectMany(g => g.Where(o => o.Price == g.Max(m => m.Price)));
My advice when writing LINQ, do not simply attempt to convert a SQL statement exactly. Think about the desired result and develop a solution designed for LINQ.
Something along these lines:
var result = from a in context.Orders
join b in (context.Orders.Where(o => o.ProductID == 5).GroupBy(o => o.CustomerID).Select(g => new { CustomerID = g.Key, Price = g.Max(o => o.Price)))
on new {a.CustomerID, a.Price} equals new {b.CustomerID, b.Price}
where a.ProductID == 5
select a.ID;

modify linq to get first 5 elements

var lastArticles = from a in be.MyTable
where a.id == 1
join c in be.OtherTable on a.parent equals c.id
orderby a.timestamp descending
select new { a, cName = c.name};
I need to get the first 5 elements.
I'm doing it by
.Take(5)
but is there a way to do in in the linq statement?
No, you need to use Skip() and Take() as method calls. There is no LINQ-specific equivalent.
var lastArticles = (from a in be.MyTable
where a.id == 1
join c in be.OtherTable on a.parent equals c.id
orderby a.timestamp descending
select new { a, cName = c.name }).Take(5);
A linq Query should always be separate from the products of running that query.
.Take() produces results, and thus should be separate and distinct from the query.
//data query
var lastArticlesQuery = from a in be.MyTable
where a.id == 1
join c in be.OtherTable on a.parent equals c.id
orderby a.timestamp descending
select new { a, cName = c.name};
//results of that query at this time
var lastArticles = lastArticlesQuery.Take(5);
This code is only syntatic sugar, utlimately it will be converted to a LINQ-methods chain that will look something like:
var lastArticles = be.MyTable
.Where(a => a.id == 1)
.Join(be.OtherTable, a => a.parent, c => c.id,
(a, c) => new { a, c})
.OrderByDescending(#t => #t.a.timestamp)
.Select(#t => new { #t.a, cName = #t.c.name });
So having a keyword for Take() would only add to the sytactic sugar and it would need to be re-converted as well.
In short, no, the only way is to use the Take() method.

Categories

Resources