Can make left join in Entity Framework without dataset - c#

I have this query
return _ctx.TestPackages.Where(s => s.Id == TestPackageId).
Join(_ctx.TestPackageReportDetails, s => s.Id, d => d.TestPackageId, (s, d) => new { reportDetail = d, testpack = s }).
Join(_ctx.TestPackageReports, p => p.reportDetail.TestPackageReportId, o => o.Id, (p, o) => new { combined = p, report = o })
.ToList()
As you can see my query makes join between 3 tables TestPackages TestPackageReportDetails and TestPackageReports. When I have more than one record in TestPackageReportDetails with same testpackageid, the result is repeated 3 times in the output. How can I avoid the repetition?
Should I make a left join between TestPackageReportDetails and TestPackages? If yes how can I do that?

If your intention is to Eager load the report details then you should be using .Include :
_ctx.TestPackages.Include(t=>t.TestPackageReportDetails.TestPackageReports).Where(s => s.Id == TestPackageId);

Since you're only selecting three fields (As shown before you edit your answer again and remove the select) then you can do this:
(from s in _ctx.TestPackages
join d in _ctx.TestPackageReportDetails,
on s.Id equals d.TestPackageId
join r in _ctx.TestPackageReports
on s.Id equals r.reportDetail.TestPackageReportId
where s.Id == TestPackageId
select new
{
s.Id,
s.packageNumber,
s.Size,
s.TestPackageOrder
}).Distinct().ToList().Select(m=> new ..) // continue your normal selection

Related

Inner join in C# with lambda

I'm new to LINQ and Lambda. I want to make query that will get me for every studyYear (1,2,3) students who are studying that year. I've done it without Lambda but I really want to know how to do it with lambda.
var res = from s in db.student
join u in db.EnrolledYear
on s.ID equals u.studentID
join g in db.studyYear
on u.studyYearID equals g.ID
select new
{
Year = g.year,
StudentFName = s.FirstName
StudentLName = s.LastName
};
I checked out other examples with lambda but I didn't really understand .
What I managed is to make inner join between student and enrolled year. Now I don't understand how to finish inner join between enrolled year and study year.
I'm stuck here, I need to make one more join:
var res = db.EnrolledYear.Join(db.student,
u => u.studentID, s => s.ID,
(enroll, student) => new
{
Godina = enroll.year,
FName = student.FirstName
LName = student.LastName
})
.Join(.....?)
Use Include.
Something like:
db.students.Include(x => x.EnrolledYears).ThenInclude(x=>x.StudyYear).Select(new ...)
Every clause in a query will correspond to a lambda call. Just go down to every clause and convert to an appropriate call.
This particular query could be written like so:
db.student
.Join(db.EnrolledYear, s => s.ID, u => u.studentID, (s, u) => new { s, u })
.Join(db.studyYear, x => x.u.studyYearID, g => g.ID, (x, g) => new { x.s, x.u, g })
.Select(x => new
{
Year = x.g.year,
StudentFName = x.s.FirstName,
StudentLName = x.s.LastName,
});

How to Perform a Join Operation with a Group by In EF CORE

I have two tables Clients and ClientEvaluations, those tables are connected through a foreign key. Each ClientEvaluation has FK to a single Client Entity.
Now i need to query all clients with their last evaluation, no more than one valuation per client. Note that each evaluation has a date.
This code here achieves that in SQL.
SELECT C.Id, MAX(E.EvaluationDate) FROM [dbo].[Clients] as C
JOIN [dbo].[ClientEvaluations] AS E ON E.ClientId = C.Id
GROUP BY C.Id
I have also tried this but the problem with what I'm trying to achieve is that i need to get back from this query the Client entity properties as well.
var lastEvaluations = _db.ClientEvaluations.GroupBy(x => x.ClientId, (x, y) => new { ClientId = x, EvaluationDate = y.Max(z => z.EvaluationDate), }).ToList();
But the query here of course only returns the ClientId and the date, how can i include the whole client entity?
I hope you have configured _dbContext correctly. Then you can use include to do the join operation.
var results = _dbcontext
.Clients
.Include(x => x.ClientEvaluations) //join
.GroupBy(y => y.Id) // group by
.Select(z => new
{
Id = z.Key.Value,
Max = z.Max(x => x.EvaluationDate),
}).ToList();
Or
var results = from c in _dbcontext.Clients
join e in _dbcontext.ClientEvaluations
on c.Id equals e.ClientId
group c by c.Id into cg
select new
{
Id = Id = cg.FirstOrDefault().Id,
Max = cg.Max(x => x.EvaluationDate),
}).ToList();

