664 Stimmen

LEFT OUTER JOIN in LINQ

Wie führt man eine linke äußere Verknüpfung in C# LINQ to objects ohne Verwendung von join-on-equals-into Klauseln? Gibt es eine Möglichkeit, dies zu tun mit where Klausel? Richtiges Problem: Für inner join ist einfach und ich habe eine Lösung wie diese

List<JoinPair> innerFinal = (from l in lefts from r in rights where l.Key == r.Key
                             select new JoinPair { LeftId = l.Id, RightId = r.Id})

aber für die linke äußere Verbindung brauche ich eine Lösung. Meine ist etwas wie dieses, aber es funktioniert nicht

List< JoinPair> leftFinal = (from l in lefts from r in rights
                             select new JoinPair { 
                                            LeftId = l.Id, 
                                            RightId = ((l.Key==r.Key) ? r.Id : 0
                                        })

donde JoinPair ist eine Klasse:

public class JoinPair { long leftId; long rightId; }

3 Stimmen

Können Sie ein Beispiel dafür geben, was Sie zu erreichen versuchen?

0 Stimmen

Normale linke äußere Verknüpfung ist so etwas wie dieses: var a = from b in bb join c in cc on b.bbbbb equals c.ccccc into dd from d in dd.DefaultIfEmpty() select b.sss; Meine Frage ist es eine Möglichkeit zu tun, dass ohne Verwendung von join-on-equals-into-Klauseln etwas wie dieses var a = from b in bb from c in cc where b.bbb == c.cccc ... und so weiter...

2 Stimmen

Sicher gibt es, aber Sie sollten ein Beispiel für Ihren Code posten, den Sie bereits haben, damit die Leute Ihnen eine bessere Antwort geben können

6voto

Reese De Wind Punkte 196

Ich möchte noch hinzufügen, dass die MoreLinq-Erweiterung jetzt sowohl homogene als auch heterogene linke Joins unterstützt

http://morelinq.github.io/2.8/ref/api/html/Overload_MoreLinq_MoreEnumerable_LeftJoin.htm

Beispiel:

//Pretend a ClientCompany object and an Employee object both have a ClientCompanyID key on them

return DataContext.ClientCompany
    .LeftJoin(DataContext.Employees,                         //Table being joined
        company => company.ClientCompanyID,                  //First key
        employee => employee.ClientCompanyID,                //Second Key
        company => new {company, employee = (Employee)null}, //Result selector when there isn't a match
        (company, employee) => new { company, employee });   //Result selector when there is a match

EDIT :

Im Nachhinein mag das funktionieren, aber es konvertiert die IQueryable in eine IEnumerable, da morelinq die Abfrage nicht in SQL konvertiert.

Sie können stattdessen einen GroupJoin verwenden, wie hier beschrieben: https://stackoverflow.com/a/24273804/4251433

Dadurch wird sichergestellt, dass es als IQueryable erhalten bleibt, falls Sie später weitere logische Operationen darauf ausführen müssen.

1 Stimmen

+1 Da in der Frage ausdrücklich nach einer Lösung für "LINQ to objects" und nicht für "LINQ to SQL" usw. gefragt wurde, dies ist die beste Antwort (ohne das Rad neu zu erfinden) . Außerdem lösen viele der Antworten hier eine Ausnahme aus, wenn Elemente in der linken Sammlung, aber nicht in der rechten Sammlung vorhanden sind, was bedeutet sie sind überhaupt nicht links verbunden . Sie sind einfach nur innere Verbindungen mit sinnlosen Ausnahmen obendrauf.

5voto

Bezio Punkte 51

Erweiterungsmethode, die wie Left Join mit Join-Syntax funktioniert

public static class LinQExtensions
{
    public static IEnumerable<TResult> LeftJoin<TOuter, TInner, TKey, TResult>(
        this IEnumerable<TOuter> outer, IEnumerable<TInner> inner, 
        Func<TOuter, TKey> outerKeySelector, 
        Func<TInner, TKey> innerKeySelector, 
        Func<TOuter, TInner, TResult> resultSelector)
    {
        return outer.GroupJoin(
            inner, 
            outerKeySelector, 
            innerKeySelector,
            (outerElement, innerElements) => resultSelector(outerElement, innerElements.FirstOrDefault()));
    }
}

habe es gerade in .NET Core geschrieben und es scheint wie erwartet zu funktionieren.

Kleiner Test:

        var Ids = new List<int> { 1, 2, 3, 4};
        var items = new List<Tuple<int, string>>
        {
            new Tuple<int, string>(1,"a"),
            new Tuple<int, string>(2,"b"),
            new Tuple<int, string>(4,"d"),
            new Tuple<int, string>(5,"e"),
        };

        var result = Ids.LeftJoin(
            items,
            id => id,
            item => item.Item1,
            (id, item) => item ?? new Tuple<int, string>(id, "not found"));

        result.ToList()
        Count = 4
        [0]: {(1, a)}
        [1]: {(2, b)}
        [2]: {(3, not found)}
        [3]: {(4, d)}

3voto

Alex Punkte 382

Es gibt drei Tabellen: Personen, Schulen und Personen_Schulen, die die Personen mit den Schulen verbinden, an denen sie lernen. Ein Verweis auf die Person mit id=6 fehlt in der Tabelle persons_schools. Die Person mit id=6 wird jedoch in der Ergebnistabelle lef-joined grid angezeigt.

List<Person> persons = new List<Person>
{
    new Person { id = 1, name = "Alex", phone = "4235234" },
    new Person { id = 2, name = "Bob", phone = "0014352" },
    new Person { id = 3, name = "Sam", phone = "1345" },
    new Person { id = 4, name = "Den", phone = "3453452" },
    new Person { id = 5, name = "Alen", phone = "0353012" },
    new Person { id = 6, name = "Simon", phone = "0353012" }
};

List<School> schools = new List<School>
{
    new School { id = 1, name = "Saint. John's school"},
    new School { id = 2, name = "Public School 200"},
    new School { id = 3, name = "Public School 203"}
};

List<PersonSchool> persons_schools = new List<PersonSchool>
{
    new PersonSchool{id_person = 1, id_school = 1},
    new PersonSchool{id_person = 2, id_school = 2},
    new PersonSchool{id_person = 3, id_school = 3},
    new PersonSchool{id_person = 4, id_school = 1},
    new PersonSchool{id_person = 5, id_school = 2}
    //a relation to the person with id=6 is absent
};

var query = from person in persons
            join person_school in persons_schools on person.id equals person_school.id_person
            into persons_schools_joined
            from person_school_joined in persons_schools_joined.DefaultIfEmpty()
            from school in schools.Where(var_school => person_school_joined == null ? false : var_school.id == person_school_joined.id_school).DefaultIfEmpty()
            select new { Person = person.name, School = school == null ? String.Empty : school.name };

foreach (var elem in query)
{
    System.Console.WriteLine("{0},{1}", elem.Person, elem.School);
}

0 Stimmen

Auch wenn dies vielleicht die Antwort auf die Frage ist, geben Sie eine Erklärung zu Ihrer Antwort :)

