LEFT OUTER BERGABUNG di LINQ

539

Bagaimana cara melakukan outer outer join di C # LINQ ke objek tanpa menggunakan join-on-equals-intoklausa? Apakah ada cara untuk melakukannya dengan whereklausa? Masalah yang benar: Untuk join dalam, mudah dan saya punya solusi seperti ini

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

tapi untuk sambungan luar kiri saya butuh solusi. Milik saya adalah sesuatu seperti ini tetapi tidak berfungsi

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

di mana JoinPair adalah kelas:

public class JoinPair { long leftId; long rightId; }
Mainan
sumber
2
dapatkah Anda memberikan contoh tentang apa yang ingin Anda capai?
jeroenh
normal join luar kiri adalah sesuatu seperti ini: var a = dari b di bb bergabung c di cc di b.bbbbb sama dengan c.ccccc ke dd dari d di dd.DefaultIfEmpty () pilih b.sss; Pertanyaan saya, adakah cara untuk melakukannya dengan menggunakan klausa join-on-equals-into seperti var a = dari b di bb dari c di cc di mana b.bbb == c.cccc ... dan seterusnya .. .
Toy
1
yakin ada, tetapi Anda harus memposting contoh kode Anda yang sudah Anda miliki sehingga orang dapat memberi Anda jawaban yang lebih baik
sloth
Saya mencari "Kiri tidak termasuk " GABUNG (dan saya bingung dengan konsep "OUTER"). Jawaban ini lebih dekat dengan apa yang saya inginkan.
The Red Pea

Jawaban:

598

Sebagaimana dinyatakan pada:

101 Sampel LINQ - Gabung luar kiri

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 };
ajay_whiz
sumber
7
Saya mencoba hal yang sama tetapi mendapatkan kesalahan pada operator gabungan, yang mengatakan "Jenis salah satu ekspresi dalam klausa gabungan tidak benar."
Badhon Jain
3
@jain jika tipe Anda berbeda, join tidak akan berfungsi. Jadi, kemungkinan kunci Anda dari tipe data yang berbeda. Apakah kedua kunci int misalnya?
Yooakim
2
Apa solusinya Jain? Saya juga menghadapi kesalahan yang sama dan tipenya sama dalam kasus saya.
Sandeep
1
@ Tetap periksa kunci Anda di tempat Anda bergabung. Misalkan jika itu adalah tipe string dan int maka cukup konversikan kunci string ke int.
Ankit
2
tautan yang diperbarui: 101 Sampel LINQ - Gabung luar kiri
BukeMan
546

Jika penyedia LINQ yang digerakkan oleh basis data digunakan, gabungan luar kiri yang jauh lebih mudah dibaca dapat ditulis seperti itu:

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

Jika Anda menghilangkan DefaultIfEmpty() Anda akan memiliki gabung dalam.

Ambil jawaban yang diterima:

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

Sintaks ini sangat membingungkan, dan tidak jelas cara kerjanya ketika Anda ingin bergabung dengan tabel MULTIPLE.

Catatan
Perlu dicatat bahwafrom alias in Repo.whatever.Where(condition).DefaultIfEmpty() ini sama dengan outer-apply / left-join-lateral, yang mana setiap pengoptimal-basis data (yang layak) mampu menerjemahkan ke dalam join kiri, selama Anda tidak memperkenalkan per-baris -nilai (alias terluar yang sebenarnya berlaku). Jangan lakukan ini di Linq-2-Objects (karena tidak ada DB-optimizer ketika Anda menggunakan Linq-to-Objects).

Contoh terperinci

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

Ketika digunakan dengan LINQ 2 SQL, ia akan menerjemahkan dengan baik ke kueri SQL yang sangat mudah dibaca berikut ini:

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

Edit:

Lihat juga " Konversi permintaan SQL Server ke permintaan Linq " untuk contoh yang lebih kompleks.

