How can I concatenate a where clause using OR on LINQ? - c#

I have this code :
foreach (Package pack in Packages)
{
filteredResults = filteredResults.Where(o => o.ID == pack.ID);
}
the only problems is that I filter the result N time (so N where).
What I'd like to do is to filter the result only one time (only a where clause) with N expression. Somethings like :
Where o.ID == pack.ID OR o.ID == pack.ID OR o.ID == pack.ID OR o.ID == pack.ID...
Is it possible to do this with LINQ?

Something like the code below should work, or at least steer you in the right direction.
-- Get all the package IDs you want to select on.
var packIDs = from pack in Packages
select pack.ID;
-- Return all results where the ID is in the package ids above.
filteredResults = from result in filteredResults
where packIDs.Contains(result.ID)
select result;
The above assumes your and's were a logic mistake and you meant ors.

var packIds = Packages.Select(x=>x.ID).ToArray();
filteredResults = filteredResults.Where(o=> packIds.Contains(o.ID));
If this is linq to sql this will get translated into:
WHERE ID IN (1,2,3,4)

Something like this might help you:
filteredResults = originalResults.Where(o => Packages.Any(p => p.ID == o.ID));

Do you not want Intersect()? i.e
var ids = filteredResults.Select( fr => fr.Id ).Intersect(Packages.Select( p => p.PackID ) ) ;

I think you need to use expression with LinqKit
var v = from utente in db.Utente
select utente;
Expression<Func<Utente, bool>> expr = c => c.Age == 26;
expr = expr.Or<Utente>(c => c.Name != "Matteo");
v = v.Where(expr.Expand());
The result is:
SELECT...... FROM......
WHERE (26 = [Extent1].[Age ]) OR ('Matteo' <> [Extent1].[Name])
I have the same issue, i try this solution

Related

Linq Query with double sub queries

I am struggling converting the following SQL query I wrote into Linq. I think I'm on the right track, but I must be missing something.
The error I'm getting right now is:
System.Linq.IQueryable does not contain a definition for .Contains
Which is confusing to me because it should right?
SQL
select Users.*
from Users
where UserID in (select distinct(UserID)
from UserPermission
where SupplierID in (select SupplierID
from UserPermission
where UserID = 6))
LINQ
var Users = (from u in _db.Users
where (from up in _db.UserPermissions select up.UserID)
.Distinct()
.Contains((from up2 in _db.UserPermissions
where up2.UserID == 6
select up2.SupplierID))
select u);
EDIT: I ended up going back to SqlCommand objects as this was something I had to get done today and couldn't waste too much time trying to figure out how to do it the right way with Linq and EF. I hate code hacks :(
I think there is no need to do a distinct here (maybe I am wrong). But here is a simpler version (assuming you have all the navigational properties defined correctly)
var lstUsers = DBContext.Users.Where(
x => x.UserPermissions.Any(
y => y.Suppliers.Any(z => z.UserID == 6)
)
).ToList();
Above if you have UserID field in Supplier entity, if it is NOT you can again use the navigational property as,
var lstUsers = DBContext.Users.Where(
x => x.UserPermissions.Any(
y => y.Suppliers.Any(z => z.User.UserID == 6)
)
).ToList();
Contains() only expects a single element, so it won't work as you have it written. Try this as an alternate:
var Users = _db.Users
.Where(u => _db.UserPermissions
.Select(x => UserID)
.Distinct()
.Where(x => _db.UserPermissions
.Where(y => y.UserID == 6)
.Select(y => y.SupplierID)
.Contains(x))
);
I didn't try on my side but you can try using the let keyword:
var Users = (from u in _db.Users
let distinctUsers = (from up in _db.UserPermissions select up).Distinct()
let subQuery = (from up2 in _db.UserPermissions
where up2.UserID == 6
select up2)
where
distinctUsers.SupplierID== subQuery.SupplierID &&
u.UserID==distinctUsers.UserID
select u);

LINQ orderby in subquery

