TRÁI PHIẾU THAM GIA tham gia LINQ


539

Làm thế nào để thực hiện nối ngoài trái trong C # LINQ tới các đối tượng mà không cần sử dụng join-on-equals-intomệnh đề? Có cách nào để làm điều đó với wheremệnh đề? Vấn đề chính xác: Đối với tham gia bên trong là dễ dàng và tôi có một giải pháp như thế này

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

nhưng đối với tham gia bên ngoài bên trái tôi cần một giải pháp. Của tôi là một cái gì đó như thế này nhưng nó không hoạt động

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

Trong đó JoinPair là một lớp:

public class JoinPair { long leftId; long rightId; }

2
bạn có thể đưa ra một ví dụ về những gì bạn đang cố gắng để đạt được?
jeroenh

tham gia ngoài trái bình thường là một cái gì đó như thế này: var a = from b in bb tham gia c trong cc trên b.bbbbb bằng c.ccccc vào dd từ d trong dd.Default IfEmpty () chọn b.sss; Câu hỏi của tôi là có cách nào để thực hiện điều đó bằng cách sử dụng các mệnh đề liên kết tương tự như var a = from b in bb từ c trong cc trong đó b.bbb == c.cccc ... và cứ thế .. .
Đồ chơi

1
chắc chắn là có, nhưng bạn nên đăng một ví dụ về mã của bạn mà bạn đã có để mọi người có thể cho bạn câu trả lời tốt hơn
sloth

Tôi đang tìm kiếm một "trái trừ " THAM GIA (và tôi nhầm lẫn nó với khái niệm "NGOÀI"). Câu trả lời này gần với những gì tôi muốn.
Hạt đậu đỏ

Câu trả lời:


598

Như đã nêu trên:

101 mẫu LINQ - Tham gia bên ngoài bên trái

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
Tôi đang thử điều tương tự nhưng gặp lỗi trên toán tử nối, thông báo "Loại của một trong các biểu thức trong mệnh đề nối là không chính xác."
Badhon Jain

3
@jain nếu các loại của bạn khác nhau, tham gia sẽ không hoạt động. Vì vậy, có khả năng các khóa của bạn là của các kiểu dữ liệu khác nhau. Có phải cả hai khóa int chẳng hạn?
Yooakim

2
Giải pháp Jain là gì? Tôi cũng đang đối mặt với cùng một lỗi và các loại cũng giống nhau trong trường hợp của tôi.
Sandeep

1
@Sandeep kiểm tra chìa khóa của bạn, nơi bạn đã tham gia nó. Giả sử nếu đó là các kiểu chuỗi và int thì chỉ cần chuyển đổi khóa chuỗi thành int.
Ankit

2
liên kết được cập nhật: 101 mẫu LINQ - Tham gia bên ngoài bên trái
BukeMan

546

Nếu nhà cung cấp LINQ điều khiển cơ sở dữ liệu được sử dụng, một kết nối bên ngoài bên trái dễ đọc hơn đáng kể có thể được viết như sau:

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

Nếu bạn bỏ qua, DefaultIfEmpty()bạn sẽ có một tham gia bên trong.

Lấy câu trả lời được chấp nhận:

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

Cú pháp này rất khó hiểu và không rõ nó hoạt động như thế nào khi bạn muốn rời khỏi tham gia bảng MULTIPLE.

Lưu ý
Cần lưu ý rằng from alias in Repo.whatever.Where(condition).DefaultIfEmpty()nó giống như một ứng dụng bên ngoài / bên trái tham gia bên, mà bất kỳ trình tối ưu hóa cơ sở dữ liệu (đàng hoàng) nào cũng hoàn toàn có khả năng chuyển thành một liên kết bên trái, miễn là bạn không giới thiệu mỗi hàng -giá trị (còn gọi là áp dụng bên ngoài thực tế). Đừng làm điều này trong Linq-2-Object (vì không có trình tối ưu hóa DB khi bạn sử dụng Linq-to-Object).

Ví dụ chi tiết

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

Khi được sử dụng với SQL LINQ 2, nó sẽ dịch độc đáo sang truy vấn SQL rất dễ đọc sau đây:

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

Biên tập:

Xem thêm " Chuyển đổi truy vấn SQL Server sang truy vấn Linq " để biết ví dụ phức tạp hơn.

Ngoài ra, nếu bạn đang thực hiện nó trong Linq-2-Object (thay vì Linq-2-SQL), bạn nên thực hiện theo cách cũ (vì LINQ sang SQL dịch chính xác để tham gia các hoạt động, nhưng qua các đối tượng phương thức này buộc phải quét toàn bộ và không tận dụng các tìm kiếm chỉ mục, tại sao ...):

    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
Câu trả lời này thực sự hữu ích. Cảm ơn bạn đã thực sự cung cấp cú pháp đó là dễ hiểu.
Chris Marisic

3
WTB một truy vấn LINQ tương thích NHibernate ... :)
mxmissile

30
LINQ to SQL dịch chính xác điều này để tham gia các hoạt động. Tuy nhiên, đối với các đối tượng, phương thức này buộc phải quét toàn bộ, Đây là lý do tại sao tài liệu chính thức cung cấp giải pháp tham gia nhóm có thể tận dụng băm để tìm kiếm chỉ mục.
Tamir Daniely

3
Tôi nghĩ rằng cú pháp rõ ràng joindễ đọc và rõ ràng hơn nhiều so với wheretiếp theoDefaultIfEmpty
FindOut_Quran

1
@ user3441905: Miễn là bạn chỉ cần tham gia bảng a với bảng b, điều này có thể. Nhưng ngay khi bạn có nhiều hơn thế, nó sẽ không như vậy. Nhưng ngay cả chỉ với 2 bàn, tôi nghĩ nó quá dài dòng. Ý kiến ​​phổ biến dường như cũng chống lại bạn, vì câu trả lời này bắt đầu bằng 0 khi câu trả lời hàng đầu đã có hơn 90 lượt upvote.
Stefan Steiger

132

Sử dụng biểu thức 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
Cả Tham gia và GroupJoin không thực sự hỗ trợ tham gia trái. Thủ thuật khi sử dụng GroupJoin là bạn có thể có các nhóm trống và sau đó dịch các nhóm trống đó thành các giá trị trống. Default IfEmpty chỉ đơn giản làm điều đó, có nghĩa là Enumerable.Empty<Product>.DefaultIfEmpty()sẽ trả về một IEnumerable với một giá trị duy nhất là default(Product).
Tamir Daniely

61
Tất cả điều này để thực hiện một tham gia trái ??
FindOut_Quran

7
Cám ơn vì cái này! Không có quá nhiều ví dụ biểu hiện lambda ngoài kia, điều này làm việc cho tôi.
Johan Henkens

1
Cảm ơn câu trả lời. Nó mang lại điều gần gũi nhất với SQL LEFT OUTER THAM GIA mà tôi đã viết trong nhiều năm qua
John Gathogo

1
Không thực sự cần Chọn () cuối cùng, anon obj trong ChọnMany () có thể được cấu trúc lại cho cùng một đầu ra. Một suy nghĩ khác là kiểm tra y cho null để mô phỏng sự tương đương LEFT THAM GIA gần hơn.
Denny Jacob

46

Bây giờ là một phương thức mở rộng:

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

Sử dụng như bạn thường sử dụng tham gia:

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

Hy vọng điều này sẽ giúp bạn tiết kiệm thời gian.


44

Hãy xem ví dụ này . Truy vấn này sẽ hoạt động:

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
Có thể rđược truy cập trong mệnh đề chọn sau khi sử dụng phép nối?
Farhad Alizadeh Noori

@FarhadAlizadehNoori Có.
Po-ta-toe

Tác giả có lẽ có nghĩa là sử dụng lại rtrong frommệnh đề thứ hai . tức là from r in lrs.DefaultIfEmpty()nếu không, truy vấn này không có ý nghĩa nhiều và thậm chí có thể không được biên dịch do rnằm ngoài ngữ cảnh cho lựa chọn.
Saeb Amini

@Devart, khi tôi đọc truy vấn của bạn, nó làm tôi nhớ đến bộ phim Clockwisevới John Cleese, lol.
Matas Vaitkevicius