Juga, Jika Anda melakukannya di Linq-2-Objects (bukan Linq-2-SQL), Anda harus melakukannya dengan cara kuno (karena LINQ ke SQL menerjemahkan ini dengan benar untuk bergabung dengan operasi, tetapi lebih dari objek metode ini memaksakan pemindaian penuh, dan tidak mengambil keuntungan dari pencarian indeks, bagaimanapun ...):

    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
    }

);
Stefan Steiger
sumber
21
Jawaban ini sebenarnya sangat membantu. Terima kasih telah benar-benar menawarkan sintaks yang dapat dimengerti.
Chris Marisic
3
WTB permintaan LINQ NHibernate yang kompatibel ... :)
mxmissile
30
LINQ ke SQL menerjemahkan ini dengan benar untuk bergabung dengan operasi. Namun atas obyek metode ini memaksa pemindaian penuh, Inilah sebabnya dokumentasi resmi menawarkan solusi gabungan grup yang dapat memanfaatkan hash untuk mengindeks pencarian.
Tamir Daniely
3
Saya pikir sintaks eksplisit joinjauh lebih mudah dibaca dan jelas daripada wherediikuti olehDefaultIfEmpty
FindOut_Quran
1
@ user3441905: Selama Anda hanya perlu bergabung dengan tabel a dengan tabel b, ini mungkin saja. Tetapi begitu Anda memiliki lebih dari itu, itu tidak akan terjadi. Tetapi bahkan untuk hanya 2 meja, saya pikir itu terlalu verbal. Pendapat populer juga tampaknya menentang Anda, karena jawaban ini dimulai dengan 0 ketika jawaban teratas sudah memiliki 90+ ​​upvotes.
Stefan Steiger
132

Menggunakan ekspresi 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   
  });
N Rocking
sumber
8
Baik Bergabung dan GroupJoin tidak benar-benar mendukung bergabung-kiri. Trik dengan menggunakan GroupJoin adalah bahwa Anda dapat memiliki grup kosong dan kemudian menerjemahkan grup kosong itu menjadi nilai kosong. DefaultIfEmpty hanya melakukan itu, artinya Enumerable.Empty<Product>.DefaultIfEmpty()akan mengembalikan IEnumerable dengan nilai tunggal default(Product).
Tamir Daniely
61
Semua ini untuk melakukan join kiri ??
FindOut_Quran
7
Terima kasih untuk ini! Tidak terlalu banyak contoh ekspresi lambda di luar sana, ini berhasil untuk saya.
Johan Henkens
1
Terima kasih atas jawabannya. Ini menghasilkan hal yang paling dekat dengan SQL LEFT OUTER mentah. Saya telah menulis selama bertahun-tahun
John Gathogo
1
Tidak benar-benar membutuhkan Select () terakhir, objek anon di SelectMany () dapat di refactored untuk output yang sama. Pemikiran lain adalah menguji y untuk null untuk mensimulasikan kesetaraan LEFT JOIN yang lebih dekat.
Denny Jacob
46

Sekarang sebagai metode ekstensi:

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

Gunakan seperti biasanya Anda gunakan bergabung:

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

Semoga ini menghemat waktu Anda.

Matas Vaitkevicius
sumber
44

Lihatlah contoh ini . Kueri ini seharusnya berfungsi:

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 };
Devart
sumber
3
Dapat rdiakses di klausa pilih setelah menggunakan gabungan ke?
Farhad Alizadeh Noori
@ FarhadAlizadehNoori Ya Itu bisa.
Po-ta-toe
Penulis mungkin bermaksud untuk menggunakan kembali rpada fromklausa kedua . mis. from r in lrs.DefaultIfEmpty()Kalau tidak, kueri ini tidak masuk akal dan bahkan mungkin tidak dikompilasi karena di rluar konteks untuk pemilihan.
Saeb Amini
@Devart, ketika saya membaca permintaan Anda, itu mengingatkan saya pada film Clockwisedengan John Cleese, lol.
Matas Vaitkevicius
1
Dari kiri ke kanan ke kanan ke kanan di kanan di kiri di kiri kanan ... Oh ya ampun ... Sintaks menggunakan LEFT OUTER BERGABUNG di LINQ benar - benar tidak jelas, tetapi nama-nama ini benar-benar membuatnya semakin tidak jelas.
Mike Gledhill
19

