What is the lambda equivalent of this Linq query? - linq

What is the equivalent lambda syntax to this linq query?
Dim query = From t In _rdsqlconn.Tags Where t.TagWord = tag
Join p In _rdsqlconn.Posts On t.PostId Equals p.PostId Order By p.PostDatePublished
Descending Select p Where p.PostIsPublished = True

You can do this with a join, like so:
_rdsqlconn.Tags
.Where(t => t.TagWord == tag)
.Join(_rdsqlconn.Posts, t => t.PostId, p => p.PostId, (t, p) => p)
.Where(p => p.PostIsPublished == true)
.OrderByDescending(p => p.PostDatePublished)
but what you want to do is properly map your tables and relationships in the LINQ-to-SQL designer, and then you can use
_rdsqlconn.Posts.Where(p => p.PostIsPublished && p.Tags.Any(t => t.TagWord == tag))
.OrderByDescending(p => p.PostDatePublished)
If you have a foreign key between Posts and Tags in your database then you will be able to do this. It's much cleaner code, and removes the unnecessarily Join operator.

Related

Where in clause using linq

trying to convert a query which has 2 levels of where in clauses to linq and getting some errors. Can anybody help me on this?
Original Query:
select id
from student
where suId
in (select suId
from subjects
where cid
in (select id
from chapters
where chapter='C203'))
LINQ query:
var query = (from s in dc.students
let subs = (from su in dc.subjects
where su.cid == Convert.ToInt32(from c in dc.Chapters
where c.chapter == 'Ç203'
select c.id) //Single chapter id will be returned
select su.suid)
where subs.Contains(s.sid)
select s.id).ToArray();
Am getting below 2 errors while compiling app
'System.Linq.IQueryable' does not contain a definition for 'Contains' and the best extension method overload 'System.Linq.ParallelEnumerable.Contains(System.Linq.ParallelQuery, TSource)' has some invalid arguments
Instance argument: cannot convert from 'System.Linq.IQueryable' to 'System.Linq.ParallelQuery'
Since Linq is lazy-loading everything you don't need to cram everything into a single statement; you can do something like this:
var chapterIds = dc.Chapters
.Where(c => c.Chapter == "C023")
.Select(c => c.Id);
var subjectIds = dc.Subjects
.Where(s => chapterIds.Contains(s.Cid))
.Select(s => s.Suid);
var students = dc.Students
.Where(s => subjectIds.Contains(s.Suid))
.Select(s => s.Sid)
.ToArray();
This way you can debug each subquery by looking at what it returns.
However, looking at your original select you can rewrite the whole thing as a Join and get rid of the bugging issue:
var students = dc.Chapters.Where(c => c.Chapter == "C023")
.Join(dc.Subjects,
c => c.Id,
s => s.Cid,
(chapter, subject) => subject)
.Join(dc.Students,
subj => subj.Suid,
student => student.Suid,
(subj, st) => st.Sid)
.ToArray();

Convert linq query to lambda expression

I am having trouble converting this linq query to a lambda expression, I tried to solve it using include but not successful, please help
(from PS in _dbNavigation.Table1
join CP in _dbNavigation.Table2 on PS.PropName equals CP.PropName
where PS.IsDeleted == false && PS.UserName.Equals("REX")
select CP).ToList();
_dbNavigation.Table1
.Join(_dbNavigation.Table2, t1 => t1.PropName, t2 => t2.PropName, (t1, t2) => new { t1, t2 })
.Where(x => x.t1.IsDeleted == false && x.t2.UserName == "REX")
.Select(x => x.t2);

Linq To Entities Optional Distinct

Earlier I put a question on Stackoverflow about how to remove duplicate records in a list of objects, based on a particular property within each object.
I got the answer I was looking for (see below), a query which returns a distinct list of objects using MainHeadingID as the property to remove duplicates.
public IList<tblcours> GetAllCoursesByOrgID(int id)
{
return _UoW.tblcoursRepo.All.
Where(c => c.tblCourseCategoryLinks.Any(cl => cl.tblUnitCategory.tblUnit.ParentID == id))
.GroupBy(c => c.MainHeadingID)
.Select(g => g.FirstOrDefault())
.ToList();
}
However, now I need more help! Is there anyway of amending the query above so that, it only removes duplicate values when MainHeadingID is not equal to 180. I tried amending GroupBy line to
.GroupBy(c => c.MainHeadingID != 180)
However, this didn't work.
Any help would be much appreciated with this.
Thanks.
Following works for LINQ to SQL:
return _UoW.tblcoursRepo.All
.Where(c => c.tblCourseCategoryLinks.Any(cl => cl.tblUnitCategory.tblUnit.ParentID == id))
.GroupBy(c => c.MainHeadingID)
//.SelectMany(g => g.Key == 180 ? g : g.Take(1))
.SelectMany(g => g.Take(g.Key == 180 ? Int32.MaxValue : 1))
.ToList();
Comments: SelectMany in query above selects all items from group where MainHeadingID equals to 180, but it takes only one item form other groups (i.e. distinct result). Linq to SQL cannot translate commented out part, but thanks to #usr there is way around.
Linq to Entities cannot translate even simplified query. I think only option for you in this case is simple concating result of two queries:
Expression<Func<tblcours, bool>> predicate = x =>
x.tblCourseCategoryLinks.Any(cl => cl.tblUnitCategory.tblUnit.ParentID == id)
int headingId = 180;
return _UoW.tblcoursRepo.All
.Where(c => c.MainHeadingID != headingId)
.Where(predicate)
.GroupBy(c => c.MainHeadingID)
.Select(g => g.FirstOrDefault())
.Concat(_UoW.tblcoursRepo.All
.Where(c => c.MainHeadingID == headingId)
.Where(predicate))
.ToList();
lazyberezovsky's answer fails due to an EF bug (which is not surprising given the quality of EF's LINQ support). It can be made to work with a hack:
.SelectMany(g => g.Key == 180 ? g.Take(int.MaxValue) : g.Take(1))
or
.SelectMany(g => g.Take(g.Key == 180 ? int.MaxValue : 1))
Note that performance will not be particularly good due to the way this is translated to SQL.

