22

I have two tables: a WorkItem table, and a WorkItemNote table. How do I return a WorkItem and all of the WorkItemNotes that meet a certain criteria?

I think this should be simple, almost like a conditional "Include", right?

Daniel Daranas
  • 22,454
  • 9
  • 63
  • 116
Esteban Araya
  • 29,284
  • 24
  • 107
  • 141

1 Answers1

40

I've been planning on writing a tip on this but your question beat me to the punch.

Assuming a WorkItem has many WorkItemNotes

you can do this:

var intermediary = (from item in ctx.WorkItems
              from note in item.Notes
              where note.SomeProp == SomeValue
              select new {item, note}).AsEnumerable();

This produces an anonymous element for each WorkItemNote that matches, and holds the corresponding WorkItem too.

EF identity resolution insures that the same WorkItem (by reference) is returned multiple times if it has multiple WorkItemNotes that match the criteria.

I assume that next you want to just get back to just the WorkItems, like this:

var workItems = intermediary.Select(x => x.item).Distinct().ToList();

Then if you now do this:

foreach(var workItem in workItems)
{
   Console.WriteLine(workItem.Notes.Count)
}

You will see that WorkItemNotes that match the original filter have been added to the Notes collection of each workItem.

This is because of something called Relationship Fixup.

I.e. this gives you what you want conditional include.

Hope this helps

Alex

Alex James
  • 20,874
  • 3
  • 50
  • 49
  • Really? x.Item will have the appropriate WorkItemNotes? That's awesome! I'm glad you posted this because what I currently have queries the db for every WorkItem. Thanks! – Esteban Araya Oct 08 '09 at 04:44
  • One question: Why does intermediary have to be IEnumerable? Can it be IQueryable? – Esteban Araya Oct 08 '09 at 04:49
  • Yeap this works. You can use this for other tricks too, like sorting, see Tip 1 of my tips series! – Alex James Oct 08 '09 at 04:51
  • It needs to be IEnumerable because otherwise the projecting code changes the query plan, and only the WorkItem is pulled back. But in this particular case I don't think the PERF would be any better even if you could use IQueryable. – Alex James Oct 08 '09 at 04:54
  • 4
    Alex: I wish I could give you more upvotes; this works like a charm! I also used your tip # 22 to include related objects for WorkItemNote. Thanks again! – Esteban Araya Oct 08 '09 at 05:32
  • @Alex, I want to extend the statement above to do add a where clause to WorkItems, but the catch is..... I want to include all workItem records even if they dont have a workItemNote. (when i do it, I only get workitem records that have a note. I hope you can help me! – tkerwood Jul 01 '11 at 08:50
  • @Alex, Hang on... I worked it out. WorkItems.Where(q => q.TypeID == 1).Select(q => new { WorkItem = q, Note = q.Notes.Where(a => a.Code == "5") }) Thanks for the starting point. – tkerwood Jul 01 '11 at 09:02
  • @AlexJames What if "SomeValue" was a list of objects from a database? I'm using row level security to give granular CRUD access to each WorkItemNotes. This uses a join table. I'm trying Tip8 from your blog, but I can't get it to work. I will also need to include the permissions with each WorkItemNote (probably easiest with an Eager Load via the FK) http://stackoverflow.com/questions/8190177/ef-query-with-conditional-include-that-uses-joins – makerofthings7 Nov 19 '11 at 02:02
  • 4
    And over 2 years later, this is still helping people! Thank you. Any chance we can get an .Include that implements conditional including natively? – Joe Dec 06 '11 at 00:11
  • In EF6 (with db first) this is working for me only if lazy loading is disabled, otherwise it loads everything. Can you help me how to fix this? – dvjanm Nov 11 '15 at 14:45