Convert SqlServer Query to LINQ - c#

I have written the below given query in SQL server 2008:
(SELECT p.postid,
p.title,
p.disabled,
l.locationname
FROM posts p
INNER JOIN categories c
ON p.categoryid = c.categoryid
INNER JOIN users u
ON p.userid = u.userid
INNER JOIN tags t
ON p.tagid = t.tagid
INNER JOIN locations l
ON p.locationid = l.locationid
LEFT JOIN postimages pm
ON p.postid = pm.postid
WHERE p.categoryid = 1
GROUP BY p.postid,
p.title,
p.disabled,
l.locationname)
ORDER BY p.postid DESC
I want to write the above query in LINQ.
I tried a little and able to write the query below:
var objPosts = (from p in _dbcontext.Posts
join us in _dbcontext.Users on p.UserId equals us.UserId
join tag in _dbcontext.Tags on p.TagId equals tag.TagId
join cat in _dbcontext.Categories on p.CategoryId equals cat.CategoryId
join loc in _dbcontext.Locations on p.LocationId equals loc.LocationId
join img in _dbcontext.PostImages on p.PostId equals img.PostId into gj
from postimg in gj.DefaultIfEmpty()
where p.Disabled == false && p.CategoryId == userPost.CategoryId || p.UserId == userPost.UserId || p.TagId == userPost.TagId || p.LocationId == userPost.LocationId
orderby p.PostId descending
select new
{
PostId = p.PostId,
PostTitle = p.Title,
//ImageInfo = postimg.ImagePath,
//ThumbNailInfo = p.ThubNailInfo,
PostShortDescription = p.ShortDescription,
UserId = us.UserId,
UserName = us.Name,
TagId = tag.TagId,
TagTitle = tag.Title,
CategoryId = cat.CategoryId,
CategoryName = cat.CategoryName,
LocationId = loc.LocationId,
LocationName = loc.LocationName
});
I am unable to apply the group by logic in LINQ. Can anyone please convert my SQL to LINQ. Thanks

group new { p, l } by new
{
p.postid,
p.title,
p.disabled,
l.locationname
} into g
orderby g.Key.PostId descending
select new
{
g.key.postid,
g.key.title,
g.key.disabled,
g.key.locationname
}

The missing part in your query is that you are not selecting anything...
You got the joins ok, left outer join ok, predicates (where), ordering, but no projection, and no group by.
Below should get you started:
group p by p.PostId into pg
select new {Post=p, Location=loc};
This would return return a collection of new objects whose properties Post and Location.

Related

Multiple Left Join with Linq and Defaults Values

I'm trying to write a query that contains multiple left joins in linq in a c# .netcore 2.2 application. I've tried writing the query in linq but it is not properly retrieving all the rows. Query I'm trying to convert is as follows.
select ISNULL(b.Id, '00000000-0000-0000-0000-000000000000') as 'Bat Id', p.Id as 'ProductId', p.RP, u.UnitName as 'UnitName', ISNULL(b.QTY,0) as 'BusQty', p.[Name] as 'Product Name'
from Products p left join Bat b
ON p.Id = b.ProductId
left join Units u on p.UOId = u.Id;
linq I have so far
var allProducts = (from p in _db.Products
join s in _db.Bat on p.Id equals s.ProductId into ps
from s in ps.DefaultIfEmpty()
join u in _db.Units on p.UOId equals u.Id
select new
{
BatId = s.Id == null ? Guid.NewGuid() : s.Id,
RP = p.RP,
BusQty = s.QTY == null ? 0 : s.QTY,
ProductName = p.Name,
UnitName = u.UnitName,
ProductId = p.Id,
}).ToList();
You are missing DefaultIfEmpty() on Units, thereby turning it into an inner join
var allProducts = (
from p in _db.Products
join s in _db.Bat on p.Id equals s.ProductId into ps
from s in ps.DefaultIfEmpty()
join u in _db.Units on p.UOId equals u.Id into us
from u in us.DefaultIfEmpty()
select new
{
BatId = s.Id ?? Guid.NewGuid(),
RP = p.RP,
BusQty = s.QTY ?? 0,
ProductName = p.Name,
UnitName = u.UnitName,
ProductId = p.Id,
}).ToList();

Struggling to convert SQL query to it's LINQ equivalent - Multiple joins, groupings and aggregate functions

