Column getting lost in LINQ with Method Syntax after group by - c#

I'm pretty new to LINQ and trying to figure it out. I have the following statement:
Context.dataset1
.Join(
Context.dataset2,
r => r.ID, o => o.ID,
(r, o) => new { PartID = r.PartID, Quantity = r.Quantity1 - r.Quantity2, Date = o.Date })
.GroupBy(
column => new { column.Date },
(key, group) => new {Date = key.Date, Quantity = group.Sum(g => g.Quantity) })
.Where(x => x.Quantity > 0);
the return data set looks like this
| Date | Quantity |
| ------------- | ---------|
| 2022-01-01 | 333 |
| 2022-01-02 | 444 |
| 2022-03-03 | 444 |
what i want it to look like is
| PartID | Date | Quantity |
|--------| ------------- | ---------|
|1 | 2022-01-01 | 333 |
|1 | 2022-01-02 | 444 |
|2 | 2022-03-03 | 444 |
Basically it seems that when I do the groupby I lose access to the PartId column since i'm no specifying it inside the groupby. I'm not sure how to make it appear without grouping by it which I don't want to do.
Any help would be great. Thanks.

What if two different part ids exist for the same date? What part id would it show? If you really want the part id, then you need to include the part id in your group by. For example:
column => new { column.PartID, column.Date }
This will mean that if you have multiple part ids for the same date, you will have as many rows for that date as you have distinct part ids. Based on your comments, this seems like what you're after.

Related

Filter LINQ To Entities (EF Core) query by List of HashSet of String and Enum

