Linq statement Where within a Where - linq

I am building a query tool for use by non technical staff to retrieve records from the database.
I have a form with various drop downs which can be selected by the user depending on what they are looking for.
I have come across a problem where my query is returning records that do not match the users selection.
I believe this is only happening when I am querying the joined tables.
I have the following:
results = results.Where(c => c.CustomerEnrollment
.Where(x => x.CustomerCategoryID == CustomerCategoryID)
.Any());
results = results.Where(c => c.CustomerEnrollment
.Where(x => x.StartDate <= DateRangeStart && x.EndDate >= DateRangeStart)
.Any());
This will return results for the correct category but not within the specified date range.
I have also tried:
results = results.Where(c => c.CustomerEnrollment
.Any(x => x.CustomerCategoryID == CustomerCategoryID));

Try changing your date range check as;
Change:
x => x.StartDate <= DateRangeStart && x.EndDate >= DateRangeStart
To:
//StartDate should be greater than or equal to
//EndDate should be less than or equal to
//Also you are using same variable DateRangeStart to check start and end
x => x.StartDate >= DateRangeStart && x.EndDate <= DateRangeEnd

Your query returns categories that have any CustomerEnrollment having their Id, and also have any CustomerEnrollment in the the required data range, but these CustomerEnrollments are not necessarily the same.
To make sure that you get categories with CustomerEnrollments that fulfill both conditions you have to do:
results = results.Where(c => c.CustomerEnrollment
.Where(x => x.CustomerCategoryID == CustomerCategoryID
&& x.StartDate <= DateRangeStart
&& x.EndDate >= DateRangeStart)
.Any());
With PredicateBuilder you can parametrize the conditions:
using LinqKit;
...
var pred = Predicate.True<CustomerEnrollment>();
if (CustomerCategoryID > 0)
pred = pred.And(c => c.CustomerCategoryID == CustomerCategoryID);
if (DateRangeStart.HasValue)
pred = pred.And(c => c.StartDate <= DateRangeStart
&& c.EndDate >= DateRangeStart);
results = results.AsExpandable()
.Where(c => c.CustomerEnrollment.AsQueryable()
.Any(pred));
The combination of .AsExpandable() and .AsQueryable() appears to be the only way to avoid exceptions.

Related

Lambda Where clause for Today and Tomorrow Dates

I have a Linq query that works with various Where clause additions.
But I also want to return the SingleDate for an Event where it is today or tomorrow.
Dances_Moderated = Dances_Moderated
.Where(x => x.SingleDate == DateTime.Today)
&& (x => x.SingleDate == DateTime.Today.AddDays(1))
.ToList();
The code after the && is underlined in red, and the error says:
CS0023: Operator '.' cannot be applied to operand of type 'lambda expression'
You should use || (or) instead of && (and): x.SingleDate.Date should be either Today or tomorrow. I've added .Date in order to be on the safe side of the road: when we use == we want to compare dates only, not time parts.
Dances_Moderated = Dances_Moderated
.Where(x => x.SingleDate.Date == DateTime.Today ||
x.SingleDate.Date == DateTime.Today.AddDays(1))
.ToList();
You have couple of errors in your code:
First: Syntax error,
Second: Business error. Your SingleDate never gonna equal to datetime.now. You should use greater and less operator to do that.
Your code should be like this:
Dances_Moderated = Dances_Moderated.Where(x => x.SingleDate >= DateTime.Today && x.SingleDate <= DateTime.Today.AddDays(1)).ToList();

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

Left outer join with eagar loading

