How to get row count 3 table join and lambda expressions? - linq

I'm trying to get a row count for reports that a user has that match a particular channelId. I've tried using lambda expressions without any luck.
int count =
_reportsRepository.
GetMany(r => r.UserId == user.Id &&
(r.Charts.Any(cr => cr.Channels.Any(ch => ch.Id == channel.Id))).Any()).Count();

What about
int count = _reportsRepository.Where(
r => r.UserId == user.Id &&
r.User.Channels.Any(c => c.Id == channel.Id)).Count();

Related

Left joining a table's id with the maximum id from a subquery

I am using .NET core and trying to write a Linq query to list Cases. I have 3 tables that I am trying to join together:
Cases - Includes CaseID and CaseStatusID
CaseStatuses - Includes CaseStatusID (a Case has one of these)
CaseStatusHistory - Includes CaseStatusHistoryID, CaseID, CaseStatusID (a Case has many of these).
I can join Cases and CaseStatuses easily on CaseStatusID but am not sure how to join CaseStatusHistory on CaseStatusHistoryID = a subquery selecting the MAX(CaseStatusHistoryID) WHERE CaseID and CaseStatusID matches. I could even select the CaseStatusHitory later in my results but am not sure how.
This is what I need to convert
SELECT
c.CaseID,
csh.DateTimeAdded
FROM dbo.Cases c
JOIN dbo.CaseStatuses cs
ON c.CaseStatusID = cs.CaseStatusID
LEFT JOIN dbo.CaseStatusHistory csh
ON csh.CaseStatusHistoryID =
(
SELECT MAX(CaseStatusHistoryID)
FROM dbo.CaseStatusHistory
WHERE CaseID = c.CaseID
AND cs.CaseStatusID = c.CaseStatusID
)
This is what I have so far in Linq
IQueryable<CasesViewModel> objs =
from c in _db.Cases
from cs in _db.CaseStatuses.Where(cs => cs.CaseStatusId == c.CaseStatusId.Value).DefaultIfEmpty()
select new CasesViewModel
{
CaseID = c.CaseId,
CaseStatus = cs.CaseStatus
//StatusChangeDate = csh.DateTimeAdded
};
I need to add something like this
From csh In CaseStatusHistories
Where
(
From _csh In CaseStatusHistories
Where _csh.CaseID = c.CaseID AndAlso _csh.CaseStatusID = c.CaseStatusID
Select _csh.CaseStatusHistoryID
).Max = csh.CaseStatusHistoryID
Here is what I have so far but it does not return any result - just seems to time out.
IQueryable<CasesViewModel> objs =
from c in _db.Cases
from cs in _db.CaseStatuses.Where(cs => cs.CaseStatusId == c.CaseStatusId.Value).DefaultIfEmpty()
from rg in _db.RepairingGarages.Where(rg => rg.RepairingGarageId == c.RepairingGarageId.Value).DefaultIfEmpty()
from rgb in _db.Businesses.Where(rgb => rgb.BusinessId == rg.BusinessId.Value).DefaultIfEmpty()
from ec in _db.EngineeringCompanies.Where(ec => ec.EngineeringCompanyId == c.EngineeringCompanyId.Value).DefaultIfEmpty()
from ecb in _db.Businesses.Where(ecb => ecb.BusinessId == ec.BusinessId.Value).DefaultIfEmpty()
from csh in _db.CaseStatusHistory.Where(csh => csh.CaseStatusHistoryId ==
(
from _csh in _db.CaseStatusHistory
where _csh.CaseId == c.CaseId && _csh.CaseStatusId == c.CaseStatusId.Value
select _csh.CaseStatusHistoryId
).Max()).DefaultIfEmpty()
select new CasesViewModel
{
CaseID = c.CaseId,
RepairingGarage = rgb.BusinessName,
Engineer = ecb.BusinessName,
CaseStatus = cs.CaseStatus,
StatusChangeDate = csh.DateTimeAdded
};
I just need to get the StatusChangeDate which is the DateTimeAdded in the CaseStatusHistory table. Cases can be at the same status more than once, so I just need the DateTimeAdded with the highest CaseStatusHistoryID for a particular Case. Thank you very much for any help.
This appears to answer my question
IQueryable<CasesViewModel> objs =
from c in _db.Cases
from cs in _db.CaseStatuses.Where(cs => cs.CaseStatusId == c.CaseStatusId.Value).DefaultIfEmpty()
from rg in _db.RepairingGarages.Where(rg => rg.RepairingGarageId == c.RepairingGarageId.Value).DefaultIfEmpty()
from rgb in _db.Businesses.Where(rgb => rgb.BusinessId == rg.BusinessId.Value).DefaultIfEmpty()
from ec in _db.EngineeringCompanies.Where(ec => ec.EngineeringCompanyId == c.EngineeringCompanyId.Value).DefaultIfEmpty()
from ecb in _db.Businesses.Where(ecb => ecb.BusinessId == ec.BusinessId.Value).DefaultIfEmpty()
select new CasesViewModel
{
CaseID = c.CaseId,
RepairingGarage = rgb.BusinessName,
Engineer = ecb.BusinessName,
CaseStatus = cs.CaseStatus,
StatusChangeDate =
(
from _csh in _db.CaseStatusHistory
where _csh.CaseId == c.CaseId && _csh.CaseStatusId == cs.CaseStatusId
orderby _csh.CaseStatusHistoryId descending
select _csh.DateTimeAdded
).FirstOrDefault()
};
The only trouble with not joining is that I am not sure if I can sort by this date now. Any better ideas? Thank you.

