Cách thực hiện Nối giữa nhiều bảng trong LINQ lambda


91

Tôi đang cố gắng thực hiện Nối giữa nhiều bảng trong LINQ. Tôi có các lớp sau:

Product {Id, ProdName, ProdQty}

Category {Id, CatName}

ProductCategory{ProdId, CatId} //association table

Và tôi sử dụng mã sau (trong đó product, categoryproductcategorylà các phiên bản của các lớp trên):

var query = product.Join(productcategory, p => p.Id, pc => pc.ProdID, (p, pc) => new {product = p, productcategory = pc})
                   .Join(category, ppc => ppc.productcategory.CatId, c => c.Id, (ppc, c) => new { productproductcategory = ppc, category = c});

Với mã này, tôi nhận được một đối tượng từ lớp sau:

QueryClass { productproductcategory, category}

Trường hợp danh mục sản phẩm thuộc loại:

ProductProductCategoryClass {product, productcategory}

Tôi không hiểu "bảng" đã tham gia ở đâu, tôi đã mong đợi một lớp duy nhất chứa tất cả các thuộc tính từ các lớp liên quan.

Mục đích của tôi là điền vào một đối tượng khác với một số thuộc tính do truy vấn:

CategorizedProducts catProducts = query.Select(m => new { m.ProdId = ???, m.CatId = ???, //other assignments });

làm thế nào tôi có thể đạt được mục tiêu này?


Tôi không hiểu ... tại sao m.ProdId = ??? thay vì prodId = m.ProdId ?
Adriano Repetti

Bởi vì tôi không biết trước như thế nào để điều hướng và get PRODID
CiccioMiami

Câu trả lời:


181

Đối với các phép nối, tôi thực sự thích cú pháp truy vấn cho tất cả các chi tiết được ẩn một cách vui vẻ (không ít trong số đó là các định danh minh bạch liên quan đến các phép chiếu trung gian theo cách rõ ràng trong tương đương cú pháp dấu chấm). Tuy nhiên, bạn đã hỏi về Lambdas mà tôi nghĩ bạn có mọi thứ bạn cần - bạn chỉ cần tổng hợp tất cả lại với nhau.

var categorizedProducts = product
    .Join(productcategory, p => p.Id, pc => pc.ProdId, (p, pc) => new { p, pc })
    .Join(category, ppc => ppc.pc.CatId, c => c.Id, (ppc, c) => new { ppc, c })
    .Select(m => new { 
        ProdId = m.ppc.p.Id, // or m.ppc.pc.ProdId
        CatId = m.c.CatId
        // other assignments
    });

Nếu cần, bạn có thể lưu phép nối vào một biến cục bộ và sử dụng lại sau đó, tuy nhiên ngược lại, thiếu các chi tiết khác, tôi không thấy lý do gì để giới thiệu biến cục bộ.

Ngoài ra, bạn có thể ném Selectvào lambda cuối cùng của giây Join(một lần nữa, miễn là không có hoạt động nào khác phụ thuộc vào kết quả nối) sẽ cho:

var categorizedProducts = product
    .Join(productcategory, p => p.Id, pc => pc.ProdId, (p, pc) => new { p, pc })
    .Join(category, ppc => ppc.pc.CatId, c => c.Id, (ppc, c) => new {
        ProdId = ppc.p.Id, // or ppc.pc.ProdId
        CatId = c.CatId
        // other assignments
    });

... và nỗ lực cuối cùng để bán cho bạn cú pháp truy vấn, điều này sẽ giống như sau:

var categorizedProducts =
    from p in product
    join pc in productcategory on p.Id equals pc.ProdId
    join c in category on pc.CatId equals c.Id
    select new {
        ProdId = p.Id, // or pc.ProdId
        CatId = c.CatId
        // other assignments
    };

Bạn có thể bị ràng buộc về việc liệu cú pháp truy vấn có khả dụng hay không. Tôi biết một số cửa hàng có nhiệm vụ như vậy - thường dựa trên quan điểm rằng cú pháp truy vấn có phần hạn chế hơn cú pháp dấu chấm. Có những lý do khác, chẳng hạn như "tại sao tôi nên học cú pháp thứ hai nếu tôi có thể làm mọi thứ và hơn thế nữa trong cú pháp dấu chấm?" Như phần cuối cùng này cho thấy - có những chi tiết mà cú pháp truy vấn ẩn có thể khiến nó rất đáng được chấp nhận với sự cải thiện về khả năng đọc mà nó mang lại: tất cả những dự đoán và số nhận dạng trung gian mà bạn phải xây dựng đều không phải là front-and-center- giai đoạn trong phiên bản cú pháp truy vấn - chúng là một phần nền tảng. Tắt hộp xà phòng của tôi ngay bây giờ - dù sao, cảm ơn vì câu hỏi. :)


3
Cảm ơn giải pháp của bạn đã hoàn thiện hơn. Tôi đồng ý rằng cú pháp truy vấn trong một số trường hợp rõ ràng hơn nhưng bạn đã đoán đúng, tôi đã được yêu cầu sử dụng lambda. Hơn nữa tôi phải làm này tham gia hơn 6 bảng, và các ký hiệu dấu chấm trong trường hợp này là gọn gàng hơn
CiccioMiami

@devgeezer Điều gì xảy ra nếu chúng ta cần thêm điều kiện trong JOINcâu lệnh? làm sao chúng ta làm việc đó bây giờ? Ví dụ, trong join pc in productcategory on p.Id equals pc.ProdIddòng, chúng ta cần thêm and p.Id == 1.
Trực thăng tấn công Harambe

Có vẻ như nghi ngờ rằng bạn muốn p.Id == 1vì đó là một bộ lọc nơi hơn là một tiêu chí kết hợp. Cách bạn muốn làm một tham gia vào nhiều tiêu chí chung là sử dụng một loại vô danh: join pc in productcategory on new { Id = p.Id, Other = p.Other } equals new { Id = pc.ProdId, Other = pc.Other }. Điều này hoạt động trong Linq-to-Objects và tôi cho rằng điều này cũng sẽ hoạt động với các truy vấn cơ sở dữ liệu. Với Cơ sở dữ liệu, bạn có thể bỏ qua các truy vấn nối phức tạp bằng cách xác định khóa ngoại sao cho phù hợp và truy cập dữ liệu liên quan thông qua thuộc tính liên quan.
devgeezer

Cảm ơn bạn cho giải pháp sạch sẽ.
Thomas.Benz

Trong ví dụ của bạn: trong cú pháp dấu chấm, ppc ppc.p là kiểu ẩn danh phải không? Trong cú pháp truy vấn, p.id bạn sử dụng trong lần chọn cuối cùng vẫn là đối tượng sản phẩm phải không? Vì vậy, với cú pháp truy vấn dễ dàng hơn nếu bạn nối nhiều bảng để thực hiện các thao tác trong shema trả về cuối cùng như min minby?
CDrosos

12

Những gì bạn đã thấy là những gì bạn nhận được - và đó chính xác là những gì bạn yêu cầu, tại đây:

(ppc, c) => new { productproductcategory = ppc, category = c}

Đó là một biểu thức lambda trả về một kiểu ẩn danh với hai thuộc tính đó.

Trong Danh mục Sản phẩm của bạn, bạn chỉ cần đi qua các thuộc tính đó:

CategorizedProducts catProducts = query.Select(
      m => new { 
             ProdId = m.productproductcategory.product.Id, 
             CatId = m.category.CatId, 
             // other assignments 
           });

Cảm ơn. Tôi hiểu cuộc thảo luận về lớp ẩn danh nhưng các thuộc tính của nó chỉ chứa các đối tượng lớp thực hiện truy vấn? Và điều gì xảy ra sau khi tôi thực hiện 2 phép nối? productproductcategory.product không được tham gia với danh mục phải không?
CiccioMiami

@CiccioMiami: Vâng, các thuộc tính là tham chiếu đến các đối tượng, vâng. Bạn không thực sự rõ ý của bạn là "không tham gia" - bạn không nhận được thông tin gì từ truy vấn của mình mà bạn muốn lấy?
Jon Skeet

Với lần tham gia đầu tiên, tôi nhận được sự kết hợp giữa các sản phẩm và danh mục sản phẩm. Với thứ hai, tôi nhận được sự kết hợp giữa productcategory (sản phẩm đã tham gia) và danh mục. Điều đó có nghĩa là thông tin về liên kết nhiều chỉ được chứa trong danh mục sản phẩm. Điều này có nghĩa là sản phẩm (và danh mục) chỉ được kết hợp với danh mục sản phẩm.
CiccioMiami

1
@CiccioMiami: Xin lỗi, tôi không theo dõi bạn - nhưng nếu bạn chỉ định tham gia, nó sẽ thực hiện. Bạn đã thử sử dụng mã trong câu trả lời của tôi chưa? Nó không làm những gì bạn muốn?
Jon Skeet

Xin lỗi, tôi muốn lấy mã của bạn. Việc phân CatId công công việc tốt. Vì ProdIdnó phải là m.productproductcategory.product.IdHOẶC m.productproductcategory.productcategory.ProdId. Hai nhiệm vụ khác nhau, đầu tiên là trên sản phẩm (kết hợp với productcategory), thứ hai là với productcategoryđã kết hợp với cả hai productcategory. Bạn có theo suy luận của tôi không?
CiccioMiami

5

hãy xem mã mẫu này từ dự án của tôi

public static IList<Letter> GetDepartmentLettersLinq(int departmentId)
{
    IEnumerable<Letter> allDepartmentLetters =
        from allLetter in LetterService.GetAllLetters()
        join allUser in UserService.GetAllUsers() on allLetter.EmployeeID equals allUser.ID into usersGroup
        from user in usersGroup.DefaultIfEmpty()// here is the tricky part
        join allDepartment in DepartmentService.GetAllDepartments() on user.DepartmentID equals allDepartment.ID
        where allDepartment.ID == departmentId
        select allLetter;

    return allDepartmentLetters.ToArray();
}

trong mã này, tôi đã tham gia 3 bảng và tôi đã nhập điều kiện tham gia từ mệnh đề where

lưu ý: các lớp Dịch vụ chỉ bị biến dạng (đóng gói) các hoạt động cơ sở dữ liệu


2
 public ActionResult Index()
    {
        List<CustomerOrder_Result> obj = new List<CustomerOrder_Result>();

       var  orderlist = (from a in db.OrderMasters
                         join b in db.Customers on a.CustomerId equals b.Id
                         join c in db.CustomerAddresses on b.Id equals c.CustomerId
                         where a.Status == "Pending"
                         select new
                         {
                             Customername = b.Customername,
                             Phone = b.Phone,
                             OrderId = a.OrderId,
                             OrderDate = a.OrderDate,
                             NoOfItems = a.NoOfItems,
                             Order_amt = a.Order_amt,
                             dis_amt = a.Dis_amt,
                             net_amt = a.Net_amt,
                             status=a.Status,  
                             address = c.address,
                             City = c.City,
                             State = c.State,
                             Pin = c.Pin

                         }) ;
       foreach (var item in orderlist)
       {

           CustomerOrder_Result clr = new CustomerOrder_Result();
           clr.Customername=item.Customername;
           clr.Phone = item.Phone;
           clr.OrderId = item.OrderId;
           clr.OrderDate = item.OrderDate;
           clr.NoOfItems = item.NoOfItems;
           clr.Order_amt = item.Order_amt;
           clr.net_amt = item.net_amt;
           clr.address = item.address;
           clr.City = item.City;
           clr.State = item.State;
           clr.Pin = item.Pin;
           clr.status = item.status;

           obj.Add(clr);



       }

1
Mặc dù đoạn mã này có thể giải quyết câu hỏi, bao gồm một lời giải thích thực sự giúp cải thiện chất lượng bài đăng của bạn. Hãy nhớ rằng bạn đang trả lời câu hỏi cho người đọc trong tương lai và những người đó có thể không biết lý do cho đề xuất mã của bạn.
Dr Rob Lang

0
var query = from a in d.tbl_Usuarios
                    from b in d.tblComidaPreferidas
                    from c in d.tblLugarNacimientoes
                    select new
                    {
                        _nombre = a.Nombre,
                        _comida = b.ComidaPreferida,
                        _lNacimiento = c.Ciudad
                    };
        foreach (var i in query)
        {
            Console.WriteLine($"{i._nombre } le gusta {i._comida} y nació en {i._lNacimiento}");
        }

chỉ đơn giản vậy thôi, nhưng tốt hơn với lambda exp như một số người đã nói.
Alex Martinez

0

đã lâu rồi nhưng câu trả lời của tôi có thể giúp ích cho ai đó:

nếu bạn đã xác định mối quan hệ đúng cách, bạn có thể sử dụng điều này:

        var res = query.Products.Select(m => new
        {
            productID = product.Id,
            categoryID = m.ProductCategory.Select(s => s.Category.ID).ToList(),
        }).ToList();
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.