1
Từ trái sang phải sang trái trong các quyền ở bên trái ở bên trái ... Ôi trời ơi ... Cú pháp sử dụng LEFT OUTER THAM GIA trong LINQ thực sự không rõ ràng, nhưng những cái tên này thực sự không rõ ràng hơn.
Mike Gledhill

19

Việc thực hiện nối ngoài bên trái bằng các phương thức mở rộng có thể trông giống như

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

Kết quả sau đó phải chăm sóc các phần tử 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
Tuy nhiên, đây chỉ là một tùy chọn cho LINQ cho các đối tượng và sẽ không thể dịch truy vấn sang bất kỳ nhà cung cấp truy vấn nào, đây là trường hợp sử dụng phổ biến nhất cho thao tác này.
Phục vụ

13
Nhưng câu hỏi là "Làm thế nào để thực hiện tham gia bên ngoài bên trái trong C # LINQ tới các đối tượng ..."
Bertrand

12

hãy xem ví dụ này

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:

bây giờ bạn có thể include elements from the leftngay cả khi yếu tố đó has no matches in the right, trong trường hợp của chúng tôi, chúng tôi đã gỡ bỏArlene ngay cả khi anh ta không có quyền phù hợp

đây là tài liệu tham khảo

Cách thực hiện: Thực hiện các liên kết ngoài trái (Hướng dẫn lập trình C #)


đầu ra phải là: Arlene: Không tồn tại
user1169587 29/12/18

10

Đây là hình thức chung (như đã được cung cấp trong các câu trả lời khác)

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

Tuy nhiên đây là một lời giải thích mà tôi hy vọng sẽ làm rõ điều này thực sự có nghĩa là gì!

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

về cơ bản tạo ra một tập kết quả b_temp riêng biệt có hiệu quả bao gồm null 'hàng' cho các mục ở phía bên tay phải (các mục trong 'b').

Sau đó, dòng tiếp theo:

from b_value in b_temp.DefaultIfEmpty()

.. xác định trên tập kết quả đó, đặt giá trị null mặc định cho 'hàng' ở phía bên phải và đặt kết quả của hàng bên phải kết hợp với giá trị của 'b_value' (nghĩa là giá trị ở bên phải bên tay, nếu có một bản ghi phù hợp, hoặc 'null' nếu không có).

Bây giờ, nếu phía bên tay phải là kết quả của một truy vấn LINQ riêng biệt, thì nó sẽ bao gồm các loại ẩn danh, chỉ có thể là 'một cái gì đó' hoặc 'null'. Tuy nhiên, nếu đó là một liệt kê (ví dụ: Danh sách - trong đó MyObjectB là một lớp có 2 trường), thì có thể cụ thể về giá trị 'null' mặc định nào được sử dụng cho các thuộc tính của nó:

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

Điều này đảm bảo rằng bản thân 'b' không phải là null (nhưng các thuộc tính của nó có thể là null, sử dụng các giá trị null mặc định mà bạn đã chỉ định) và điều này cho phép bạn kiểm tra các thuộc tính của b_value mà không nhận được ngoại lệ tham chiếu null cho b_value. Lưu ý rằng đối với DateTime nullable, một loại (DateTime?) Tức là 'DateTime nullable' phải được chỉ định là 'Loại' của null trong đặc tả cho 'Default IfEmpty' (điều này cũng sẽ áp dụng cho các loại không phải là 'nguyên bản' 'nullable, ví dụ: double, float).

Bạn có thể thực hiện nhiều phép nối ngoài bên trái bằng cách xâu chuỗi cú pháp trên.


1
b_value đến từ đâu?
Jack Fraser

9

Đây là một ví dụ nếu bạn cần tham gia nhiều hơn 2 bảng:

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

Tham chiếu: https://stackoverflow.com/a/17142392/2343


4

Phương thức mở rộng hoạt động như tham gia trái với cú pháp Tham gia

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

chỉ cần viết nó trong .NET core và nó dường như đang hoạt động như mong đợi.

Bài kiểm tra nhỏ:

        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

Đây là một phiên bản khá dễ hiểu bằng cách sử dụng cú pháp phương thức:

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

Có ba bảng: người, trường học và người_số, kết nối người với trường họ học. Một tài liệu tham khảo cho người có id = 6 không có trong bảng person_schools. Tuy nhiên, người có id = 6 được trình bày trong lưới kết quả lef.

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

Trong khi đây có thể là câu trả lời của câu hỏi cung cấp một số giải thích về câu trả lời của bạn :)
Amir

2

Đây là một cú pháp SQL so với cú pháp LINQ cho các phép nối bên trong và bên trái. Bên trái tham gia:

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

"Ví dụ sau đây là một nhóm tham gia giữa sản phẩm và danh mục. Đây thực chất là liên kết bên trái. Biểu thức trả về dữ liệu ngay cả khi bảng danh mục trống. Để truy cập các thuộc tính của bảng danh mục, bây giờ chúng ta phải chọn từ kết quả có thể đếm được bằng cách thêm từ cl trong câu lệnh catList.Default IfEmpty ().


1

Thực hiện các phép nối ngoài trái trong linq C # // Thực hiện các phép nối ngoài trái

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

Tôi muốn nói thêm rằng nếu bạn nhận được tiện ích mở rộng MoreLinq thì hiện tại đã có hỗ trợ cho cả hai bên đồng nhất và không đồng nhất.

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

thí dụ:

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

BIÊN TẬP:

Nhìn lại, điều này có thể hoạt động, nhưng nó chuyển đổi IQueryable thành IEnumerable vì morelinq không chuyển đổi truy vấn thành SQL.

Thay vào đó, bạn có thể sử dụng GroupJoin như được mô tả tại đây: https://stackoverflow.com/a/24273804/4251433

Điều này sẽ đảm bảo rằng nó vẫn là một IQueryable trong trường hợp bạn cần thực hiện các hoạt động logic tiếp theo về nó sau này.


1

Cách dễ dàng là sử dụng từ khóa Let. Điều này làm việc cho tôi.

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
}

