Linq query join one table row to another table multiple row - c#

Table name: Namelist
id | Name
------------
1 |xxxxx
2 |yyyyy
Table name: Category
id | Nameid |Categoryid
-----------------
1 |1 |5
2 |1 |4
3 |2 |3
4 |2 |8
I need a linq query result like this
id | Name |Categoryid
-----------------
1 |xxxx |5,4
2 |yyyy |3,8
I tried below linq but it displays first category id only
var list = from n in namelist
join c in category on n.id equals c.nameid
select new
{
n.id,
n.name,
c.categoryid
}

You can do this with Group Join and join all the category id's in the group with String.Join like this:-
var result = (from n in namelist
join c in categories
on n.Id equals c.NameId into g
select new
{
id = n.Id,
Name = n.Name,
CategorieIds = g.Select(x => x.CategoryId)
}).AsEnumerable()
.Select(x => new
{
Id = x.id,
Name = x.Name,
CategoryIds = String.Join(",",x.CategorieIds))
});

You can try String.Join:
var list = namelist
.Select(n => new {
n.id,
n.name,
categoryids = String.Join(",", category
.Where(c => c.nameid == n.id)
.Select(c => c.categoryid))
});

Use String.Join(). I modified your statement:
var list = from n in namelist
join c in category on n.id equals c.nameid
group g by new { n.id, n.name }
select new
{
id = g.Key.id,
Name = g.Key.name,
Categoryid = String.Join(",", g.Select(x => x.c.categoryid))
}

First you take a join on NameList and Category table and then bring them into objects using ToList or AsEnumerable methods like
var list = (from n in db.NameList
join c in db.Category on n.id equals c.nameid
select new
{
id = n.id,
Name = n.name,
Categoryid = c.id
}).ToList();
var list2 = from l in list
group by new {l.id, l.Name} into groupings
from g in groupings select new{
g.Key.id,
g.Key.Name,
CategoryId = string.Joing(",", groupings.Where(x=>x.NameId == g.Key.id).Select(y=>y.CategoryId))
};
The benefit of using ToList for fetching data from db is that you will only one query in database and all required records are fetched in the memory. The second statement will group those records by id and Name and will apply string.Join on CategoryId. Please note that if you use string.join method on Linq-to-Entities query, it will fail because this method cannot be converted into sql expression.

Related

Join where lambda query in EF Core 3.1

I have a problem to write in lambda query this sql query:
select c.Id, c.Name, c.SomeNumber, count(*) from TableA a
inner join TableB b
on a.Id = b.aId
inner join TableC c
on c.BId = b.Id
where b.Status = N'Approved'
and c.Scope = N'Scope1'
group by a.Id, a.Name, a.SomeNumber
Can you guys help me with this one ? I want to write lambda query to execute this in code. I'm using EF Core 3.1
This is what I ended up so far:
var query = await _dbContext.TableA.Where(a => a.TableB.Any(b => b.Status.Equals("Approved")
&& b.TableC.Any(c => c.Scope.Equals("Scope1"))))
.GroupBy(g => new { Id = g.Id, Name = g.Name, SomeNumber = g.SomeNumber })
.Select(s => new { Id = s.Key.Id, Name = s.Key.Name, SomeNumber = s.Key.SomeNumber, Count = s.Count() })
.GroupBy(g => g.Id).Select(s => new {Id = s.Key, Count = s.Count()}).ToListAsync();
Well, this is corrected query. I have used Query syntax which is more readable when query has lot of joins or SelectMany.
var query =
from a in _dbContext.TableA
from b in a.TableB
from c in b.TableC
where b.Status == "Approved" && c.Scope == "Scope1"
group a by new { a.Id, a.Name, a.SomeNumber } into g
select new
{
g.Key.Id,
g.Key.Name,
g.Key.SomeNumber,
Count = g.Count()
}
var result = await query.ToListAsync();
It's maybe easier to start from the many end and work up through the navigation properties
tableC
.Where(c => c.Scope == "Scope1" && c.BEntity.Status == "Approved")
.GroupBy(c => new
{
c.BEntity.AEntity.Id,
c.BEntity.AEntity.Name,
c.BEntity.AEntity.SomeNumber
})
.Select(g => new { g.Key.Id, g.Key.Name, g.Key.SomeNumber, Ct = g.Count()})
EF knows how to do joins when you navigate around the object tree in the where. By starting at the many and and working up to the 1 end of the relationship it means you don't have to get complex with asking "do any of the children of this parent have a status of ..."

