So you have classes like:
class Venue
{
public int Id {get; set;}
public string Location {get; set;}
public string Name {get; set;}
}
class VenueReservation
{
public int Id {get; set;}
public int VenueId {get; set;} // foreign key to Venue
public DateTime StartDateTime {get; set;}
public DateTime EndDateTime {get; set;}
}
And you have:
IQueryable<Venue> Venues => ... // Probably DbSet<Venue>
IQueryable<VenueReservation> VenueReservations => ...
I want a list of available venues after they filled in the start and end date, showing only available venues for their selected time slots.
So you need to fetch "Venues with their zero or more VenueReservations". Once you've got them, you could keep only those Venues that have no Reservation at all during the selected TimeSlot.
Get VenuesWithTheirReservations
Normally to get "Venues with their zero or more VenueReservations", I'd use Queryable.GroupJoin
. Some people mentioned that .net core doesn't support GroupJoin. In that case, use a left outer join followed by a GroupBy:
var venuesWithTheirReservations = venues.GroupJoin(venueReservations,
venue => venue.Id, // from every Venue take the Id
venueReservation => venueReservation.VenueId, // from every Reservation take the foreign key
(venue, reservationsForThisVenue) => new
{
Venue = venue,
Reservations = reservationsForThisVenue,
});
Or LINQ left outer join followed by GroupBy:
var venuesWithTheirReservations = from venue in venues
join reservation in venueReservations
on venue.Id equals reservation.VenueId into g
from reservationForThisVenu in g.DefaultIfEmpty()
select new
{
Venue = venue,
Reservation = reservationForThisVenue,
})
.GroupBy(joinResult => joinResult.Venue,
// parameter resultSelector: for every venue, and all reservations for this venue
// make one new
(venue, reservationsForThisVenue) => new
{
Venue = venue,
Reservations = reservationsForThisVenue,
});
Keep only the available Venues
So, now you've got the venues, each with their reservations, you can use Queryable.Where
to keep only those Venues that have no reservation at all during the time slot.
In other words: none of the Reservations should overlap with the time slot.
- all Reservations should either end before the time slot starts
(= the complete Reservation is before the time slot)
- OR start after the time slot ends
(= the complete Reservation is after the time slot.
We don't want Reservations that are partly during the time slot
So continuing the LINQ:
DateTime timeSlotStart = ...
DateTime timeSlotEnd = ...
var availableVenues = venuesWithTheirReservations.Where(venue =>
// every Reservation should end before timeSlotStart
// OR start after the timeSlotEnd:
venue.Reservations.All(reservation =>
// end before timeSlotStart OR
reservation.EndDateTime <= timeSlotStart ||
// start after the timeSlotEnd:
reservation.StartDatetime >= timeSlotEnd));