Group by clause in Entity Framework or Micro ORM using lambda - entity-framework

How to use following query by using Lambda Expression (Entity Framework or Micro ORM)?
SELECT a.RestaurantID
,MAX(a.EventDate) LastPeriodCloseDate
FROM (
SELECT RestaurantID
,EventType
,EventDate
FROM SystemCalendar
WHERE EventType = 'P'
) a
GROUP BY RestaurantID
HAVING MAX(a.EventDate) BETWEEN '2014-10-31'
AND '2015-03-31'

Your inner select isn't necessary. You can just do this:
DateTime start = new DateTime(2014, 10, 31);
DateTime end = new DateTime(2015, 3, 31);
var query = _db.SystemCalendar
.Where(x => x.EventType == "P")
.GroupBy(x => x.RestaurantID,
x => new
{
RestaurantID = x.Key,
LastPeriodCloseDate = x.Max(y => y.EventDate)
})
.Where(x => x.LastPeriodCloseDate >= start &&
x.LastPeriodCloseDate < end);

Related

Select FROM Subquery without starting with another context object

I am trying to model the following MSSQL query that I am trying to replicate in netCore 2.2 - EF Core:
SELECT
wonum,
MIN(requestdate) AS startdate,
MAX(requestdate) AS enddate,
MIN(laborcode)
FROM
(
SELECT
wo.wonum,
sw.requestdate,
wo.wolablnk AS 'laborcode'
FROM
DB1.dbo.web_users wu INNER JOIN
DB2.dbo.workorder wo on
wu.laborcode = wo.wolablnk INNER JOIN
DB2.dbo.sw_specialrequest sw on
wo.wonum = sw.wonum
WHERE
wo.status in ('LAPPR', 'APPR', 'REC') AND
sw.requestdate > GETDATE()
) a
GROUP BY
wonum
ORDER by
I have the subquery portion built and working but that leaves me at an impasse:
var workOrders = await _db1Context.Workorder
.Where(r => r.Status == "LAPPR" || r.Status == "APPR" || r.Status == "REC")
.ToListAsync();
var specialRequests = await _db2Context.SwSpecialRequest
.Where(r => r.Requestdate > DateTime.Now)
.ToListAsync();
var subQuery = (from webUser in webUsers
join workOrder in workOrders on webUser.Laborcode equals workOrder.Wolablnk
join specialRequest in specialRequests on workOrder.Wonum equals specialRequest.Wonum
orderby webUser.Laborcode, specialRequest.Requestdate, specialRequest.Wonum
select new { workOrder.Wonum, Laborcode = workOrder.Wolablnk, specialRequest.Requestdate, workOrder.Workorderid })
.ToList();
I am not sure how to initiate the query I need with the subquery i've built and i'm not sure if I am on the right track even. I've looked at a couple of other examples but i'm not getting it.
Would anyone be able to shed some light on the subject and help?
Thank you!
Write LINQ query identical to the SQL and do not mix with ToListAsync(). After ToListAsync() query is sent to the server. Also you should use only one DbContext for such query.
var webUsers = _db1Context.Webuser;
var workOrders = _db1Context.Workorder
.Where(r => r.Status == "LAPPR" || r.Status == "APPR" || r.Status == "REC");
var specialRequests = _db1Context.SwSpecialRequest
.Where(r => r.Requestdate > DateTime.Now);
var subQuery =
from webUser in webUsers
join workOrder in workOrders on webUser.Laborcode equals workOrder.Wolablnk
join specialRequest in specialRequests on workOrder.Wonum equals specialRequest.Wonum
select new
{
workOrder.Wonum,
Laborcode = workOrder.Wolablnk,
specialRequest.Requestdate
};
var resultQuery =
from a in subQuery
group a by a.Wonum into g
select new
{
Wonum = g.Key,
StartDate = g.Min(x => x.Requestdate),
EndDate = g.Max(x => x.Requestdate),
Laborcode = g.Min(x => x. Laborcode)
};
// final materialization
var result = await resultQuery.ToListAsync();

EF Core 2.1, linq query not producing group by sql query and I can see just Order By at last