I have the following query that gives me expected results in SQL Server Management Studio:
SELECT
u.DisplayName,
up.ColorPreferences,
SUM(rt.Score) AS Points,
COUNT(*) AS Plans,
MAX(pl.Created) AS MaxDate
FROM
[dbo].[Users] u
INNER JOIN
[dbo].[PlanLogs] pl ON u.Id = pl.UserId
INNER JOIN
[dbo].[ResourceTypes] rt ON pl.ResourceTypeId = rt.Id
INNER JOIN
[dbo].[UserProfile] up ON pl.UserId = up.UserId
GROUP BY
u.DisplayName, up.ColorPreferences;
an I have the following working linq query:
from u in _context.Users
join pl in _context.PlanLogs on u.Id equals pl.UserId
join rt in _context.ResourceTypes on pl.ResourceTypeId equals rt.ID
join up in _context.UserProfile on pl.UserId equals up.UserId
group rt by new { u.DisplayName, up.ColorPreferences} into g
select new
{
DisplayName = g.Key.DisplayName,
ColorPrefs = g.Key.ColorPreferences,
Points = g.Sum(x => x.Score),
Plans = g.Count()
};
As you can see, it is missing MaxDate. I can't get access to MaxDate because g contains properties from rt. I've tried the following and i get "Value does not fall within the expected range"
from u in _context.Users
join pl in _context.PlanLogs on u.Id equals pl.UserId
join rt in _context.ResourceTypes on pl.ResourceTypeId equals rt.ID
join up in _context.UserProfile on pl.UserId equals up.UserId
group new { rt, pl } by new { u.DisplayName, up.ColorPreferences} into g
select new
{
DisplayName = g.Key.DisplayName,
ColorPrefs = g.Key.ColorPreferences,
Points = g.Sum(x => x.rt.Score),
Plans = g.Count()
MaxDate = g.Max(m => m.pl.Created)
};
How do i add MaxDate to the results?
Thanks
Have you tried accessing the max value from pl.created on your first linq query? why are you grouping by rt and not the whole result? try this instead :
from u in _context.Users
join pl in _context.PlanLogs on u.Id equals pl.UserId
join rt in _context.ResourceTypes on pl.ResourceTypeId equals rt.ID
join up in _context.UserProfile on pl.UserId equals up.UserId
group u by new { u.DisplayName, up.ColorPreferences} into g
select new
{
DisplayName = g.Key.DisplayName,
ColorPrefs = g.Key.ColorPreferences,
Points = g.Sum(x => x.Score),
Plans = g.Count(),
MaxDate = g.Max(m => m.pl.Created)
};
I solevd this in the end. I needed to pass the specific columns to the group not the whole table:
group new { rt.Score, pl.Created } by..
rather than
group new { rt, pl } by...
Working query:
from u in _context.Users
join pl in _context.PlanLogs on u.Id equals pl.UserId
join rt in _context.ResourceTypes on pl.ResourceTypeId equals rt.ID
join up in _context.UserProfile on pl.UserId equals up.UserId
group new { rt.Score, pl.Created } by new { u.DisplayName, up.ColorPreferences } into g
select new
{
DisplayName = g.Key.DisplayName,
ColorPrefs = g.Key.ColorPreferences,
Points = g.Sum(i => i.Score),
Plans = g.Count(),
MaxCreated = g.Max(i => i.Created).ToString("dd/MM/yyyy HH:mm")
}

T-SQL to LINQ query that has a case statement and a subquery in it

