How to get EF Framework to generate this group by query - linq

I tried a few different ways and I am not impressed with the generated output query. It seems so inefficient. It needs to be efficient and shouldn't bring back all the rows into the app layer
select year(datetaken) as yr,
month(datetaken) as mth,
day(datetaken) as dy,
count(*) as totalpics
from photos
where photos.dateTaken <= #cutoffdate
group by year(datetaken), month(datetaken), day(datetaken)
order by yr asc, mth asc, dy asc
LINQ query:
var query = ctx.Photos.Where(p => p.DateTaken <= maxCutOffDate)
.GroupBy(p => new { p.DateTaken.Year, p.DateTaken.Month, p.DateTaken.Day })
.Select(grp => grp);
var results = query.ToList();

Select only the grouping key (grp.Key):
var query = ctx.Photos.Where(p => p.DateTaken <= maxCutOffDate)
.GroupBy(p => new { p.DateTaken.Year, p.DateTaken.Month, p.DateTaken.Day })
.Select(grp => grp.Key);
var results = query.ToList();
Edit
Or, including the count of totalpics:
var query = ctx.Photos.Where(p => p.DateTaken <= maxCutOffDate)
.GroupBy(p => new { p.DateTaken.Year, p.DateTaken.Month, p.DateTaken.Day })
.Select(grp => new
{
Date = grp.Key,
TotalPics = grp.Count()
});
var results = query.ToList();

You can access the key properties through grp.Key and the count through grp.Count().
var query = ctx.Photos.Where(p => p.DateTaken <= maxCutOffDate)
.GroupBy(p => new { p.DateTaken.Year, p.DateTaken.Month, p.DateTaken.Day })
.Select(grp => new
{
grp.Key.Year,
grp.Key.Month,
grp.Key.Day,
Count = grp.Count()
});
var results = query.ToList();

Related

LinqToDB Exception cannot be converted to SQL

Hello I have an issue with this query
var queryGrouped = queryFiltered
.GroupBy(c => new { c.Id, c.TableOneId, c.TableOneName, c.TableTwoId, c.TableTwoName, c.TableTwoCode, c.TableThreeId, c.TableThreeName, c.Description, c.EffectiveDate, c.CreatedBy, c.ServiceGroupName })
.DisableGuard()
.Select(cg => new Model
{
Id = cg.Key.Id,
TableOneId = cg.Key.TableOneId,
TableOneName = cg.Key.TableOneName,
TableTwoId = cg.Key.TableTwoId,
TableTwoCode = cg.Key.TableTwoCode,
TableTwoName = cg.Key.TableTwoName,
TableThreeId = cg.Key.TableThreeId,
TableThreeName = cg.Key.TableThreeName,
Description = cg.Key.Description,
EffectiveDate = cg.Key.EffectiveDate,
EffectiveDateText = cg.Key.EffectiveDate != null ? cg.Key.EffectiveDate.Value.ToString("MM/dd/yyyy") : string.Empty,
ServiceGroupName = string.Join(", ", cg.Select(g => g.ServiceGroupName).Distinct()),
CreatedBy = cg.Key.CreatedBy
}).OrderBy(x => x.ServiceGroupName).ToListAsync();
If i run this when try to order by the field ServiceGroup it returns this message
LinqToDB.Linq.LinqException: ''Join(", ", cg.Select(g => g.ServiceGroupName).Distinct())' cannot be converted to SQL.'
So I don't know how to order by this field ServiceGroupName, thanks for any answer.
I would suggest to make grouping on the client side. Note that I have removed ServiceGroupName from grouping key.
var data = await queryFiltered
.Select(c => new {
c.Id,
c.TableOneId,
c.TableOneName,
c.TableTwoId,
c.TableTwoName,
c.TableTwoCode,
c.TableThreeId,
c.TableThreeName,
c.Description,
c.EffectiveDate,
c.CreatedBy,
c.ServiceGroupName
})
.ToListAsync();
var queryGrouped = data
.GroupBy(c => new { c.Id, c.TableOneId, c.TableOneName, c.TableTwoId, c.TableTwoName, c.TableTwoCode, c.TableThreeId, c.TableThreeName, c.Description, c.EffectiveDate, c.CreatedBy })
.Select(cg => new Model
{
Id = cg.Key.Id,
TableOneId = cg.Key.TableOneId,
TableOneName = cg.Key.TableOneName,
TableTwoId = cg.Key.TableTwoId,
TableTwoCode = cg.Key.TableTwoCode,
TableTwoName = cg.Key.TableTwoName,
TableThreeId = cg.Key.TableThreeId,
TableThreeName = cg.Key.TableThreeName,
Description = cg.Key.Description,
EffectiveDate = cg.Key.EffectiveDate,
EffectiveDateText = cg.Key.EffectiveDate != null ? cg.Key.EffectiveDate.Value.ToString("MM/dd/yyyy") : string.Empty,
ServiceGroupName = string.Join(", ", cg.Select(g => g.ServiceGroupName).Distinct()),
CreatedBy = cg.Key.CreatedBy
})
.OrderBy(x => x.ServiceGroupName)
.ToList();