linq group by two columns and get only rows with same group by values

I want to retrieve data by group two columns ( Parent_Id and Name ) using LINQ and get the result only the rows with the same group by values.
Child
---------
Id Parent_Id Name
1 1 c1
2 1 c2
3 2 c1 <-----
4 2 c1 <-----
5 3 c2
6 3 c3
7 4 c4 <-----
As you can see, for Parent_Id 1 and 2, Name are different. So, I don't what those rows.
The result I want is like
Parent_Id Name
2 c1
4 c4
What I have tried is
from c in Child
group c by new
{
c.Parent_Id,
c.Name
} into gcs
select new Child_Model()
{
Parent_Id = gcs.Key.Parent_Id,
Name= gcs.Key.Name
};
But it return all rows.
As you describe it you should group by Parent_id only and get the groups that have distinct Names:
var result = children
.GroupBy(c => c.Parent_Id)
.Where(g => g.Select(t => t.Name).Distinct().Count() == 1)
.Select(g => new
{
Parent_Id = g.Key,
Name = g.Select(c => c.Name).First()
});
Reduced to final edit as per Gert Arnold's request:
var result = from r in (from c in children
where !children.Any(cc => cc.Id != c.Id &&
cc.Parent_Id == c.Parent_Id &&
cc.Name != c.Name)
select new {
Parent_Id = c.Parent_Id,
Name = c.Name
}).Distinct().ToList()
select new Child_Model
{
Parent_Id = r.Parent_Id,
Name = r.Name
};
var myModel = Child.GroupBy( c => $"{c.Parent_Id}|{c.Name}",
(k, list) => new Child_Model{
Parent_Id = list.First().Parent_Id,
Name = list.First().Parent_Id,
Count = list.Count()})
.Max (cm => cm.Count);
You can add a condition to filter result (groupName.Count() > 1):
from c in childs
group c by new { c.Parent_Id, c.Name } into gcs
where gcs.Count() > 1
select new { gcs.Key.Parent_Id, gcs.Key.Name }

Group by one table and sum from another table in Linq

I have this Linq query:
from i in data.Items
join tdi in data.TDItems on i.itemId equals tdi.itemId
group i by i.ItemId
into selection
select new
{
itemId = selection.Key
number = selection.Sum(x => x.quantity) // quantity is a field in TDItems
}
How do I create this sum function? because I'm grouping by an attribute in the Items table, I can't call a Sum on the TDItems table.
group new { i, tdi } by i.ItemId
...
select new
{
selection.Sum(x => x.tdi.quantity)
}
from c in (from i in #as
join tdi in bs on i.itemId equals tdi.itemId
select new
{
itemId = i.itemId,
quantity = tdi.quantity
})
group c by c.itemId
into selection
select new
{
itemId = selection.Key,
number = selection.Sum(x => x.quantity) // quantity is a field in TDItems
};

Linq to SQL with GroupBy and Detail - Is there a better way to do this?