NHibernate Fetch / ThenFetch for joined siblings

I have the following (simplified) hierarchy of entities:
RootClass
->DescriptorClass
->SomeChild->DescriptorClass
->SomeGrandChild
I would like to fetch everything in a single query, if possible.
Currently I have the following:
Session.Query<RootClass>().Where(/*some expressions here*/)
.Fetch(v => v.DescriptorClass)
.Fetch(v => v.SomeChild).ThenFetch(v => v.SomeGrandChild)
.Fetch(v => v.SomeChild).ThenFetch(v => v.DescriptorClass);
it works fine but it creates an SQL query with two joins on SomeChild. Obviously, I have to get rid of that second Fetch(v => v.SomeChild) but I cannot find how to do it.
I tried:
Session.Query<RootClass>().Where(/*some expressions here*/)
.Fetch(v => v.DescriptorClass)
.Fetch(v => v.SomeChild).ThenFetch(v => v.SomeGrandChild)
.ThenFetch(v => v.DescriptorClass); //<- wrong, tries to find DescriptorClass on SomeGranchild
and
Session.Query<RootClass>().Where(/*some expressions here*/)
.Fetch(v => v.DescriptorClass)
.Fetch(v => v.SomeChild).ThenFetch(v => v.SomeGrandChild)
.Fetch(v => v.DescriptorClass); //<- wrong, loads the same DescriptorClass of RootClass, not on SomeChild
How do I tell NHibernate to create a single join on SomeChild and then fetch SomeGrandChild and DescriptorClass of SomeChild?
For this type of query, switch to using the LINQ query syntax instead of lambdas as it gives you more control and typically outputs more efficient and cleaner SQL.
Take a look at the sample below and notice how I am able to reference the Customer entity multiple times by using the alias 'c'.
var customers =
(
from c in session.Query<Customer>()
from a in c.Addresses
from pn in c.PhoneNumbers
where c.Status == "Active"
&& a.City == "Dallas"
&& pn.AreaCode == "972"
select c )
.ToList();
This will result in the following SQL using NHibernate:
SELECT
customer0_.CustomerId as Customer1_135_0_,
customer0_.Status as Customer1_135_1_
FROM
Customer customer0_
INNER JOIN
CustomerAddresses customeraddr1_
ON customer0_.CustomerId=customeraddr1_.CustomerId
INNER JOIN
CustomerPhoneNumbers customerphon2_
ON customer0_.CustomerId=customerphon2_.CustomerId
WHERE
customer0_.Status='Active' /* #p0 */
AND customeraddr1_.City = 'Dallas' /* #p1 */
AND customerphon2_.AreaCode = '972' /* #p2 */;

Complex Linq Collection Query

I have this DB diagram and want to make a query to find all UserLists in a given region. RegionId is supplied.
So I can get all the departments by this code (may not be the best way..):
var region = context.Regions.Find(regionId);
IEnumerable<Department> departments = region.Areas
.SelectMany(a => a.Workplaces)
.SelectMany(w => w.Departments);
The Account can have many UserLists, and an Account can be linked to many Departments. Can someone formulate a queryto achieve this please?
for completeness the final code was:
List<UserList> query2 = context.Regions.Where(r => r.RegionId == regionId)
.SelectMany(r => r.Areas)
.SelectMany(a => a.Workplaces)
.SelectMany(w => w.Departments)
.SelectMany(d => d.AccountsAllowedToPost)
.Distinct()
.SelectMany(da => da.Lists).ToList();
You can use the let syntax (or the .Select method) to navigate the ManyToOne relationship.
var query =
from r in context.Regions
where r.RegionId == regionId
from a in r.Areas
from w in a.Workplaces
from d in w.Departments
from da in d.DepartmentAccounts
let acc = da.Account
from u in acc.UserLists
select u;
var query2 = context.Regions.Where(r => r.RegionId == regionId)
.SelectMany(r => r.Areas)
.SelectMany(a => a.Workplaces)
.SelectMany(w => w.Departments)
.SelectMany(d => d.DepartmentAccounts)
.Select(da => da.Account)
.SelectMany(acc => acc.UserLists);

Resources