I have this Linq to Entities (EF Core) query which looks like below
var query = (from p in db.Samples
join q in db.Items on p.Id equals q.SampleId
Where p.active = IsActive and p.Id = GivenId
group new
{
p.Name,
p.Address,
p.Marks,
p.LocationId,
q.EmailId,
q.Grade
}
by new
{ q.Grade }
into data
select new DataSummary()
{
UserName = data.Name,
Grade = data.Min(x => x.Grade),
Email = data.Min(x => x.Email,
Total = data.Sum(x => x.Marks)
}.ToList()
Now I have a constant List of Hashset of Grades and Location that looks like this:
public List<(HashSet<string> Grades, HashSet<Location> Loctions)> LocationGrades => new()
{
(new() { "A", "B" }, new()), // Includes all location
(new() { "C"}, new(){
Location.Boston, //Location is Enum
Location.Maine
}
}
I want to get the data where if the student has grade A or B include all location and if the student has grade C only include Boston and Maine.
Is it possible to integrate this within the LINQ to Entities query?
Sample Table
| ID | Name | Address | Marks | LocationId |
|-----|-------|---------|-------|-------------|
| 234 | Test | 123 St | 240 | 3 (Maine) |
| 122 | Test1 | 234 St | 300 | 5 (Texas) |
| 142 | Test1 | 234 St | 390 | 1 (Boston) |
Items Table
| ID | SampelId | Grade | Email |
|----|----------|-------|-------|
| 12 | 234 | A | a.com |
| 13 | 122 | C | b.com |
| 14 | 142 | C | c.com |
So, In the table above I shouldn't get Texas row but get Boston row as they both have Grade C but Texas does not exist in the HashSet combo.
Okay, now I got it. You have to add dynamic ORed constraints to the query based on a given list of elements. This is a little tricky, because AND can be done with using multiple .Where() statements, but OR not. I did something similar recently against CosmosDB by using LinqKit and the same should also work against EF.
In your case you probably of to do something like this:
...
into data
.WhereAny(grades, (item, grade) => item.Grade == grade)
select new DataSummary()
...
I think the given example doesn't match your exact case, but it allows you to define multiple ORed constraints from a given list and I think this is the missing part you're searching. Take care to use within the lambda method only definitions which are also supported by EF core. The given inner enumeration (in this example grades) will be iterated on the client side and can be dynamically build with everything available in C#.

Entity Framework get one of each combination by date

I'm using Entity Framework in ASP.NET. I have something like a table like this:
+----+--------------------------+-------+-------+------------+
| id | content | type1 | type2 | date |
+----+--------------------------+-------+-------+------------+
| 0 | Some text | TypeA | TypeB | 2013-04-01 |
| 1 | Some older text | TypeB | TypeA | 2012-03-01 |
| 2 | Some even older texttext | TypeB | TypeC | 2011-01-01 |
| 3 | A dog | TypeC | TypeB | 2013-04-01 |
| 4 | And older dog | TypeC | TypeB | 2012-03-01 |
| 5 | An even older dog | TypeA | TypeC | 2011-01-01 |
| 6 | More text | TypeA | TypeB | 2013-03-01 |
+----+--------------------------+-------+-------+------------+
I already can obtain the most recent occurrence of type 1 or type 2, but I want to query the database to obtain the most recent occurrence of the combination of two types:
+----+--------------------------+-------+-------+------------+
| id | content | type1 | type2 | date |
+----+--------------------------+-------+-------+------------+
| 0 | Some text | TypeA | TypeB | 2013-04-01 |
| 3 | A dog | TypeC | TypeB | 2013-04-01 |
| 5 | An even older dog | TypeA | TypeC | 2011-01-01 |
+----+--------------------------+-------+-------+------------+
Thanks!
EDIT: The columns type1 or type2 are basically the same, so if Type1=A and Type2=B it's the same as Type1=B and Type2=A.
This would do the trick, except for one little issue, which I'll describe after the code sample.
var grouped = datacontext.Table
.GroupBy(
item => new { item.Type1, item.Type2 },
(key, groupData) => groupData.OrderBy(x => x.TheDate).First())
.ToArray();
The problem is that this way the grouping is done on the combination of Type1 and Type2, in that order. So what you are saying in the comments, which is that Type1=A AND Type2=B is equal to Type1=B AND Type2=A is a problem.
I really have no idea if EF (or L2S) can compile this, but it is worth a try.
var grouped = datacontext.Table
.Select(x => new Data // TODO: Update to the correct typename
{
x.Id,
x.Content,
Type1 = x.Type1.CompareTo(x.Type2) > 0 ? x.Type2 : x.Type1,
Type2 = x.Type1.CompareTo(x.Type2) > 0 ? x.Type1 : x.Type2,
x.TheDate
})
.GroupBy(
item => new { item.Type1, item.Type2 },
(key, groupData) => groupData.OrderBy(x => x.TheDate).First())
.ToArray();
If the above doesn't compile, then an alternative option is to correct the Type1/Type2 values after the database-grouping, and perform the grouping again in-memory. Yes, this means it will group twice (once by the database, once in-memory), but it will work, and won't require to import the complete table into memory.
var grouped = datacontext.Table
.GroupBy(
item => new { item.Type1, item.Type2 },
(key, groupData) => groupData.OrderBy(x => x.TheDate).First())
.AsEnumerable()
.GroupBy(
item => new {
// Group by the possible-swapped-values of Type1 and Type2
Type1 = x.Type1.CompareTo(x.Type2) > 0 ? x.Type2 : x.Type1
Type2 = x.Type1.CompareTo(x.Type2) > 0 ? x.Type1 : x.Type2
},
(key, groupData) => groupData.OrderBy(x => x.TheDate).First())
.ToArray();
I think I like the last option the best, since:
it can be translated to sql (no special features like string.CompareTo)
at most it will be twice the amount of records that the database will yield, not anymore (which in my opinion is acceptable without knowing the amount of total data we're talking about)

LINQ grouping by nullable child and parent

everyone!
I've just faced a problem with timing out in my LINQ query.
I have 3 tables: Work, Projects and Subprojects.
Projects:
+--------+
| Id |<--+<--+
| Name | | |
+--------+ | |
SubProjects: | |
+--------+ | |
+->| Id | | |
| | Name | | |
| | ProjId |---+ |
| +--------+ |
| Work: |
| +------------+ |
| | Id | |
| | Type | |
| | ProjId |---+
+--| SubProjId | (nullable)
+------------+
I need to create a report based on Subprojects:
Group by subproject Id,
if subproject Id is null -> group by project Id
I've solved it by making two queries and then merging them, but when sometimes it times out.
I was doing it with
result1.AddRange(result2);
because
var temp = result1.Concat(result2);
is throwing an Exception:
Internal .NET Framework Data Provider error 1004, 0, Unresolvable Var used in Command: VarType=Computed, Id=2090.
Can somebody help me with creating it in one query?
I'm not sure what your code looks like so this might not be perfect but you could try something like this:
var result = from work in works
group work by work.SubProjId ?? work.ProjId into groupedWorks
select groupedWorks.ToList();
or
var result = works.GroupBy(work => work.SubProjId ?? work.ProjId).ToList();
try this query
var itemlist =contex.Work.where(x=>x.SubProjId !=null).Groupby(x=>x.SubProjId).Concat(Contex.Work.where(x=>x.SubProjId ==null).Groupby(x=>x.ProjId)).ToList();
I'm guessing this is what you need:
var groups = from work in ctx.Works // the work table
group work // we want to group whole work "rows"
// we are grouping by project id and subproject id
by new { ProjId = work.ProjId, SubProjId = work.SubProjId }
into g // and we are calling the grouping 'g'
select g; // select the group
// example of doing something with the groupings
foreach (var group in groups)
{
var key = group.Key; // gets a { ProjId, SubProjId } tuple
foreach (var work in group)
{
// each work is a row in the Work-table
}
}

group by dates and make mathematics on it using linq

I have datatable looks like this:
| date | value |
| 1/1/2013 10:28 | 5 |
| 1/1/2013 10:29 | 6 |
| 2/1/2013 01:54 | 6.5 |
| 2/1/2013 02:24 | 6.7 |
| 2/1/2013 03:14 | 8 |
I want to group the table into days.
then to calculate the avarage value of every group.
then to make avarage of all the values I calculated in step 2.
is there any good way to do it through linq ?
Thanks
of course you can use linq for this purpose:
var results = from p in list
group p by p.date.Date into g
select new { date = g.Key, value = g.Average(p=> p.value) };
var endAverage = results.Average(x => x.value);

How do you duplicate rows based on a quantity in a cell? LINQ to Entities

I have an Orders table and an Assignments table which I join together using LINQ to Entities. Each order has a product and a quantity. Each order has a number of assignments up to the quantity. I want to output the following:
Orders:
ID | OrderCode | Quantity | Other Columns...
1 | 30000-1 | 3 | ...
2 | 41000-7 | 2 | ...
Assignments:
OrderID | Assignment | Other Columns...
1 | 4526 | ...
2 | 2661 | ...
2 | 5412 | ...
I want to output a table like:
OrderCode | Assignment
30000-1 | 4526
30000-1 |
30000-1 |
41000-7 | 2661
41000-7 | 5412
Any advice would be welcome!
I would split the task up into three parts.
First, I'd use LINQ to Entities to get the full collection of orders, each with it's corresponding collection of assignments:
var a = (from o in orders
join a in assignments on s.Id equals a.OrderId into oa
//Notice that I use oa.DefaultIfEmpty(). This essentially the way to do a
//LEFT JOIN in LINQ. You'll want to do a LEFT JOIN if you don't
//want to exclude order codes that have no assignments
select new { o.OrderCode, o.Quantity, Assignments = oa.DefaultIfEmpty() })
.ToList();
a returns the following for your example:
OrderCode | Assignment
30000-1 | 4526
41000-7 | 2661
41000-7 | 5412
Then I'd add the "missing" rows
var b = a.SelectMany(o =>
{
var numOrdersInList = o.Count(o2 => o2.OrderCode == o.OrderCode);
return Enumerable.Range(0, o.Quantity - numOrdersInList)
.Select(i => new
{
o.OrderCode,
Assignment = Enumerable.Empty<Assignment>()
});
});
b returns the following for your example:
OrderCode | Assignment
30000-1 |
30000-1 |
Then I'd concat the two enumerables.
var c = a.Select(o => new { o.OrderCode, o.Assignment })
.Concat(b);
Finally, the concatenation should return what you expect for your example:
OrderCode | Assignment
30000-1 | 4526
30000-1 |
30000-1 |
41000-7 | 2661
41000-7 | 5412

Categories

Resources