Implementasi gabungan luar kiri dengan metode ekstensi dapat terlihat seperti

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

Selektor hasil kemudian harus mengurus elemen nol. 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));
   }
Bertrand
sumber
4
Namun, ini hanya opsi untuk LINQ ke objek, dan tidak akan dapat menerjemahkan kueri ke penyedia kueri mana pun, yang merupakan kasus penggunaan paling umum untuk operasi ini.
Servy
13
Tetapi pertanyaannya adalah "Bagaimana melakukan bagian luar kiri bergabung dalam C # LINQ ke objek ..."
Bertrand
12

lihat contoh ini

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:

sekarang Anda dapat include elements from the leftbahkan jika elemen itu has no matches in the right, dalam kasus kami kami mengambil kembali Arlenebahkan dia tidak cocok di sebelah kanan

di sini adalah referensi

Cara: Melakukan Penggabungan Luar Kiri (Panduan Pemrograman C #)

Basheer AL-MOMANI
sumber
output harus: Arlene: Tidak Ada
user1169587
10

Ini adalah bentuk umum (sebagaimana telah disediakan dalam jawaban lain)

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

Namun inilah penjelasan yang saya harap akan menjelaskan apa arti sebenarnya ini!

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

pada dasarnya menciptakan set hasil yang terpisah b_temp yang secara efektif menyertakan null 'rows' untuk entri di sisi kanan (entri di 'b').

Kemudian baris selanjutnya:

from b_value in b_temp.DefaultIfEmpty()

..iterates atas hasil yang ditetapkan, mengatur nilai nol default untuk 'baris' di sisi kanan, dan mengatur hasil dari baris sisi kanan bergabung dengan nilai 'b_value' (yaitu nilai yang ada di kanan di sisi lain, jika ada catatan yang cocok, atau 'nol' jika tidak ada).

Sekarang, jika sisi kanan adalah hasil dari permintaan LINQ yang terpisah, itu akan terdiri dari jenis anonim, yang hanya bisa berupa 'sesuatu' atau 'nol'. Namun, jika enumerable (mis. Daftar - di mana MyObjectB adalah kelas dengan 2 bidang), maka dimungkinkan untuk spesifik tentang nilai default 'null' yang digunakan untuk propertinya:

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

Ini memastikan bahwa 'b' itu sendiri bukan nol (tetapi propertinya bisa nol, menggunakan nilai null default yang telah Anda tentukan), dan ini memungkinkan Anda untuk memeriksa properti b_value tanpa mendapatkan pengecualian referensi nol untuk b_value. Perhatikan bahwa untuk DateTime yang dapat dibatalkan, jenis (DateTime?) Yaitu 'nullable DateTime' harus ditentukan sebagai 'Jenis' dari nol dalam spesifikasi untuk 'DefaultIfEmpty' (ini juga akan berlaku untuk jenis yang tidak secara asli 'nullable mis. double, float).

Anda dapat melakukan beberapa gabungan luar kiri hanya dengan merantai sintaks di atas.

Chris Halcrow
sumber
1
dari mana b_value berasal?
Jack Fraser
9

Berikut ini contoh jika Anda perlu bergabung lebih dari 2 tabel:

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

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

Sameer Alibhai
sumber
4

Metode ekstensi yang berfungsi seperti gabung kiri dengan Gabung sintaks

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

hanya menulisnya di .NET core dan tampaknya berfungsi seperti yang diharapkan.

Tes kecil:

        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)}
Bezio
sumber
4

Berikut ini adalah versi yang cukup mudah dimengerti menggunakan sintaksis metode:

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 }));
Tim Pohlmann
sumber
3

Ada tiga tabel: orang, sekolah, dan person_schools, yang menghubungkan orang-orang dengan sekolah tempat mereka belajar. Referensi ke orang dengan id = 6 tidak ada dalam tabel persons_schools. Namun orang dengan id = 6 disajikan dalam hasil lef-join grid.

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);
}
Alex
sumber
Meskipun ini mungkin jawaban dari pertanyaan, berikan penjelasan tentang jawaban Anda :)
Amir
2