I want to do a simple subquery in LINQ to EF
I did something like this:
from p in db.SomeTable
let o = db.SomeTableWithDate
.OrderByDescending(t => t.Date)
.FirstOrDefault(lt => lt.SomeValue == value)
select new {p, o}
Everything compiles and LINQ isn't complaining, but the result is wrong.
The generated SQL is an OUTER APPLY with a TOP 1, but there is no 'ORDER BY'.
I also tried this:
from p in db.SomeTable
select new {
p,
o = db.SomeTableWithDate
.OrderByDescending(t => t.Date)
.FirstOrDefault(lt => lt.SomeValue == value)
}
But I get the same result. (I prefer 'let' because then I can use variables from the previous 'let' query)
So here is my question: how can i make LINQ do a real subquery with orderby?
I want to get the latest date from a linked table
Solution
The answer from boran solved it. I just had to do a seperate where first.
from p in db.SomeTable
let o = db.SomeTableWithDate
.Where(lt => lt.SomeValue == value)
.OrderByDescending(t => t.Date)
.FirstOrDefault()
select new {p, o}
from p in db.SomeTable
let o = db.SomeTableWithDate.Where(lt => lt.SomeValue == value)
.OrderByDescending(t => t.Date)
.FirstOrDefault()
select new {p, o}
Because you order after filtering, this query will probably have better performance too.

how to convert T-SQL Query to linq

i have 4 table in SQL: DocumentType,ClearanceDocument,Request, RequestDocument.
i want when page load and user select one request, show all Document Based on clearanceType in RequestTable and check in RequestDocument and when exist set is_exist=true
I have written this query with SqlServer Query Editor for get result this Scenario but i can't convert this Query to Linq
select *,
is_Orginal=
(select is_orginal from CLEARANCE_REQUEST_DOCUMENT
where
DOCUMENT_ID=a.DOCUMENT_ID and REQUEST_ID=3)
from
DOCUMENT_TYPES a
where
DOCUMENT_ID in
(select DOCUMENT_ID from CLEARANCE_DOCUMENTS dt
where
dt.CLEARANCE_ID=
(SELECT R.CLEARANCE_TYPE FROM CLEARANCE_REQUEST R
WHERE
R.REQUEST_ID=3))
i write this Query in linq but not work
var list = (from r in context.CLEARANCE_REQUEST
where r.REQUEST_ID == 3
join cd in context.CLEARANCE_DOCUMENTS on r.CLEARANCE_TYPE equals cd.CLEARANCE_ID
join dt in context.DOCUMENT_TYPES on cd.DOCUMENT_ID equals dt.DOCUMENT_ID into outer
from t in outer.DefaultIfEmpty()
select new
{
r.REQUEST_ID,
cd.CLEARANCE_ID,
t.DOCUMENT_ID,
t.DOCUMENT_NAME,
is_set=(from b in context.CLEARANCE_REQUEST_DOCUMENT where
b.REQUEST_ID==r.REQUEST_ID && b.DOCUMENT_ID==t.DOCUMENT_ID
select new{b.IS_ORGINAL})
}
).ToList();
I want convert this Query to LINQ. Please help me. Thanks.
There is no need to manually join objects returned from an Entity Framework context.
See Why use LINQ Join on a simple one-many relationship?
If you use the framework as intended your job will be much easier.
var result = var clearanceTypes = context.CLEARANCE_REQUEST
.Single(r => r.REQUEST_ID == 3)
.CLEARANCE_DOCUMENTS
.SelectMany(dt => dt.DOCUMENT_TYPES)
.Select(a => new
{
DocumentType = a,
IsOriginal = a.CLEARANCE_REQUEST_DOCUMENT.is_original
});
Since your query won't be executed untill you iterate over the data, you can split your query in several subqueries to help you obtain the results like this:
var clearanceIds = context.CLEARANCE_REQUEST
.Where(r => r.REQUEST_ID == 3)
.Select(r => r.CLEARANCE_TYPE);
var documentIds = context.CLEARANCE_DOCUMENTS
.Where(dt => clearanceIds.Contains(dt.CLEARANCE_ID))
.Select(dt => dt.DOCUMENT_ID);
var result = context.DOCUMENT_TYPES
.Where(a => documentIds.Contains(a.DOCUMENT_ID))
.Select(a => new
{
// Populate properties here
IsOriginal = context.CLEARANCE_REQUEST_DOCUMENT
.Single(item => item.DOCUMENT_ID == a.DOCUMENT_ID &&
item.REQUEST_ID == 3)
.IS_ORIGINAL
})
.ToList();

