Linq query to sum by group - c#

I have a data table like this:
Category Description CurrentHours CTDHours
LC1 Cat One 5 0
LC2 Cat Two 6 0
LC3 Cat Three 18 0
LC1 Cat One 0 9
LC2 Cat Two 0 15
LC4 Cat Four 0 21
That I need to Group and Sum to this:
Category Description CurrentHours CTDHours
LC1 Cat One 5 14
LC2 Cat Two 6 21
LC3 Cat Three 18 0
LC4 Cat Four 0 21
In other words I need to sum the two Hours columns grouping by the Category and Description columns.
I know that I could build a new table and loop through the existing data and sum the data into the new table but I thought there would be an easier way to do it using Linq. I've googled it for a few hours but all the examples I found didn't seem to fit what I was trying to do.
BTW, the odbc driver that creates the data table does not have the capability for sub queries, etc. or I would have just done it using SQL.

Use anonymous object to group by category and description. Here is Linq to DataSet query which returns grouped hours:
from r in table.AsEnumerable()
group r by new {
Category = r.Field<string>("Category"),
Description = r.Field<string>("Description")
} into g
select new {
Category = g.Key.Category,
Description = g.Key.Description,
CurrentHours = g.Sum(x => x.Field<int>("CurrentHours"),
CTDHours = g.Sum(x => x.Field<int>("CurrentHours") + x.Field<int>("CTDHours"))
}
If you are querying database (not clear from question):
from r in context.Table
group r by new {
r.Category,
r.Description
} into g
select new {
g.Key.Category,
g.Key.Description,
CurrentHours = g.Sum(x => x.CurrentHours),
CTDHours = g.Sum(x => x.CTDHours + x.CurrentHours)
}

You need to sum CurrentHours and CTDhours, so -
select new {
...
CTDHours = g.Sum(x => x.Field<int>("CTDHours") + g.Sum(x => x.Field<int>("CurrentHours")
}

Related

Join Error | Unable to create a constant value of type only primitive

Very new with LINQ here.
I have the following data in my table (TableA):
ID Name SubmissionNo
1 Jim A-1
2 Andy A-2
3 Rick A-2
4 Mary A-3
5 Zim A-4
6 Loren A-1
I then need to create a query which will allow me to get from that table, those records which have duplicate submission numbers.
Here's my solution so far (Context is the database context):
var duplicates = (from tbl in Context.TableA.AsNoTracking()
group tbl by tbl.SubmissionNo into grp
select new { count = grp.Count(), submissionNo = grp.Key})
.Where(x => x.count > 1)
.OrderBy(y => y.submissionNo).ToList();
The variable duplicates then contains the record:
count submissionNo
2 A-1
2 A-2
I then write the main query which will allow me to get all the records from TableA which has duplicate submissionNo
var myList = (from tbl in Context.TableA.AsNoTracking()
join dup in duplicates on tbl.SubmissionNo equals dup.submissionNo
select new
{
ID = tbl.ID,
Name = tbl.Name,
SubmissionNo = tbl.SubmissionNo
})
.ToList();
I am then getting an error for the myList query with
Unable to create a constant value of type 'Anonymous Type'. Only primitive types or enumeration types are supported in this context.
I think there must be a better way to do this as from the TableA above, I practically want the following results:
ID Name SubmissionNo
1 Jim A-1
2 Andy A-2
3 Rick A-2
6 Loren A-1
Your first query, slightly modified, has all information you need:
var myList = from tbl in Context.TableA.AsNoTracking()
group tbl by tbl.SubmissionNo into grp
where grp.Count() > 1
from item in grp
select new
{
count = grp.Count(),
submissionNo = grp.Key,
item.Name,
);
The pattern group into grp - from item in grp is a commonly used query pattern to group items and then flatten the group again, while keeping in touch with the group data (like Count() and Key).
Now you don't need the join anymore and the exception doesn't occur. By the way, the exception tells you that EF can only handle joins with collections of primitive types (int etc.), because it has to translate the whole expression into SQL. There's simply no translation for rich objects like TableA.
By the way, the query can be improved by removing the repeated Count():
var myList = from tbl in Context.TableA.AsNoTracking()
group tbl by tbl.SubmissionNo into grp
let count = grp.Count()
where count > 1
from item in grp
select new
{
count = count,
submissionNo = grp.Key,
item.Name,
);
This will generate a more efficient SQL statement containing one COUNT instead of two.
Since Entity Framework does not support joining in-memory collections of objects with database collections, a common workaround for this is to filter using Contains.
First, you need to get the IDs to filter on:
var duplicates = (from tbl in Context.TableA.AsNoTracking()
group tbl by tbl.SubmissionNo into grp
select new { count = grp.Count(), submissionNo = grp.Key})
.Where(x => x.count > 1)
.OrderBy(y => y.submissionNo)
.ToList();
var duplicateIds = duplicates.Select(x => x.submissionNo).ToList();
And then change your query to perform a WHERE...IN instead of a JOIN:
var myList = (from tbl in Context.TableA.AsNoTracking()
where duplicateIDs.Contains(tbl.SubmissionNo)
select new
{
ID = tbl.ID,
Name = tbl.Name,
SubmissionNo = tbl.SubmissionNo
})
.ToList();

linq query to fetch employee name of those employee who is working on exactly 3 projects

i an having two tables as follows:
1)employee
2)project
employee table records:
empid empname project_id
1 abc 101
2 pqr 100
3 lmn 99
4 abc 99
5 abc 100
project table:
pid pname
99 jknkj
100 nkj
101 kjkjn
now i want to fetch employee name of those employee who is working on exactly 3 project?
i want linq query.can anybody with me with linq query???
if you want to fetch employees who are working on exactly 3 projects, do something like this:
var Counts =
from e in employees
group p by e.empname into g
where g.Count == 3
select new { Employee = g.Key, ProjectCount = g.Count() };
and if you want to want fetch project on which exactly 3 employees working then do like this:
var Counts =
from e in employees
group p by e.projectid into g
where g.Count == 3
select new { Porject= g.Key, EmployeeCount = g.Count() };
You can use GroupBy and group your Employees based on project_id then get the groups that contains exactly 3 employees:
db.Employees.GroupBy(x => x.project_id)
.Where(x => x.Count() == 3)
.SelectMany(x => x.Select(e => e.empname));
Edit: the below comment is correct, you can group your records by emp_name in order the fix that issue:
db.Employees.GroupBy(x => x.empname)
.Where(x => x.Count() == 3)
.Select(x => x.Key);

EF Sum between 3 tables

Say we got a Database design like this.
Customer
Id Name
1 John
2 Jack
Order
Id CustomerId
1 1
2 1
3 2
OrderLine
Id OrderId ProductId Quantity
1 1 1 10
2 1 2 20
3 2 1 30
4 3 1 10
How would I create an entity framework query to calculate the total Quantity a given Customer has ordered of a given Product?
Input => CustomerId = 1 & ProductId = 1
Output => 40
This is what I got so far, through its not complete and still missing the Sum.
var db = new ShopTestEntities();
var orders = db.Orders;
var details = db.OrderDetails;
var query = orders.GroupJoin(details,
order => order.CustomerId,
detail => detail.ProductId,
(order, orderGroup) => new
{
CustomerID = order.CustomerId,
OrderCount = orderGroup.Count()
});
I find it's easier to use the special Linq syntax as opposed to the extension method style when I'm doing joins and groupings, so I hope you don't mind if I write it in that style.
This is the first approach that comes to mind for me:
int customerId = 1;
int productId = 1;
var query = from orderLine in db.OrderLines
join order in db.Orders on orderLine.OrderId equals order.Id
where order.CustomerId == customerId && orderLine.ProductId == productId
group orderLine by new { order.CustomerId, orderLine.ProductId } into grouped
select grouped.Sum(g => g.Quantity);
// The result will be null if there are no entries for the given product/customer.
int? quantitySum = query.SingleOrDefault();
I can't check what kind of SQL this will generate at the moment, but I think it should be something pretty reasonable. I did check that it gave the right result when using Linq To Objects.

How to sum a field grouped by another in LINQ?

I am trying to find a away to SUM all the QUANTITY for a specific RECIPE (all its ingredients) into a single value to get the TOTAL QUANTITY
Assuming I have the following dataset:
RecipeName IngredientName ReceiptWeight
Food1 Ingredient1 5
Food1 Ingredient2 2
Food2 Ingredient1 12
Food2 Ingredient3 1
And I would expect to get the following:
RecipeName ReceiptWeight
Food1 7
Food2 13
The code I have so far is:
Grouping =
(
from data in dataset
group data by data.RecipeName into recipeGroup
let fullIngredientGroups = recipeGroup.GroupBy(x => x.IngredientName)
select new ViewFullRecipe()
{
RecipeName = recipeGroup.Key,
ReceiptWeight = ????
How can I get the value for RecipeWeight?
Thanks,
LINQ does have sum
from d in dataset
group d by new { d.RecipeName } into g
select new {
g.Key.RecipeName,
ReceiptWeight = g.sum(o => o.ReceiptWeight)
}

linq group by, order by

I have the following list
ID Counter SrvID FirstName
-- ------ ----- ---------
1 34 66M James
5 34 66M Keith
3 55 45Q Jason
2 45 75W Mike
4 33 77U Will
What I like to do is to order by ID by ascending and then
get the first value of Counter, SrvID which are identical (if any).
So the output would be something like:
ID Counter SrvID FirstName
-- ------ ----- ---------
1 34 66M James
2 45 75W Mike
3 55 45Q Jason
4 33 77U Will
Note how ID of 5 is removed from the list as Counter and SrvID was identical to what I had for ID 1 but as ID 1 came first
I removed 5.
This is what I would do but not working
var result = (from ls in list1
group ts by new {ls.Counter, ls.SrvID}
order by ls.ID
select new{
ls.ID,
ls.Counter.FirstOrDefault(),
ls.SrvID.First,
ls.FirstName}).ToList()
list1.GroupBy(item => new { Counter = item.Counter, SrvID = item.SrvID })
.Select(group => new {
ID = group.First().ID,
Counter = group.Key.Counter,
SrvID = group.Key.SrvID,
FirstName = group.First().FirstName})
.OrderBy(item => item.ID);
Group the records up, and pick a winner from each group.
var query =
from record in list1
group record by new {record.Counter, record.SrvID } into g
let winner =
(
from groupedItem in g
order by groupedItem.ID
select groupedItem
).First()
select winner;
var otherQuery = list1
.GroupBy(record => new {record.Counter, record.SrvID })
.Select(g => g.OrderBy(record => record.ID).First());

Categories