Recursively select all child ids and join in one string using Linq - c#

I have a category model look like this:
public class Category
{
public int Id { get; set; }
public int ParentCategoryId { get; set; }
public string ChildIDs { get; set; }
}
I want to Recursively select all child ids and join in ChildIDs like this: 12,13,17,31
This is my Linq query, but it returns 1 level childern:
from cg in db.Categories
where cg.Id == id
join urc in
(from x in db.UrlRecords where x.EntityName == "Category" select x) on cg.Id equals urc.EntityId into urccg
from f in urccg.DefaultIfEmpty()
select new
{
cg,
f,
ParentCategoryId = cg.ParentCategoryId,
ChildIds = string.Join(",", (from x in db.Categories where x.ParentCategoryId == cg.Id select x.Id))
}

Related

LINQ uses extra inner join

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

one-to-many with Default

I have News. One news can have many Tags, Categories and Images. Special for it i thing about extended model.
public class NewsExt
{
public News News { get; set; }
public List<Categories> Categories { get; set; }
public List<Images> Images { get; set; }
public List<Tags> Tags { get; set; }
}
or
public class NewsExt
{
public Base.News News { get; set; }
public Categories Categories { get; set; }
public Images Images { get; set; }
public Tags Tags { get; set; }
}
I work with second variant and want something like this:
return
((from n in db.News
from i in db.Images.DefaultIfEmpty()
from t in db.Tags.DefaultIfEmpty()
from c in db.Categories.DefaultIfEmpty()
select new NewsExt()
{
News = n,
Images = i,
Tags = t,
Categories = c
}).GroupBy(news => news.News))
I want *Array of NewsExt** with one News and all other entities without repeating in each element of this array.
For the first viewmodel I try:
(from n in db.News
select new NewsExt()
{
News = n,
Images = (from i in db.Images.DefaultIfEmpty()
from n_i in db.News_Image
where n_i.news_id == n.id
select i).ToList(),
Tags = (from t in db.Tags.DefaultIfEmpty()
from n_t in db.News_Tag
where n_t.news_id == n.id
select t).ToList(),
Categories = (from c in db.Categories.DefaultIfEmpty()
from n_c in db.News_Category
where n_c.news_id == n.id
select c).ToList()
}).ToList();
But have excess records.
I decide to use first viewmodel.
(from n in db.News
select new NewsExt()
{
News = n,
Images = (from i in db.Images.DefaultIfEmpty()
join n_i in db.News_Image on i.id equals n_i.image_id
where n_i.news_id == n.id
select i).ToList(),
Tags = (from t in db.Tags.DefaultIfEmpty()
join n_t in db.News_Tag on t.id equals n_t.tag_id
where n_t.news_id == n.id
select t).ToList(),
Categories = (from c in db.Categories.DefaultIfEmpty()
join n_c in db.News_Category on c.id equals n_c.category_id
where n_c.news_id == n.id
select c).ToList()
}).ToList();

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

Categories