Entity Framework/ Linq - groupby and having clause - linq

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();

Related

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();

Why is Linq that slow (see provided examples)

This Linq is very slow:
IEnumerable<string> iedrDataRecordIDs = dt1.AsEnumerable()
.Where(x => x.Field<string>(InputDataSet.Column_Arguments_Name) == sArgumentName
&& x.Field<string>(InputDataSet.Column_Arguments_Value) == sArgumentValue)
.Select(x => x.Field<string>(InputDataSet.Column_Arguments_RecordID));
IEnumerable<string> iedrDataRecordIDs_Filtered = dt2.AsEnumerable()
.Where(x => iedrDataRecordIDs.Contains(
x.Field<string>(InputDataSet.Column_DataRecordFields_RecordID))
&& x.Field<string>(InputDataSet.Column_DataRecordFields_Field)
== sDataRecordFieldField
&& x.Field<string>(InputDataSet.Column_DataRecordFields_Value)
== sDataRecordFieldValue)
.Select(x => x.Field<string>(InputDataSet.Column_DataRecordFields_RecordID));
IEnumerable<string> ieValue = dt2.AsEnumerable()
.Where(x => x.Field<string>(InputDataSet.Column_DataRecordFields_RecordID)
== iedrDataRecordIDs_Filtered.FirstOrDefault()
&& x.Field<string>(InputDataSet.Column_DataRecordFields_Field) == sFieldName)
.Select(x => x.Field<string>(InputDataSet.Column_DataRecordFields_Value));
if (!ieValue.Any()) //very slow at this point
return iedrDataRecordIDs_Filtered.FirstOrDefault();
This change accelerates it by a factor of 10 or more
string sRecordID = dt2.AsEnumerable()
.Where(x => iedrDataRecordIDs.Contains(
x.Field<string>(InputDataSet.Column_DataRecordFields_RecordID))
&& x.Field<string>(InputDataSet.Column_DataRecordFields_Field)
== sDataRecordFieldField
&& x.Field<string>(InputDataSet.Column_DataRecordFields_Value)
== sDataRecordFieldValue)
.Select(x => x.Field<string>(InputDataSet.Column_DataRecordFields_RecordID))
.FirstOrDefault();
IEnumerable<string> ieValue = dt2.AsEnumerable()
.Where(x => x.Field<string>(InputDataSet.Column_DataRecordFields_RecordID) == sRecordID
&& x.Field<string>(InputDataSet.Column_DataRecordFields_Field) == sFieldName)
.Select(x => x.Field<string>(InputDataSet.Column_DataRecordFields_Value));
if (!ieValue.Any()) //very fast at this point
return iedrDataRecordIDs_Filtered.FirstOrDefault();
The only change is that I store the result directly in a new variable and use create the where clause with this value instead of a LINQ query (which should be calculated when needed). But LINQ seems to calculate it in a bad way here or am I doing something wrong?
Here some values of my data
dt1.Rows.Count 142
dt1.Columns.Count 3
dt2.Rows.Count 159
dt2.Columns.Count 3
iedrDataRecordIDs.Count() 1
iedrDataRecordIDs_Filtered.Count() 1
ieValue.Count() 1
You're asking why
IEnumerable<string> iedrDataRecordIDs_Filtered = data;
foreach (var item in collection)
{
// do something with
iedrDataRecordIDs_Filtered.FirstOrDefault();
}
is slower than
string sRecordID = data.FirstOrDefault();
foreach (var item in collection)
{
// do something with
sRecordID;
}
Very simply because you're evaluating the iedrDataRecordIDs collection every time you get the FirstOrDefault. This isn't a concrete object, it's an enumerable set. That's really just a function that returns some objects. Every time you query it the function will be called and you'll pay that execution cost.
If you change
IEnumerable<string> iedrDataRecordIDs_Filtered = dt2.AsEnumerable()...
var recordIDs = iedrDataRecordIDs_Filtered.ToList();
and then use recordIDs.FirstOrDefault() you'll see a huge performance increase.

linq to sql fetching all the records category wise in the list<> and then looping

i am fetching all the records from the database with the help of this query organization wise. they become about 30-40 records
List<PagesRef> paages = (from pagess in pagerepository.GetAllPages()
join pagesref in pagerepository.GetAllPageRef()
on pagess.int_PageId equals pagesref.int_PageId
where (pagess.int_PostStatusId != 3 && pagess.int_OrganizationId == Authorization.OrganizationID)
&& pagesref.int_PageRefId == pagesref.Pages.PagesRefs.FirstOrDefault(m => m.int_PageId == pagess.int_PageId && m.bit_Active == true && (m.vcr_PageTitle != null && m.vcr_PageTitle != "")).int_PageRefId
select pagesref).ToList();
next the next step what i want to do is to loop through the above list as linq to object query without going to the database to generate 3 level hierarchical record. can some one give me some insight or idea how can i do it?
edit
var parentrecord = paages.Where(n => n.Pages.int_PageParent == 0).OrderBy(m => m.Pages.int_SortOrder == null).OrderBy(m => m.int_PageId);
foreach (var secondlevel in parentrecord) // if parentrecord found
{
var seclevel = paages.Where(m => m.Pages.int_PageParent == secondlevel.Pages.int_PageId).OrderBy(m => m.Pages.int_SortOrder == null).OrderBy(m => m.Pages.int_SortOrder);
secondlevel.vcr_PageTitle = "parent";
pagesreff.Add(secondlevel); // if parentrecord found then loop and add in there
foreach (var thdlevel in seclevel)
{
var thirdlevel = paages.Where(m => m.Pages.int_PageParent == thdlevel.Pages.int_PageId).OrderBy(m => m.Pages.int_SortOrder == null).OrderBy(m => m.int_PageId).OrderBy(m => m.Pages.int_SortOrder);
thdlevel.vcr_PageTitle = "child";
pagesreff.Add(thdlevel); // if parentrecord child found then loop and add in there
foreach (var thd in thirdlevel)
{
thd.vcr_PageTitle = "subchild";
pagesreff.Add(thd); // if parentrecord child found then loop and add in there
}
}
}
After ToList(); linq-to-sql go to database and get rows. After that, you have collection of objects and can do what you want with linq to objects:
var filteredList = paages.Where(someFilter);
there will be no new sql requests.
Update
Your problem is that you filter in navigation property, so you should load your navigation property with your first query. I'm not sure (linq-to-sql was many years ago:)), but this should help you (I assume that m.Pages is of type Page):
List<PagesRef> paages = (from pagess in pagerepository.GetAllPages()
join pagesref in pagerepository.GetAllPageRef()
on pagess.int_PageId equals pagesref.int_PageId
where (pagess.int_PostStatusId != 3 && pagess.int_OrganizationId == Authorization.OrganizationID)
&& pagesref.int_PageRefId == pagesref.Pages.PagesRefs.FirstOrDefault(m => m.int_PageId == pagess.int_PageId && m.bit_Active == true && (m.vcr_PageTitle != null && m.vcr_PageTitle != "")).int_PageRefId
select pagesref).AssociateWith<Page>.ToList();

