Marvin Klein
Marvin Klein

Reputation: 1756

LINQ two Where clauses

does anybody know how I can chain my where clauses?

I would like to filter the items in my main list by the items found in the second result.

I have the following example code

@foreach (var artikel in Controller.Artikel
    .Where(x => x.LieferantenArtikel
                .Where(y => y.Lieferant.LieferantId == bestellung.LieferantId)
)
{
    <option value="@artikel.Artikelnummer">@artikel.Artikelnummer</option>
}

The first .Where is just to access the list of my object, which has the real check I need.

Upvotes: 3

Views: 130

Answers (3)

Alexander H&#248;st
Alexander H&#248;st

Reputation: 1028

I find non-nested LINQs to be much more readable. Flatten things out with SelectMany, and then use Where. Note that you need to make sure Equals() is implemented in Artikel so Distinct() does its job properly.

var artikels = Controller.Artikel
    .SelectMany(x => x.LieferantenArtikel, (x, l) => (Artikel: x, Lieferant: l))
    .Where(x => x.Lieferant.LieferantId == bestellung.LieferantId)
    .Select(x => x.Artikel)
    .Distinct();

@foreach(var artikel in artikels)
{
    <option value="@artikel.Artikelnummer">@artikel.Artikelnummer</option>
}

Upvotes: 0

LWChris
LWChris

Reputation: 4221

To add a bit more context to the other answers, which hopefully helps you to understand how to efficiently use LINQ:

What you tried to program using .Where() is technically "where exists". Translated into LINQ naively, that would be

Controller.Artikel
    .Where(x =>
        x.LieferantenArtikel
            .Where(y => y.Lieferant.LieferantId == bestellung.LieferantId)
            .Count() > 0 // naive "exists", but bad; for reasons see below
    )

.Count() > 0 however is very inefficient, since this would require the execution of .Count() to evaluate the statement, which in turn requires to determine the accurate result set.

This is where .Any() steps in: it only determines if there is at least one element in the IEnumerable by checking them and immediately returning true when the first item matches. So only if there is no match, all items of "LieferantenArtikel" have to be enumerated and checked, otherwise a smaller subset will do.

Upvotes: 2

Lajos Arpad
Lajos Arpad

Reputation: 77063

Your way of chaining .Where() into .Where() is technically correct. The problem is that the outer .Where() under the current situation does not evaluate to boolean. However, that's a requirement. The purpose of a .Where() is to define a filter for a collection that results in a subset of that collection. You can check whether .Any() item fulfills that condition:

@foreach (var artikel in Controller.Artikel
    .Where(x => x.LieferantenArtikel
                .Any(y => y.Lieferant.LieferantId == bestellung.LieferantId))
)
{
    <option value="@artikel.Artikelnummer">@artikel.Artikelnummer</option>
}

.

Upvotes: 5

Related Questions