Single link query where clause to take only `n` of each type from a single model/table

Having a table Entities with a column type (between other columns) how can I select only n entities from type A and m entities from type b in a single linq query where clause? Is it possible?
I am looking for something like this:
var x = from s in db.Entities
where s.type == `A` && (????) < n
|| s.type == `B` && (????) < m
select s
Current solution with combined queries:
var x = entities.Where(e => e.type == `A`).Take(n).Union(
entities.Where(e => e.type == `B`).Take(m));
You could use GroupBy:
var x = db.Entities.GroupBy(x => x.type)
.Where(g => g.Key == "A" || g.Key == "B")
.SelectMany(g => g.Key == "A" ? g.Take(n) : g.Take(m));
But I don't know why you don't like your Union-based solution - it should result in just one query sent to the database anyway.

Linq: Where count greater than value

I have a linq query which accepts a list of date and port combinations. This query has to return data from a table, CruiseCalendar, where these combinations are found, but only when the count is greater than one. I cant work out the groupby and count syntax. var shipRendezvous is where I'm stuck.
var dateAndPort = (from r in context.CruiseCalendar
where r.ShipId == shipId
&& r.CruiseDayDate >= dateRange.First
&& r.CruiseDayDate <= dateRange.Last
select new DateAndPort
{
Date = r.CruiseDayDate,
PortId = r.PortId
});
var shipRendezvous = (from r in context.CruiseCalendar
where (dateAndPort.Any(d => d.Date == r.CruiseDayDate
&& d.PortId == r.PortId))
orderby r.CruiseDayDate // (Added since first posting)
select r).ToList();
regards, Guy
If I understood you correctly, you are filterting for every set which matches any of the results of dateAndPort and then want to group it by itsself to get a count. Of the grouping results you only want those resultsets, which occur more then once.
var shipRendezvous = (from r in context.CruiseCalendar
where (dateAndPort.Any(d => d.Date == r.CruiseDayDate
&& d.PortId == r.PortId))
select r)
.GroupBy(x => x.CruiseDayDate) //Groups by every combination
.Where(x => x.Count() > 1) //Where Key count is greater 1
.ToList();
Based on your comment, you want to flatten the list again. To do so, use SelectMany():
var shipRendezvous = (from r in context.CruiseCalendar
where (dateAndPort.Any(d => d.Date == r.CruiseDayDate
&& d.PortId == r.PortId))
select r)
.GroupBy(x => x.CruiseDayDate) //Groups by every combination
.Where(x => x.Count() > 1) //Where Key count is greater 1
.SelectMany(x => x)
.ToList();

Entity Framework/ Linq - groupby and having clause

