GroupBy in LINQ - c#

I need to group the table according to the Month in the table.
The query goes this way:
var query =
from a in db.Dates
from b in db.Facts
where a.Count_Key == b.Date_key
select new
{
a.Month,
b.Fact_key
};
From this query I try to group by Month
query = query.GroupBy(x => x.Month);
Grid1.DataSource = query;
Grid1.DataBind();
Then I get the following error which says:
Cannot implicitly convert IGrouping
int ? into IQueryable

You can do this:
var query2 = query.GroupBy(x => x.Month);
Grid1.DataSource = query2;
Grid1.DataBind();
The problem is caused because the var query is implicitly inferred from its usage. The query is of type IQueryable<SomeAnonymousType> and the GroupBy method returns an IQueryable<IGrouping<int?, SomeAnonymousType>>. Those are simply two different types.

You can't reassign back to the query variable as the results of those two queries are different types.
Your first query is essentially this:
IQueryable<'a> query =
from a in db.Dates
from b in db.Facts
where a.Count_Key == b.Date_key
select new
{
a.Month,
b.Fact_key
};
I.e. it returns a straight IQueryable of the anonymous type.
Your second query returns an IQueryable<IGrouping<'a>>, i.e. an IQueryable of a group of the anonymous type:
IQueryable<IGrouping<'a>> groupedQuery = query.GroupBy(x => x.Month);
Therefore because the return types are different you can't assign the result of the grouping back to the original variable.

Related

Query List returns Null when add toList

I've tried to provide a simplified version of the problem I'm dealing with. Query 1 is enumerated and returns a List of PersonViewModel.
However, when I change the query and add a .ToList() before the select statement, query returns the value null.
The reason for this is because I want it enumerated first, so I can deal with the values in memory - in order to use functions such as .toString which cannot be converted to SQL.
Returns as expected
var query = (from s in context.PersonDetails.GetQueryable(x => x.Id == Id)
select new PersonViewModel
{
Name = s.Person.Firstname,
Number = s.Person.Number
}).ToList();
Returns null
var query = (from s in context.PersonDetails.GetQueryable(x => x.Id == Id).toList()
select new PersonViewModel
{
Name = s.Person.Firstname,
Number = s.Person.Number
}).ToList();

Select distinct column names in Linq

My table contains several columns and I need to select distinct rows in two specific columns using Linq.
My SQL equivalent is:
Select distinct Level1Id, Level1Name
from levels
What I currently do is:
db.levels.GroupBy(c=> c.Level1Id).Select(s => s.First())
This will retrieve the whole row not only Level1Id and Level1Name. How can I specify the columns I want to retrieve in this linq query?
With Select, you can specify the columns in an anonymous object and then use Distinct on that:
db.levels.Select(l => new{ l.Level1Id, l.Level1Name }).Distinct();
try
db.levels.Select(c => new {c.Level1Id, c.Level1Name}).Distinct();
Specify the two columns in your LINQ query select, create an anonymous object with Level1Id and Level1Name properties:
var query = (from v in db.levels
select new { Level1Id = v.Level1Id, Level1Name = v.Level1Name }).Distinct();
and use each item like this:
foreach (var r in query){
int valId = r.LevelId;
int level = r.Level1Name;
//do something
}
You are so close, one more step:
var result = db.levels.GroupBy(c=> new { c.Level1Id, c.Level1Name })
.Select(s => s.First())
The key thing is: Anonymous type uses structural comparison, that's why GroupBy or any other answer do work.

MVC search a LINQ query

