Tham gia / Ở đâu với LINQ và Lambda


458

Tôi gặp rắc rối với một truy vấn được viết bằng LINQ và Lambda. Cho đến nay, tôi đang nhận được rất nhiều lỗi ở đây là mã của tôi:

int id = 1;
var query = database.Posts.Join(database.Post_Metas,
                                post => database.Posts.Where(x => x.ID == id),
                                meta => database.Post_Metas.Where(x => x.Post_ID == id),
                                (post, meta) => new { Post = post, Meta = meta });

Tôi mới sử dụng LINQ, vì vậy tôi không chắc liệu truy vấn này có đúng không.


11
Bạn đang cố gắng để thực hiện?
Germán Rodríguez

4
bạn muốn truy vấn để làm gì trong một câu?
thợ săn

6
Selectors chìa khóa của bạn đang cách quá phức tạp. Nếu bạn muốn chọn theo id, chỉ cần x => x.ID là ổn.
Eric Lippert

1
Tôi muốn nhận một bài đăng từ cơ sở dữ liệu và dữ liệu meta cho bài đăng đó.
David

Câu trả lời:


1057

Tôi thấy rằng nếu bạn quen thuộc với cú pháp SQL, sử dụng cú pháp truy vấn LINQ sẽ rõ ràng hơn, tự nhiên hơn và giúp phát hiện lỗi dễ dàng hơn:

var id = 1;
var query =
   from post in database.Posts
   join meta in database.Post_Metas on post.ID equals meta.Post_ID
   where post.ID == id
   select new { Post = post, Meta = meta };

Nếu bạn thực sự bế tắc khi sử dụng lambdas, cú pháp của bạn khá lạc lõng. Đây là cùng một truy vấn, sử dụng các phương thức mở rộng LINQ:

var id = 1;
var query = database.Posts    // your starting point - table in the "from" statement
   .Join(database.Post_Metas, // the source table of the inner join
      post => post.ID,        // Select the primary key (the first part of the "on" clause in an sql "join" statement)
      meta => meta.Post_ID,   // Select the foreign key (the second part of the "on" clause)
      (post, meta) => new { Post = post, Meta = meta }) // selection
   .Where(postAndMeta => postAndMeta.Post.ID == id);    // where statement

10
@Emanuele Greco, liên quan đến chỉnh sửa của bạn, "Bình đẳng trên các trường ID được đặt trong điều kiện THAM GIA; bạn không cần sử dụng mệnh đề WHERE!": Mệnh đề WHERE không kiểm tra sự bằng nhau giữa các trường ID, đó là kiểm tra sự bình đẳng giữa ID bài cột và tham số id được khai báo bên ngoài truy vấn.
Daniel Schaffer

9
Phần tuyệt vời lambdavà được trích dẫn dễ sử dụng và dễ hiểu
Piotr Kula

1
ví dụ tuyệt vời
đồ chơi

1
Đôi khi giải thích về lambda được viết bằng lambda. Giải thích rõ.
Pinch

80

Bạn có thể đi hai cách với điều này. Sử dụng LINQPad (vô giá nếu bạn chưa quen với LINQ) và cơ sở dữ liệu giả, tôi đã xây dựng các truy vấn sau:

Posts.Join(
    Post_metas,
    post => post.Post_id,
    meta => meta.Post_id,
    (post, meta) => new { Post = post, Meta = meta }
)

hoặc là

from p in Posts
join pm in Post_metas on p.Post_id equals pm.Post_id
select new { Post = p, Meta = pm }

Trong trường hợp cụ thể này, tôi nghĩ cú pháp LINQ sạch hơn (tôi thay đổi giữa hai tùy thuộc vào cách dễ đọc nhất).

Điều tôi muốn chỉ ra là nếu bạn có khóa ngoại thích hợp trong cơ sở dữ liệu của mình, (giữa post và post_meta) thì có lẽ bạn không cần tham gia rõ ràng trừ khi bạn cố tải một số lượng lớn hồ sơ . Ví dụ của bạn dường như cho thấy rằng bạn đang cố tải một bài đăng và đó là dữ liệu meta. Giả sử có nhiều bản ghi post_meta cho mỗi bài đăng, thì bạn có thể làm như sau:

var post = Posts.Single(p => p.ID == 1);
var metas = post.Post_metas.ToList();

Nếu bạn muốn tránh vấn đề n + 1, thì bạn có thể nói rõ ràng LINQ với SQL để tải tất cả các mục liên quan trong một lần (mặc dù đây có thể là một chủ đề nâng cao khi bạn quen thuộc hơn với L2S). Ví dụ bên dưới cho biết "khi bạn tải Bài đăng, cũng tải tất cả các bản ghi được liên kết với nó thông qua khóa ngoại được biểu thị bằng thuộc tính 'Post_metas':

var dataLoadOptions = new DataLoadOptions();
dataLoadOptions.LoadWith<Post>(p => p.Post_metas);

var dataContext = new MyDataContext();
dataContext.LoadOptions = dataLoadOptions;

var post = Posts.Single(p => p.ID == 1); // Post_metas loaded automagically

Có thể thực hiện nhiều LoadWithcuộc gọi trên một bộ DataLoadOptionscho cùng một loại hoặc nhiều loại khác nhau. Nếu bạn làm điều này rất nhiều, bạn có thể chỉ muốn xem xét bộ nhớ đệm.


1
LinqPad CRM 2016 ?
Kiquenet

49

Daniel có một lời giải thích tốt về các mối quan hệ cú pháp, nhưng tôi đã đặt tài liệu này cho nhóm của mình để làm cho nó đơn giản hơn một chút để họ hiểu. Hy vọng điều này sẽ giúp ai đónhập mô tả hình ảnh ở đây