I am writing a linq having a left outer join with conditions on right side table.
my code
var leftHotelRooms = db.HotelRooms.Include(hr=>hr.HotelRoomBlackoutPeriods)
.Where(room => hotelCodes.Contains(room.UserHotelId) &&
room.Scope == scope &&
room.IsDeleted == false &&
room.IsEnabled == true &&
room.MaxCapacity >= minimumNumberOfGuests)
.GroupJoin(db.HotelRoomBlackoutPeriods,
room => room.Id,
blackoutPeriod => blackoutPeriod.HotelRoomId,
(room, blackoutPeriod) => new
{
room,
blackoutPeriods = blackoutPeriod.DefaultIfEmpty()
})
.Select(a => new {a.room,a.blackoutPeriods})
.Where(
x => x.blackoutPeriods.Any(y => DbFunctions.DiffDays(y.StartDate, checkIn) != 0
&& (DbFunctions.DiffDays(y.StartDate, checkIn) < 0) ? (DbFunctions.DiffDays(y.StartDate, checkOut) >= 0 ? false : true) : true &&
(DbFunctions.DiffDays(y.StartDate, checkIn) > 0) ? (DbFunctions.DiffDays(y.EndDate, checkIn) <= 0 ? false : true) : true
))
.ToList();
}
which works perfectly fine but it does not eagerly load, when i convert result of above query to my business model it again fires some queries on db.HotelRoomBlackoutPeriods
Please provide the optimum way to achieve this.
thanks in advance.
sorry (if i am asking something nonsense ) in advance
Eager loading does not work when you change the shape of the result set. Shape of your result set is HotelRoom type. Once you use any manual join or projection, you are changing the result set and eager loading request is ignored.

Why am I geting this error: Operator ā€˜&&ā€™ cannot be applied to operands of type System.Linq.IQueryable?

Iā€™m trying to retrieve data from an entity and populate a viewModel property like this:
viewModel.Enrollments = db.Enrollments.Where(b => b.classDays == "Monday") && (db.Enrollments.Where(b => b.CourseID == courseID);
but I get a operator && cannot be applied to operands of type System.Linq.IQerable<> error. Can you help with a way to find all Monday class with the same ID?
I tried this: viewModel.Enrollments = db.Enrollments.Where(b => b.classDays == "Monday") but I get all Mondays courses but I want to limit them to a specific courseID.
Please help!
You need to examine your parentheses. This code won't even compile:
viewModel.Enrollments = db.Enrollments.Where(b => b.classDays == "Monday")
&& (db.Enrollments.Where(b => b.CourseID == courseID);
In that code you're trying to use && between two calls to .Where(), which return an IQueryable. You probably mean to use && within the .Where() clause:
viewModel.Enrollments = db.Enrollments.Where(b => (b.classDays == "Monday")
&& (b.CourseID == courseID));
Or perhaps append a second .Where() clause:
viewModel.Enrollments = db.Enrollments.Where(b => b.classDays == "Monday")
.Where(b => b.CourseID == courseID);
Note that .Where() can be chained indefinitely, essentially resulting in applying each clause in turn in an AND fashion in the resulting query.
&& Operator should be used with conditions inside where NOT with sets of enrollments (in your case)
Try This:
viewModel.Enrollments = db.Enrollments.Where(b => (b.classDays == "Monday") && (b.CourseID == courseID));

Exception when using LINQ SUM

I'm trying to get the SUM of "bookings" and I get error "The cast to value type 'Int32' failed because the materialized value is null. Either the result type's generic parameter or the query must use a nullable type."
var bookings = entities.Bookings.Where(x => x.ID == id &&
x.StartDate <= bookingEnd &&
x.EndDate >= bookingStart)
.Sum(x => x.BookingQuantity);
How shall I fix this? I need to get 0 if it ever gets to be null else its bookings.
Try the null coalescing operator:
var bookings = entities.Bookings.Where(x => x.ID == id &&
x.StartDate <= bookingEnd &&
x.EndDate >= bookingStart &&
x.BookingQuantity != null)
.Sum(x => (int?)x.BookingQuantity) ?? 0;
or declare bookings as a nullable int
int? bookings = ...
The compilers type inference is picking up the result of Sum as a plain int, which should never be null.
This page suggests a fix to this problem;
Sum(x => (int?)x.BookingQuantity) ?? 0;
Add checking for null.
var bookings = entities.Bookings.Where(x => x.ID == id &&
x.StartDate <= bookingEnd &&
x.EndDate >= bookingStart &&
x.BookingQuantity != null)
.Sum(x => x.BookingQuantity);

Resources