Ini adalah sintaks SQL dibandingkan dengan sintaks LINQ untuk gabungan luar dalam dan kiri. Kiri Luar Gabung:

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

"Contoh berikut ini grup bergabung antara produk dan kategori. Ini pada dasarnya adalah gabungan kiri. Ekspresi ke dalam mengembalikan data bahkan jika tabel kategori kosong. Untuk mengakses properti dari tabel kategori, kita sekarang harus memilih dari hasil yang dapat dihitung dengan menambahkan pernyataan dari cl di catList.DefaultIfEmpty ().

ozkary
sumber
1

Lakukan sambungan luar kiri di linq C # // Lakukan sambungan luar kiri

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/

Hamid
sumber
1

Saya ingin menambahkan bahwa jika Anda mendapatkan ekstensi MoreLinq sekarang ada dukungan untuk gabungan kiri yang homogen dan heterogen sekarang

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

contoh:

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

Dalam retrospeksi ini mungkin berhasil, tetapi itu mengubah IQueryable ke IEnumerable sebagai morelinq tidak mengubah kueri ke SQL.

Anda dapat menggunakan GroupJoin sebagai gantinya diuraikan di sini: https://stackoverflow.com/a/24273804/4251433

Ini akan memastikan bahwa ia tetap sebagai IQueryable jika Anda perlu melakukan operasi logis lebih lanjut di kemudian hari.

Reese De Wind
sumber
1

Cara mudah adalah dengan menggunakan kata kunci Biarkan. Ini bekerja untuk saya.

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
}

Ini adalah simulasi Left Join. Jika setiap item dalam tabel B tidak cocok dengan item, BItem mengembalikan nol

mahdi moghimi
sumber
0

Jika Anda perlu bergabung dan memfilter sesuatu, itu bisa dilakukan di luar gabung. Filter dapat dilakukan setelah membuat koleksi.

Dalam hal ini jika saya melakukan ini dalam kondisi bergabung saya mengurangi baris yang dikembalikan.

Kondisi ternary digunakan (= n == null ? "__" : n.MonDayNote,)

  • Jika objeknya null(jadi tidak ada yang cocok), maka kembalikan apa yang ada setelah ?. __, pada kasus ini.

  • Lain, kembali apa setelah :, n.MonDayNote.

Berkat kontributor lain di situlah saya mulai dengan masalah saya sendiri.


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

KELUARAN

Manikandan Deivasigamani
sumber
0

Sesuai jawaban saya untuk pertanyaan serupa, di sini:

Linq ke SQL kiri luar bergabung menggunakan sintaks Lambda dan bergabung pada 2 kolom (kunci gabungan bergabung)

Dapatkan kodenya di sini , atau tiru repo github saya , dan mainkan!

Pertanyaan:

        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,
            });
Adam Cox
sumber
0

Ikhtisar: Dalam cuplikan kode ini, saya mendemonstrasikan cara mengelompokkan berdasarkan ID di mana Table1 dan Table2 memiliki hubungan satu ke banyak. Saya grup di Id, Field1, dan Field2. Subquery sangat membantu, jika pencarian Tabel ketiga diperlukan dan itu akan membutuhkan hubungan join kiri. Saya menunjukkan pengelompokan bergabung kiri dan linq subquery. Hasilnya setara.

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>();
Singa Emas
sumber
-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

     });
saktiprasad swain
sumber
-1

Solusi sederhana untuk LEFT OUTER BERGABUNG :

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

catatan :

  • Untuk meningkatkan kinerja, SetB dapat dikonversi ke Kamus (jika itu dilakukan maka Anda harus mengubah ini :! SetB.Contains (stA.Id) ) atau HashSet
  • Ketika ada lebih dari satu bidang yang terlibat ini bisa dicapai dengan menggunakan operasi Set dan kelas yang mengimplementasikan: IEqualityComparer
Enrique Ramos
sumber
Gabung luar kiri akan mengembalikan kecocokan setAdan setBdalam jawabannya.
NetMage