Đây là một mô phỏng của Left Join. Nếu mỗi mục trong bảng B không khớp với mục A, BItem trả về null


0

Nếu bạn cần tham gia và lọc trên một cái gì đó, điều đó có thể được thực hiện bên ngoài tham gia. Bộ lọc có thể được thực hiện sau khi tạo bộ sưu tập.

Trong trường hợp này nếu tôi làm điều này trong điều kiện nối tôi sẽ giảm các hàng được trả về.

Điều kiện ternary được sử dụng (= n == null ? "__" : n.MonDayNote,)

  • Nếu đối tượng là null(vì vậy không khớp), sau đó trả về những gì sau ?. __, trong trường hợp này.

  • Khác, trả lại những gì sau :, n.MonDayNote.

Cảm ơn những người đóng góp khác là nơi tôi bắt đầu với vấn đề của riêng mình.


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

ĐẦU RA


0

Theo câu trả lời của tôi cho một câu hỏi tương tự, ở đây:

Linq to SQL bên trái tham gia bên ngoài bằng cú pháp Lambda và tham gia trên 2 cột (khóa kết hợp tổng hợp)

Lấy mã ở đây , hoặc sao chép repo github của tôi và chơi!

Truy vấn:

        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

Tổng quan: Trong đoạn mã này, tôi trình bày cách nhóm theo ID trong đó Bảng 1 và Bảng 2 có mối quan hệ một đến nhiều. Tôi nhóm trên Id, Field1 và Field2. Truy vấn con là hữu ích, nếu cần phải tra cứu Bảng thứ ba và nó sẽ yêu cầu mối quan hệ tham gia trái. Tôi hiển thị một nhóm tham gia trái và một linq truy vấn phụ. Kết quả tương đương.

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

Giải pháp đơn giản cho LEFT OUTER THAM GIA :

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

ghi chú :

  • Để cải thiện hiệu suất, SetB có thể được chuyển đổi thành Từ điển (nếu điều đó được thực hiện thì bạn phải thay đổi điều này : ! SetB.Contains (stA.Id) ) hoặc a HashSet
  • Khi có nhiều hơn một trường liên quan, điều này có thể đạt được bằng cách sử dụng các thao tác Set và một lớp thực hiện: IEqualityComparer

Một kết nối bên ngoài bên trái sẽ trả lại kết hợp setAsetBtrong câu trả lời.
NetMage
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.