How to get all the fields after groupby in LINQ

I have this object
With Fecha="Julio2017" I have 2 items and I need to group these three items by SubcontratistaId and Fecha.
item1.Realizado=4060.000 and item2.Realizado=-4060.000 So I need to show in Julio2017 the value of 0
So I try this
private IEnumerable<SubcontratacionesEnFecha> GetRealizadosEnFecha(string proyectoId)
.....
return realizadosAbonadosEnFechaAcumulados
.GroupBy(x => new { x.SubcontratistaId, x.Fecha })
But now I don't know how to get the values of all the items grouped
If I try this I get error
If I try this
return realizadosAbonadosEnFechaAcumulados
.GroupBy(x => new { x.SubcontratistaId, x.Fecha })
.Select(x=>x.First())
.ToList();
I get this
That is, I get the first value of the items grouped
Any idea Please?
Thanks
GroupBy returns IGrouping<,>, which has Key and itself is IEnumerable<> of grouped items. So more probable usage is:
return realizadosAbonadosEnFechaAcumulados
.GroupBy(x => new { x.SubcontratistaId, x.Fecha })
.Select(g => new
{
g.Key.SubcontratistaId,
g.Key.Fecha,
Items = g.ToList()
})
.ToList();
I Found this way
var agrupacion = from p in realizadosAbonadosEnFechaAcumulados
group p by new { p.SubcontratistaId, p.Fecha } into grupo
select grupo;
foreach (var grupo in agrupacion)
{
foreach (var objetoAgrupado in grupo)
{
resultAgrupado.Add(new SubcontratacionesEnFecha
{
SubcontratistaId = objetoAgrupado.SubcontratistaId,
NombreSubcontratista= objetoAgrupado.NombreSubcontratista,
ProyectoId = objetoAgrupado.ProyectoId,
Fecha = objetoAgrupado.Fecha,
Mes = objetoAgrupado.Mes,
Año = objetoAgrupado.Año,
Realizado = objetoAgrupado.Realizado,
Planificado = objetoAgrupado.Planificado
});
}
}
return resultAgrupado;

How to create an identity column within a lambda?

How can I make the expression add a counter (ID) that is 1,2,3,4 etc... ? I just want to have some unqiue key to identify the data
var query = data.Select(x =>
new DemoItemV1
{
Id = x.Field<double>("ID"),
AreaId = x.Field<int>("1Area ID"),
CategoryTitle = x.Field<string>("2Table Title")
}).ToList();
I don't think you can do this purely in a Linq-to-Sql or Linq-to-Entities query. But, since you're already materializing it to a list, you can do this:
var query = data.Select(x =>
new DemoItemV1
{
AreaId = x.Field<int>("1Area ID"),
CategoryTitle = x.Field<string>("2Table Title")
})
.AsEnumerable()
.Select((x, i) => { x.ID = i + 1; return x })
.ToList();