I'm a complete beginner in LINQ and I would like to convert this T-SQL query in LINQ
SELECT
CASE
WHEN D.IsBaseloadDefined = 1
THEN COUNT(D.DeviceID)
ELSE
(SELECT COUNT(DORG.DeviceID)
FROM DeviceOrganization DORG
INNER JOIN Organization ORG ON DORG.OrganizationID = ORG.OrganizationID
INNER JOIN BaseloadOrganization BO ON ORG.BaseloadOrganizationId = BO.OrganizationID
INNER JOIN Baseload BL ON BO.BaseloadID = BL.BaseloadID
WHERE DORG.DeviceID = D.DeviceID
AND BL.RecursUntil >= GETDATE()
GROUP BY DORG.DeviceID)
END AS [Nb of devices]
FROM DeviceOrganization DO
INNER JOIN Device D ON DO.DeviceID = D.DeviceID
LEFT JOIN BaseloadDevice BD ON D.DeviceID = BD.DeviceID
LEFT JOIN Baseload B ON BD.BaseloadID = B.BaseloadID AND B.RecursUntil >= GETDATE()
INNER JOIN OrganizationHierarchy OH ON DO.OrganizationID = OH.SubOrganizationID
WHERE OH.OrganizationID = 6
AND D.IsActive = 1
group by D.DeviceID, D.IsBaseloadDefined
I've seen this topic but I don't really understand the answer
The only thing I could do so far is this, and now I'm completly lost
from deviceO in _context.DeviceOrganizations
join d in _context.Devices on deviceO.DeviceID equals d.DeviceID
join bd in _context.BaseloadDevices on d.DeviceID equals bd.DeviceID
join b in _context.Baseloads on bd.BaseloadID equals b.BaseloadID
join oh in _context.OrganizationHierarchies on deviceO.OrganizationID equals oh.SubOrganizationID
where oh.OrganizationID == OrganizationId
where d.IsActive == true
where b.RecursUntil <= DateTime.Now
group d.DeviceID by d.DeviceID).Count()
Instead of get count of group
group d.DeviceID by d.DeviceID).Count()
you should save result in variable
var data = from deviceO in _context.DeviceOrganizations
join d in _context.Devices on deviceO.DeviceID equals d.DeviceID
join bd in _context.BaseloadDevices on d.DeviceID equals bd.DeviceID
join b in _context.Baseloads on bd.BaseloadID equals b.BaseloadID
join oh in _context.OrganizationHierarchies on deviceO.OrganizationID equals oh.SubOrganizationID
where oh.OrganizationID == OrganizationId
where d.IsActive == true
where b.RecursUntil <= DateTime.Now
and then you should do something like this:
//group by 2 properties
var result = data.GroupBy(d => new { d.DeviceID, d.IsBaseloadDefined })
.Select(g =>
{
//for each group we get IsBaseloadDefined property
var IsBaseloadDefined = g.Key.IsBaseloadDefined;
if (IsBaseloadDefined == 1)
{
return g.Count();
}
else
{
// here another select that return count:
//(SELECT COUNT(DORG.DeviceID)
//FROM DeviceOrganization DORG
// INNER JOIN Organization ORG ON DORG.OrganizationID = ORG.OrganizationID
//INNER JOIN BaseloadOrganization BO ON ORG.BaseloadOrganizationId = BO.OrganizationID
//INNER JOIN Baseload BL ON BO.BaseloadID = BL.BaseloadID
//WHERE DORG.DeviceID = D.DeviceID
//AND BL.RecursUntil >= GETDATE()
//GROUP BY DORG.DeviceID)
//in this query you should return Count() of group
return 1; //return group.Count() instead 1
}
});
I hope this helps you

Linq With Entities List within Distinct List

