How to get All select keys in Linq Group By Clause - c#

I am trying to get the rest of the select keys in this linq query but the intellisense is giving me an error
var query2 = from row2 in query1
group row2 by row2.questionid into g
where g.Count() > 0
select new
{
questionid1, //Error here
time, //Error here
thecount = g.Count()
};
How do I get those select keys?

I'm assuming that questionid and time are the properties you want to group on:
You can only get the keys you grouped on from g, and as Jon has suggested the where clause doesn't actually do anything.
Try this:
var query2 = from row2 in query1
group row2 by new { row2.questionid, row2.time } into g
// where g.Count() > 0
select new
{
g.Key.questionid,
g.Key.time,
thecount = g.Count()
};

It's not at all clear what you're asking, I'm afraid. What is "time"? What's the difference between questionid and questionid1?
By the time you've grouped, you've basically got a sequence of groups rather than a sequence of questions. When you project those groups, you can use the Key property of the group to get at the key which forms that group, and you can use the sequence of values within the group (which is what g.Count() is doing in your example). For example, you could take the first result within the group, and access individual fields within that.
If you could give more of an idea of what data you've got and what you're trying to achieve, we're much more likely to be able to help you.
As an additional note - your where clause isn't doing anything at the moment. When you group by a key, you never get any "empty" groups - LINQ isn't going to make up keys which aren't in the actual data.

Related

Self join in LINQ

SELECT
FW1.id, count(*)
FROM
firmware FW1
LEFT JOIN
firmware FW2 ON FW1.firmware_group_id = FW2.firmware_group_id
AND FW1.br_date < FW2.br_date
AND FW2.[public]= '1'
GROUP BY
FW1.id
I am looking to convert into linq query. As I know less than symbol cannot be converted into Linq query. Please suggest how to do it. I have a string date and I need to compare into linq.
As you said, Linq does not support other types of join outside of EquiJoin. Docs is pretty clear on what you can do to bypass this:
you could use a simple cross join (a cartesian product), then applying in the where clause the conditions for your non-equijoin.
Or you could use a temporary variable to store a new table with only the attributes you need for your query and, like before, applying the conditions in the where clause.
In your case, a possible Linq query could be this one:
from f1 in firmwares
from f2 in firmwares
let f1_date = DateTime.Parse(f1.Dt)
let f2_date = DateTime.Parse(f2.Dt)
where f1.Group_Id == f2.Group_Id && f1_date < f2_date
group f1 by f1.Id into fres
select new {
Id = fres.Key,
Count = fres.Count()
};
However I am still thinking how to emulate the LEFT JOIN without casting it to a group join.
Of course the < symbol can be used. Just use method syntax instead of query syntax!
Every FW1 has zero or more FW2s. Every FW2 belongs to exactly one FW1. This one-to-many is implemented using foreign key firmware_group_id.
Apparently you want all FW1s, each with the number of its FW2s, that have a property public with a value equal to 1 and a property br-date larger than the value of the br-date of the FW1.
Whenever you want an item with its many sub-items (using a foreign key), like s School with its Students, a Customer with his Orders, a Book with his Pages, you'll need Enumerable.GroupJoin
var result = FW1.GroupJoin(FW2, // GroupJoin FW1 and FW2
fw1 => fw1.firmware_group_id, // from fw1 take the primary key firmware_group_id
fw2 => fw2.firmware_group_id, // from fw2 take the foreing key firmware_group_id
(fw1, fw2s) => new // from every fw1, with all its matching fw2s
{ // make one new object containing the following properties:
Id = fw1.Id,
// Count the fw2s of this fw1 that have public == 1
// and a date larger than fw1.date
Count = fw2.Where(fw2 => fw2.Public == 1 && fw1.br_date < fw2.br_date)
.Count(),
});
Note:

Why is linq reversing order in group by

