Linq Query with double sub queries - c#

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

Related

How to write Entity Framework query to be able to join to the ICollection?

I'm trying to work out the Entity Framework syntax to return the User.Name, User.Email for the given Profile.email.
1 profile can have N categories. 1 category can have 1 User.
Profile - ID, email, Name, CreatedDate
Category - ID, ProfileId, Name, UserID
User - ID, Name, Email
In SQL I would write:
SELECT U.NAME, U.EMAIL
FROM PROFILE P
JOIN CATEGORY C ON P.ID = C.PROFILEID
JOIN USER U ON C.USERID = U.ID
WHERE P.EMAIL = 'SOME#EMAIL.COM'
Here is what I tried:
var data = await _context.Profiles
.AsNoTracking()
.Where(p => p.Categories.Users.email == 'some#email.com')
.Select(u => new
{
UName = u.Name,
UEmail = u.Email
}).ToListAsync();
The problem is that p.Categories is an ICollection, so I don't know how to proceed because p.Categories doesn't give me access to the .Users. I can write p.Categories.Where.... but I'm not sure how to proceed.
Instead of starting with _context.Profiles. should I be starting with _context.Users.?
Can someone help me on how to think about the approach when writing the Entity Framework query?
If I understood your model correctly, this should work:
var data = await _context.Categories.AsNoTracking()
.Where(c=>c.Profile.email == "some#email.com")
.Select(c=>new {
UName=c.User.Name,
UEmail=c.User.Email
}).ToListAsync();
Ofcourse this requires your model to have navigation properties set.
So just start your query the Categories in LINQ form:
from c in _context.Categories
where c.Profile.Email == someEmail
select new { c.User.Name, c.User.Email }
or in Lambda form:
_context.Categories
.Where( c => c.Profile.Email == someEmail )
.Select( c => new {c.User.Name, c.User.Email}
or start from Profiles and use SelectMany, whose LINQ form looks like
from p in _context.Profiles
from c in p.Categories
where p.Email == someEmail
select new {c.User.Name, c.User.Email}
or in Lambda form:
_context.Profiles
.Where(p => p.Email == someEmail)
.SelectMany(p => p.Categories)
.Select( c => new {c.User.Name, c.User.Email} )

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.

Is there any way to optimize this LINQ to Entities query?

I was asked to produce a report that is driven by a fairly complex SQL query against a SQL Server database. Since the site of the report was already using Entity Framework 4.1, I thought I would attempt to write the query using EF and LINQ:
var q = from r in ctx.Responses
.Where(x => ctx.Responses.Where(u => u.UserId == x.UserId).Count() >= VALID_RESPONSES)
.GroupBy(x => new { x.User.AwardCity, x.Category.Label, x.ResponseText })
orderby r.FirstOrDefault().User.AwardCity, r.FirstOrDefault().Category.Label, r.Count() descending
select new
{
City = r.FirstOrDefault().User.AwardCity,
Category = r.FirstOrDefault().Category.Label,
Response = r.FirstOrDefault().ResponseText,
Votes = r.Count()
};
This query tallies votes, but only from users who have submitted a certain number of required minimum votes.
This approach was a complete disaster from a performance perspective, so we switched to ADO.NET and the query ran very quickly. I did look at the LINQ generated SQL using the SQL Profiler, and although it looked atrocious as usual I didn't see any clues as to how to optimize the LINQ statement to make it more efficient.
Here's the straight TSQL version:
WITH ValidUsers(UserId)
AS
(
SELECT UserId
FROM Responses
GROUP BY UserId
HAVING COUNT(*) >= 103
)
SELECT d.AwardCity
, c.Label
, r.ResponseText
, COUNT(*) AS Votes
FROM ValidUsers u
JOIN Responses r ON r.UserId = u.UserId
JOIN Categories c ON r.CategoryId = c.CategoryId
JOIN Demographics d ON r.UserId = d.Id
GROUP BY d.AwardCity, c.Label, r.ResponseText
ORDER BY d.AwardCity, s.SectionName, COUNT(*) DESC
What I'm wondering is: is this query just too complex for EF and LINQ to handle efficiently or have I missed a trick?
Using a let to reduce the number of r.First()'s will probably improve performance. It's probably not enough yet.
var q = from r in ctx.Responses
.Where()
.GroupBy()
let response = r.First()
orderby response.User.AwardCity, response.Category.Label, r.Count() descending
select new
{
City = response.User.AwardCity,
Category = response.Category.Label,
Response = response.ResponseText,
Votes = r.Count()
};
Maybe this change improve the performance, removing the resulting nested sql select in the where clause
First get the votes of each user and put them in a Dictionary
var userVotes = ctx.Responses.GroupBy(x => x.UserId )
.ToDictionary(a => a.Key.UserId, b => b.Count());
var cityQuery = ctx.Responses.ToList().Where(x => userVotes[x.UserId] >= VALID_RESPONSES)
.GroupBy(x => new { x.User.AwardCity, x.Category.Label, x.ResponseText })
.Select(r => new
{
City = r.First().User.AwardCity,
Category = r.First().Category.Label,
Response = r.First().ResponseText,
Votes = r.Count()
})
.OrderByDescending(r => r.City, r.Category, r.Votes());

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

Categories

Resources