How can I make this more concise?

var users = from user in st.Users
where user.UDID == cr.User.Udid
select user;
var cityIds from city in users.First().Cities
select city.ID;
DoSomethingWith(cityIds);
It started as this query:
select CityID from UserCities inner join User on User.ID=UserID where User.UDID=#UDID;
I can't seem to get the join syntax right with Linq-to-Entities
Using query expressions isn't really helping you here, and you wouldn't need two of them anywehere. Here's a direct translation:
var cityIds = st.Users
.Where(user => user.UDID == cr.User.Udid)
.First()
.Cities
.Select(city => city.ID);
Now use the fact that First can take a predicate, and you can remove the Where:
var cityIds = st.Users
.First(user => user.UDID == cr.User.Udid)
.Cities
.Select(city => city.ID);
I figured out what I wanted to achieve.
var cityIds = from city in st.Users.First(x => x.UDID == cr.User.Udid).Cities
select city.ID

Linq can these sub queries be optimised?

This query takes a group of comments, then counts their upvotes and downvotes in the tblCommentVotes table.
At the moment, it counts these via the select new statement, in the form of a subquery. Would this be more efficient if it was in some sort of group by in the main query? Also if it would, could anyone show me how to do this, as I can't work out how you would do this.
// Get comments
var q = (
from C in db.tblComments
where
C.CategoryID == Category &&
C.IdentifierID == Identifier
join A in db.tblForumAuthors on C.UserID equals A.Author_ID
orderby C.PostDate descending
select new
{
C,
A.Username,
UpVotes = (from V in db.tblCommentVotes where V.CommentID == C.ID && V.UpVote == true select new { V.ID }).Count(),
DownVotes = (from V in db.tblCommentVotes where V.CommentID == C.ID && V.UpVote == false select new { V.ID }).Count()
}
)
.Skip(ToSkip > 0 ? ToSkip : 0)
.Take(ToTake > 0 ? ToTake : int.MaxValue);
What you need to do is to do an left outer join of the db.tblCommentVotes in the query expression, cause probably there might be no commentVotes?
When you have that, you should be able to perform ONE query in order to get your result.
It might look like this:
var q = (
from C in db.tblComments
where
C.CategoryID == Category &&
C.IdentifierID == Identifier
join A in db.tblForumAuthors on C.UserID equals A.Author_ID
// the following two lines are the left outer join thing.
join voteTemp in db.tblCommentVotes on voteTemp.CommentID equals C.ID into voteJoin
from vote in voteJoin.DefaultIfEmpty()
orderby C.PostDate descending
group C by new { Comment = C, Username = A.Username } into g
select new
{
g.Key.Comment,
g.Key.Username,
UpVotes = g.Count(x => x.UpVote),
DownVotes = g.Count(x => !x.UpVote)
}
)
.Skip(ToSkip > 0 ? ToSkip : 0)
.Take(ToTake > 0 ? ToTake : int.MaxValue);
This is untested and might not even compile, but I think it should be something like this.
db.tblComments.Where(c => c.CategoryID == Category && c.IdentifierID == Identifier)
.Join(db.tblForumAuthors, c => c.UserID, a => a.Author_ID,
(c, a) =>
new
{
CommentID = c,
AuthorName = a.UserName,
UpVotes = c.Join(db.tblCommentVotes, c => c.CommentID
v => v.CommentID,
(c, v) => v).Count(v => v.UpVote)
DownVotes = c.Join(db.tblCommentVotes, c => c.CommentID
v => v.CommentID,
(c, v) => v).Count(v => v.DownVote)
});
To optimise it's best first to measure.
Try, using something like LinqPad to view the generated SQL
Then use SQL Server Management Studio to see the query plan for that SQL
or:
Try running the code and seeing what SQL trace tells you is happening
Without the DB, it's quite hard (but fun) to guess whether that Linq will result in a single query or in multiple queries for working out the UpVotes and DownVotes. My guess is that calculating the UpVotes and DownVotes this way could be quite expensive - it may result in 2 additional queries per comment.
http://www.thereforesystems.com/view-query-generate-by-linq-to-sql/
without analyzing whats being output this question is impossible to answer.. however the link provided above should give you the tools necessary to perform this analysis yourself.

Categories

Resources