Subquery to get maximum value in LINQ query in C# - c#

I'm trying to create a sub-query to obtain the latest date from a table, unrelated to the rest of the query.
My query is below. I'd like to select the highest date in a sub-table as a value and project it to my model below.
My other tabe is Feedback and contains a date value and a username field
return (from t1 in db.TaskAppointmentOpens
from t2 in db.Tasks.Where(t => (t.Task_ID == t1.Parent_Task_ID))
from t3 in db.UserNames.Where(t => (t.User_Username == t2.OwnerTypeItem_ID))
where ((t2.Item_ID > 0) && (t2.Type_ID > 0) && (t2.Creator == user) && (t1.AppointmentEnd < DateTime.Now) && (t1.AppointmentStart > EntityFunctions.AddMonths(DateTime.Now, -6)) && (from i in db.AppointmentFeedbacks where i.AppointmentId == t1.ID select i).Count() == 0)
group new {t2, t3} by new {
t2.OwnerTypeItem_ID, t3.Name
} into g
let oldestAppointment = g.Min(uh => uh.t2.Due_Date)
select new TelesalesNeglectedFeedbackModel
{
UserFullName = g.Key.Name,
QtyOutstanding = g.Select(x => x.t2.Task_ID).Distinct().Count(),
OldestAppointment = oldestAppointment
LastDateInOtherTable = HERE <======
}).Take(5).ToList();

You should be able to access the table and field this way:
LastDateInOtherTable = db.Feedback.Max(f => f.Date)
Or use a let clause as you've done for the other column and then assign it later:
let lastDate = db.Feedback.Max(f => f.Date)
...
LastDateInOtherTable = lastDate

Related

Problems recreating LINQ query from SQL script