I have a linq query which seems to be reversing one column of several in some rows of an earlier query:
var dataSet = from fb in ds.Feedback_Answers
where fb.Feedback_Questions.Feedback_Questionnaires.QuestionnaireID == criteriaType
&& fb.UpdatedDate >= dateFeedbackFrom && fb.UpdatedDate <= dateFeedbackTo
select new
{
fb.Feedback_Questions.Feedback_Questionnaires.QuestionnaireID,
fb.QuestionID,
fb.Feedback_Questions.Text,
fb.Answer,
fb.UpdatedBy
};
Gets the first dataset and is confirmed working.
This is then grouped like this:
var groupedSet = from row in dataSet
group row by row.UpdatedBy
into grp
select new
{
Survey = grp.Key,
QuestionID = grp.Select(i => i.QuestionID),
Question = grp.Select(q => q.Text),
Answer = grp.Select(a => a.Answer)
};
While grouping, the resulting returnset (of type: string, list int, list string, list int) sometimes, but not always, turns the question order back to front, without inverting answer or questionID, which throws it off.
i.e. if the set is questionID 1,2,3 and question A,B,C it sometimes returns 1,2,3 and C,B,A
Can anyone advise why it may be doing this? Why only on the one column? Thanks!
edit: Got it thanks all! In case it helps anyone in future, here is the solution used:
var groupedSet = from row in dataSet
group row by row.UpdatedBy
into grp
select new
{
Survey = grp.Key,
QuestionID = grp.OrderBy(x=>x.QuestionID).Select(i => i.QuestionID),
Question = grp.OrderBy(x=>x.QuestionID).Select(q => q.Text),
Answer = grp.OrderBy(x=>x.QuestionID).Select(a => a.Answer)
};
Reversal of a grouped order is a coincidence: IQueryable<T>'s GroupBy returns groups in no particular order. Unlike in-memory GroupBy, which specifies the order of its groups, queries performed in RDBMS depend on implementation:
The query behavior that occurs as a result of executing an expression tree that represents calling GroupBy<TSource,TKey,TElement>(IQueryable<TSource>, Expression<Func<TSource,TKey>>, Expression<Func<TSource,TElement>>) depends on the implementation of the type of the source parameter.`
If you would like to have your rows in a specific order, you need to add OrderBy to your query to force it.
How I do it and maintain the relative list order, rather than apply an order to the resulting set?
One approach is to apply grouping to your data after bringing it into memory. Apply ToList() to dataSet at the end to bring data into memory. After that, the order of subsequent GrouBy query will be consistent with dataSet. A drawback is that the grouping is no longer done in RDBMS.

Convert SQL statement to Linq-to-SQL with 2 columns with a count(*) not working

I have a SQL statement that I need to convert to Linq-to-SQL.
The SQL statement that works is
Select
Comment, count(*) as counted
from
[ESO].[ESO].[DOCCCOIssues]
group by
comment
Linq I have an issue with
var issueModel = new IssuesModel();
var query = (from c in DOCCCOIssues//.IssuesModels
//group c by c.Comment into g
select new
{
c.Comment,
//Count = g.Count()
});
I know how to specify a bunch of fields
I did comment out an example i saw of someone doing a group by with a count - not sure how to get this to work
I have seen the linq queries of ONLY getting select c).Count(), and that is not want I want
Once you have done the group by you have a list where each entry is iself an IEnumerable of the things in each group and has a Key property (the thing you grouped on, in this case Comment). See Group Elements in a Sequence on MSDN.
So in your result, you just need to return the Key as the Comment and then the Count() in that grouping, as you already tried:
var query = (from c in DOCCCOIssues//.IssuesModels
group c by c.Comment into g
select new
{
Comment = g.Key,
Count = g.Count()
});
Working .NetFiddle

How can I use sum with group by in LINQ?

I have the following many companies. Some of the companies have subjects and others do not. Something like this:
CompanyID Subjects
1 2
2 4
3 1
4 0
I am trying to create a LINQ report that will give me this information. This is what I have so far. It correctly does an outer join so that even companies with no subjects are include in the list. Once I have that data then I group the date by company title. The problem is that the last select does not work correctly. Can someone suggest how I can get the sum. I was able to use count() but I need a sum as the way I have set things up is that when there are no subjects a value of 0 goes into Subjects and where there is 1 a value of one goes there. So by summing the count of Subjects at each break in the group I should be able to find out how many subjects are assigned to the company.
var test1 = from c in companies
join s in subjects
on "0000" + c.RowKey equals s.PartitionKey into outer
from s in outer.DefaultIfEmpty()
select new
{
Title = c.Title,
Subjects = ((s == null) ? 0 : 1)
} into split
group split by split.Title into g
select new
{
Title = g.Key,
total = g.sum(s => s.Subjects)
};
You wrote the "sum" method without the capital "s". C# is case sensitive, so I think that's your problem, if you're getting a compile error.
Otherwise include the actual result of your query and what you expect, so we can compare it and try to find the problem.

C# LINQ query (MYSQL EF) - Distinct and Latest Records

I have a table, lets call it Record. Containing:
ID (int) | CustID (int) | Time (datetime) | Data (varchar)
I need the latest (most recent) record for each customer:
SQL
select * from record as i group by i.custid having max(id);
LINQ version 1
dgvLatestDistinctRec.DataSource = from g in ee.Records
group g by g.CustID into grp
select grp.LastOrDefault();
This throws an error:
System.NotSupportedException was unhandled by user code Message=LINQ
to Entities does not recognize the method 'Faizan_Kazi_Utils.Record
LastOrDefault[Record
](System.Collections.Generic.IEnumerable`1[Faizan_Kazi_Utils.Record
])' method, and this method cannot be translated into a store
expression. Source=System.Data.Entity
LINQ version 2
var list = (from g in ee.Records
group g by g.CustID into grp
select grp).ToList();
Record[] list2 = (from grp in list
select grp.LastOrDefault()).ToArray();
dgvLatestDistinctRec.DataSource = list2;
This works, but is inefficient because it loads ALL records from the database into memory and then extracts just the last (most recent member) of each group.
Is there any LINQ solution that approaches the efficiency and readability of the mentioned SQL solution?
Update:
var results = (from rec in Record group rec by rec.CustID into grp
select new
{
CustID = grp.Key,
ID = grp.OrderByDescending(r => r.ID).Select(x => x.ID).FirstOrDefault(),
Data = grp.OrderByDescending(r => r.ID).Select(x => x.Data).FirstOrDefault()
}
);
So I made a test table and wrote a Linq -> SQL Query that will do exactly what you need. Take a look at this and let me know what you think. Only thing to keep in mind if this query is scaled I believe it will run a query to the DB for each and every CustID record after the grouping in the select new. The only way to be sure would be to run SQL Tracer when you run the query for info on that go here .. http://www.foliotek.com/devblog/tuning-sql-server-for-programmers/
Original:
Could you do something like this? from g in ee.Records where g.CustID == (from x in ee.Records where (g.CustID == x.CustID) && (g.ID == x.Max(ID)).Select(r => r.CustID))
That's all pseudo code but hopefully you get the idea.
I'm probably too late to help with your problem, but I had a similar issue and was able to get the desired results with a query like this:
from g in ee.Records
group g by g.CustID into grp
from last in (from custRec in grp where custRec.Id == grp.Max(cr => cr.Id) select custRec)
select last
What if you replace LastOrDefault() with simple Last()?
(Yes, you will have to check your records table isn't empty)
Because I can't see a way how MySQL can return you "Default" group. This is not the thing that can be simply translated to SQL.
I think grp.LastOrDefault(), a C# function, is something that SQL doesn't know about. LINQ turns your query into an SQL query for your db server to understand. You might want to try and create an stored procedure instead, or another way to filter out what your looking for.
The reason your second query works is because the LINQ to SQL returns a list and then you do a LINQ query (to filter out what you need) on a C# list, which implements the IEnumerable/IQueryable interfaces and understands the grp.LastOrDefault().
I had another idea:
// Get a list of all the id's i need by:
// grouping by CustID, and then selecting Max ID from each group.
var distinctLatest = (from x in ee.Records
group x by x.CustID into grp
select grp.Max(g => g.id)).ToArray();
// List<Record> result = new List<Record>();
//now we can retrieve individual records using the ID's retrieved above
// foreach (int i in distinctLatest)
// {
// var res = from g in ee.Records where g.id == i select g;
// var arr = res.ToArray();
// result.Add(res.First());
// }
// alternate version of foreach
dgvLatestDistinctRec.DataSource = from g in ee.Records
join i in distinctLatest
on g.id equals i
select g;

Categories

Resources