translating complex T-SQL query to LINQ to Entities - c#

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

Related

EF linq: order of where and select in single() query

Which query is optimized :
db.news.Select(c => new { c.Title, c.Date, c.ID })
.SingleOrDefault(c => c.ID == 1);
or
db.news.Where(c => c.ID == 1)
.Select(c => new { c.Title, c.Date })
.SingleOrDefault();
I want this query:
select title, date
where id = 1
in global which one is better where before select, or where after select?
Generally, Where before Select (Where first approach) is more performant, since it filters your result set first, and then executes Select for filtered values only.

How can I transalte SQL Query to LINQ

I trying to translate SQL Query to Linq statement:
SELECT f.BarcodeNumber,m.Name, f.Model, SUM(f.Quantity) as FoundedAssetsQty, ISNULL(a.Quantity,0) as AssetQty
FROM [InventoryDB].[dbo].[FoundedAssets] f
join [InventoryDB].[dbo].[PhisicalStockCheckSheets] p on p.ID = f.PhisicalStockCheckSheetId
join [InventoryDB].[dbo].[Inventories] i on i.ID = p.InventoryId
left join [InventoryDB].[dbo].[Assets] a on a.BarcodeNumber = f.BarcodeNumber
join [InventoryDB].[dbo].[Manufacturers] m on m.ID = f.ManufacturerId
where p.InventoryId = 10
group by f.BarcodeNumber, a.Quantity, f.Model, m.Name
I have no idea how to do it. I tried many ways but I fail. Could anyone help me?
I tried to use Linqer, but when I configure the connection it fails, so I write the linq instruction myself. Finally I found the answer. I have not mentioned the relations between entities but it is not important here.
var summary = _context.FoundedAssets.Include(f => f.Manufacturer).
Include(f => f.Asset).
Include(f => f.PhisicalStockCheckSheet).ThenInclude(f => f.Inventory).
Where(f => f.PhisicalStockCheckSheet.Inventory.ID == id).
Select(x => new InventorySummaryModel()
{
BarcodeNumber = x.BarcodeNumber.Value,
ManufacturerName = x.Manufacturer.Name,
Model = x.Model,
AssetsQuantity = x.Asset.Quantity,
FoundedAssetQuantity = x.Quantity
}).ToList();
var groupedSummary = summary.GroupBy(x => x.BarcodeNumber).Select(x => new InventorySummaryModel()
{
BarcodeNumber = x.First().BarcodeNumber,
ManufacturerName = x.First().ManufacturerName,
Model = x.First().Model,
FoundedAssetQuantity = x.Sum(a => a.FoundedAssetQuantity),
AssetsQuantity = x.First().AssetsQuantity
}).ToList();
Maybe exists any easier approach but this one works properly.

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

Can make left join in Entity Framework without dataset

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

How to use the 'NOT IN' function in LINQ?

var query2 = (from tc in Entities.TutorCourses
join c
in Entities.Course
on tc.CourseId equals c.Id
where tc.CourseId == id
select tc.Username).ToList();
var query1 = Entities.User
.Select(u => u.Role
.Select(p => p.Name)
.Contains("Tutor"));
I am trying to return all the Users from a database that are in query1 except all those Users that are in query2 as above.
How can I achieve this? I tried using returning query1 by adding .Except(query2); in the end but I am not sure which is the best method to implement a 'NOT IN' function in LINQ
That 2nd query just looks wrong. Try this:
var query2 = (from tc in Entities.TutorCourses
join c
in Entities.Course
on tc.CourseId equals c.Id
where tc.CourseId == id
select tc.Username).ToList();
var query1 = Entities.User
.Where(u =>
query1.Any(un => un != u.Username) &&
u.Role.Name.Contains("Tutor"))
.Select(u => u.Role);
If this all query are the same type try to you this method
Intersect
You can use the Any to find matches between two lists, then add a ! to specify that you want the ones that don't match. Here's an example that should work for you:
var excludedList = query1.Where(x => !query2.Any(y => y.Id == x.Id));
How about query1.Where(c=> !query2.Contains(c));

Categories