LEFT OUTER JOIN dans LINQ


539

Comment effectuer la jointure externe gauche en C # LINQ aux objets sans utiliser de join-on-equals-intoclauses? Y a-t-il moyen de le faire avec l' wherearticle? Problème correct: pour la jointure interne est facile et j'ai une solution comme celle-ci

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})

mais pour la jointure externe gauche, j'ai besoin d'une solution. Le mien est quelque chose comme ça mais ça ne marche pas

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
                                        })

où JoinPair est une classe:

public class JoinPair { long leftId; long rightId; }

2
pouvez-vous donner un exemple de ce que vous essayez de réaliser?
jeroenh

la jointure externe gauche normale est quelque chose comme ceci: 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; Ma question est de savoir comment faire cela en utilisant des clauses join-on-equals-into quelque chose comme ceci var a = from b in bb from c in cc where b.bbb == c.cccc ... and so on .. .
Jouet

1
bien sûr, mais vous devriez publier un exemple de votre code que vous avez déjà afin que les gens puissent vous donner une meilleure réponse
paresse

Je cherchais un "Left excluant " JOIN (et je l'ai confondu avec le concept de "OUTER"). Cette réponse était plus proche de ce que je voulais.
The Red Pea

Réponses:


598

Comme indiqué sur:

101 échantillons LINQ - jointure externe gauche

var q =
    from c in categories
    join p in products on c.Category equals p.Category into ps
    from p in ps.DefaultIfEmpty()
    select new { Category = c, ProductName = p == null ? "(No products)" : p.ProductName };

7
J'essaie la même chose mais j'obtiens une erreur sur l'opérateur de jointure, qui dit "Le type d'une des expressions dans la clause join est incorrect."
Badhon Jain

3
@jain si vos types sont différents, la jointure ne fonctionnera pas. Vos clés sont donc probablement de types de données différents. Les deux clés sont-elles int par exemple?
Yooakim

2
Quelle est la solution Jain? Je suis également confronté à la même erreur et les types sont les mêmes dans mon cas également.
Sandeep

1
@Sandeep vérifier vos clés où vous l'avez rejoint. Supposons que ceux-ci soient de type chaîne et int, puis convertissent simplement la clé de chaîne en int.
Ankit


546

Si un fournisseur LINQ piloté par une base de données est utilisé, une jointure externe gauche beaucoup plus lisible peut être écrite comme telle:

from maintable in Repo.T_Whatever 
from xxx in Repo.T_ANY_TABLE.Where(join condition).DefaultIfEmpty()

Si vous omettez le, DefaultIfEmpty()vous aurez une jointure interne.

Prenez la réponse acceptée:

  from c in categories
    join p in products on c equals p.Category into ps
    from p in ps.DefaultIfEmpty()

Cette syntaxe est très déroutante, et il n'est pas clair comment cela fonctionne lorsque vous souhaitez quitter les tables MULTIPLE.

Remarque
Il convient de noter que from alias in Repo.whatever.Where(condition).DefaultIfEmpty()c'est la même chose qu'une application externe / jointure latérale gauche, que tout optimiseur de base de données (décent) est parfaitement capable de traduire en jointure gauche, tant que vous n'introduisez pas de ligne par ligne. -valeurs (alias une application externe réelle). Ne faites pas cela dans Linq-2-Objects (car il n'y a pas d'optimiseur de base de données lorsque vous utilisez Linq-to-Objects).

Exemple détaillé

var query2 = (
    from users in Repo.T_User
    from mappings in Repo.T_User_Group
         .Where(mapping => mapping.USRGRP_USR == users.USR_ID)
         .DefaultIfEmpty() // <== makes join left join
    from groups in Repo.T_Group
         .Where(gruppe => gruppe.GRP_ID == mappings.USRGRP_GRP)
         .DefaultIfEmpty() // <== makes join left join

    // where users.USR_Name.Contains(keyword)
    // || mappings.USRGRP_USR.Equals(666)  
    // || mappings.USRGRP_USR == 666 
    // || groups.Name.Contains(keyword)

    select new
    {
         UserId = users.USR_ID
        ,UserName = users.USR_User
        ,UserGroupId = groups.ID
        ,GroupName = groups.Name
    }

);


var xy = (query2).ToList();

Lorsqu'il est utilisé avec LINQ 2 SQL, il se traduira parfaitement par la requête SQL très lisible suivante:

SELECT 
     users.USR_ID AS UserId 
    ,users.USR_User AS UserName 
    ,groups.ID AS UserGroupId 
    ,groups.Name AS GroupName 
FROM T_User AS users

LEFT JOIN T_User_Group AS mappings
   ON mappings.USRGRP_USR = users.USR_ID

LEFT JOIN T_Group AS groups
    ON groups.GRP_ID == mappings.USRGRP_GRP

Éditer:

Voir aussi " Convertir une requête SQL Server en requête Linq » pour un exemple plus complexe.

De plus, si vous le faites dans Linq-2-Objects (au lieu de Linq-2-SQL), vous devez le faire à l'ancienne (car LINQ to SQL le traduit correctement pour joindre des opérations, mais sur des objets, cette méthode force une analyse complète, et ne tire pas parti des recherches d'index, peu importe ...):

    var query2 = (
    from users in Repo.T_Benutzer
    join mappings in Repo.T_Benutzer_Benutzergruppen on mappings.BEBG_BE equals users.BE_ID into tmpMapp
    join groups in Repo.T_Benutzergruppen on groups.ID equals mappings.BEBG_BG into tmpGroups
    from mappings in tmpMapp.DefaultIfEmpty()
    from groups in tmpGroups.DefaultIfEmpty()
    select new
    {
         UserId = users.BE_ID
        ,UserName = users.BE_User
        ,UserGroupId = mappings.BEBG_BG
        ,GroupName = groups.Name
    }

);