I have some SQL and am trying to make the equivalent in LINQ. This is the SQL:
SELECT Categories.CategoryDescription, Categories.CategoryType AS Type,
Categories.Category, COUNT(CategoryLinks.OrgID) AS CountOfOrgs
FROM CategoryLinks
INNER JOIN Categories ON Categories.CategoryID = CategoryLinks.CategoryID
GROUP BY Categories.Category, Categories.CategoryType, Categories.CategoryDescription
ORDER BY CategoryDescription ASC
Essentially, I want a list of everything from the Categories table and a count of the number of OrgId's in the CategoryLinks table that links to it.
Below is the query I am performing at the moment. There has to be a more efficient way to do this. Am I wrong?
var cnts = (from c in db.Categories
join cl in db.CategoryLinks on c.CategoryID equals cl.CategoryID
group new { c, cl } by new
{
c.CategoryID
} into g
select new
{
CategoryID = g.Key.CategoryID,
categoryCount = g.Count()
});
var results = (from c in db.Categories
join cn in cnts on c.CategoryID equals cn.CategoryID
select new
{
c.CategoryID,
c.CategoryDescription,
c.CategoryType,
Category = c.Category1,
cn.categoryCount
});
I think you want to use the GroupJoin method:
Categories.GroupJoin(
CategoryLinks,
x => x.CategoryID,
y => y.CategoryID,
(x,y) => new{
x.CategoryID,
x.CategoryDescription,
x.CategoryType,
Category = x.Category1,
CategoryCount = y.Count() })
In query syntax, this is written as join..into:
from c in db.Categories
join cl in db.CategoryLinks on c.CategoryID equals cl.CategoryID into catGroup
select new
{
c.CategoryID,
c.CategoryDescription,
c.CategoryType,
Category = c.Category1,
CategoryCount = catGroup.Count()
}
Try this:
var bbb = categories.Join(categoryLinks, c => c.CategoryID, cl => cl.CategoryId, (c, cl) => new {c, cl})
.GroupBy(g => g.c)
.Select(g => new {count = g.Count(), Category = g.Key});
It returns count and all data that is in Category. We group by all columns in category and place result in new anonymous type variable that contains 2 properties: Count, that contains count and Category that is of type Category and contains all data that is in category row.
If you want, you can rewrite it as:
var bbb = categories.Join(categoryLinks, c => c.CategoryID, cl => cl.CategoryId, (c, cl) => new {c, cl})
.GroupBy(g => g.c)
.Select(g => new
{
CategoryID = g.Key.CategoryId,
CategoryDescription = g.Key.CategoryDescription,
CategoryType = g.Key.CategoryType,
Category = g.Key.Category1,
categoryCount = g.Count()
});

Use LINQ to group data from DataTable

I want to use LINQ to group data from a DataTable (columns: userid, chargetag, charge).
The content could look like this:
userid chargetag charge
-----------------------------
user1 tag3 100
user2 tag3 100
user3 tag5 250
I need something like this as a result:
chargetag count sum
-------------------------
tag3 2 200
tag5 1 250
This is what I have so far:
var groupedData = from b in dataTable.AsEnumerable()
group b by b.Field<string>("chargetag") into g
let count = g.Count()
select new
{
ChargeTag = g.Key,
Count = count,
};
I can extract the name of the chargetag and the number of it.
How would I have to change the LINQ query to access the sum of charges as well?
Thanks in advance :-)
Regards,
Kevin
That's pretty easy - just use the Sum extension method on the group.
var groupedData = from b in dataTable.AsEnumerable()
group b by b.Field<string>("chargetag") into g
select new
{
ChargeTag = g.Key,
Count = g.Count(),
ChargeSum = g.Sum(x => x.Field<int>("charge"))
};
(I've removed the let clause here as it wasn't really buying you anything.)
Now that may be inefficient; it may end up grouping twice in order to perform two aggregation operations. You could fix that like with a query continuation like this, if you really wanted:
var groupedData = from b in dataTable.AsEnumerable()
group b by b.Field<string>("chargetag") into g
select new
{
ChargeTag = g.Key,
List = g.ToList(),
} into g
select new
{
g.ChargeTag,
Count = g.List.Count,
ChargeSum = g.List.Sum(x => x.Field<int>("charge"))
};
Or with a let clause instead:
var groupedData = from b in dataTable.AsEnumerable()
group b by b.Field<string>("chargetag") into g
let list = g.ToList()
select new
{
ChargeTag = g.Key,
Count = list.Count,
ChargeSum = list.Sum(x => x.Field<int>("charge"))
};

Categories