I have a SQL Query that gets Distinct rows of Vendor Contacts, which is returning the proper number of rows:
SELECT DISTINCT v.VendorID, c.ContactID,
v.VendorName,
c.FirstName, c.MiddleName, c.LastName,
FROM VendorContacts vc
INNER JOIN Contact c
ON c.ContactID = vc.ContactID
INNER JOIN Vendor v
ON v.VendorID = vc.VendorID
LEFT JOIN [ContactServices] psvc
ON psvc.ContactID = c.ContactID
AND psvc.VendorID = v.VendorID
I have a method that I want to return of custom type, based on the above query:
public List<ProviderContactInfo> GetProviderContactInfo(ProviderContactInfo searchInfo)
{
using (var db = new MyContext())
{
var providerContactInfo =
(from vc in db.VendorContacts
join ps in db.ContactServices on new { vc.ContactID, vc.VendorID } equals new { ps.ContactID, ps.VendorID } into ps_join
from ps in ps_join.DefaultIfEmpty()
join c in db.Contacts on vc.ContactID equals c.ContactID
join v in db.Vendors on vc.VendorID equals v.VendorID
orderby vc.ContactID descending
select new ProviderContactInfo()
{
VendorName = v.VendorName,
FirstName = c.FirstName,
MiddleName = c.MiddleName,
LastName = c.LastName,
Services = (from o in db.ContactServices
join cps in db.Contacts on o.ContactID equals cps.ContactID
join vps in db.Vendors on o.VendorID equals vps.VendorID
join s in db.Services on o.ServiceID equals s.ServiceID
where ps.ServiceID == o.ServiceID
&& o.ContactID == c.ContactID
&& o.VendorID == v.VendorID
select s).ToList()
}).Distinct().ToList();
return providerContactInfo;
}
}
I'm getting the error:
Additional information: The 'Distinct' operation cannot be applied to
the collection ResultType of the specified argument.
Everything works fine when I remove the Services property from the new ProviderContactInfo so I'm sure it's in the way I'm trying to populate that property (which is of type List<Service>)
I know there are a lot of questions regarding Linq with Distinct etc but I couldn't find anything on this specific problem.
Please help!
EDIT This code works:
public List<ProviderContactInfo> GetProviderContactInfo(ProviderContactInfo searchInfo)
{
using (var db = new MyContext())
{
var providerContactInfo =
(from vc in db.VendorContacts
join c in db.Contacts on vc.ContactID equals c.ContactID
join v in db.Vendors on vc.VendorID equals v.VendorID
orderby vc.ContactID descending
select new ProviderContactInfo()
{
VendorName = v.VendorName,
FirstName = c.FirstName,
MiddleName = c.MiddleName,
LastName = c.LastName,
Services = (from o in db.ContactServices
join cps in db.Contacts on o.ContactID equals cps.ContactID
join vps in db.Vendors on o.VendorID equals vps.VendorID
join s in db.Services on o.ServiceID equals s.ServiceID
where o.ContactID == c.ContactID
&& o.VendorID == v.VendorID
select s).ToList()
}).Distinct().ToList();
return providerContactInfo;
}
}
You should add one more step to the process, to avoid applying Distinct() on the objects that have a collection (Services) property, the LINQ provider does not know how to handle it.
var providerContactInfo = from vc in db.VendorContacts
join ps in db.ContactServices on new { vc.ContactID, vc.VendorID } equals new { ps.ContactID, ps.VendorID } into ps_join
from ps in ps_join.DefaultIfEmpty()
join c in db.Contacts on vc.ContactID equals c.ContactID
join v in db.Vendors on vc.VendorID equals v.VendorID
orderby vc.ContactID descending
group ps by new
{
v.VendorName,
c.FirstName,
c.MiddleName,
c.LastName,
c.ContactID,
v.VendorID
} into g
select new ProviderContactInfo()
{
VendorName = g.Key.VendorName,
FirstName = g.Key.FirstName,
MiddleName = g.Key.MiddleName,
LastName = g.Key.LastName,
Services = (from e in g
from o in db.ContactServices
join cps in db.Contacts on o.ContactID equals cps.ContactID
join vps in db.Vendors on o.VendorID equals vps.VendorID
join s in db.Services on o.ServiceID equals s.ServiceID
where e.ServiceID == o.ServiceID
&& o.ContactID == g.Key.ContactID
&& o.VendorID == g.Key.VendorID
select s).ToList()
}
Wouldn't this be much simplier:
public IQueryable<VendorContact> GetProviderContactInfo(ProviderContactInfo searchInfo)
{
using (var db = new MyContext())
{
return providerContactInfo=db.VendorContacts
.Include(vc=>vc.Contacts)
.Include(vc=>vc.Services)
.Include(vc=>vc.Vendor)
.OrderByDescending(vc=>vc.ContactID);
}
}

simplifying two queries into one with the same types linq

I have this code:
var commentData = from o in quack.BlogComments
join u in quack.AdminUsers
on o.UserId equals u.AdminUserId
where blogid == o.BlogId
select new
{
o.Comment,
o.CommentDate,
u.FirstName,
u.LastName
};
var commentData2 = from o in quack.BlogComments
join u in quack.RegularUsers
on o.UserId equals u.RegularUserId
where blogid == o.BlogId
select new
{
o.Comment,
o.CommentDate,
u.FirstName,
u.LastName
};
var l = commentData.ToList();
l.AddRange(commentData2);
As you can see above I am doing 2 different queries to the database and then adding them together to generate a single list to be used in the gridview.
What I want is to only use 1 query to the database and will result to two of those table combined.
How can I do it? is it possible with multiple joins?
You should use Concat:
var commentData = (from o in quack.BlogComments
join u in quack.AdminUsers
on o.UserId equals u.AdminUserId
where blogid == o.BlogId
select new
{
o.Comment,
o.CommentDate,
u.FirstName,
u.LastName
}).Concat(from o in quack.BlogComments
join u in quack.RegularUsers
on o.UserId equals u.RegularUserId
where blogid == o.BlogId
select new
{
o.Comment,
o.CommentDate,
u.FirstName,
u.LastName
});
var l = commentData.ToList();

Categories