If you start only with the given personId (or userId ), you can make this request in one reverse direction, for example:
var posts = context.Posts .Intersect(context.People .Where(p => p.Id == givenPersonId) .SelectMany(p => p.InterestTags.SelectMany(t => t.Posts))) .ToList();
This means an INTERSECT statement in SQL.
You can also do this in two rounds:
var interestTagsOfPerson = context.People.Where(p => p.Id == givenPersonId) .Select(p => p.InterestTags.Select(t => t.Id)) .SingleOrDefault();
Using the list of primitive types in the second request ( interestTagsOfPerson is a collection from int ) also fixes the error indicated in your Editing in the question. For Contains you cannot use object references in LINQ to Entities because EF does not know how to translate this into SQL.
I don’t know which of the two approaches is faster (SQL experts may have a better idea), but they will probably start testing the first option. (I tested a bit and it seems to have returned the correct results, but this is the first time I've used INTERSECT .)
Edit
To give an idea of the generated SQL (taken from SQL Profiler):
The first query (with INTERSECT ) creates this SQL query:
SELECT [Intersect1].[Id] AS [C1], [Intersect1].[Name] AS [C2], FROM (SELECT [Extent1].[Id] AS [Id], [Extent1].[Name] AS [Name], FROM [dbo].[Posts] AS [Extent1] INTERSECT SELECT [Join1].[Id] AS [Id], [Join1].[Name] AS [Name], FROM [dbo].[PersonInterestTags] AS [Extent2] INNER JOIN (SELECT [Extent3].[TagId] AS [TagId], [Extent4].[Id] AS [Id], [Extent4].[Name] AS [Name] FROM [dbo].[PostInterestTags] AS [Extent3] INNER JOIN [dbo].[Posts] AS [Extent4] ON [Extent3].[PostId] = [Extent4].[Id] ) AS [Join1] ON [Extent2].[TagId] = [Join1].[TagId] WHERE 1 = [Extent2].[PersonId]) AS [Intersect1]
The second option:
Query1 (list of user tag identifiers):
SELECT [Project1].[Id] AS [Id], [Project1].[C1] AS [C1], [Project1].[TagId] AS [TagId] FROM ( SELECT [Limit1].[Id] AS [Id], [Extent2].[TagId] AS [TagId], CASE WHEN ([Extent2].[PersonId] IS NULL) THEN CAST(NULL AS int) ELSE 1 END AS [C1] FROM (SELECT TOP (2) [Extent1].[Id] AS [Id] FROM [dbo].[People] AS [Extent1] WHERE 1 = [Extent1].[Id] ) AS [Limit1] LEFT OUTER JOIN [dbo].[PersonInterestTags] AS [Extent2] ON [Limit1].[Id] = [Extent2].[PersonId] ) AS [Project1] ORDER BY [Project1].[Id] ASC, [Project1].[C1] ASC
Request 2 for final messages:
SELECT [Extent1].[Id] AS [Id], [Extent1].[Name] AS [Name], FROM [dbo].[Posts] AS [Extent1] WHERE EXISTS (SELECT 1 AS [C1] FROM [dbo].[PostInterestTags] AS [Extent2] WHERE ([Extent1].[Id] = [Extent2].[PostId]) AND ([Extent2].[TagId] IN (1,2,3)) )
In this example, the returned query is 1 (1,2,3), therefore, (1,2,3) in the IN clause in query 2.