SINISTRA ESTERNO UNISCITI a LINQ


539

Come eseguire l'unione esterna sinistra in C # LINQ agli oggetti senza usare le join-on-equals-intoclausole? C'è un modo per farlo con la whereclausola? Problema corretto: il join interno è semplice e ho una soluzione come questa

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

ma per il join esterno sinistro ho bisogno di una soluzione. Il mio è qualcosa del genere ma non funziona

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

dove JoinPair è una classe:

public class JoinPair { long leftId; long rightId; }

2
puoi fare un esempio di ciò che stai cercando di ottenere?
jeroenh,

il join esterno sinistro normale è qualcosa del genere: var a = da b in bb join c in cc su b.bbbbb è uguale a c.ccccc in dd da d in dd.DefaultIfEmpty () selezionare b.sss; La mia domanda è che esiste un modo per farlo con le clausole join-on-equal-into qualcosa come questo var a = da b in bb da c in cc dove b.bbb == c.cccc ... e così via ... .
Toy

1
certo che c'è, ma dovresti pubblicare un esempio del tuo codice che hai già in modo che le persone possano darti una risposta migliore
bradipo

Stavo cercando un JOIN " Escludendo la sinistra " (e l'ho confuso con il concetto di "ESTERNO"). Questa risposta era più vicina a ciò che volevo.
The Red Pea,

Risposte:


598

Come indicato su:

101 campioni LINQ - Join esterno sinistro

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
Sto provando la stessa cosa ma ricevo un errore sull'operatore di join, che dice "Il tipo di una delle espressioni nella clausola di join non è corretto".
Badhon Jain,

3
@jain se i tuoi tipi sono diversi, il join non funzionerà. Quindi probabilmente le tue chiavi sono di tipi di dati diversi. Entrambe le chiavi sono int per esempio?
Yooakim,

2
Qual è la soluzione Jain? Sto anche affrontando lo stesso errore e anche i tipi sono gli stessi nel mio caso.
Sandeep,

1
@Sandeep controlla le tue chiavi dove ti sei unito. Supponiamo che quelli siano di tipo stringa e int, quindi converti la chiave stringa in int.
Ankit,

2
collegamento aggiornato: 101 esempi LINQ
Join

546

Se viene utilizzato un provider LINQ basato su database, un join esterno sinistro significativamente più leggibile può essere scritto come tale:

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

Se si omette, DefaultIfEmpty()si avrà un join interno.

Prendi la risposta accettata:

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

Questa sintassi è molto confusa e non è chiaro come funzioni quando si desidera abbandonare le tabelle MULTIPLE.

Nota
Va notato che from alias in Repo.whatever.Where(condition).DefaultIfEmpty()è lo stesso di un outer-apply / left-join-side, che qualsiasi ottimizzatore di database (decente) è perfettamente in grado di tradurre in un join sinistro, purché non si introduca per riga -values ​​(aka un vero esterno applicato). Non farlo in Linq-2-Objects (perché non c'è alcun ottimizzatore DB quando si utilizza Linq-to-Objects).

Esempio dettagliato

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

Se utilizzato con LINQ 2 SQL, si tradurrà perfettamente nella seguente query SQL molto leggibile:

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

Modificare:

Vedere anche " Converti query SQL Server in query Linq " per un esempio più complesso.

Inoltre, se lo stai facendo in Linq-2-Objects (invece di Linq-2-SQL), dovresti farlo alla vecchia maniera (perché LINQ in SQL lo traduce correttamente per unire le operazioni, ma su oggetti questo metodo impone una scansione completa e non sfrutta le ricerche dell'indice, tuttavia ...):

    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
Questa risposta è effettivamente utile. Grazie per aver effettivamente offerto una sintassi comprensibile.
Chris Marisic,

3
Compro una query LINQ compatibile con NHibernate ... :)
mxmissile,

30
LINQ in SQL lo traduce correttamente per unire le operazioni. Negli oggetti, tuttavia, questo metodo forza una scansione completa, ecco perché la documentazione ufficiale offre la soluzione di join di gruppo che può sfruttare gli hash per l'indicizzazione delle ricerche.
Tamir Daniely,

3
Penso che la sintassi di esplicito joinsia molto più leggibile e chiara di un whereseguito daDefaultIfEmpty
FindOut_Quran

1
@ user3441905: Finché devi solo unire la tabella a alla tabella b, questo può essere. Ma non appena avrai più di quello, non lo sarà. Ma anche solo per 2 tavoli, penso che sia eccessivamente verbo. Anche l'opinione popolare sembra essere contro di te, poiché questa risposta è iniziata con 0 quando la risposta più alta aveva già più di 90 voti.
Stefan Steiger,

132

Usando l'espressione 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
Sia Join che GroupJoin non supportano realmente il join sinistro. Il trucco con l'utilizzo di GroupJoin è che puoi avere gruppi vuoti e quindi tradurre quei gruppi vuoti in valori vuoti. DefaultIfEmpty lo fa semplicemente, il che significa Enumerable.Empty<Product>.DefaultIfEmpty()che restituirà un IEnumerable con un singolo valore di default(Product).
Tamir Daniely,

61
Tutto questo per eseguire un join sinistro ??
FindOut_Quran,

7
Grazie per questo! Non ci sono troppi esempi di espressioni lambda là fuori, questo ha funzionato per me.
Johan Henkens,

1
Grazie per la risposta. Ha prodotto la cosa più vicina al SQL grezzo LEFT OUTER JOIN che ho scritto negli anni
John Gathogo,

1
Non è necessario l'ultimo Select (), anon obj in SelectMany () può essere refactored per lo stesso output. Un altro pensiero è testare y per null per simulare una più vicina equivalenza LEFT JOIN.
Denny Jacob,

46

Ora come metodo di estensione:

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

Usa come faresti normalmente con join:

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

Spero che questo ti faccia risparmiare un po 'di tempo.


44

Dai un'occhiata a questo esempio . Questa query dovrebbe funzionare:

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
È possibile raccedere nella clausola select dopo aver utilizzato un join in?
Farhad Alizadeh Noori,

@FarhadAlizadehNoori Sì. Può.
Po-ta-toe,

L'autore probabilmente intendeva riutilizzarlo rnella seconda fromclausola. cioè from r in lrs.DefaultIfEmpty()Altrimenti questa query non ha molto senso e probabilmente non si compila nemmeno perché rè fuori contesto per la selezione.
Saeb Amini,

@Devart, quando ho letto la tua domanda mi ha ricordato il film Clockwisecon John Cleese, lol.
Matas Vaitkevicius,

1
Da sinistra a destra in avanzi di diritti a sinistra in leftRights ... Oh jeez ... La sintassi dell'utilizzo di LEFT OUTER JOIN in LINQ non è davvero chiara, ma questi nomi lo rendono ancora più poco chiaro.
Mike Gledhill

19

Potrebbe apparire un'implementazione del join esterno sinistro con metodi di estensione

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

Il selettore dei risultati deve quindi occuparsi degli elementi null. 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
Questa è solo un'opzione per LINQ per gli oggetti e non sarà in grado di tradurre la query in nessun provider di query, che è il caso d'uso più comune per questa operazione.
Servito il

13
Ma la domanda era "Come eseguire il join esterno sinistro in C # LINQ agli oggetti ..."
Bertrand

12

guarda questo esempio

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:

ora puoi farlo include elements from the leftanche se quell'elemento has no matches in the right, nel nostro caso, abbiamo ripreso Arleneanche se non ha corrispondenza nella destra

ecco il riferimento

Procedura: eseguire join esterni sinistro (Guida per programmatori C #)


l'output dovrebbe essere: Arlene: non esiste
user1169587

10

Questo è il modulo generale (come già fornito in altre risposte)

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

Tuttavia, ecco una spiegazione che spero chiarirà cosa significa in realtà!

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

crea essenzialmente un set di risultati separato b_temp che include effettivamente null 'righe' per le voci sul lato destro (voci in 'b').

Quindi la riga successiva:

from b_value in b_temp.DefaultIfEmpty()

..si sposta su quel set di risultati, impostando il valore null predefinito per la "riga" sul lato destro e impostando il risultato della riga sul lato destro unendo il valore di "b_value" (ovvero il valore che si trova sulla destra lato mano, se c'è un record corrispondente, o 'null' se non lo è).

Ora, se il lato destro è il risultato di una query LINQ separata, sarà costituito da tipi anonimi, che possono essere solo "qualcosa" o "null". Se è comunque un elenco (ad esempio un elenco - in cui MyObjectB è una classe con 2 campi), è possibile essere specifici su quali valori 'null' predefiniti vengono utilizzati per le sue proprietà:

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

Questo assicura che la "b" stessa non sia nulla (ma le sue proprietà possono essere nulle, usando i valori null predefiniti che hai specificato), e questo ti permette di controllare le proprietà di b_value senza ottenere un'eccezione di riferimento null per b_value. Si noti che per un DateTime nullable, un tipo di (DateTime?), Ovvero "DateTime nullable", deve essere specificato come "Tipo" del null nella specifica per "DefaultIfEmpty" (ciò si applica anche ai tipi che non sono "nativi" 'nullable es. double, float).

È possibile eseguire più join esterni a sinistra semplicemente concatenando la sintassi sopra.


1
da dove viene b_value?
Jack Fraser,

9

Ecco un esempio se devi unire più di 2 tabelle:

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

Rif: https://stackoverflow.com/a/17142392/2343


4

Metodo di estensione che funziona come join sinistro con sintassi 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()));
    }
}