I have a linq query which has Group By clause, but the Group By is not happening on sql server.
I tried a simple query and the Group By is happening on sql server.
Please guide me why this different behavior??
I want that group-by on server for performance improvement.
Simple query where I get group-by if I log the sql query:
var testt = (from doc in _patientRepository.Documents
group doc by doc.DocumentType into G
select new
{
Key = G.Key
}).ToList();
Generated sql:
Executed DbCommand (247ms) [Parameters=[], CommandType='Text',
CommandTimeout='30']
SELECT [doc].[DocumentType] AS [Key]
FROM [Document] AS [doc]
GROUP BY [doc].[DocumentType]
Issue query:
var patX = (from doc in _patientRepository.Documents
join pat in _patientRepository.Patients
on doc.PatientId.ToString().ToLower() equals pat.PatientId.ToString().ToLower()
where doc.Source.ToLower() != "testclient.server.postman" &&
pat.Deleted == false && sfHCPs.Contains(pat.HcpId.ToLower())
select new Document()
{
DocumentId = doc.DocumentId,
CreationDateTime = doc.CreationDateTime,
DocumentType = doc.DocumentType,
PatientId = doc.PatientId,
DocumentTypeVersion = doc.DocumentTypeVersion,
Source = doc.Source,
PayloadLeft = DocumentMapper.DeserializePayload(doc.PayloadLeft),
PayloadRight = DocumentMapper.DeserializePayload(doc.PayloadRight),
PayloadBoth = DocumentMapper.DeserializePayload(doc.PayloadBoth),
IsSalesforceSynced = doc.IsSalesforceSynced,
HcpId = pat.HcpId
}).GroupBy(p => new { p.PatientId, p.DocumentType })
.Select(g => g.OrderByDescending(p => p.CreationDateTime).FirstOrDefault())
.Where(x => x.IsSalesforceSynced == false)
.ToList();
Why don't it have group-by sql generated:
Microsoft.EntityFrameworkCore.Database.Command[20101]
Executed DbCommand (200ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
SELECT [doc].[DocumentId], [doc].[CreationDateTime], [doc].[DocumentType], [doc].[PatientId], [doc].[DocumentTypeVersion], [doc].[Source], [doc].[PayloadLeft], [doc].[PayloadRight], [doc].[PayloadBoth], [doc].[IsSalesforceSynced], [pat].[HcpId]
FROM [Document] AS [doc]
INNER JOIN [Patient] AS [pat] ON LOWER(CONVERT(VARCHAR(36), [doc].[PatientId])) = LOWER(CONVERT(VARCHAR(36), [pat].[PatientId]))
WHERE ((LOWER([doc].[Source]) <> N'testclient.server.postman') AND ([pat].[Deleted] = 0)) AND LOWER([pat].[HcpId]) IN (N'4e7103a9-7dff-4fa5-b540-a32a31be2997', N'abc1', N'def2', N'ghi3')
ORDER BY [doc].[PatientId], [doc].[DocumentType]
I tried below approach but same sql generated:
var patX = ((from doc in _patientRepository.Documents
join pat in _patientRepository.Patients
on doc.PatientId.ToString().ToLower()
equals pat.PatientId.ToString().ToLower()
where doc.Source.ToLower() != "testclient.server.postman" &&
pat.Deleted == false && sfHCPs.Contains(pat.HcpId.ToLower())
select new Document()
{
DocumentId = doc.DocumentId,
CreationDateTime = doc.CreationDateTime,
DocumentType = doc.DocumentType,
PatientId = doc.PatientId,
DocumentTypeVersion = doc.DocumentTypeVersion,
Source = doc.Source,
PayloadLeft = DocumentMapper.DeserializePayload(doc.PayloadLeft),
PayloadRight = DocumentMapper.DeserializePayload(doc.PayloadRight),
PayloadBoth = DocumentMapper.DeserializePayload(doc.PayloadBoth),
IsSalesforceSynced = doc.IsSalesforceSynced,
HcpId = pat.HcpId
}).GroupBy(p => new { p.PatientId, p.DocumentType })
.Select(g => g.OrderByDescending(p => p.CreationDateTime).FirstOrDefault())
.Where(x => x.IsSalesforceSynced == false))
.ToList();
Before version 2.1, in EF Core the GroupBy LINQ operator would always be evaluated in memory.Now support translating it to the SQL GROUP BY clause in most common cases.
change code : try to place .GroupBy before .select method(first select)
Consider re-ordering the query so the select to the new class is last:
var p1 = from doc in _patientRepository.Documents
join pat in _patientRepository.Patients on doc.PatientId.ToString().ToLower() equals pat.PatientId.ToString().ToLower()
where doc.Source.ToLower() != "testclient.server.postman" && pat.Deleted == false && sfHCPs.Contains(pat.HcpId.ToLower())
group new { doc, pat.HcpId } by new { doc.PatientId, doc.DocumentType } into dpg
select dpg.OrderByDescending(dp => dp.doc.CreationDateTime).FirstOrDefault();
var patX = (from dp in p1
where !dp.doc.IsSalesforceSynced
select new Document() {
DocumentId = dp.doc.DocumentId,
CreationDateTime = dp.doc.CreationDateTime,
DocumentType = dp.doc.DocumentType,
PatientId = dp.doc.PatientId,
DocumentTypeVersion = dp.doc.DocumentTypeVersion,
Source = dp.doc.Source,
PayloadLeft = DocumentMapper.DeserializePayload(dp.doc.PayloadLeft),
PayloadRight = DocumentMapper.DeserializePayload(dp.doc.PayloadRight),
PayloadBoth = DocumentMapper.DeserializePayload(dp.doc.PayloadBoth),
IsSalesforceSynced = dp.doc.IsSalesforceSynced,
HcpId = dp.HcpId
})
.ToList();

Evaluate EF query with MAX on the server

Using Entity Framework Core 2.2 I have the following query:
var user = await context.Users.AsNoTracking()
.Include(x => x.Lessons).ThenInclude(x => x.LessonLevel)
.FirstOrDefaultAsync(x => x.Id == userId);
var lessons = context.Lessons.AsNoTracking();
.Where(x => x.LessonLevelId < user.Lessons.Max(y => y.LessonLevelId));
Thus query evaluates locally and I get the message:
The LINQ expression 'Max()' could not be translated and will be evaluated locally.'
How can I make this query evaluate on the server?
Update
Based on DavigG answer I made it work using:
var maxLessonLevelId = user.Lessons.Max(y => y.LessonLevelId););
var lessons = context.Lessons.AsNoTracking();
.Where(x => x.LessonLevelId < maxLessonLevelId);
I know the following evaluates locally but shouldn't evaluate on the server?
var lessons = context.Lessons.AsNoTracking();
.Where(x => x.LessonLevelId <
context.Users.AsNoTracking()
.Where(y => y.Id == userId)
.Select(y => y.Lessons.Max(z => z.LessonLevelId))
.FirstOrDefault());
Is it possible to use a child queries that evaluates on the server?
Get the max value as a separate query, for example:
var maxLessonLevelId = user.Lessons.Max(y => y.LessonLevelId);
Then you can can get the lessons like this:
var lessons = context.Lessons.AsNoTracking()
.Where(x => x.LessonLevelId < maxLessonLevelId);