Điều đó sẽ không hiệu quả khi bạn chỉ đơn giản là xử lý một danh sách các giá trị như chúng tôi đang ở đây. Không có tài sản id trên đối tượng.
Talspaugh27

Tôi đã tìm thấy điều này thực sự hữu ích, nhưng tôi đã gặp một lỗi yêu cầu tôi phải thêm cột tham gia. Cũng nhìn vào câu trả lời được đăng bởi @Mark Byers, cột tham gia có Post_IDtrường trong bí danh thứ hai meta => meta.Post_ID. Trong ví dụ trong hình minh họa này, g.idphần của câu lệnh chọn ban đầu JOIN gStatus g on g.idkhông được sao chép trong biểu thức Lambda cuối cùng.
Xúc xíchFingers

3
Tôi đã không cố đăng bài này như một tài liệu tham khảo về linq thực tế cần có để trả lời được đăng bởi OP, nó giống như một tài liệu tham khảo về cách chuyển SQL sang định dạng Linq để đầu vào của tôi khác một chút so với câu hỏi ban đầu. Nếu tôi đã tạo một lớp cho các giá trị gStatus, tôi sẽ đặt một thuộc tính id cho nó và sau đó, nó sẽ tham gia với g => g.id Tôi đã sử dụng một danh sách các giá trị để cố gắng giữ mã đơn giản nhất có thể.
Talspaugh27

@ Talspaugh27 Vậy tại sao trong truy vấn SQL, nó lại tham gia vào gStatus trên g.id? Đó là một sai lầm hay cố ý?
Kịch

@Drammy trong bảng sql mỗi cột phải có tên, vì vậy đây là bảng 1 cột để giữ các id này, tôi chỉ sử dụng một cột có tên id, Danh sách <int> không có vấn đề đó. Nếu tôi đã thiết lập nó như vậy public class IdHolder{ int id } thì đã sử dụng đối tượng đó trong gStatus List<IdHolder> gStatus = new List<IdHolder>(); gStatus.add(new IdHolder(){id = 7}); gStatus.add(new IdHolder(){id = 8}); thì nó sẽ thay đổi Linq để t =>t.value.TaskStatusId, g=>g.id thay đổi đó có hợp lý không?
Talspaugh27

37

Bộ chọn khóa của bạn không chính xác. Họ nên lấy một đối tượng của loại bảng được đề cập và trả lại khóa để sử dụng trong phép nối. Tôi nghĩ bạn có ý này:

var query = database.Posts.Join(database.Post_Metas,
                                post => post.ID,
                                meta => meta.Post_ID,
                                (post, meta) => new { Post = post, Meta = meta });

Bạn có thể áp dụng mệnh đề where sau đó, không phải là một phần của bộ chọn khóa.


9

Đăng bài vì khi tôi bắt đầu LINQ + EntityFramework, tôi đã nhìn chằm chằm vào những ví dụ này trong một ngày.

Nếu bạn đang sử dụng EntityFramework và bạn có một thuộc tính điều hướng được đặt tên Metatrên Postđối tượng mô hình của bạn được thiết lập, điều này thật dễ dàng. Nếu bạn đang sử dụng thực thể và không có thuộc tính điều hướng đó, bạn còn chờ gì nữa?

database
  .Posts
  .Where(post => post.ID == id)
  .Select(post => new { post, post.Meta });

Nếu bạn đang thực hiện mã trước tiên, bạn sẽ thiết lập thuộc tính như vậy:

class Post {
  [Key]
  public int ID {get; set}
  public int MetaID { get; set; }
  public virtual Meta Meta {get; set;}
}

5

Tôi đã làm một cái gì đó như thế này;

var certificationClass = _db.INDIVIDUALLICENSEs
    .Join(_db.INDLICENSECLAsses,
        IL => IL.LICENSE_CLASS,
        ILC => ILC.NAME,
        (IL, ILC) => new { INDIVIDUALLICENSE = IL, INDLICENSECLAsse = ILC })
    .Where(o => 
        o.INDIVIDUALLICENSE.GLOBALENTITYID == "ABC" &&
        o.INDIVIDUALLICENSE.LICENSE_TYPE == "ABC")
    .Select(t => new
        {
            value = t.PSP_INDLICENSECLAsse.ID,
            name = t.PSP_INDIVIDUALLICENSE.LICENSE_CLASS,                
        })
    .OrderBy(x => x.name);

4

Nó có thể là một cái gì đó như

var myvar = from a in context.MyEntity
            join b in context.MyEntity2 on a.key equals b.key
            select new { prop1 = a.prop1, prop2= b.prop1};

1

1 bằng 1 hai bảng khác nhau tham gia

var query = from post in database.Posts
            join meta in database.Post_Metas on 1 equals 1
            where post.ID == id
            select new { Post = post, Meta = meta };

1

Truy vấn linq này nên làm việc cho bạn. Nó sẽ nhận được tất cả các bài viết có meta bài.

var query = database.Posts.Join(database.Post_Metas,
                                post => post.postId, // Primary Key
                                meta => meat.postId, // Foreign Key
                                (post, meta) => new { Post = post, Meta = meta });

Truy vấn SQL tương đương

Select * FROM Posts P
INNER JOIN Post_Metas pm ON pm.postId=p.postId

bạn đã đóng dấu ngoặc đơn trong đó sau thông số thứ ba ... "không quá tải cho Tham gia có ba đối số"
LastTribunal

3
Điều này giống hệt với câu trả lời được chấp nhận và 7 năm sau -1
reggaeg Ức
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.