appena scritto in .NET core e sembra funzionare come previsto.

Piccolo 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

Ecco una versione abbastanza facile da capire usando la sintassi del metodo:

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

Esistono tre tabelle: persone, scuole e persone_scuole, che collegano le persone alle scuole in cui studiano. Un riferimento alla persona con id = 6 è assente nella tabella persone_scuole. Tuttavia, la persona con id = 6 viene presentata nella griglia di risultato unita a sinistra.

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

Anche se questa è forse la risposta alla domanda, fornisci alcune spiegazioni sulla tua risposta :)
Amir,

2

Questa è una sintassi SQL paragonabile alla sintassi LINQ per i join esterni interni ed esterni. Join esterno sinistro:

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

"L'esempio seguente unisce un gruppo tra prodotto e categoria. Questo è essenzialmente il join sinistro. L'espressione into restituisce dati anche se la tabella delle categorie è vuota. Per accedere alle proprietà della tabella delle categorie, dobbiamo ora selezionare dal risultato enumerabile aggiungendo l'istruzione from cl in catList.DefaultIfEmpty ().


1

Esegue i join esterni a sinistra in linq C # // Esegue i join esterni a sinistra

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

Vorrei aggiungere che se si ottiene l'estensione MoreLinq ora esiste il supporto per i join sinistri sia omogenei che eterogenei

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