Getting the 1st value in a subset of data

I have a result coming back from a LINQ statement that looks like this:
Id dataId dataVal
A 1 1000
A 2 2000
A 3 3000
A 3 3001
A 3 3002
What I'd like is to just get the 1st item (dataId = 3 and dataVal = 3000)
Here is my query that is generating the above result:
var myIds = myList
.Where(a => ListIds.Contains(a.dataId))
.Select(x=> new
{
Id = x.Id,
DataId = x.dataId,
DataValue = x.DataValue
}).ToList().Distinct();
Do I need to do some grouping or is there an easier way?
Group your items by dataId, and then select first item from each group:
var myIds = (from a in myList
where ListIds.Contains(a.dataId)
group a by a.dataId into g
let firstA = g.OrderBy(x => x.DataValue).First()
select new {
Id = firstA.Id,
DataId = g.Key,
DataValue = firstA.DataValue
}).ToList();
Or with extension methods (it returns first item in original order):
var myIds = myList
.Where(a => ListIds.Contains(a.dataId))
.GroupBy(a => a.dataId)
.Select(g => new
{
Id = g.First().Id,
DataId = g.Key,
DataValue = g.First().DataValue
}).ToList();
Use .FirstOrDefault() after the Select
var myIds = myList
.Where(a => ListIds.Contains(a.dataId))
.Select(x=> new
{
Id = x.Id,
DataId = x.dataId,
DataValue = x.DataValue
}).FirstOrDefault();

LINQ lambda expression for many-to-one inner join involving max()

I have the following schema:
Table1
ID int
Table2
ID int
Table1ID int
Datetime datetime
Table3
ID int
Table2ID int
Name varchar(255)
All columns are not null. How do I write the following SQL query in LINQ using lambda expressions?
select Table1.*
from Table2
inner join (
select Table1ID, max(Datetime) as Datetime
from Table2
group by Table1ID
) a on Table2.Table1ID = a.Table1ID and Table2.Datetime = a.Datetime
inner join Table3 on Table2.ID = Table3.Table2ID
inner join Table1 on Table1.ID = Table2.Table1ID
where Name = 'me'
EDIT:
I am using LINQ to EF. I have tried
var myEntities = new MyEntities();
var a = myEntities.Table2.Select(x => new { x.Id, x.Datetime }).GroupBy(x => x.Id).Select(x => new { Id = x.Key, Datetime = x.Max(y => y.Datetime) });
var b = myEntities.Table2.Join(a.ToList(), x => new { Id = x.Table1Id, x.Datetime }, y => new { y.Id, y.Datetime }, (x, y) => x.Id);
return myEntities.Table3.Where(x => x.Name == "me" && b.Contains(x.Table2Id)).Select(x => x.Table2.Table1).ToList();
but it comes back with
System.NotSupportedException: Unable to create a constant value of type 'Anonymous type'. Only primitive types ('such as Int32, String, and Guid') are supported in this context.
highlighting the last line above. The stack trace shows it is ToList() throwing this exception.
I figured it out; it was that
var b = myEntities.Table2.Join(a.ToList(),
should be
var b = myEntities.Table2.Join(a,
Also, the query should be
var myEntities = new MyEntities();
var a = myEntities.Table2.Select(x => new { x.Table1Id, x.Datetime }).GroupBy(x => x.Table1Id).Select(x => new { Table1Id = x.Key, Datetime = x.Max(y => y.Datetime) });
var b = myEntities.Table2.Join(a, x => new { x.Table1Id, x.Datetime }, y => new { y.Table1Id, y.Datetime }, (x, y) => x);
return b.Join(myEntities.Table3, x => x.Id, y => y.Table2Id, (x, y) => new { x.Table1, y.Name }).Where(x => x.Name == "me").Select(x => x.Table1).ToList();

Resources