21
Cette réponse est réellement utile. Merci d'avoir proposé une syntaxe compréhensible.
Chris Marisic

3
WTB une requête LINQ compatible NHibernate ... :)
mxmissile

30
LINQ to SQL traduit cela correctement pour joindre des opérations. Sur les objets cependant, cette méthode force une analyse complète, c'est pourquoi la documentation officielle propose la solution de jointure de groupe qui peut tirer parti des hachages pour indexer les recherches.
Tamir Daniely

3
Je pense que la syntaxe d'explicite joinest beaucoup plus lisible et claire que celle wheresuivie parDefaultIfEmpty
FindOut_Quran

1
@ user3441905: Tant que vous n'avez qu'à joindre la table a à la table b, cela peut être le cas. Mais dès que vous en aurez plus, ce ne sera plus le cas. Mais même pour seulement 2 tables, je pense que c'est trop verbeux. L'opinion populaire semble également être contre vous, car cette réponse a commencé par 0 alors que la réponse du haut comptait déjà plus de 90 votes positifs.
Stefan Steiger

132

Utilisation de l'expression lambda

db.Categories    
  .GroupJoin(db.Products,
      Category => Category.CategoryId,
      Product => Product.CategoryId,
      (x, y) => new { Category = x, Products = y })
  .SelectMany(
      xy => xy.Products.DefaultIfEmpty(),
      (x, y) => new { Category = x.Category, Product = y })
  .Select(s => new
  {
      CategoryName = s.Category.Name,     
      ProductName = s.Product.Name   
  });

8
Join et GroupJoin ne prennent pas vraiment en charge la jointure gauche. L'astuce avec l'utilisation de GroupJoin est que vous pouvez avoir des groupes vides, puis traduire ces groupes vides en valeurs vides. DefaultIfEmpty fait simplement cela, ce Enumerable.Empty<Product>.DefaultIfEmpty()qui signifie retournera un IEnumerable avec une seule valeur de default(Product).
Tamir Daniely

61
Tout cela pour effectuer une jointure gauche ??
FindOut_Quran