I have a very simple query
var query = (from s in db.Stocks
select s).ToList();
and now basically I want to then search that query to see if any of the stock objects names in the query contain "a"
I have this
if(!string.IsNullOrEmpty(searchModel.Name) )
{
query = query.Where(x => x.Name.Contains(searchModel.Name));
}
but I am getting this error
Cannot implicity convert type "system.collections.generic.ienumerable<etc etc> to system.collections.generic.list
How can I search my query result?
Its ok I have the answer
I just need to add .ToList to the end of my query statement like so
if(!string.IsNullOrEmpty(searchModel.Name) )
{
query = query.Where(x => x.Name.Contains(searchModel.Name)).ToList();
}
The reason is because in the first query
var query = (from s in db.Stocks
select s).ToList();
you have fixed the data type of the variable query to be List (toList()) . Now when you query it again with Where it returns IEnumerable and can't be assigned to the type List . You can do it in one line as in below
var query = from s in db.Stocks
where s.contains(searchModel.Name)
select s
try
query = query.Where(x => x.Name.Contains(searchModel.Name)).ToList();
you can re-write the query as following:
var query = (from s in db.Stocks.ToList()
where !string.IsNullOrEmpty(searchModel.Name) ? s.Contains(searchModel.Name) : true
select s).ToList();
the new added line means: if SearchModel.Name is filled then search using it, otherwise do nothing
or you can keep your code as it, but add .ToList() after the table name, or remote .ToList() on the end of your query code which enforce the datatype of result

Looking for a list of items in a query

I am passing a string list of studentKeys to my LINQ to Entities query.
So I want to return those data that are in that coming list.
public IQueryable<Students> GetStudentsFromKeys(List<string> studentKeys)
{
var result = from a in this.Context.Students
where // ?
return result.ToList();
}
But How should I actually write such a query? I want to query that table and if its keys are the same as one of the keys in that list, return it as a result.
You can do:
var result = from a in this.Context.Students
where studentKeys.Contains(a.StudentKey)
select a;
Or with method syntax as:
var query = this.Context.Students
.Where(r => studentKeys.Contains(r.StudentKey));
(assuming StudentKey is the name of the field you want to compare)
This would generate query similar to
SELECT * from Students WHERE StudentKey in ("1","2","3")

Subquery in Where Clause of LINQ statement

So I tried to follow this example to have a sub-query in the where clause of this LINQ query.
var innerquery =
from app in context.applications
select new { app.app_id };
IEnumerable<postDatedCheque> _entityList = context.postDatedCheques
.Where(e => innerquery.Contains(e.appSancAdvice.application.app_id));
The objective was to select those records from postDatedCheques that have app_id in applications table.
But I am getting following erros inside the where clause:
Delegate 'System.Func' does not
take 1 arguments
Cannot convert lambda expression to type 'string' because it is not
a delegate type
'System.Linq.IQueryable' does not contain a
definition for 'Contains' and the best extension method overload
'System.Linq.ParallelEnumerable.Contains(System.Linq.ParallelQuery,
TSource)' has some invalid arguments
Instance argument: cannot convert from
'System.Linq.IQueryable' to
'System.Linq.ParallelQuery'
What am I coding incorrect?
I think a simple join would do the job. It will filter out the 'cheques' that have no relative 'app':
var _entitylist =
from cheque in context.postDatedCheques
join app in context.applications on cheque.appSancAdvice.application equals app
select cheque;
Edit:
Solutions using a .Contains(...) will be translated into a SQL IN statement. Which will be very inefficient. Linq join is translated into SQL INNER JOIN which is very efficient if your DB schema is well trimmed (FKs, index)
What about?
IEnumerable<postDatedCheque> _entityList = context.postDatedCheques.Where(
e => context.applications.Any(
x => e.appSancAdvice.application.app_id == x.app_id));
And if you want to use two statements, set the first as an expression function.
Expression<Func<string, bool>> innerQuery =
x => context.applications.Any(y => y.app_id == x);
IEnumerable<postDatedCheque _entityList =
context.postDatedCheques.Where(
x => innerQuery(x.appSancAdvice.application.app_id));
innerquery is a IQueryable of anonymous type that contains an app_id.
The line Contains(e.appSancAdvice.application.app_id) doesn't make sense since e.appSancAdvice.application.app_id and the anonymous type are not the same type.
Simply do:
var _entityList = context.postDatedCheques
.Where(e =>
context.applications
.Select(a => a.app_id)
.Contains(e.appSancAdvice.application.app_id));
Try this instead:
var innerquery =
from app in context.applications
select new { app.app_id };
IEnumerable<postDatedCheque> _entityList = context.postDatedCheques
.Where(e => innerquery.Any(a => a.app_id == e.appSansAdvice.application.app_id));

Categories