Given the query below
public TrainingListViewModel(List<int> employeeIdList)
{
this.EmployeeOtherLeaveItemList =
CacheObjects.AllEmployeeOtherLeaves
.Where(x => x.OtherLeaveDate >= Utility.GetToday() &&
x.CancelDate.HasValue == false &&
x.OtherLeaveId == Constants.TrainingId)
.OrderBy(x => x.OtherLeaveDate)
.Select(x => new EmployeeOtherLeaveItem
{
EmployeeOtherLeave = x,
SelectedFlag = false
}).ToList();
}
I want to put in the employeeIdList into the query.
I want to retrieve all of the x.OtherLeaveDate values where the same x.OtherLeaveDate exists for each join where x.EmployeeId = (int employeeId in employeeIdList)
For example if there are EmployeeIds 1, 2, 3 in employeeIdList and in the CacheObjects.AllEmployeeOtherLeaves collection there is a date 1/1/2001 for all 3 employees, then retreive that date.
If I read you well it should be something like
var grp = this.EmployeeOtherLeaveItemList =
CacheObjects.AllEmployeeOtherLeaves
.Where(x => x.OtherLeaveDate >= Utility.GetToday()
&& x.CancelDate.HasValue == false
&& x.OtherLeaveId == Constants.TrainingId
&& employeeIdList.Contains(x.EmployeeId)) // courtesy #IronMan84
.GroupBy(x => x.OtherLeaveDate);
if (grp.Count() == 1)
{
var result = g.First().Select(x => new EmployeeOtherLeaveItem
{
EmployeeOtherLeave = x,
SelectedFlag = false
})
}
First the data is grouped by OtherLeaveDate. If the grouping results in exactly one group, the first (and only) IGrouping instance is taken (which is a list of Leave objects) and its content is projected to EmployeeOtherLeaveItems.
To the where statement add "&& employeeIdList.Contains(x.EmployeeId)"
I need to thank #IronMan84 and #GertArnold for helping me along, and I will have to admonish myself for not being clearer in the question. This is the answer I came up with. No doubt it can be improved but given no one has responded to say why I will now tick this answer.
var numberOfEmployees = employeeIdList.Count;
var grp = CacheObjects.AllEmployeeOtherLeaves.Where(
x =>
x.OtherLeaveDate >= Utility.GetToday()
&& x.CancelDate.HasValue == false
&& x.OtherLeaveId == Constants.TrainingId
&& employeeIdList.Contains(x.EmployeeId))
.GroupBy(x => x.OtherLeaveDate)
.Select(x => new { NumberOf = x.Count(), Item = x });
var list =
grp.Where(item => item.NumberOf == numberOfEmployees).Select(item => item.Item.Key).ToList();

Linq strangeness

I was receiving repeating rows from this linq query:
public static Func<DataContext, string, IQueryable<Building>>
GearFilteredBuildings =
CompiledQuery.Compile((DataContext db, string filter) =>
from b in db.Building
join r in db.Router on b equals r.Building
orderby !b.Active
where filter.Length == 5 && r.Name.Substring(1, 5).ToLower() == filter
|| filter.Substring(0, 3) == r.Name.Substring(3, 3).ToLower()
select b);
After some fiddling, I got the distinct Buildings with this:
public static Func<DataContext, string, IQueryable<Building>>
GearFilteredBuildings =
CompiledQuery.Compile((DataContext db, string filter) =>
(from b in db.Building
join r in db.Router on b equals r.Building
orderby !b.Active
where filter.Length == 5 && r.Name.Substring(1, 5).ToLower() == filter
|| filter.Substring(0, 3) == r.Name.Substring(3, 3).ToLower()
group b by b.Id into g
select g) as IQueryable<Building>);
Is this an acceptable solution? How else might this be done?
Not sure (couldn't test it IDE) but select...join...lalala can replaced with Linq-Chain syntax:
db.Routers
.Where(r => filter.Length == 5 && r.Name.Substring(1, 5).ToLower() == filter || filter.Substring(0, 3) == r.Name.Substring(3, 3).ToLower())
.GroupBy(r => r.Building)
.Select(g => g.Key)
.OrderBy(b => !b.Active)
Also: as I can see no joins are really requred in your query, as you have navigation properties (r.Building)in your model.
Or another approach could be used, select all needed buildings, and use .Distinct() afterwards:
db.Routers
.Where (r => filter.Length == 5 && r.Name.Substring(1, 5).ToLower() == filter || filter.Substring(0, 3) == r.Name.Substring(3, 3).ToLower())
.Select(r => r.Building)
.Distinct()
.OrderBy(b => !b.Active)

Resources