C# grouping loses row - c#

I have got the following SQL-Table:
---------------------------------------------------
| ID | Line1 | Line2 | Line3 | Line4 | Line5 |
---------------------------------------------------
| 1 | Software | Citrix | XenApp | Null | Null |
---------------------------------------------------
| 2 | Software | Citrix | XenApp | Null | Null |
---------------------------------------------------
I used this code in order to group it by Line3:
var KategorienLine3 = result.GroupBy(x => x.Line3).ToList();
In which result represents the list including the 2 entries.
Now this grouping results in this output:
[0] -> Key = XenApp
[1] -> Key = XenApp
But I don't have access to Line2. I would like to include it in the result. How can I do that, so that I have access to that as well?
It don't want to group by it!! I just want to have it in the result.
Thats what it gives me after the grouping. I want to include Line2 as well.

The data is there. It is just in the IGrouping<TKey, TResult> object returned by the GroupBy. The reason you don't have access to Line2 is that each grouping contains a collection of records that are of that group - and each record there is of your object's type, and has the Line2 property.
To retrieve it project the data as you want it to show:
// method syntax
var result = data.GroupBy(key => key.Line3, item => item.Line2)
.Select(g => new
{
g.Key,
Line2 = g.ToList()
}).ToList();
// query syntax
var result = from item in data
group item.Line2 by item.Line3 into g
select new
{
g.Key,
Line2 = g.ToList()
};

Related

Column getting lost in LINQ with Method Syntax after group by

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.

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#.

How to increate the performance when using group in LINQ

Database
Cars | CarDetails | Owners
----------------------------------
Id | CarDetailId | Id
Name | CarId | CarId
Type | CarId | OwnerName
| | PhoneNumber
LINQ Code
var intiQuery = from c in Cars
join cd in CarDetails
join o in Owners
select new { c,cd,o}
var results = from qry in intiQuery
group new { qry.c, qry.cd, qry.o} by qry.c.Id into g
select new
select new { CarId= g.Key,
Name = g.Select(g=>g.c.Name).FirstOrDefault(),
Type = g.Select(g=>g.c.Type).FirstOrDefault(),
Price= g.Select(g=>g.cd.Price).FirstOrDefault(),
OwnerName= g.Select(g=>g.o.OwnerName).FirstOrDefault(),
PhoneNumber= g.Select(g=>g.o.PhoneNumber).FirstOrDefault(),
}
My question is simply how to increase the performance when calling this query as you can see for each field, I need to.Select().FirstOrDefault() to get the corresponding data. If let's says I got 100 data I will need to get the data one by one 500 times it will take ages to display the data.
Extra Info in case someone not clear.
Cars
Id |Name |Type
-----------------------------------
1 |Toyota |Family
CarDetails
CarDetailId | CarId | Price
-----------------------------------
1 | 1 | 200000
Owners
Id| CarId | OwnerName | PhoneNumber
-----------------------------------
1 | 1 | Mitch | 48774800
2 | 1 | Camilo | 87404078
The result I wanted to get is something like this hope some of you can have a clearer picture
CarId| Name | Type | Price |OwnerName |PhoneNumber
----------------------------------------------------------------------
1 | Toyota | Family | 200000 | Mitch,Camilo | 48774800,87404078
This is absolutely not the best solution, but it will certainly be easy to understand and if you are not filtering the data then it may be good enough.
As you state, the performance problem is because your hitting your database hundreds of time, and so we can easily avoid that by simply pulling the information from the database in three simple queries.
var allCars = Cars.ToList();
var allCarDetails = CarDetails.ToList();
var allOwners= Owners.ToList();
Once you have all this information in memory, you can manipulate the in-memory objects to produce the results you need.
var results = (from car in allCars
let owners = allOwners.Where(a => a.CarID == car.Id)
select new
{
CarID = car.Id,
car.Name,
car.Type,
Price = allCarDetails.Where(a => a.CarID == car.Id).Select(a => a.Price).SingleOrDefault(),
OwnerName = String.Join(',', owners.Select(a => a.Name)),
PhoneNumber = String.Join(',', owners.Select(a => a.PhoneNumber))
}
);
If your tables have a lot of extra fields which you have not mentioned, then we may want to change the initial three queries to just pull the information required.

How to sort a List<String> using data from a column in a ListView

In my application, there is a ListView that contains the Location name and the Picking Priority (Lowest is chosen first) of all the locations that products are kept in. As well as this, I am also creating temporary List's that contain only the locations an individual product is stored in. For example:-
LISTVIEW List<String>
-------- ------------
__________________________ __________________________
|Location |Picking Priority| | Location |
|---------|----------------| |--------------------------|
| A | 100 | | A |
| B | 50 | | C |
| C | 500 | | D |
| D | 150 | |__________________________|
|_________|________________|
What I want to happen is the List to be ordered based on the Picking Priority of that location in the ListView, lowest to highest.
In effect, this would mean the List above would now look like this:-
__________________________
| Location |
|--------------------------|
| A |
| D |
| C |
|__________________________|
How would this be possible to do?
You can order the items based on the listitems, it's ugly, it's slow (slower than using a class with both properties) but it can work.
I assume ListViewItem.Text contains the name of the location and ListViewItem.Subitems[1].Text is the column with the priority (in integer format):
var items = theListView.Items.Cast<ListViewItem>();
var sortedList = theStringList.OrderBy(d => items.Where(i => i.Text == d).Select(i => int.Parse(i.Subitems[1].Text)).First()).ToList();

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

Categories

Resources