3voto

Adam Cox Punkte 2839

Wie in meiner Antwort auf eine ähnliche Frage, hier:

Linke äußere Verknüpfung von Linq zu SQL unter Verwendung der Lambda-Syntax und Verknüpfung mit 2 Spalten (zusammengesetzter Verknüpfungsschlüssel)

Holen Sie sich die Code hier oder klonen mein Github-Repositorium und spielen!

Abfrage:

        var petOwners =
            from person in People
            join pet in Pets
            on new
            {
                person.Id,
                person.Age,
            }
            equals new
            {
                pet.Id,
                Age = pet.Age * 2, // owner is twice age of pet
            }
            into pets
            from pet in pets.DefaultIfEmpty()
            select new PetOwner
            {
                Person = person,
                Pet = pet,
            };

Lambda:

        var petOwners = People.GroupJoin(
            Pets,
            person => new { person.Id, person.Age },
            pet => new { pet.Id, Age = pet.Age * 2 },
            (person, pet) => new
            {
                Person = person,
                Pets = pet,
            }).SelectMany(
            pet => pet.Pets.DefaultIfEmpty(),
            (people, pet) => new
            {
                people.Person,
                Pet = pet,
            });

3voto

mahdi moghimi Punkte 492

Der einfachste Weg ist die Verwendung des Schlüsselworts Let. Das funktioniert bei mir.

from AItem in Db.A
Let BItem = Db.B.Where(x => x.id == AItem.id ).FirstOrDefault() 
Where SomeCondition
Select new YourViewModel
{
    X1 = AItem.a,
    X2 = AItem.b,
    X3 = BItem.c
}

Dies ist eine Simulation von Left Join. Wenn jedes Element in Tabelle B nicht mit Element A übereinstimmt, gibt BItem null zurück.

1 Stimmen

Beachten Sie, dass dies nur sinnvoll ist, wenn Sie für jedes Element in der linken Eingabeliste genau 1 Element in Ihrer Ausgabeliste haben wollen. Wenn die rechte Liste Duplikate enthält, werden diese über FirstOrDefault() . Es handelt sich also nicht um eine echte linke Verknüpfung. Es ist jedoch immer noch nützlich für häufige Situationen wie das Abrufen von Daten aus einem Lookup, das eindeutige Schlüssel hat.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X