Perform GroupJoin and Join in single query in Entity Framework Core

I am trying to do a join inside a groupjoin using Linq. Here's the scenario - for each lineitem in an order, I want to get some attributes from the variant for that line item(such as variant options and imageid which are stored in variant and product databases). i want to join lineitem, variant and product databases to get these extra information for the lineitem and then groupjoin this lineitem with the order table. i have done this and got the desired result using two queries but I am not sure how many database queries are run. And in the first case(ie in the join, a limit cannot be specified so I just want to make sure that the first iqueryable in the code below is evaluated only when .ToListAsync() is called). Is this the right way to do it? Any help would be greatly appreciated!
public async Task<dynamic> GetOrdersAsync(User user, int pageNumber = 1)
{
var perPage = 10;
var lineItemAndVariant = from l in _context.LineItems
join v in _context.Variants
on l.EcomPlatformVariantId equals v.EcomPlatformVariantId
join p in _context.Products
on v.ProductId equals p.Id
where p.UserId == user.Id
select new
{
l.Id,
l.OrderId,
l.Price,
l.Quantity,
l.EcomPlatformLineItemId,
l.EcomPlatformVariantId,
l.Title,
ImageId = v.ImageId ?? p.ThumbnailId, // for adding image
p.MyVendor,
p.MyVendorId,
v.Option1,
v.Option2,
v.Option3,
p.VariantOption1Label,
p.VariantOption2Label,
p.VariantOption3Label
};
var orders = await _context.Orders
.Where(o => o.UserId == user.Id)
.OrderByDescending(o => o.Id)
.Skip(perPage * (pageNumber - 1)).Take(perPage)
.GroupJoin(lineItemAndVariant, o => o.Id, l => l.OrderId,
(o, lineItems) => new
{
o.Id,
o.EcomPlatformOrderId,
o.CustomerEmail,
o.FirstName,
o.LastName,
o.Address1,
o.Address2,
o.City,
o.Company,
o.Country,
o.Currency,
o.OrderNumber,
o.Phone,
o.Province,
o.TotalPrice,
o.UserId,
o.Zip,
o.CreatedAt,
LineItems = lineItems
})
.AsNoTracking().ToListAsync();
return orders;
}

translating complex T-SQL query to LINQ to Entities

