I am trying to write a LINQ query that fetches a list of Course entities and their mapped Skill children. The are related with a join table with corresponding CourseId and SkillId. I wish to sort the Skill children with the Weight property and then with the SkillId property. I am using dot net core 2.0.
After looking at similar questions on how to sort/order a list of children to an entity here:
LINQ ".Include" orderby in subquery
Entity Framework Ordering Includes
I have come up with this:
// Create query to get all courses with their skills
var coursesWithUnorderedSkills= db.Courses
.Include(i => i.Skills)
.ThenInclude(i => i.Skill);
// Order the skills for each course
await coursesWithUnorderedSkills.ForEachAsync(x => x.Skills = x.Skills
.OrderBy(o => o.Weight)
.ThenBy(o => o.SkillId)
.ToList());
// Get a list of courses from the query
var coursesWithOrderedSkills = await q.ToListAsync();
How can this be simplified into a single query and will this query have any unexpected performance issues since I am calling ToList in the ForEachAsync call?
Models
public class Course
{
[Key]
public int Id { get; set; }
public List<CourseSkill> Skills { get; set; }
}
public class CourseSkill
{
public Course Course { get; set; }
public int CourseId { get; set; }
public Skill Skill { get; set; }
public int SkillId { get; set; }
public int Weight { get; set; } = 0;
}
public class Skill
{
[Key]
public int Id { get; set; }
}
Sorry about the comments, now with the model it looks clear to me what you are looking for. And you are right, the second statement would sort the Skills list.
Anyway, if you want to sort the child collection without risking calling twice to the database through your IQueryable, you can take first the list of courses asynchronously, and then sort the Skills in memory:
// Create the list with all courses with their skills
var coursesWithSkills= await db.Courses
.Include(i => i.Skills)
.ThenInclude(i => i.Skill)
.ToListAsync();
// Order the skills for each course once data is in memory
foreach(x in coursesWithSkills)
{
x.Skills = x.Skills.OrderBy(o => o.Weight)
.ThenBy(o => o.SkillId)
.ToList());
}
If you need that sorting part to not block the current thread, you should run it with a Task.Run rather than an async operation, as all the sorting work is CPU intensive and will be done in memory. But I wouldn't go for early optimization and I would leave the foreach block as it is until you see any performance issue.
Related
I need to fetch from the database this:
rack
it's type
single shelf with all its boxes and their box types
single shelf above the previous shelf without boxes and with shelf type
Shelves have VerticalPosition which is in centimeters from the ground - when I am querying for e.g. second shelf in rack, I need to order them and select shelf on index 1.
I have this ugly EF query now:
var targetShelf = await _warehouseContext.Shelves
.Include(s => s.Rack)
.ThenInclude(r => r.Shelves)
.ThenInclude(s => s.Type)
.Include(s => s.Rack)
.ThenInclude(r => r.Type)
.Include(s => s.Rack)
.ThenInclude(r => r.Shelves)
.Include(s => s.Boxes)
.ThenInclude(b => b.BoxType)
.Where(s => s.Rack.Aisle.Room.Number == targetPosition.Room)
.Where(s => s.Rack.Aisle.Letter == targetPosition.Aisle)
.Where(s => s.Rack.Position == targetPosition.Rack)
.OrderBy(s => s.VerticalPosition)
.Skip(targetPosition.ShelfNumber - 1)
.FirstOrDefaultAsync();
but this gets all boxes from all shelves and it also shows warning
Compiling a query which loads related collections for more than one collection navigation, either via 'Include' or through projection, but no 'QuerySplittingBehavior' has been configured. By default, Entity Framework will use 'QuerySplittingBehavior.SingleQuery', which can potentially result in slow query performance.
Also I would like to use AsNoTracking(), because I don't need change tracker for these data.
First thing: for AsNoTracking() I would need to query Racks, because it complains about circular include.
Second thing: I tried conditional include like this:
.Include(r => r.Shelves)
.ThenInclude(s => s.Boxes.Where(b => b.ShelfId == b.Shelf.Rack.Shelves.OrderBy(sh => sh.VerticalPosition).Skip(shelfNumberFromGround - 1).First().Id))
but this won't even translate to SQL.
I have also thought of two queries - one will retrieve rack with shelves and second only boxes, but I still wonder if there is some single call command for this.
Entities:
public class Rack
{
public Guid Id { get; set; }
public Guid RackTypeId { get; set; }
public RackType Type { get; set; }
public ICollection<Shelf> Shelves { get; set; }
}
public class RackType
{
public Guid Id { get; set; }
public ICollection<Rack> Racks { get; set; }
}
public class Shelf
{
public Guid Id { get; set; }
public Guid ShelfTypeId { get; set; }
public Guid RackId { get; set; }
public int VerticalPosition { get; set; }
public ShelfType Type { get; set; }
public Rack Rack { get; set; }
public ICollection<Box> Boxes { get; set; }
}
public class ShelfType
{
public Guid Id { get; set; }
public ICollection<Shelf> Shelves { get; set; }
}
public class Box
{
public Guid Id { get; set; }
public Guid ShelfId { get; set; }
public Guid BoxTypeId { get; set; }
public BoxType BoxType { get; set; }
public Shelf Shelf { get; set; }
}
public class BoxType
{
public Guid Id { get; set; }
public ICollection<Box> Boxes { get; set; }
}
I hope I explained it good enough.
Query Splitting
First, I'd recommend benchmarking the query as-is before deciding whether to attempt any optimization.
It can be faster to perform multiple queries than one large query with many joins. While you avoid a single complex query, you have additional network round-trips if your DB isn't on the same machine, and some databases (e.g. SQL Server without MARS enabled) only support one active query at a time. Your mileage may vary in terms of actual performance.
Databases do not generally guarantee consistency between separate queries (SQL Server allows you to mitigate that with the performance-expensive options of serializable or snapshot transactions). You should be cautious using a multiple-query strategy if intervening data modifications are possible.
To split a specific query, use the AsSplitQuery() extension method.
To use split queries for all queries against a given DB context,
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder
.UseSqlServer(
#"Server=(localdb)\mssqllocaldb;Database=EFQuerying;Trusted_Connection=True;ConnectRetryCount=0",
o => o.UseQuerySplittingBehavior(QuerySplittingBehavior.SplitQuery));
}
Reference.
Query that won't translate
.Include(r => r.Shelves)
.ThenInclude(s => s.Boxes.Where(b => b.ShelfId == b.Shelf.Rack.Shelves.OrderBy(sh => sh.VerticalPosition).Skip(shelfNumberFromGround - 1).First().Id))
Your expression
s.Boxes.Where(b => b.ShelfId == b.Shelf.Rack.Shelves.OrderBy(sh => sh.VerticalPosition).Skip(shelfNumberFromGround - 1).First().Id
resolves to an Id. ThenInclude() expects an expression that ultimately specifies a collection navigation (in other words, a table).
Ok, from your question I'm assuming you have a method where you need these bits of information:
single shelf with all its boxes and their box types
single shelf above the previous shelf without boxes and with shelf type
rack and it's type
Whether EF breaks up the queries or you do doesn't really make much of a difference performance-wise. What matters is how well the code is later understood and can adapt if/when requirements change.
The first step I would recommend is to identify the scope of detail you actually need. You mention that you don't need tracking, so I would expect you intend to deliver these results or otherwise consume the information without persisting changes. Project this down to just the details from the various tables that you need to be served by a DTO or ViewModel, or an anonymous type if the data doesn't really need to travel. For instance you will have a shelf & shelf type which is effectively a many-to-one so the shelf type details can probably be part of the shelf results. Same with the Box and BoxType details. A shelf would then have an optional set of applicable box details. The Rack & Racktype details can come back with one of the shelf queries.
[Serializable]
public class RackDTO
{
public int RackId { get; set; }
public int RackTypeId { get; set; }
public string RackTypeName { get; set; }
}
[Serializable]
public class ShelfDTO
{
public int ShelfId { get; set; }
public int VerticalPosition { get; set; }
public int ShelfTypeId { get; set; }
public string ShelfTypeName { get; set; }
public ICollection<BoxDTO> Boxes { get; set; } = new List<BoxDTO>();
public RackDTO Rack { get; set; }
}
[Serializable]
public class BoxDTO
{
public int BoxId { get; set; }
public int BoxTypeId { get; set; }
public string BoxTypeName { get; set; }
}
Then when reading the information, I'd probably split it into two queries. One to get the "main" shelf, then a second optional one to get the "previous" one if applicable.
ShelfDTO shelf = await _warehouseContext.Shelves
.Where(s => s.Rack.Aisle.Room.Number == targetPosition.Room
&& s.Rack.Aisle.Letter == targetPosition.Aisle
&& s.Rack.Position == targetPosition.Rack)
.Select(s => new ShelfDTO
{
ShelfId = s.ShelfId,
VerticalPosition = s.VerticalPosition,
ShelfTypeId = s.ShelfType.ShelfTypeId,
ShelfTypeName = s.ShelfType.Name,
Rack = s.Rack.Select(r => new RackDTO
{
RackId = r.RackId,
RackTypeId = r.RackType.RackTypeId,
RackTypeName = r.RackType.Name
}).Single(),
Boxes = s.Boxes.Select(b => new BoxDTO
{
BoxId = b.BoxId,
BoxTypeId = b.BoxType.BoxTypeId,
BoxTypeName = b.BoxType.Name
}).ToList()
}).OrderBy(s => s.VerticalPosition)
.Skip(targetPosition.ShelfNumber - 1)
.FirstOrDefaultAsync();
ShelfDTO previousShelf = null;
if (targetPosition.ShelfNumber > 1 && shelf != null)
{
previousShelf = await _warehouseContext.Shelves
.Where(s => s.Rack.RackId == shelf.RackId
&& s.VerticalPosition < shelf.VerticalPosition)
.Select(s => new ShelfDTO
{
ShelfId = s.ShelfId,
VerticalPosition = s.VerticalPosition,
ShelfTypeId = s.ShelfType.ShelfTypeId,
ShelfTypeName = s.ShelfType.Name,
Rack = s.Rack.Select(r => new RackDTO
{
RackId = r.RackId,
RackTypeId = r.RackType.RackTypeId,
RackTypeName = r.RackType.Name
}).Single()
}).OrderByDescending(s => s.VerticalPosition)
.FirstOrDefaultAsync();
}
Two fairly simple to read queries that should return what you need without much problem. Because we project down to a DTO we don't need to worry about eager loading and potential cyclical references if we wanted to load an entire detached graph. Obviously this would need to be fleshed out to include the details from the shelf, box, and rack that are relevant to the consuming code/view. This can be trimmed down even more by leveraging Automapper and it's ProjectTo method to take the place of that whole Select projection as a one-liner.
In SQL raw it could look like
WITH x AS(
SELECT
r.*, s.Id as ShelfId, s.Type as ShelfType
ROW_NUMBER() OVER(ORDER BY s.verticalposition) as shelfnum
FROM
rooms
JOIN aisles on aisles.RoomId = rooms.Id
JOIN racks r on r.AisleId = aisles.Id
JOIN shelves s ON s.RackId = r.Id
WHERE
rooms.Number = #roomnum AND
aisles.Letter = #let AND
r.Position = #pos
)
SELECT *
FROM
x
LEFT JOIN boxes b
ON
b.ShelfId = x.ShelfId AND x.ShelfNum = #shelfnum
WHERE
x.ShelfNum BETWEEN #shelfnum AND #shelfnum+1
The WITH uses room/aisle/rack joins to locate the rack; you seem to have these identifiers. Shelves are numbered in increasing height off ground. Outside the WITH, boxes are left joined only if they are on the shelf you want, but two shelves are returned; the shelf you want with all it's boxes and the shelf above but box data will be null because the left join fails
As an opinion, if your query is getting this level of depth, you might want to consider either using views as a shortcut in your database or use No-SQL as a read store.
Having to do lots of joins, and doing taxing operations like order by during runtime with LINQ is something I'd try my best to avoid.
So I'd approach this as a design problem, rather than a code/query problem.
In EF, All related entities loaded with Include, ThenInclude etc. produce joins on the database end. This means that when we load related master tables, the list values will get duplicated across all records, thus causing what is called "cartesian explosion". Due to this, there was a need to split huge queries into multiple calls, and eventually .AsSplitQuery() was introduced.
Eg:
var query = Context.DataSet<Transactions>()
.Include(x => x.Master1)
.Include(x => x.Master2)
.Include(x => x.Master3)
.ThenInclude(x => x.Master3.Masterx)
.Where(expression).ToListAsync();
Here we can introduce splitquery
var query = Context.DataSet<Transactions>()
.Include(x => x.Master1)
.Include(x => x.Master2)
.Include(x => x.Master3)
.ThenInclude(x => x.Master3.Masterx)
.Where(expression).AsSplitQuery.ToListAsync();
As an alternate to include this to all existing queries, which could be time consuming, we could specify this globally like
services.AddDbContextPool<EntityDataLayer.ApplicationDbContext>(options =>
{
options.EnableSensitiveDataLogging(true);
options.UseMySql(mySqlConnectionStr,
ServerVersion.AutoDetect(mySqlConnectionStr), x =>
x.UseQuerySplittingBehavior(QuerySplittingBehavior.SplitQuery)
x.EnableRetryOnFailure(
maxRetryCount: 10,
maxRetryDelay: TimeSpan.FromSeconds(30),
errorNumbersToAdd: null));
});
This will ensure that all queries are called as split queries.
Now in case we need single query, we can just override this by stating single query explicitly in individual queries. This may be done vice-versa though.
var data = await query.AsSingleQuery().ToListAsync();
The code below, is fine. It works. Things are inserted, stuff appear on the right side on the left side etc. However, there's an issue. Using a program such as LinQPad (https://www.linqpad.net), I am able to query the data and see it listed.
But, trying to do the same in C#, says that the "auto" generated linking table does not exists in the context.
Relationship: Many-to-Many.
I am using Entity Framework 6.
EDIT: What's the purpose of autogenerated linking table if you cannot use it from your C# Windows Application? Using Linq?
My Code First tables:
public class Student
{
[Key, Column(Order = 1)]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int StudentID { get; set; }
public string StudentName { get; set; }
public DateTime? DateOfBirth { get; set; }
public virtual ICollection<Course> Courses { get; set; }
}
public class Course
{
[Key, Column(Order = 1)]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int CourseId { get; set; }
[Index("CourseName", 2, IsUnique = true)]
public string CourseName { get; set; }
public virtual ICollection<Student> Students { get; set; }
}
DBContext.cs
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Student>()
.HasMany<Course>(s => s.Courses)
.WithMany(c => c.Students)
.Map(cs =>
{
cs.MapLeftKey("FK_StudentID");
cs.MapRightKey("FK_CourseID");
cs.ToTable("StudentCourse");
});
base.OnModelCreating(modelBuilder);
}
Querying from within the LinqPad, works perfectly fine:
void Main()
{
var data = (from s in Students
join sc in StudentCourse on s.StudentID equals sc.FK_StudentID
join c in Courses on c.CourseId equals sc.FK_CourseID
select new { s,c });
Console.WriteLine(data);
}
There is nothing stopping you from declaring it as an entity, but where the linking table is kept to just the required keys, it is an optimization not to have to declare it when using the navigation properties.
The key issue I think you are facing here is you're writing Linq like SQL and completely missing the navigation properties.
For instance, if I want all Courses for a particular student: Without a linking table I can do:
var studentCourses = context.Students
.Where(x => x.StudentId == studentId)
.SelectMany(x => x.Courses)
.ToList();
To get a list of each student/course combination as you outlined:
var studentCourses = context.Students
.SelectMany(s => s.Courses.Select(c => new {s, c}))
.ToList();
This gives me the list of courses for that student. I don't have to manually join up Student to Course through StudentCourse like writing an SQL statement.
Now you can certainly declare a StudentCourse entity, but this changes the relationships slightly. Rather than a Many-to-Many, you have a Many-to-One-to-Many:
public class Student
{
// ...
public virtual ICollection<StudentCourse> StudentCourses { get; set; } = new List<StudentCourse>();
}
public class Course
{
// ...
public virtual ICollection<StudentCourse> StudentCourses { get; set; } = new List<StudentCourse>();
}
public class StudentCourse
{
[Key, Column(Order=0), ForeignKey(Student)]
public int StudentId { get; set; }
[Key, Column(Order=1), ForeignKey(Course)]
public int CourseId { get; set; }
public virtual Student Student { get; set; }
public virtual Course Course { get; set; }
}
It can be tempting to name the StudentCourses property "Courses" and "Students" in their respective counterparts, but IMO this gets misleading when diving trough the navigation as I'll point out in the example below.
For those using EFCore, I believe this is still the only supported option for many-to-many. This is also a required option if you want to make any alterations to the joining table such as using a dedicated PK column, or other columns such as CreatedAt etc. to track edits etc. for the relationships.
Then to do the first example query in Linq you'd need to change it slightly to:
var studentCourses = context.Students
.Where(x => x.StudentId == studentId)
.SelectMany(x => x.StudentCourses.Course)
.ToList();
To get a list of each student/course combination with this linking entity:
var studentCourses = context.StudentCourses.ToList();
// or, if there is other data in the linking entity and you just want student and course reference:
var studentCourses = context.StudentCourses
.Select(sc => new {sc.Student, sc.Course})
.ToList();
Alternatively you could write the Linq QL statement like you did in Linqpad now that the entity is declared and can be added as a DbSet in the context. (DbSet is not required if you are using navigation properties)
Edit: added example below: (I can't vouch for it's correctness as I pretty much exclusively use the Fluent Linq syntax, not Linq QL)
var data = (from s in context.Students
join sc in context.StudentCourse on s.StudentID equals sc.StudentID
join c in context.Courses on c.CourseId equals sc.CourseID
select new { s,c });
This is why I recommend naming the property on Student as StudentCourses rather than Courses. The alternative would read:
.SelectMany(x => x.Courses.Course)
Where Courses implies I should be getting courses (as the optimization to avoid the linking entity can give you) but you're getting StudentCourses so you're left with it looking rather weird as .Courses.Course to get the actual Course.
I have been looking through other examples on SO and I am still unable to resolve this.
I have the following model structure
public class Event
{
[Key]
public int ID { get; set; }
public ICollection<EventCategory> EventCategories{ get; set; }
}
public class Category
{
[Key]
public int ID { get; set; }
public ICollection<EventCategory> EventCategories{ get; set; }
}
public class EventCategory
{
[Key]
public int ID { get; set; }
public int EventID{ get; set; }
public Event Event{ get; set; }
public int CategoryID{ get; set; }
public Category Category{ get; set; }
}
From my Events controller I am trying to use a LINQ query to only show Events where the CategoryID is equal to 1 but i keep on coming into errors with my WHERE clause I think.
UPDATE:
I have been trying multiple queries but at present it is
var eventsContext = _context.Events
.Include(e => e.EventCategories)
.Include(e=>e.EventCategories.Select(ms => ms.Category))
.Where(e=>e.ID==1)
.Take(15)
.OrderByDescending(o => o.StartDate);
This is the error I get
TIA
First, the lambda passed to Include must be a model expression. Specifically, that means you cannot use something like Select. If you're trying to include EventCategories.Category, then you should actually do:
.Include(e => e.EventCategories).ThenInclude(ms => ms.Category)
That will fix your immediate error. The next issue is that the way in which you're attempting to query the category ID is incorrect. The lamdas don't carry over from one clause to the next. In other words, when you're doing Where(e => e.ID == 1), e is Event, not Category. The fact that you just included Category doesn't limit the where clause to that context. Therefore, what you actually need is:
.Where(e => e.EventCategories.Any(c => c.CategoryID == 1))
For what it's worth, you could also write that as:
.Where(e => e.EventCategories.Any(c => c.Category.ID == 1))
Notice the . between Category and ID. Now this where clause requires joins to be made between all of Event, EventCategories, and Category, which then means you don't actually need your Include(...).ThenInclude(...) statement, since all this does is tell EF to make the same JOINs it's already making. I will still usually do the includes explicitly, though, as otherwise, if your where clause were to change in some future iteration, you may end up no longer implicitly including everything you actually want included. Just food for thought.
I'm using MVC5 EF6 and Identity 2.1.
I have two classes:
public class Incident
{
public int IncidentId {get; set;}
...//Title, Description, etc
public virtual ICollection<FollowedIncident> FollowedIncidents { get; set; }
public virtual ApplicationUser User { get; set; }
}
public class FollowedIncident
{
public int FollowedIncidentId { get; set; }
public string UserId { get; set; }
public int IncidentId { get; set; }
public virtual Incident Incident { get; set; }
public virtual ApplicationUser User { get; set; }
}
So, the users will have the ability to follow an incident. (For starters, I'm not entirely sure if I need the ICollection and public virtual relationship references, but added them just in case for the time being.)
I'm trying to create the query that will show users the results of their followed incidents. In my controller, my query starts like this (I'm using Troy Goode's paging package... i.e. listUnpaged):
IQueryable<Incident> listUnpaged = db.Incidents.OrderByDescending(d => d.IncidentDate);
Then I want to filter by followed incidents. So, I want to show incidents where userId (parameter I pass to it) is equal to UserId in FollowedIncident. I've tried like this (error about conversion to bool from IEnumerable):
listUnpaged = listUnpaged.Where(s => s.FollowedIncidents.Where(t => t.UserId == userId));
And this (no error, but doesn't filter at all):
listUnpaged = listUnpaged.Where(s => s.FollowedIncidents.All(t => t.UserId == userId));
To me, it seems it should be as simple as this:
listUnpaged = listUnpaged.Where(s => s.FollowedIncidents.UserId == userId));
But, the linq extensions don't seem to like related data child properties? (I apologize for my programming terminology as I haven't quite pieced together all the names for everything yet.)
Anyone know how to accomplish this? It seems I may not even be thinking about it correct? (...since in the past, I've always used related data to supplement or add properties to a result. This will be the first time I want to narrow results by related data.)
Thank you.
Actually you're going about getting the Incidents the wrong way.. since Incident is a navigation property of FollowedIncident you should just use
IQueryable<Incident> listUnpaged = db.FollowedIncidents
.Where(a => a.UserId == userid)
.Select(a => a.Incident)
.OrderByDescending(d => d.IncidentDate);
Another option is to use Any()
IQueryable<Incident> listUnpaged = db.Incidents
.Where(a => a.FollowedIncidents.Any(b => b.UserId == userid)
.OrderByDescending(d => d.IncidentDate);
which would be like saying
Select *
From Incidents
Where Id IN (Select IncidentId
From FollowedIncident
Where UserId = #UserId)
I'm building a simple tour booking app.
It has 3 linked tables, setup as below.
Given a specific TourCategoryId and a date (dte), I'm trying to build an object which I can then go on to populate a viewmodel with.
However I'm not understanding how to check the TourDate field against dte.
Am I trying to do too much in one statement? Can anyone help me with what I thought would be a simple query?
Thanks for any advice:
My linq is:
var tours = db.Tours
.Include(x => x.TourDate)
.Include(x => x.TourDate.Select(b => b.Booking))
.Where(t => t.TourCategoryId == id &&
t.TourDate.Any(td => td.Date==dte));
(also pseudo code)
.Where(v => v.Booking.NumberBooked.Sum() < v.Tours.PlacesAvailable)
I've updated this with suggestions below, but it is returning all td.Date - and not filtering, as you can see from the screenshot below, showing all 3 records returned, and not just those where td.Date == dte:
Is it the .Any that is maybe not correct?
The end result should be
List of Tours => List of Dates => List of Bookings - where the dates are filtered to the dte eg:
Tour1- night tours
--TourDate - filtered to 02/03/2015
----Booking1
----Booking2
----Booking3
Tour2- day tours
--TourDate - filtered to 02/03/2015
----Booking1
----Booking2
Tour3- multi-day tours
--TourDate - filtered to 02/03/2015
----Booking1
----Booking2
----Booking3
----Booking4
Thanks again, Mark
public class Tour
{
public int TourId { get; set; }
public int TourCategoryId { get; set; }
public string TourName { get; set; }
public int PlacesAvailable { get; set; }
public virtual ICollection<TourDate> TourDate { get; set; }
}
public class TourDate
{
public int TourDateId { get; set; }
public int TourId { get; set; }
public DateTime Date { get; set; }
public virtual Tour Tour { get; set; }
public virtual ICollection<Booking> Booking { get; set; }
}
public class Booking
{
public int BookingId { get; set; }
public int TourDateId { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public int NumberBooked { get; set; }
public virtual TourDate TourDate { get; set; }
}
From what I understand you want any Tour in TourCategoryId which has a TourDate on dte.
This may work for you.
var tours =
db.Tours
.Include(x => x.TourDate)
.Include(x => x.TourDate.Select(b => b.Booking))
.Where(t => t.TourCategoryId == id &&
t.TourDate.Any(td => td.Date == dte));
To filter the TourDate.
foreach(var tour in tours)
{
tour.TourDate = tour.TourDate.Where(td => td.Date == dte).ToList()
}
The problem you are facing is that the business object you work with does not fit in the class hierarchy you use.
Try not to work strictly with the database mapped classes. Your business objects in this case are something different from just a list of instances of the Tour class that are matched to the db strucure. Just use the db classes to form a query, with the help of all the navigation properties you created, but dont try to fit all the queries you need to the same class hierarchy as you need for a view model.
You should build your query around bookings, not tours. select the bookings matching your criteria, including the one in pseudocode, then group by the dates and tours and select some kind of anonimous object, representing the groups. Then translate those to view model classes you need, not necessary all of which are database mapped.
In many cases, you do not even need ro hydrate an entity just to get some property loaded.
No need to load an elephant just to be able to take a picture.
Include is really just a workaround, like killing the elephant and loading by parts.