7
Merci pour cela! Pas trop d'exemples d'expression lambda là-bas, cela a fonctionné pour moi.
Johan Henkens

1
Merci d'avoir répondu. Cela a donné la chose la plus proche du SQL LEFT OUTER JOIN brut que j'ai écrit au fil des ans
John Gathogo

1
Vous n'avez pas vraiment besoin du dernier Select (), l'obj anon dans SelectMany () peut être refactorisé pour la même sortie. Une autre idée est de tester y pour null pour simuler une équivalence LEFT JOIN plus proche.
Denny Jacob

46

Maintenant comme méthode d'extension:

public static class LinqExt
{
    public static IEnumerable<TResult> LeftOuterJoin<TLeft, TRight, TKey, TResult>(this IEnumerable<TLeft> left, IEnumerable<TRight> right, Func<TLeft, TKey> leftKey, Func<TRight, TKey> rightKey,
        Func<TLeft, TRight, TResult> result)
    {
        return left.GroupJoin(right, leftKey, rightKey, (l, r) => new { l, r })
             .SelectMany(
                 o => o.r.DefaultIfEmpty(),
                 (l, r) => new { lft= l.l, rght = r })
             .Select(o => result.Invoke(o.lft, o.rght));
    }
}

Utilisez comme vous utiliseriez normalement join:

var contents = list.LeftOuterJoin(list2, 
             l => l.country, 
             r => r.name,
            (l, r) => new { count = l.Count(), l.country, l.reason, r.people })

J'espère que cela vous fera gagner du temps.


44

Jetez un oeil à cet exemple . Cette requête devrait fonctionner:

var leftFinal = from left in lefts
                join right in rights on left equals right.Left into leftRights
                from leftRight in leftRights.DefaultIfEmpty()
                select new { LeftId = left.Id, RightId = left.Key==leftRight.Key ? leftRight.Id : 0 };

3
Peut- ron accéder à la clause select après avoir utilisé une jointure dans?
Farhad Alizadeh Noori

@FarhadAlizadehNoori Oui, c'est possible.
Po-ta-toe

L'auteur voulait probablement réutiliser rdans la deuxième fromclause. à savoir from r in lrs.DefaultIfEmpty()Sinon , cette requête n'a pas beaucoup de sens et n'a probablement même pas compiler en raison d' rêtre hors contexte pour la sélection.
Saeb Amini

@Devart, quand j'ai lu votre requête, cela m'a rappelé un film Clockwiseavec John Cleese, lol.
Matas Vaitkevicius

1
De gauche à droite dans leftrights en droits à gauche dans leftRights ... Oh jeez ... La syntaxe de l'utilisation de LEFT OUTER JOIN dans LINQ n'est vraiment pas claire, mais ces noms la rendent encore plus floue.
Mike Gledhill

19

Une implémentation de la jointure externe gauche par des méthodes d'extension pourrait ressembler à

public static IEnumerable<Result> LeftJoin<TOuter, TInner, TKey, Result>(
  this IEnumerable<TOuter> outer, IEnumerable<TInner> inner
  , Func<TOuter, TKey> outerKeySelector, Func<TInner, TKey> innerKeySelector
  , Func<TOuter, TInner, Result> resultSelector, IEqualityComparer<TKey> comparer)
  {
    if (outer == null)
      throw new ArgumentException("outer");

    if (inner == null)
      throw new ArgumentException("inner");

    if (outerKeySelector == null)
      throw new ArgumentException("outerKeySelector");

    if (innerKeySelector == null)
      throw new ArgumentException("innerKeySelector");

    if (resultSelector == null)
      throw new ArgumentException("resultSelector");

    return LeftJoinImpl(outer, inner, outerKeySelector, innerKeySelector, resultSelector, comparer ?? EqualityComparer<TKey>.Default);
  }

  static IEnumerable<Result> LeftJoinImpl<TOuter, TInner, TKey, Result>(
      IEnumerable<TOuter> outer, IEnumerable<TInner> inner
      , Func<TOuter, TKey> outerKeySelector, Func<TInner, TKey> innerKeySelector
      , Func<TOuter, TInner, Result> resultSelector, IEqualityComparer<TKey> comparer)
  {
    var innerLookup = inner.ToLookup(innerKeySelector, comparer);

    foreach (var outerElment in outer)
    {
      var outerKey = outerKeySelector(outerElment);
      var innerElements = innerLookup[outerKey];

      if (innerElements.Any())
        foreach (var innerElement in innerElements)
          yield return resultSelector(outerElment, innerElement);
      else
        yield return resultSelector(outerElment, default(TInner));
     }
   }