This is the SQL code the produces the correct results:
select s.Code, s.Name, coalesce(ss.Url, a.Url), a.SocialMediaTypeKey
from School s
Left join
(
SELECT dbo.SchoolSocialMedia.SocialMediaTypeKey
, SchoolSocialMedia.Url
, dbo.Department.Name
, dbo.Department.ImportBusinessKey
FROM dbo.SchoolSocialMedia
INNER JOIN dbo.Department ON dbo.SchoolSocialMedia.DepartmentId = dbo.Department.Id
) A
ON 1 = 1
Left join dbo.SchoolSocialMedia ss ON ss.SchoolId = s.Id and ss.SocialMediaTypeKey = a.SocialMediaTypeKey
where s.[DeactivatedDate] is null
This is how far I have gotten in C#, but it is not producing the correct results--in fact, it is returning zero results:
var departmentSocialMediaResult =
from ssm in context.SchoolSocialMedia
from d in context.Department.Where(d => d.Id == ssm.DepartmentId)
select new { ssm.SocialMediaTypeKey,
ssm.Url,
d.Name,
ssm.SchoolId };
var result =
(from s in context.School
from ssm in context.SchoolSocialMedia.DefaultIfEmpty()
from dssm in departmentSocialMediaResult.DefaultIfEmpty()
.Where(dssm => dssm.SchoolId == s.Id && dssm.SocialMediaTypeKey == ssm.SocialMediaTypeKey)
select new { ssm.SchoolId, ssm.SocialMediaTypeKey, ssm.Url })
.ToDictionary(ssm => new SchoolSocialMediaKey(
ssm.SchoolId, ssm.SocialMediaTypeKey),
ssm => ssm.Url);
Does anyone have any suggestions on how to better convert the T-SQL to LINQ to Entities? What am I doing wrong? TIA.
UPDATE:
Thank you, #Aducci, your response is the correct answer. Since the result is being put into a dictionary, this is what I ended up using:
var query =
(from s in context.School
from a in
(
from ssm in context.SchoolSocialMedia
join d in context.Department on ssm.DepartmentId equals d.Id
select new
{
ssm.SocialMediaTypeKey,
ssm.Url,
d.Name
}
).DefaultIfEmpty()
from ss in context.SchoolSocialMedia
.Where(x => s.Id == x.SchoolId)
.Where(x => a.SocialMediaTypeKey == x.SocialMediaTypeKey)
.DefaultIfEmpty()
select new
{
ss.SchoolId,
Url = ss.Url ?? a.Url,
a.SocialMediaTypeKey
}).Distinct();
return
query
.ToDictionary(
ssm => new SchoolSocialMediaKey(
ssm.SchoolId, ssm.SocialMediaTypeKey),
ssm => ssm.Url);
I am sure there is a better way to write the original query, but instead of spending too much time analyzing I just translated it into linq. In general, your linq query should have the same structure as the tsql query like this:
var query =
from s in context.School
from a in
(
from ssm in context.SchoolSocialMedia
join d in context.Department on ssm.DepartmentId equals d.Id
select new
{
ssm.SocialMediaTypeKey,
ssm.Url,
d.Name,
d.ImportBusinessKey
}
).DefaultIfEmpty()
from ss in context.SchoolSocialMedia
.Where(x => s.Id == x.SchoolId)
.Where(x => a.SocialMediaTypeKey == x.SocialMediaTypeKey)
.DefaultIfEmpty()
select new
{
s.Code,
s.Name,
Url = ss.Url ?? a.Url,
a.SocialMediaTypeKey
};

Linq query to order list by occurences of foreign key in another list

I have two classes, Profile and Download. Download has the foreign key ProfileID which maps to the ID in Profile. Each row in Download represents one download for the connected profile.
I'm having trouble making a linq query that fetches a list of Profiles that is ordered in how many times it has been downloaded.
EDIT:
Here's what I have so far in the function.
IndexViewModel model = new IndexViewModel();
model.NewSubtitles = (from Profile in _ProfileRepo.GetAll()
orderby Profile.ID descending
select Profile).Take(5).ToList();
// This doesn't work:
// model.TopSubtitles = (from n in _ProfileRepo.GetAll()
// join d in _DownloadRepo.GetAll() on n.ID equals d.ProfileID into c
// group c by c.ProfileID into g
// orderby g.Count() descending
// select n).Take(5).ToList();
return View(model);
Try this :
model.NewSubtitles = (from Profile in _ProfileRepo.GetAll()
join downloads in _DownloadRepo.GetAll() on Profile.UserId equals downloads.UserId
group downloads by Profile into p
orderby p.Count() descending
select new {p.Key.UserId , p.Key.UserName , p.Count()).Take(5).ToList();
Have you tried something like :
from d in Downloads
orderby d.Profiles.Count()
...
?
Should do what you want:
model.TopSubtitles = (from p in _ProfileRepo.GetAll()
join d in _DownloadRepo.GetAll() on p.ID equals d.ProfileId
group d by p into g
orderby g.Count() descending
select g.Key).Take(5).ToList();
and for the LINQ syntax challenged:
model.TopSubtitles = _ProfileRepo.GetAll()
.Join(_DownloadRepo.GetAll(), p => p.ID, d => d.ProfileId, (p, d) => new { Profile = p, Download = d })
.GroupBy(x => x.Profile)
.OrderByDescending(g => g.Count())
.Select (g => g.Key)
.Take(5)
.ToList();

Categories