Cannot implicitly convert type 'DateTime' to 'bool' linq query

I have this LINQ query:
var resourcePlanningInWeek = resourcePlanning.Where(rp => rp.PlanDate >= dateFrom && rp.PlanDate <= dateTo);
var holidays = new HolidayManager().GetByPeriod(dateFrom, dateTo);
var resourcePlanningExcludedHolidays= resourcePlanningInWeek.Where(rpiw => ( holidays.Where(h => h.HolidayDate = rpiw.PlanDate).Count = 0))
When executed, I get following error:
Cannot implicitly convert type 'DateTime' to 'bool'
Somewone know why?
You need to use == instead of = when you want to make a comparison. That's relevant at two places in the last line.
The call to Count is missing the parentheses.
var resourcePlanningExcludedHolidays = resourcePlanningInWeek
.Where(rpiw => holidays.Where(h => h.HolidayDate == rpiw.PlanDate)
.Count() == 0);
However, there is a better way of writing this:
var resourcePlanningExcludedHolidays = resourcePlanningInWeek
.Where(rpiw => !holidays.Any(h => h.HolidayDate == rpiw.PlanDate));
This is better, because:
It is shorter and more readable
It stops enumerating holidays as soon as the condition is true the first time. Count() always enumerates the complete list.
An even better approach would be to use a HashSet<DateTime>:
var holidays
= new HashSet<DateTime>(new HolidayManager().GetByPeriod(dateFrom, dateTo)
.Select(x => x.HolidayDate));
var resourcePlanningExcludedHolidays
= resourcePlanningInWeek.Where(rpiw => !holidays.Contains(rpiw.PlanDate));
You are missing an equal symbol at:
h.HolidayDate = rpiw.PlanDate
It should be:
var resourcePlanningExcludedHolidays=
resourcePlanningInWeek.Where(rpiw => ( holidays.Where(h => h.HolidayDate == rpiw.PlanDate).Count = 0))

How would I write this dynamic linq query?

Basically I want to write a linq query to order the number of days they were present. But I have got these six time filters- Today,Yesterday,current month,previous month,current year,previous year.So now I have this queries which I have simplified but before these queries below, I actually order these employees on different aspects and after ordering it as you can see I assign rank and then at the same time find out his count(which may or may not be used to rank them later)-
var result=datacontext.Employee(c=>c.Company.Id==companyId).Select((k, index) => new EmployeeDTO()
{
EmployeeId=k.Employee.Id,
CompanyId=Employee.Company.Id
PresentCount=(from e in employeeAttendance
where d.RecNum == k.recnum
&& d.date_present.Year == DateTime.Today.Year
&& d.date_present.Month == DateTime.Today.Month
&& d.date_present.Day == DateTime.Today.Day
select d).Count()
}
So now when the filter is say previous year I have -
var result=datacontext.Employee(c=>c.Company.Id==companyId).Select((k, index) => new EmployeeDTO()
{
Position=
EmployeeId=k.Employee.Id,
CompanyId=Employee.Company.Id
PresentCount=(from e in employeeAttendance
where d.RecNum == k.recnum
&& d.date_present.Year == (DateTime.Today.Year-1)
}
and if have it on Current Month I have -
var result=datacontext.Employee(c=>c.Company.Id==companyId).Select((k, index) => new EmployeeDTO()
{
EmployeeId=k.Employee.Id,
CompanyId=Employee.Company.Id
PresentCount=(from e in employeeAttendance
where d.RecNum == k.recnum
&& d.date_present.Month == DateTime.Today.Month
&& d.date_present.Year == DateTime.Today.Year
}
I basically want to combine all these in one query with basically having like a dynamic clause for finding out the present count ?
Just create a simple wrapper. Eg:
IQueryable<EmployeeDTO> GetEmployeeCount(Expression<Func<DateTime, bool>> pred)
{
var result=datacontext.Employee(c=>c.Company.Id==companyId).
Select((k, index) => new EmployeeDTO()
{
EmployeeId=k.Employee.Id,
CompanyId=Employee.Company.Id,
PresentCount=(from e in employeeAttendance
where d.RecNum == k.recnum && pred(d.date_present)
select d).Count()
});
return result;
}
Usage:
var r = GetEmployeeCount(d => d.Year == (DateTime.Today.Year-1));
var r = GetEmployeeCount(
d => d.Month == DateTime.Today.Month && d.Year == DateTime.Today.Year);

Resources