Le sélecteur de résultats doit alors prendre en charge les éléments nuls. Fx.

   static void Main(string[] args)
   {
     var inner = new[] { Tuple.Create(1, "1"), Tuple.Create(2, "2"), Tuple.Create(3, "3") };
     var outer = new[] { Tuple.Create(1, "11"), Tuple.Create(2, "22") };

     var res = outer.LeftJoin(inner, item => item.Item1, item => item.Item1, (it1, it2) =>
     new { Key = it1.Item1, V1 = it1.Item2, V2 = it2 != null ? it2.Item2 : default(string) });

     foreach (var item in res)
       Console.WriteLine(string.Format("{0}, {1}, {2}", item.Key, item.V1, item.V2));
   }

4
Cependant, il ne s'agit que d' une option pour LINQ to objets et ne pourra pas traduire la requête vers les fournisseurs de requêtes, ce qui est le cas d'utilisation le plus courant pour cette opération.
Servy

13
Mais la question était "Comment effectuer la jointure externe gauche en C # LINQ aux objets ..."
Bertrand

12

regardez cet exemple

class Person
{
    public int ID { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Phone { get; set; }
}

class Pet
{
    public string Name { get; set; }
    public Person Owner { get; set; }
}

public static void LeftOuterJoinExample()
{
    Person magnus = new Person {ID = 1, FirstName = "Magnus", LastName = "Hedlund"};
    Person terry = new Person {ID = 2, FirstName = "Terry", LastName = "Adams"};
    Person charlotte = new Person {ID = 3, FirstName = "Charlotte", LastName = "Weiss"};
    Person arlene = new Person {ID = 4, FirstName = "Arlene", LastName = "Huff"};

    Pet barley = new Pet {Name = "Barley", Owner = terry};
    Pet boots = new Pet {Name = "Boots", Owner = terry};
    Pet whiskers = new Pet {Name = "Whiskers", Owner = charlotte};
    Pet bluemoon = new Pet {Name = "Blue Moon", Owner = terry};
    Pet daisy = new Pet {Name = "Daisy", Owner = magnus};

    // Create two lists.
    List<Person> people = new List<Person> {magnus, terry, charlotte, arlene};
    List<Pet> pets = new List<Pet> {barley, boots, whiskers, bluemoon, daisy};

    var query = from person in people
        where person.ID == 4
        join pet in pets on person equals pet.Owner  into personpets
        from petOrNull in personpets.DefaultIfEmpty()
        select new { Person=person, Pet = petOrNull}; 



    foreach (var v in query )
    {
        Console.WriteLine("{0,-15}{1}", v.Person.FirstName + ":", (v.Pet == null ? "Does not Exist" : v.Pet.Name));
    }
}

// This code produces the following output:
//
// Magnus:        Daisy
// Terry:         Barley
// Terry:         Boots
// Terry:         Blue Moon
// Charlotte:     Whiskers
// Arlene:

maintenant vous pouvez include elements from the leftmême si cet élément has no matches in the right, dans notre cas, nous sommes revenus Arlenemême s'il n'a pas de correspondance dans la droite

voici la référence

Comment: effectuer des jointures externes gauches (Guide de programmation C #)


la sortie doit être: Arlene: n'existe pas
user1169587

10

Ceci est le formulaire général (comme déjà fourni dans d'autres réponses)

var c =
    from a in alpha
    join b in beta on b.field1 equals a.field1 into b_temp
    from b_value in b_temp.DefaultIfEmpty()
    select new { Alpha = a, Beta = b_value };

Cependant, voici une explication qui, je l'espère, clarifiera ce que cela signifie réellement!

join b in beta on b.field1 equals a.field1 into b_temp

crée essentiellement un jeu de résultats séparé b_temp qui inclut effectivement des «lignes» nulles pour les entrées sur le côté droit (entrées dans «b»).

Puis la ligne suivante:

from b_value in b_temp.DefaultIfEmpty()

..itère sur cet ensemble de résultats, en définissant la valeur nulle par défaut pour la 'ligne' sur le côté droit et en définissant le résultat de la jointure de la ligne de droite sur la valeur de 'b_value' (c'est-à-dire la valeur qui est à droite côté côté, s'il y a un enregistrement correspondant, ou «null» s'il n'y en a pas).

Maintenant, si le côté droit est le résultat d'une requête LINQ distincte, il sera composé de types anonymes, qui ne peuvent être que «quelque chose» ou «nul». Si c'est un énumérable cependant (par exemple une liste - où MyObjectB est une classe avec 2 champs), alors il est possible d'être précis sur les valeurs par défaut 'null' qui sont utilisées pour ses propriétés:

var c =
    from a in alpha
    join b in beta on b.field1 equals a.field1 into b_temp
    from b_value in b_temp.DefaultIfEmpty( new MyObjectB { Field1 = String.Empty, Field2 = (DateTime?) null })
    select new { Alpha = a, Beta_field1 = b_value.Field1, Beta_field2 = b_value.Field2 };

Cela garantit que 'b' lui-même n'est pas nul (mais ses propriétés peuvent être nulles, en utilisant les valeurs nulles par défaut que vous avez spécifiées), et cela vous permet de vérifier les propriétés de b_value sans obtenir une exception de référence nulle pour b_value. Notez que pour un DateTime nullable, un type de (DateTime?) C'est-à-dire 'DateTime nullable' doit être spécifié comme 'Type' du null dans la spécification pour 'DefaultIfEmpty' (cela s'appliquera également aux types qui ne sont pas 'nativement' 'nullable par exemple double, float).

Vous pouvez effectuer plusieurs jointures externes gauches en enchaînant simplement la syntaxe ci-dessus.


1
d'où vient b_value?
Jack Fraser

9

Voici un exemple si vous devez joindre plus de 2 tables:

from d in context.dc_tpatient_bookingd
join bookingm in context.dc_tpatient_bookingm 
     on d.bookingid equals bookingm.bookingid into bookingmGroup
from m in bookingmGroup.DefaultIfEmpty()
join patient in dc_tpatient
     on m.prid equals patient.prid into patientGroup
from p in patientGroup.DefaultIfEmpty()

Réf: https://stackoverflow.com/a/17142392/2343


4

Méthode d'extension qui fonctionne comme la jointure gauche avec la syntaxe Join

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()));
    }
}

