Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

LINQ many-to-many relationship, how to write a correct WHERE clause?

I use many-to-many relationship for my tables.

There is a query:

var query = from post in context.Posts
        from tag in post.Tags where tag.TagId == 10
        select post;

Ok, it works fine. I get posts having the tag specified by id.

I have a collection of tag ids. And i want to get posts having every tag in my collection.

I try the following way:

var tagIds = new int[]{1, 3, 7, 23, 56};

var query = from post in context.Posts
        from tag in post.Tags where tagIds.Contains( tag.TagId )
        select post;

It doesn't work. The query returns all posts having ANY one of the specified tags.

I want to get a clause like this but dynamicaly for any count of tags in the collection:

post.Tags.Whare(x => x.TagId = 1 && x.TagId = 3 && x.TagId = 7 && ... )
like image 705
Jean Louis Avatar asked May 08 '12 19:05

Jean Louis


People also ask

Can we use multiple where clause in LINQ?

Well, you can just put multiple "where" clauses in directly, but I don't think you want to. Multiple "where" clauses ends up with a more restrictive filter - I think you want a less restrictive one.

How many ways can you write LINQ queries?

LINQ provides you three different ways to write a LINQ query in C# or VB.

What is let clause in LINQ?

The Let keyword allows you to create a range variable and initialized with the result of the query expression and then you are allowed to use that variable with the upcoming clause in the same query.

What is any () in LINQ?

The Any operator is used to check whether any element in the sequence or collection satisfy the given condition. If one or more element satisfies the given condition, then it will return true. If any element does not satisfy the given condition, then it will return false.


1 Answers

You shouldn’t project each post’s tags in the outer query; rather, you need to use an inner query which performs the check for the outer filter. (In SQL, we used to call it a correlated subquery.)

var query = 
    from post in context.Posts
    where post.Tags.All(tag => tagIds.Contains(tag.TagId))
    select post;

Alternate syntax:

var query = 
    context.Posts.Where(post =>
        post.Tags.All(tag => 
            tagIds.Contains(tag.TagId)));

Edit: Correcting per Slauma’s clarification. The version below returns posts which contain, at least, all the tags in the tagIds collection.

var query = 
    from post in context.Posts
    where tagIds.All(requiredId => post.Tags.Any(tag => tag.TagId == requiredId))
    select post;

Alternate syntax:

var query = 
    context.Posts.Where(post => 
        tagIds.All(requiredId => 
            post.Tags.Any(tag =>
                tag.TagId == requiredId)));

Edit2: Corrected above per Slauma. Also including another alternative making full use of query syntax below:

// Project posts from context for which
// no Ids from tagIds are not matched
// by any tags from post
var query =
    from post in context.Posts
    where
    ( 
        // Project Ids from tagIds that are
        // not matched by any tags from post
        from requiredId in tagIds
        where
        (
            // Project tags from post that match requiredId
            from tag in post.Tags
            where tag.TagId == requiredId
            select tag
        ).Any() == false
        select requiredId 
    ).Any() == false
    select post;

I’ve used .Any() == false to simulate the NOT EXISTS operator in Transact-SQL.

like image 195
Douglas Avatar answered Sep 19 '22 17:09

Douglas