I'm struggling in 'translating' a SQL query to a LINQ query. My SQL query looks like this:
SELECT S.SummonerName, LE.LeaguePoints, LS.DateTime, SUM(LE.LeaguePoints)
FROM LadderEntries LE
JOIN Sumonners S on LE.SummonerId = S.Id
JOIN LadderSnapshots LS on LE.LadderSnapshotId = LS.Id
WHERE LS.Region = 'euw1'
AND DateTime = '2019-06-14 00:00:00'
OR DateTime = '2019-06-13 00:00:00'
and LS.Region = 'euw1'
GROUP BY S.SummonerName
This query gives me the desired result. However, so far I got the following LINQ query:
from LE in _database.LadderEntries
join S in _database.Sumonners on LE.SummonerId equals S.Id
join LS in _database.LadderSnapshots on LE.LadderSnapshot.Id equals LS.Id
where LS.Region == param.Region && (LS.DateTime == param.Date || LS.DateTime == param.Date.AddDays(-1))
group new {LE, S, LS} by S.SummonerName
into C
select new GetLadderEntryDifferencesEntry
{
LpDifference = C.Select(a => a.LE.LeaguePoints).Sum(),
SummonerName = C.Select(a => a.S.SummonerName).FirstOrDefault()
};
But this gives me the error InvalidOperationException: Error generated for warning 'Microsoft.EntityFrameworkCore.Query.QueryClientEvaluationWarning: The LINQ expression 'GroupBy([S].SummonerName, new <>f__AnonymousType5'3(LE = [LE], S = [S], LS = [LS]))' could not be translated and will be evaluated locally.'. on execution.
I'm wondering what I'm doing wrong.
Note: I'm using sqllite and ef core if that makes a difference.
I believe the issue will be with:
&& (LS.DateTime == param.Date || LS.DateTime == param.Date.AddDays(-1))
try this:
var firstDate = param.Date;
var secondDate = param.Date.AddDays(-1);
then
&& (LS.DateTime == firstDate || LS.DateTime == secondDate)
possibly it may be taking exception to:
SummonerName = C.Select(a => a.S.SummonerName).FirstOrDefault()
or how the grouping is arranged.
Are these entities set up with references and can the expression be simplified?
var ladderEntryDifferences = _database.LadderSnapshots
.Where(x =>x.Region = param.Region
&& (x => x.DateTime == firstDate || x.DateTime == secondDate)
.GroupBy(x => x.LadderEntry.Summoner.SummonerName)
.Select( g => new GetLadderEntryDifferencesEntry
{
LpDifference = g.Sum(x => x.LeagueEntry.LeaguePoints),
SummonerName = g.Key // This will be the SummonerName
}).ToList();
The above is a guess about the structure, and pulling the group by from memory, but it might give you some ideas to try. If your context does not expose LadderSnapshots at a top level, you should be able to compose it from the LadderEntry level as well...
Ok here is what i got, first
LS.Region = 'euw1'
AND DateTime = '2019-06-14 00:00:00'
OR DateTime = '2019-06-13 00:00:00'
and LS.Region = 'euw1'
is not the same as the below linq, where the parameters is missing in the sql.
&& (LS.DateTime == param.Date || LS.DateTime == param.Date.AddDays(-1))
Now about the group by you could try to do it like this instead
from LE in _database.LadderEntries
join S in _database.Sumonners on LE.SummonerId equals S.Id
join LS in _database.LadderSnapshots on LE.LadderSnapshot.Id equals LS.Id
where LS.Region == param.Region && (LS.DateTime == param.Date || LS.DateTime == param.Date.AddDays(-1))
group LE by new {LE.SummonerName, S.SummonerName, LS.SummonerName}
into C
select new GetLadderEntryDifferencesEntry
{
LpDifference = C.Select(a => a.LE.LeaguePoints).Sum(),
SummonerName = C.Select(a => a.S.SummonerName).FirstOrDefault()
};

Why Same where clause need to write multiple times in a Linq Query for following SQL

What will be the proper linq syntax of below SQL Query ?
select a.id, a.AppointmentStatusID, ad.ID as DetailID
from [dbo].[Appointment] a, [dbo].[AppointmentDetail] ad
where a.[ID] = ad.[AppointmentID]
and a.CompanyID = 'a3dea87a-804e-4115-98cf-472988cf1678'
and a.LocationID = '3165caca-2a48-46f0-bbed-578cff29167t'
and ad.AppDateFrom <= {ts '2017-11-14 23:59:31'}
and ad.AppDateTo >= {ts '2017-11-14 00:00:00'}
and ad.[ApprovalStatusID] = 2
Problem I faced:
I required to filter Where Condition two times 1st at the time within the join & 2nd time during the object.Select expression, please check bellow
var results = (from a in appointments
join ad in _appointmentDetailRepository.GetAll() on a.ID equals ad.AppointmentID
where ad.ApprovalStatusID == 2
&& DbFunctions.TruncateTime(ad.AppDateFrom) <= DbFunctions.TruncateTime(viewmodel.AppointmentDate)
&& DbFunctions.TruncateTime(ad.AppDateTo) >= DbFunctions.TruncateTime(viewmodel.AppointmentDate)
orderby a.ID
select new Appointment
{
ID = a.ID,
CompanyID = a.CompanyID,
LocationID = a.LocationID,
AppointmentDetail = a.AppointmentDetail.Select(ad => new AppointmentDetail
{
ID = ad.ID,
AppDateFrom = ad.AppDateFrom,
AppDateTo = ad.AppDateTo,
AppointmentStatusID = ad.AppointmentStatusID,
}).Where(ad=> ad.ApprovalStatusID == 2
&& DbFunctions.TruncateTime(ad.AppDateFrom) <= DbFunctions.TruncateTime(viewmodel.AppointmentDate)
&& DbFunctions.TruncateTime(ad.AppDateTo) >= DbFunctions.TruncateTime(viewmodel.AppointmentDate)).ToList()
}).GroupBy(x => x.ID).Select(x => x.DefaultIfEmpty().FirstOrDefault());
Query : Why I required to write Where clause 2 times ?
Required Result
An Appointment Object --> Containing ICollection<AppoinmentDetails> if Details.Where Condition == True
From what I see (without knowing the model you have), it looks like you should use the already joined and filtered Details from ad instead of looking it up again from the Property a.AppointmentDetail...
Untested:
select new Appointment
{
ID = a.ID,
CompanyID = a.CompanyID,
LocationID = a.LocationID,
AppointmentDetail = ad.ToList(), // <-- don't you think?
...
}
For the given SQL query
select a.id, a.AppointmentStatusID, ad.ID as DetailID
from [dbo].[Appointment] a, [dbo].[AppointmentDetail] ad
where a.[ID] = ad.[AppointmentID]
and a.CompanyID = 'a3dea87a-804e-4115-98cf-472988cf1678'
and a.LocationID = '3165caca-2a48-46f0-bbed-578cff29167t'
and ad.AppDateFrom <= {ts '2017-11-14 23:59:31'}
and ad.AppDateTo >= {ts '2017-11-14 00:00:00'}
and ad.[ApprovalStatusID] = 2
LINQ query can be written as
var results = (from a in appointments
join ad in appointmentDetails on a.ID equals ad.AppointmentID
where ad.ApprovalStatusID == 2
&& a.CompanyID == "a3dea87a-804e-4115-98cf-472988cf1678"
&& a.LocationID == "3165caca-2a48-46f0-bbed-578cff29167t"
&& ad.AppDateFrom.Date <= viewmodel.AppointmentDate.Date
&& ad.AppDateTo.Date >= viewmodel.AppointmentDate.Date
select new
{
ID = a.ID,
AppointmentStatusID = a.AppointmentStatusID,
DetailID = ad.ID
}).ToList();
You can also write it like
var results = appointmentDetails
.Where(ad => ad.AppDateFrom.Date <= viewmodel.AppointmentDate.Date
&& ad.AppDateTo.Date >= viewmodel.AppointmentDate.Date
&& ad.ApprovalStatusID == 2
&& ad.Appointment.CompanyID == "a3dea87a-804e-4115-98cf-472988cf1678"
&& ad.Appointment.LocationID == "3165caca-2a48-46f0-bbed-578cff29167t")
.Select(ad =>
new
{
ID = ad.Appointment.ID,
AppointmentStatusID = ad.Appointment.AppointmentStatusID,
DetailID = ad.ID
})
.ToList()
As per the updated question, to get the the Appointment object with a collection of AppointmentDetails, please try this query
var results = appointmentDetails
.Where(ad => ad.AppDateFrom.Date <= viewmodel.AppointmentDate.Date
&& ad.AppDateTo.Date >= viewmodel.AppointmentDate.Date
&& ad.ApprovalStatusID == 2
&& ad.Appointment.CompanyID == "a3dea87a-804e-4115-98cf-472988cf1678"
&& ad.Appointment.LocationID == "3165caca-2a48-46f0-bbed-578cff29167t")
.Select(ad =>
new
{
ID = ad.Appointment.ID,
AppointmentStatusID = ad.Appointment.AppointmentStatusID,
Detail = ad
})
.AsEnumerable()
.GroupBy(a => new { a.ID, a.AppointmentStatusID })
.Select(a => new Appointment
{
ID = a.Key.ID,
AppointmentStatusID = a.Key.AppointmentStatusID,
AppointmentDetails = a.Select(d => d.Detail).ToList()
})
.ToList();

Linq Min in Select new & Multiple GroupBy columns

I want to write following query in Linq
INSERT INTO INOUTNEW (CODE,INDATE,TIME_DATE1,INOUTFLAG,TIME_FLD1,TIME_FLD2,TIME_FLD3)
SELECT CODE,MIN(INDATE),TIME_DATE1,'I',TIME_FLD1,TIME_FLD2,'31/05/2015' FROM INOUT
WHERE TIME_FLD1='T0003' AND INDATE >= '31/05/2015' AND INDATE <= '31/05/2015'
AND TIME_DATE1='31/05/2015'
GROUP BY CODE,TIME_DATE1,TIME_FLD1,TIME_FLD2
SO I am trying this :-
var data = ctx.tblInOut.Where(m => m.CompanyId == companyId && m.Time_Field1 == item.ShiftCode && m.InDate == StrInStart && m.InDate <= StrInEnd && m.Time_Date1 == InputDate).Select(m =>
new
{
EmployeeId = m.EmployeeId,
InDate = Min(m.InDate),
Time_Date1 = m.Time_Date1,
InOutFlag = m.InOutFlag
}).ToList();
I am stuck in Min Part. How to get Min in Select? And How to add multiple GroupBy in Linq?
Try something like this:
var data = ctx.tblInOut
.Where(m =>
m.CompanyId == companyId &&
m.Time_Field1 == item.ShiftCode &&
m.InDate == StrInStart &&
m.InDate <= StrInEnd &&
m.Time_Date1 == InputDate
)
.GroupBy(m =>
new {
m.Code,
m.Time_Date1,
m.Time_FLD1,
m.Time_FLD2
})
.Select(g =>
new
{
m.Key.Code,
InDate = m.Min(gg => gg.InDate),
m.Key.Time_Date1,
Something = "I",
m.Key.Time_FLD1,
m.Key.Time_FLD2,
SomeDate = "31/05/2015"
}).ToList();
To get Min, you must group first - otherwise it's trying to call Min on a single element.
.Key simply references the key of the group (in this case, a tuple of Code, Date1, Time_FLD1, Time_FLD2)

How to write the following query in LINQ

I have a sql query and would like to convert it into linq
SELECT CAST([Date] AS DATE),
COUNT([ID]) AS 'Amount of Systems'
FROM [DemoDB].[dbo].[Servers]
WHERE [ServerID] IN ('ServerX') AND [Type] = 'Complete'
GROUP BY CAST([Date] AS DATE)
ORDER BY CAST([Date] AS DATE)
this will return the result as follows
What I have tried
//fromDP and toDP are the names of the Datepicker's
var query = (this.db.Servers
.Where(x => x.Date >= fromDP.SelectedDate.Value &&
x.Date <= toDP.SelectedDate.Value));
var query_Success = query.Count(p => p.Type == "Complete"
&& (p.ServerID == "ServerX"));
and I have the result as Count on the whole ( for example, if I select from from April 1st to April 15th , the result is the sum of all "complete"), but I need count for each day in this selected range. the result I will bind to the column chart.
how to proceed ?
If I understood correctly the author wants to use only the date without the time. To do this with EF we can use the method EntityFunctions.TruncateTime for trimming the time portion. I will build on #steaks answer:
db.Servers.Where(s => s.ServerId == "ServerX" && s.Type == "Complete")
.GroupBy(s => EntityFunctions.TruncateTime(s.Date))
.OrderBy(s => s.Key)
.Select(g => new {Date = g.Key, AmountOfSystems = g.Count()});
this.db.Servers.Where(s => s.ServerId == "ServerX" && s.Type == "Complete")
.GroupBy(s => s.Date)
.OrderBy(s => s.Key)
.Select(g => new { Date = g.Key, AmountOfSystems = g.Count() });
Change the Where clause to read
Where(s => s.ServerId == "ServerX" && s.Type == "Complete" && s.Date >= fromDP.SelectedDate.Value && s.Date <= toDP.SelectedDate.Value)
to filter to a limited date range.
EDIT
As #vvs0205 suggested. Use EntityFunctions class to manipulate the date column as you please: http://msdn.microsoft.com/en-us/library/system.data.objects.entityfunctions.aspx
Something like this
var fromDate = fromDP.SelectedDate.Value;
var toDate= toDP.SelectedDate.Value;
var q = from server in this.db.Servers
where (server.Date >= fromDate && server.Date<=toDate && server.ServerID="ServerX" && server.Type=="Complete")
group server by server.Date
into g
orderby g.Key
select new
{
Date = g.Key,
Count = g.Count()
};
var results = q.ToList();

How to merge 5 different type of linq queries into one based on a column

I am really confused on a report I need. As of today, most of my reports were simple, so I was able to do them myself easily. But being a newbie in sql/dlinq, I cannot find my way through the following:
var closingStock =
(from p in session.Query<Product>()
select new
{
p.Id,
p.Name,
p.Batch,
p.Rate,
ClosingStock = p.Quantity - p.AllocatedQuantity,
p.DivisionId
}).ToList();
var distributedQuantityAfterPeriod =
(from i in session.Query<OutwardInvoiceItem>()
where i.ParentInvoice.Date > ToDate
select new
{
Id = i.Product.Id,
DistributedAfter = i.Quantity
}).ToList();
var distributedQuantityInPeriod =
(from i in session.Query<OutwardInvoiceItem>()
where i.ParentInvoice.Date >= FromDate && i.ParentInvoice.Date <= ToDate
select new
{
Id = i.Product.Id,
Distributed = i.Quantity
}).ToList();
var receivedQuantityAfterPeriod =
(from i in session.Query<InwardInvoiceItem>()
where i.ParentInvoice.Date > ToDate
select new
{
Id = i.Product.Id,
ReceivedAfter = i.Quantity
}).ToList();
var receivedQuantityInPeriod =
(from i in session.Query<InwardInvoiceItem>()
where i.ParentInvoice.Date >= FromDate && i.ParentInvoice.Date <= ToDate
select new
{
Id = i.Product.Id,
Received = i.Quantity
}).ToList();
As you can see, I am trying to build a inventory movement report for a specific date. I have the following problems:
1. How can I reduce the five queries? Is it possible?
2. How can I merge the data provided by these queries into one table which is grouped on the product id and summed on the quantity related columns? As of now, I am using for loops which are really slow.
What I am using:
C# 4, nHibernate, Sqlite
Any help will be very highly appreciated.
Regards,
Yogesh.
to reduce roundtrips use .Future() instead of .List()
let all queries return
group i by i.Id into g
select new
{
Id = g.Key,
Quantity = g.Sum(x => x.Quantity)
}).Future();
and do
var alltogether = groupedDistributedQuantityAfterPeriod
.Concat(groupedDistributedQuantityInPeriod)
.Concate(...);
from g in alltogether
group g by g.key into all
select new
{
Id = all.Key,
Quantity = all.Sum(x => x.Quantity)
};
Update:
you can reduce the number of queries with
from i in session.Query<OutwardInvoiceItem>()
where (i.ParentInvoice.Date > ToDate) || (i.ParentInvoice.Date >= FromDate && i.ParentInvoice.Date <= ToDate)
select ...
from i in session.Query<InwardInvoiceItem>()
where (i.ParentInvoice.Date > ToDate) || (i.ParentInvoice.Date >= FromDate && i.ParentInvoice.Date <= ToDate)
select ...

Categories