vient de l'écrire dans .NET core et il semble fonctionner comme prévu.

Petit 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)}

4

Voici une version assez facile à comprendre en utilisant la syntaxe de la méthode:

IEnumerable<JoinPair> outerLeft =
    lefts.SelectMany(l => 
        rights.Where(r => l.Key == r.Key)
              .DefaultIfEmpty(new Item())
              .Select(r => new JoinPair { LeftId = l.Id, RightId = r.Id }));

3

Il existe trois tableaux: personnes, écoles et personnes_écoles, qui connectent les personnes aux écoles dans lesquelles ils étudient. Une référence à la personne avec id = 6 est absente dans le tableau personnes_écoles. Cependant, la personne avec id = 6 est présentée dans la grille résultat jointe à gauche.

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);
}

Bien que ce soit peut-être la réponse à la question, donnez des explications sur votre réponse :)
Amir

2

Il s'agit d'une syntaxe SQL comparable à la syntaxe LINQ pour les jointures internes et externes externes. Jointure externe gauche:

http://www.ozkary.com/2011/07/linq-to-entity-inner-and-left-joins.html

"L'exemple suivant fait une jointure de groupe entre le produit et la catégorie. Il s'agit essentiellement de la jointure gauche. L'expression into renvoie des données même si la table des catégories est vide. Pour accéder aux propriétés de la table des catégories, nous devons maintenant sélectionner le résultat énumérable en ajoutant le from cl dans l'instruction catList.DefaultIfEmpty ().


