How would I write this dynamic linq query? - linq

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

Related

How can i optimize my entity framework query for better performance and more readability

There is a heavy page in my site called HotelDetail, that several things related to hotel will be load in this page. hotel, hotel's services,destinations of places to hotel,user's comments,hotel's room with their Services, prices and capacity.
All of this will be load in one time with below code, now I want to optimize this code, what's solution to optimize this for better performance and more readability ? is there a way to not load all of then in one time?
public PlaceViewModel GetPlaceDetail(string languageKey,string catKey , string placeKey, SearchViewModel searchOptions)
{
DateTime startDate = searchOptions.CheckIn.ToMiladiDateTime();
DateTime endDate = searchOptions.CheckOut.ToMiladiDateTime();
int nights = (int)endDate.Subtract(startDate).TotalDays;
placeViewModel.NightCount = nights;
var query = (from r in _unitOfWork.RoomServiceRepository.Get()
join
a in _unitOfWork.InventoryRepository.Get()
on r equals a.RoomService
where r.Room.Place.Id == place.Id && !r.SoftDelete && !r.Room.SoftDelete &&
a.Date >= startDate && a.Date < endDate && (a.CertainAvailability + a.FloatAvailability) > 0
select new { a, MaxCapacity = r.Room.Capacity + r.ExtraCapacity, r.RoomId });
placeViewModel.HasAvailability = query.ToList().Count != 0;
var grp = query.GroupBy(y => y.a.RoomServiceId)
.Select(z => new {
key = z.Key,
count = z.Count(),
minAvail = z.Min(ax => ax.a.CertainAvailability + ax.a.FloatAvailability),
minDate = z.Min(y => y.a.Date),
minPrice = z.Min(ax=>ax.a.Price),
price = z.Average(ax=>ax.a.Price) });
var tmp = query.Where(x => grp.Any(q =>
q.key == x.a.RoomServiceId &&
q.count == nights &&
q.minDate == x.a.Date)).ToList();
foreach (var item in tmp)
{
var roomViewModel = placeViewModel.Rooms.FirstOrDefault(x => x.Id == item.RoomId);
var avail = new AvailabilityViewModel(item.a, item.MaxCapacity);
avail.Availability = grp.FirstOrDefault(x => x.key == item.a.RoomServiceId).minAvail;
var otherInfos = grp.SingleOrDefault(x => x.key == item.a.RoomServiceId);
avail.JabamaPrice = otherInfos.price;
avail.JabamaMinPriceInPeriod = otherInfos.minPrice;
roomViewModel.Availabilites.Add(avail);
if (maxDiscount == null || (maxDiscount != null &&(maxDiscount.BoardPrice - maxDiscount.JabamaPrice) < (avail.BoardPrice - avail.JabamaPrice)))
maxDiscount = avail;
if (minPrice == null || (minPrice != null && avail.JabamaPrice > 0 && minPrice.JabamaPrice > avail.JabamaPrice))
minPrice = avail;
}
var discountQuery = tmp.Where(x => x.a.RoomService.ExtraCapacity == 0 && x.a.Date == minPrice.Date);
try
{
if (discountQuery.Any())
maxDiscountOfMinPercentageDay = tmp != null && minPrice != null ? discountQuery.Max(x => (x.a.BoardPrice - x.a.Price) / x.a.BoardPrice * 100) : 0;
else
maxDiscountOfMinPercentageDay = 0;
}
catch (Exception)
{
maxDiscountOfMinPercentageDay = 0;
}
foreach (var roomVM in placeViewModel.Rooms)
{
if (roomVM.Availabilites.Count() == 0)
roomVM.Availabilites.Add(new AvailabilityViewModel(-1));
}
}
I'm far from familiar with Linq but I do know some of the pitfalls of SQL. One of the things I notice is that you (and many other people!) use .Count != 0 to figure out if there might or might not be a matching record. In SQL this is a very inefficient approach as the system will effectively go over the entire table to find and count the records that match the prerequisite. In such cases I'd advice to use a WHERE EXISTS() construction; I believe Linq has something along the lines of .Any() that works similarly.
This is also what you do in real life: when someone asks you if there is any cheese left in the fridge, you will not start counting all types of cheese you can find in there but rather stop after the first type of cheese you see and answer 'Yes there is'; there is no added value to going through the entire fridge, it would only cost extra time.
PS: This probably also is true for roomVM.Availabilites.Count() == 0 which might be better handled with inversed logic...
Disclaimer: it could be that the EntityFwk is smart enough to optimize this for you in the background... I don't know; I very much doubt it but like I said, I'm no specialist. I'd suggest to start by first figuring out how long each part of your current code takes and then optimize the slowest ones first. Having a baseline also gives you a better idea if your changes are having any effect, be it positive or negative.

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

where clause evaluated by if

I have a linq query that returns a schedule for a doctor in a specific date, that works ok:
var scheduledList = db_hms.Scheduled
.Include("Natural_Person")
.Include("Type_Schedule")
.Where(s => s.id_medical_doctor == idMedic && s.date_time >= date && s.date_time <= EndDate).OrderBy(s => s.date_time).ToList();
but now, i have 2 extra fields i need to filter, so i need a way to evaluate if applay the filter or not, i was thinking in remove rhe orderby and the toList()and do somthing like:
if (idTipoConsulta != -1)
{
scheduledList.Where(m => m.id_ps_schedule == idTipoConsulta);
}
if (idEspecialidad != -1)
{
scheduledList.Where(m => m.id_specialty == idTipoConsulta);
}
return scheduledList.OrderBy(s => s.date_time).ToList();
but it does not seem to work.
Calling Where doesn't affect the object you call it on - it returns a new query with the filter applied. So you want to use the return value appropriately. For example:
if (idTipoConsulta != -1)
{
scheduledList = scheduledList.Where(m => m.id_ps_schedule == idTipoConsulta);
}
// etc

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

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

Resources