LINQ uses extra inner join - c#

Here are my simplified models:
public class Resource
{
public int id { get; set; }
public string Name { get; set; }
}
public class Upgrade
{
public int Id { get; set; }
public virtual Resource Res { get; set; }
public int Lvl { get; set; }
public int Amount { get; set; }
}
Basically, I need to group by Resource and get [Name] from "Resource" and sum([Amount]) from "Upgrade".
Here is the LINQ:
from u in _db.Upgrades
join r in _db.Resources on u.Res equals r
where u.Lvl > levelFrom
&& u.Lvl <= levelTo
group u by new { r.id, r.Name } into grp
select new UpgradeCost()
{
resName = grp.Key.Name,
resAmount = grp.Sum(k => k.Amount),
};
And here is the SQL I get (Sqlite):
SELECT "r0"."Name" AS "resName", COALESCE(SUM("u"."Amount"), 0) AS "resAmount"
FROM "Upgrades" AS "u"
LEFT JOIN "Resources" AS "r" ON "u"."Resid" = "r"."id"
INNER JOIN "Resources" AS "r0" ON "r"."id" = "r0"."id"
WHERE ("u"."Lvl" > #__levelFrom_0) AND ("u"."Lvl" <= #__levelTo_1)
GROUP BY "r0"."id", "r0"."Name"
LINQ uses extra INNER JOIN to group by.
I want it to be made like this:
SELECT "r"."Name" AS "resName", COALESCE(SUM("u"."Amount"), 0) AS "resAmount"
FROM "Upgrades" AS "u"
LEFT JOIN "Resources" AS "r" ON "u"."Resid" = "r"."id"
WHERE ("u"."Lvl" > #__levelFrom_0) AND ("u"."Lvl" <= #__levelTo_1)
GROUP BY "r"."id", "r"."Name"

Additional join generated when used u.Res navigation property. Actually you don't need explicit joins here.
from u in _db.Upgrades
where u.Lvl > levelFrom
&& u.Lvl <= levelTo
group u by new { u.Res.id, u.Res.Name } into grp
select new UpgradeCost()
{
resName = grp.Key.Name,
resAmount = grp.Sum(k => k.Amount),
};

Related

Convert SQL Request to linq query c# (left outer join and inner join)

I am a new developer and I am used to doing my linq c # queries. I have to convert an sql query to linq and I have a problem with a left outer join and an inner join right after. Here is the query below, can you help me?
select ETAB_NOM, ETAB_CODE, count(etab_nom) as nbFiches from tbl_offreSec OS
left outer join tbl_ecoleSec ES on ES.code = OS.FK_ecole
inner join etablissement on etab_code = ES.FK_etablissement
where OS.archive = 0 and OS.descriptionOrtho = 0 and len(OS.description) > 1
group by etab_nom, etab_code
order by ETAB_NOM
My class(condensed):
public class tbl_offreSec
{
public int id { get; set; }
public int FK_ecole { get; set; }
public string description { get; set; }
public Nullable<int> descriptionOrtho { get; set; }
public int archive { get; set; }
}
publi class tbl_ecoleSec
{
public int id { get; set; }
public int code { get; set; }
public int FK_etablissement { get; set; }
}
public partial class ETABLISSEMENT
{
public int ETAB_ID { get; set; }
public int ETAB_CODE { get; set; }
public string ETAB_NOM { get; set; }
}
UPDATE
In connection with the answer of Svyatoslav:
IQueryable<ValidOrthoSecList> query = (from OS in db.tbl_offreSec
join ES in db.tbl_ecoleSec on OS.FK_ecole equals ES.code into gj
from ES in gj.DefaultIfEmpty()
join ET in db.ETABLISSEMENTs on ES.FK_etablissement equals ET.ETAB_CODE
where OS.archive == 0 && OS.descriptionOrtho == 0 && OS.description.Length > 1
group ET by new { ET.ETAB_NOM, ET.ETAB_CODE } into g
select new ValidOrthoSecList
{
ETAB_NOM = g.Key.ETAB_NOM,
ETAB_CODE = g.Key.ETAB_CODE,
nbFiche = g.Sum(x => x.ETAB_NOM == null ? 0 : 1)
}).OrderBy(e => e.ETAB_NOM);
So there is your query:
var query =
from os in ctx.tbl_offreSec
join es in ctx.tbl_offreSec on os.FK_ecole equals es.code into gj
from es in gj.DefaultIfEmpty()
join et in ctx.ETABLISSEMENT on es.FK_etablissement equals et.ETAB_CODE
where os.archive == 0 && os.descriptionOrtho == 0 and os.description.Length > 1
group et by new { et.ETAB_NOM, et.ETAB_CODE } into g
select new
{
g.Key.ETAB_NOM,
g.Key.ETAB_CODE,
nbFiches = g.Sum(x => x.ETAB_NOM == null ? 0 : 1)
}
var query = query.OrderBy(e => e.ETAB_NOM);
Also consider to do not use LEFT JOIN, since following INNER JOIN will filter out empty records.

Use a filter in an outer join in linq

I have the following entities:
public class Company
{
public string CompanyName { get; set; }
public int ID { get; set; }
}
public class CompanyCurrency
{
public int Id { get; set; }
public int CompanyId { get; set; }
public decimal Rate { get; set; }
public int CurrencyId { get; set; }
}
public class Currency
{
public int ID { get; set; }
public string Name { get; set; }
}
I need to get the list of currencies for a country. If a country does not have an entry for a currency I need a line for that missing entry too.
The statement I have right now is:
var currencies =
from c in Currencies
join cc in CompanyCurrency
on c.ID equals cc.CurrencyId
into jointable
from resultiten in jointable.DefaultIfEmpty()
select new {c.Name ,
HasEntry = resultiten == null ? 0:1,
rate = resultiten != null ? resultiten.Rate:0 ,
} ;
This is not filtered by a countryID . I tried to add a filter by
from c in Currencies
join cc in CompanyCurrency
on c.ID equals cc.CurrencyId
into jointable
from resultiten in jointable.DefaultIfEmpty()
where resultiten.CompanyId == 1 || resultiten == null
select new {c.Name ,
HasEntry = resultiten == null ? 0:1,
rate = resultiten != null ? resultiten.Rate:0
But that does not have a result for a currency that has en entry for a company other then companyID 1.
The cooresponding SQL query would be
select *
from [dbo].[Currency] c
left outer join [dbo].[CompanyCurrency] cc
on c.id = cc.Currencyid
and cc.[Companyid] = 1
You need to either apply the filter before the join:
join cc in CompanyCurrency.Where(e => e.CompanyId == 1)
or as part of the join
on new { CurrencyId = c.ID, CompanyId = 1 } equals new { cc.CurrencyId, cc.CompanyId }
For inner joins it doesn't really matter, but for outer join it's important (the same btw applies to SQL queries).

Linq - Join where ID's !=, select new + distinct?

I have the following classes.
Course;
public class Course
{
//pk
public int Id{ get; set; }
public int SourceCourseId { get; set; }
public string Name { get; set; }
}
Registration
public class Registration
{
//primary key
public int Id { get; set; }
//...more fields
public int CourseId { get; set; }
}
I want to obtain a collection of annonymous objects with the two fields below for all Courses that are Distinct in the registrations table that are not in the Courses table.
var distinctCourses = (from registration in db.Registrations
join courses in db.Courses on registration.CourseId equals courses.SourceCourseId
where registration.CourseId != courses.SourceCourseId
select new
{
SourceCourseId = registration.CourseId,
Name = registration.CourseName,
}).Distinct().ToList();
For some reason the above is returning 0... Any suggestions?
try a left join:
var query = from r in registrations
join c in courses on r.CourseId equals c.id into newCourses
from nullCourse in newCourses.DefaultIfEmpty()
where nullCourse == null
select new { }
Edit - per comment from Alex :
Also, your where clause needs to change to
where nullCourse == null
Edit - changed join columns and added correct where clause.
Edit - group registrations on CourseID so they will be distinct
var distinctCourses =
(from registration in db.Registrations
group registration by registration.CourseId into grp
from reg in grp
join courses in db.Courses on reg.CourseId equals courses.SourceCourseId into newCourses
from nullCourse in newCourses.DefaultIfEmpty()
where nullCourse == null
select new
{
SourceCourseId = reg.CourseId,
Name = reg.CourseName,
}).ToList();
Try this
var result = Registrations.GroupJoin(Courses,r=>r.CourseId,c=>c.SourceCourseId,
(k,g) => new {k,g})
.Where(x=>x.g.Count()==0)
.Select(s=> new {id=s.k.CourseId,name=s.k.CourseName});

Using Join, Group By, and Sum in Entity Framework

My models look like:
public class ReturnItem
{
public int returnItemId { get ; set; }
public int returnRequestId { get; set; }
public int quantity { get; set; }
public string item { get; set; }
}
public class ReturnRequest
{
public int returnRequestId { get; set; }
public string orderNumber { get; set; }
public IEnumerable<ReturnItem> returnItems { get; set; }
}
And I have the following query:
SELECT item, sum(quantity)
FROM ReturnItem
JOIN ReturnRequest
ON ReturnRequest.returnRequestId = ReturnItem.returnRequestId
WHERE ReturnRequest.orderNumber = '1XX'
GROUP BY item
How do I convert the query to Entity Framework and return a List<ReturnItem>? Can I use .Include instead of .Join?
from ri in db.ReturnItems
join rr in db.ReturnRequests
on ri.returnRequestId equals rr.returnRequestId
where rr.orderNumber == "1XX"
group ri by ri.item into g
select new {
Item = g.Key,
Quantity = g.Sum(i => i.quantity)
}
You can't use Include instead of Join because Include translated into Left Outer Join but you need Inner Join here.
But you can use navigation property to perform join implicitly:
db.ReturnRequests
.Where(rr => rr.orderNumber == "1XX")
.SelectMany(rr => rr.returnItems)
.GroupBy(ri => ri.item)
.Select(g => new {
Item = g.Key,
Quantity = g.Sum(ri => ri.quantity)
});

Get Linq Subquery into a User Defined Type

I have a linq query, which is further having a subquery, I want to store the result of that query into a user defined type, my query is
var val = (from emp in Employees
join dept in Departments
on emp.EmployeeID equals dept.EmployeeID
select new Hello
{
EmployeeID = emp.EmployeeID
Spaces = (from order in Orders
join space in SpaceTypes
on order.OrderSpaceTypeID equals space.OrderSpaceTypeID
where order.EmployeeID == emp.EmployeeID group new { order, space } by new { order.OrderSpaceTypeID, space.SpaceTypeCode } into g
select new
{
ID = g.Key.SpaceTypeID,
Code = g.Key.SpaceTypeCode,
Count = g.Count()
})
}).ToList();
Definition for my Hello class is
public class Hello
{
public IEnumerable<World> Spaces { get; set; }
public int PassengerTripID { get; set; }
}
Definition for my World class is
public class World
{
public int ID { get; set; }
public string Code { get; set; }
public int Count { get; set; }
}
You are creating anonymous object but you need to specify type name World
select new World
{
ID = g.Key.SpaceTypeID,
Code = g.Key.SpaceTypeCode,
Count = g.Count()
})

Categories