1

Effectuer des jointures externes gauches dans linq C # // Effectuer des jointures externes gauches

class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

class Child
{
    public string Name { get; set; }
    public Person Owner { get; set; }
}
public class JoinTest
{
    public static void LeftOuterJoinExample()
    {
        Person magnus = new Person { FirstName = "Magnus", LastName = "Hedlund" };
        Person terry = new Person { FirstName = "Terry", LastName = "Adams" };
        Person charlotte = new Person { FirstName = "Charlotte", LastName = "Weiss" };
        Person arlene = new Person { FirstName = "Arlene", LastName = "Huff" };

        Child barley = new Child { Name = "Barley", Owner = terry };
        Child boots = new Child { Name = "Boots", Owner = terry };
        Child whiskers = new Child { Name = "Whiskers", Owner = charlotte };
        Child bluemoon = new Child { Name = "Blue Moon", Owner = terry };
        Child daisy = new Child { Name = "Daisy", Owner = magnus };

        // Create two lists.
        List<Person> people = new List<Person> { magnus, terry, charlotte, arlene };
        List<Child> childs = new List<Child> { barley, boots, whiskers, bluemoon, daisy };

        var query = from person in people
                    join child in childs
                    on person equals child.Owner into gj
                    from subpet in gj.DefaultIfEmpty()
                    select new
                    {
                        person.FirstName,
                        ChildName = subpet!=null? subpet.Name:"No Child"
                    };
                       // PetName = subpet?.Name ?? String.Empty };

        foreach (var v in query)
        {
            Console.WriteLine($"{v.FirstName + ":",-25}{v.ChildName}");
        }
    }

    // This code produces the following output:
    //
    // Magnus:        Daisy
    // Terry:         Barley
    // Terry:         Boots
    // Terry:         Blue Moon
    // Charlotte:     Whiskers
    // Arlene:        No Child

https://dotnetwithhamid.blogspot.in/


1

Je voudrais ajouter que si vous obtenez l'extension MoreLinq, il y a maintenant un support pour les jointures gauches homogènes et hétérogènes maintenant

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

exemple:

//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

ÉDITER:

Rétrospectivement, cela peut fonctionner, mais il convertit l'IQueryable en un IEnumerable car morelinq ne convertit pas la requête en SQL.

Vous pouvez à la place utiliser un GroupJoin comme décrit ici: https://stackoverflow.com/a/24273804/4251433

Cela garantira qu'il restera comme un IQueryable au cas où vous auriez besoin de faire d'autres opérations logiques dessus plus tard.


1

Le moyen le plus simple consiste à utiliser le mot clé Let. Cela fonctionne pour moi.

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
}

Il s'agit d'une simulation de Left Join. Si chaque élément de la table B ne correspond pas à un élément A, BItem renvoie null


0

Si vous devez joindre et filtrer quelque chose, cela peut être fait en dehors de la jointure. Le filtrage peut être effectué après la création de la collection.

Dans ce cas, si je le fais dans la condition de jointure, je réduis les lignes qui sont retournées.

La condition ternaire est utilisée (= n == null ? "__" : n.MonDayNote,)

  • Si l'objet est null(donc pas de correspondance), retournez ce qui se trouve après le ?. __, dans ce cas.

  • Sinon, ce retour est après :, n.MonDayNote.