esempio:

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

MODIFICARE:

In retrospettiva questo può funzionare, ma converte IQueryable in IEnumerable poiché morelinq non converte la query in SQL.

Puoi invece utilizzare un GroupJoin come descritto qui: https://stackoverflow.com/a/24273804/4251433

Ciò garantirà che rimanga come IQueryable nel caso in cui sia necessario eseguire ulteriori operazioni logiche su di esso in un secondo momento.


1

Il modo più semplice è usare la parola chiave Let. Questo funziona per me.

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
}

Questa è una simulazione di Left Join. Se ogni articolo nella tabella B non corrisponde ad un articolo, BItem restituisce null


0

Se devi unirti e filtrare qualcosa, ciò può essere fatto al di fuori del join. Il filtro può essere eseguito dopo aver creato la raccolta.

In questo caso, se lo faccio nella condizione di join, riduco le righe che vengono restituite.

Viene utilizzata la condizione ternaria (= n == null ? "__" : n.MonDayNote,)

  • Se l'oggetto è null(quindi nessuna corrispondenza), restituisce ciò che segue ?. __, in questo caso.

  • Altrimenti, restituire ciò che è dopo il :, n.MonDayNote.

Grazie agli altri collaboratori è qui che ho iniziato con il mio problema.


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

PRODUZIONE


0

Secondo la mia risposta a una domanda simile, qui:

Unione esterna sinistra da Linq a SQL usando la sintassi Lambda e unione su 2 colonne (chiave di unione composita)

Ottieni qui il codice o clona il mio repository github e gioca!

Query:

        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

Panoramica: in questo frammento di codice, dimostrerò come raggruppare per ID in cui Tabella1 e Tabella2 hanno una relazione uno a molti. Raggruppo su Id, Field1 e Field2. La sottoquery è utile, se è richiesta una terza ricerca nella tabella e avrebbe richiesto una relazione di join a sinistra. Mostro un raggruppamento di join di sinistra e un linq di subquery. I risultati sono equivalenti.

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

Soluzione semplice per l' UNIONE ESTERNA SINISTRA :

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

note :

  • Per migliorare le prestazioni, SetB può essere convertito in un dizionario (in tal caso, è necessario modificarlo :! SetB.Contains (stA.Id) ) o un HashSet
  • Quando è coinvolto più di un campo, ciò potrebbe essere ottenuto utilizzando le operazioni Set e una classe che implementa: IEqualityComparer

Un join esterno sinistro restituirebbe la corrispondenza setAe setBnella risposta.
NetMage,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.