Linq query using result of another query

I have a query that gets data in the form of an IQueryable
var assys = assetrelationshipRepository.GetAll()
.Where(x => x.AssetId == siteAssetId)
.Where(x => x.RelationshipTypeId == (long)AssetRelationshipTypeEnum.Parent)
.Where(x => x.RelatedAsset.AssetTypeId == (long)AssetTypeEnum.Assembly)
.Select(x => x.RelatedAsset.CustomAssetAttributes2);
For every 'assy' that is returned, I'd like to get it's AssetId and use this to get a list of 'subassys', see below. For each 'assy' record, the assyId variable should be substituted for its AssetId.
var subassys = assetrelationshipRepository.GetAll()
.Where(x => x.AssetId == assyId)
.Where(x => x.RelationshipTypeId == (long)AssetRelationshipTypeEnum.Parent)
.Where(x => x.RelatedAsset.AssetTypeId == (long)AssetTypeEnum.SubAssy)
.Select(x => x.RelatedAsset.CustomAssetAttributes2);
I assume I'll need to use ForEach, does anyone know if what I'm trying to do is possible?
Thanks
Applying ForEach would be extremely bad approach to retrieve your desired result. You could apply join and group for that queries instead.
var assysQuery = assetrelationshipRepository.GetAll()
.Where(x => x.AssetId == siteAssetId)
.Where(x => x.RelationshipTypeId == (long)AssetRelationshipTypeEnum.Parent)
.Where(x => x.RelatedAsset.AssetTypeId == (long)AssetTypeEnum.Assembly);
Then apply join and group;
var subAssysQuery =
from assy in assysQuery
join subAssy in assetrelationshipRepository.GetAll() on assy.Id equals subAssy.AssetId
where
subAssy.RelationshipTypeId == (long)AssetRelationshipTypeEnum.Parent &&
subAssy.RelatedAsset.AssetTypeId == (long)AssetTypeEnum.Assembly
group assy by assy.Id into g
select new
{
AssyId = g.Key,
SubAssets = g.Select(x => x.RelatedAsset.CustomAssetAttributes2),
};
This should give you CustomAssetAttributes2 value of subassys for per assy record.
Note : Also, I suggest you to use known type for select clause instead
of anonymous type.

Except Sql query in Linq

I am using the bellow sql to get available room:
select rooms.number
from rooms
EXCEPT
(SELECT rooms.number
FROM dbo.Bookings
INNER JOIN dbo.Rooms ON Extent1.RoomId = dbo.Rooms.Id
WHERE
(Bookings.StartDate <= '2014-12-01')
AND (Bookings.EndDate >= '2016-12-05')
)
Could you please help me to convert it to Linq? (like "var list= from a in db...."
Thanks in advance
try this:
var start = DateTime.ParseExact("20141201",
"yyyyMMdd",
CultureInfo.InvariantCulture);
var end = DateTime.ParseExact("20141205",
"yyyyMMdd",
CultureInfo.InvariantCulture);
var list = dbContext.Rooms
.Except(dbContext.Bookings
.Where(e => e.StartDate >= start)
.Where(e => e.EndDate <= end)
.Select(e => e.Room))
.SELECT(e => e.Number)
.ToList();