Merci aux autres contributeurs, c'est là que j'ai commencé avec mon propre problème.


        var schedLocations = (from f in db.RAMS_REVENUE_LOCATIONS
              join n in db.RAMS_LOCATION_PLANNED_MANNING on f.revenueCenterID equals

                  n.revenueCenterID into lm

              from n in lm.DefaultIfEmpty()

              join r in db.RAMS_LOCATION_SCHED_NOTE on f.revenueCenterID equals r.revenueCenterID
              into locnotes

              from r in locnotes.DefaultIfEmpty()
              where f.LocID == nLocID && f.In_Use == true && f.revenueCenterID > 1000

              orderby f.Areano ascending, f.Locname ascending
              select new
              {
                  Facname = f.Locname,
                  f.Areano,
                  f.revenueCenterID,
                  f.Locabbrev,

                  //  MonNote = n == null ? "__" : n.MonDayNote,
                  MonNote = n == null ? "__" : n.MonDayNote,
                  TueNote = n == null ? "__" : n.TueDayNote,
                  WedNote = n == null ? "__" : n.WedDayNote,
                  ThuNote = n == null ? "__" : n.ThuDayNote,

                  FriNote = n == null ? "__" : n.FriDayNote,
                  SatNote = n == null ? "__" : n.SatDayNote,
                  SunNote = n == null ? "__" : n.SunDayNote,
                  MonEmpNbr = n == null ? 0 : n.MonEmpNbr,
                  TueEmpNbr = n == null ? 0 : n.TueEmpNbr,
                  WedEmpNbr = n == null ? 0 : n.WedEmpNbr,
                  ThuEmpNbr = n == null ? 0 : n.ThuEmpNbr,
                  FriEmpNbr = n == null ? 0 : n.FriEmpNbr,
                  SatEmpNbr = n == null ? 0 : n.SatEmpNbr,
                  SunEmpNbr = n == null ? 0 : n.SunEmpNbr,
                  SchedMondayDate = n == null ? dMon : n.MondaySchedDate,
                  LocNotes = r == null ? "Notes: N/A" : r.LocationNote

              }).ToList();
                Func<int, string> LambdaManning = (x) => { return x == 0 ? "" : "Manning:" + x.ToString(); };
        DataTable dt_ScheduleMaster = PsuedoSchedule.Tables["ScheduleMasterWithNotes"];
        var schedLocations2 = schedLocations.Where(x => x.SchedMondayDate == dMon);

0
class Program
{
    List<Employee> listOfEmp = new List<Employee>();
    List<Department> listOfDepart = new List<Department>();

    public Program()
    {
        listOfDepart = new List<Department>(){
            new Department { Id = 1, DeptName = "DEV" },
            new Department { Id = 2, DeptName = "QA" },
            new Department { Id = 3, DeptName = "BUILD" },
            new Department { Id = 4, DeptName = "SIT" }
        };


        listOfEmp = new List<Employee>(){
            new Employee { Empid = 1, Name = "Manikandan",DepartmentId=1 },
            new Employee { Empid = 2, Name = "Manoj" ,DepartmentId=1},
            new Employee { Empid = 3, Name = "Yokesh" ,DepartmentId=0},
            new Employee { Empid = 3, Name = "Purusotham",DepartmentId=0}
        };

    }
    static void Main(string[] args)
    {
        Program ob = new Program();
        ob.LeftJoin();
        Console.ReadLine();
    }

    private void LeftJoin()
    {
        listOfEmp.GroupJoin(listOfDepart.DefaultIfEmpty(), x => x.DepartmentId, y => y.Id, (x, y) => new { EmpId = x.Empid, EmpName = x.Name, Dpt = y.FirstOrDefault() != null ? y.FirstOrDefault().DeptName : null }).ToList().ForEach
            (z =>
            {
                Console.WriteLine("Empid:{0} EmpName:{1} Dept:{2}", z.EmpId, z.EmpName, z.Dpt);
            });
    }
}

class Employee
{
    public int Empid { get; set; }
    public string Name { get; set; }
    public int DepartmentId { get; set; }
}

class Department
{
    public int Id { get; set; }
    public string DeptName { get; set; }
}

PRODUCTION


0

Selon ma réponse à une question similaire, ici:

Liaison externe gauche Linq vers SQL à l'aide de la syntaxe Lambda et jointure sur 2 colonnes (clé de jointure composite)

Obtenez le code ici , ou clonez mon dépôt github , et jouez!

Requete:

        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,
            });

0

Vue d'ensemble: dans cet extrait de code, je montre comment regrouper par ID où Table1 et Table2 ont une relation un à plusieurs. Je groupe sur Id, Field1 et Field2. La sous-requête est utile, si une troisième recherche de table est requise et qu'elle aurait nécessité une relation de jointure gauche. Je montre un groupement de jointure gauche et une sous-requête linq. Les résultats sont équivalents.

class MyView
{
public integer Id {get,set};
    public String Field1  {get;set;}
public String Field2 {get;set;}
    public String SubQueryName {get;set;}                           
}

IList<MyView> list = await (from ci in _dbContext.Table1
                                               join cii in _dbContext.Table2
                                                   on ci.Id equals cii.Id

                                               where ci.Field1 == criterion
                                               group new
                                               {
                                                   ci.Id
                                               } by new { ci.Id, cii.Field1, ci.Field2}

                                           into pg
                                               select new MyView
                                               {
                                                   Id = pg.Key.Id,
                                                   Field1 = pg.Key.Field1,
                                                   Field2 = pg.Key.Field2,
                                                   SubQueryName=
                                                   (from chv in _dbContext.Table3 where chv.Id==pg.Key.Id select chv.Field1).FirstOrDefault()
                                               }).ToListAsync<MyView>();


 Compared to using a Left Join and Group new

IList<MyView> list = await (from ci in _dbContext.Table1
                                               join cii in _dbContext.Table2
                                                   on ci.Id equals cii.Id

                       join chv in _dbContext.Table3
                                                  on cii.Id equals chv.Id into lf_chv
                                                from chv in lf_chv.DefaultIfEmpty()

                                               where ci.Field1 == criterion
                                               group new
                                               {
                                                   ci.Id
                                               } by new { ci.Id, cii.Field1, ci.Field2, chv.FieldValue}

                                           into pg
                                               select new MyView
                                               {
                                                   Id = pg.Key.Id,
                                                   Field1 = pg.Key.Field1,
                                                   Field2 = pg.Key.Field2,
                                                   SubQueryName=pg.Key.FieldValue
                                               }).ToListAsync<MyView>();

-1
(from a in db.Assignments
     join b in db.Deliveryboys on a.AssignTo equals b.EmployeeId  

     //from d in eGroup.DefaultIfEmpty()
     join  c in  db.Deliveryboys on a.DeliverTo equals c.EmployeeId into eGroup2
     from e in eGroup2.DefaultIfEmpty()
     where (a.Collected == false)
     select new
     {
         OrderId = a.OrderId,
         DeliveryBoyID = a.AssignTo,
         AssignedBoyName = b.Name,
         Assigndate = a.Assigndate,
         Collected = a.Collected,
         CollectedDate = a.CollectedDate,
         CollectionBagNo = a.CollectionBagNo,
         DeliverTo = e == null ? "Null" : e.Name,
         DeliverDate = a.DeliverDate,
         DeliverBagNo = a.DeliverBagNo,
         Delivered = a.Delivered

     });

-1

Solution simple pour LEFT OUTER JOIN :

var setA = context.SetA;
var setB = context.SetB.Select(st=>st.Id).Distinct().ToList();
var leftOuter  = setA.Where(stA=> !setB.Contains(stA.Id)); 

notes :

  • Pour améliorer les performances, SetB peut être converti en dictionnaire (si cela est fait, vous devez le modifier :! SetB.Contains (stA.Id) ) ou un HashSet
  • Lorsque plusieurs champs sont impliqués, cela peut être réalisé à l'aide d' opérations Set et d'une classe qui implémentent: IEqualityComparer

Une jointure externe gauche renverrait la correspondance setAet setBla réponse.
NetMage
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.