LINQ 람다에서 여러 테이블 간 조인을 수행하는 방법


91

LINQ에서 여러 테이블간에 조인 을 수행하려고합니다 . 다음과 같은 수업이 있습니다.

Product {Id, ProdName, ProdQty}

Category {Id, CatName}

ProductCategory{ProdId, CatId} //association table

그리고 나는 다음과 같은 코드를 사용 (여기서 product, categoryproductcategory위 클래스의 인스턴스) :

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

이 코드를 사용하여 다음 클래스에서 객체를 얻습니다.

QueryClass { productproductcategory, category}

producproductcategory가 유형 인 경우 :

ProductProductCategoryClass {product, productcategory}

조인 된 "테이블"이 어디에 있는지 이해하지 못합니다 . 관련된 클래스의 모든 속성을 포함 하는 단일 클래스 를 예상했습니다 .

내 목표는 쿼리에서 생성 된 일부 속성으로 다른 개체를 채우는 것입니다.

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

이 목표를 어떻게 달성 할 수 있습니까?


이해하지 못했습니다 ... 왜 m.ProdId = ??? prodId = m.ProdId 대신 ?
Adriano Repetti 2012 년

나는 미리 알 수 없기 때문에 어떻게 GET PRODID 및 탐색
CiccioMiami

답변:


181

조인의 경우 행복하게 숨겨지는 모든 세부 사항에 대해 쿼리 구문을 강력히 선호합니다 (그 중 최소한 점 구문에서 명백한 중간 프로젝션과 관련된 투명한 식별자는 아닙니다). 그러나 필요한 모든 것을 갖추고 있다고 생각하는 Lambda에 대해 물었습니다.

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

필요한 경우 조인을 지역 변수에 저장하고 나중에 다시 사용할 수 있지만 반대로 다른 세부 정보가 부족하여 지역 변수를 도입 할 이유가 없습니다.

또한 Select두 번째 람다의 마지막 람다에 넣을 수 있습니다 Join(다시 조인 결과에 의존하는 다른 작업이없는 경우).

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

... 그리고 쿼리 구문에 대해 마지막으로 판매하려고 시도하면 다음과 같습니다.

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

쿼리 구문을 사용할 수 있는지 여부에 따라 손이 묶일 수 있습니다. 나는 일부 상점이 그러한 명령을 가지고 있음을 알고 있습니다. 종종 쿼리 구문이 도트 구문보다 다소 제한적이라는 개념에 기반합니다. "내가 도트 구문으로 모든 것을 할 수 있다면 왜 두 번째 구문을 배워야합니까?"와 같은 다른 이유가 있습니다. 이 마지막 부분이 보여 주듯이-가독성 향상과 함께 수용 할 가치가있는 쿼리 구문이 숨기는 세부 사항이 있습니다. 요리해야하는 모든 중간 프로젝션과 식별자는 기꺼이 앞과 가운데가 아닙니다. 쿼리 구문 버전의 단계-백그라운드 보풀입니다. 지금 내 비누 상자에서-어쨌든, 질문 주셔서 감사합니다. :)


3
감사합니다 귀하의 솔루션이 더 완벽합니다. 어떤 경우에는 쿼리 구문이 더 명확하다는 데 동의하지만 람다를 사용하라는 요청을 받았습니다. 또한 나는이 6 이상의 테이블을 조인하게해야하고,이 경우에는 도트 표기법은 더 깔끔
CiccioMiami

@devgeezer JOIN문장에 덧셈 조건이 필요하면 어떨까요? 어떻게하나요? 예를 들어, join pc in productcategory on p.Id equals pc.ProdId줄에 and p.Id == 1.
Harambe Attack Helicopter

p.Id == 1조인 기준보다 where-filter에 가깝기 때문에 원하는 것으로 의심되는 것 같습니다 . 둘 이상의 기준에 대해 조인을 수행하는 방법은 일반적으로 익명 유형을 사용하는 것입니다 : join pc in productcategory on new { Id = p.Id, Other = p.Other } equals new { Id = pc.ProdId, Other = pc.Other }. 이것은 Linq-to-Objects에서 작동하며 데이터베이스 쿼리에서도 동일하게 작동한다고 가정합니다. 데이터베이스를 사용하면 외래 키를 적절하게 정의하고 관련 속성을 통해 관련 데이터에 액세스하여 복잡한 조인 쿼리를 방지 할 수 있습니다.
devgeezer

깨끗한 솔루션에 감사드립니다.
Thomas.Benz

귀하의 예에서 : 도트 구문에서 ppc ppc.p는 익명 유형입니까? 쿼리 구문에서 마지막 선택에서 사용하는 p.id는 여전히 제품 개체입니다. 따라서 min minby와 같은 최종 반환 shema에서 작업을 수행하기 위해 여러 테이블을 조인하면 쿼리 구문이 더 쉽습니다.
CDrosos

12

당신이 본 것은 당신이 얻는 것입니다-정확히 당신이 요청한 것입니다.

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

이 두 속성이있는 익명 형식을 반환하는 람다 식입니다.

CategorizedProducts에서 다음 속성을 통해 이동하면됩니다.

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

감사. 익명 클래스에 대한 논의를 이해하지만 해당 속성에는 쿼리를 수행하는 클래스 개체 만 포함되어 있습니까? 2 조인을 수행하면 어떻게됩니까? productproductcategory.product가 카테고리와 결합되어 있지 않습니까?
CiccioMiami

@CiccioMiami : 음, 속성은 객체에 대한 참조 입니다. "조인되지 않음"이 의미하는 바가 명확하지 않습니다. 쿼리에서 얻고 싶지 않은 정보는 무엇입니까?
Jon Skeet

첫 번째 조인으로 제품과 제품 범주 간의 조인을 얻습니다. 두 번째로 나는 productcategory (joined product)와 category 사이의 조인을 얻습니다. 즉, 다중 조인에 대한 정보는 productproductcategory에만 포함됩니다. 이는 제품 (및 범주)이 제품 범주와 결합되었음을 의미합니다.
CiccioMiami

1
@CiccioMiami : 죄송합니다. 저는 당신을 팔로우하고 있지 않습니다.하지만 조인을 지정하면 그렇게 할 것입니다. 당신은 시도 내 대답에 코드를 사용하고 계십니까? 당신이 원하는 것을하지 않습니까?
Jon Skeet

죄송합니다. 코드를 확인하고 싶었습니다. CatId 작업 할당이 잘되었습니다. 들어 ProdId는해야 m.productproductcategory.product.IdOR m.productproductcategory.productcategory.ProdId. 두 가지 할당이 다릅니다. 첫 번째는 제품 ( productcategoryproductcategory결합 됨 )에 있고 두 번째 할당은 product및 둘 모두 와 결합되어 있습니다 category. 내 추론을 따르나요?
CiccioMiami

5

내 프로젝트에서이 샘플 코드를보세요

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

이 코드에서 3 개의 테이블을 조인하고 where 절에서 조인 조건을 확인했습니다.

참고 : 서비스 클래스는 데이터베이스 작업을 왜곡 (캡슐화)합니다.


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
이 코드 스 니펫은 질문을 해결할 수 있지만 설명을 포함하면 게시물의 품질을 향상시키는 데 큰 도움이됩니다. 미래에 독자를 위해 질문에 답하고 있으며 해당 사용자는 코드 제안 이유를 모를 수 있습니다.
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}");
        }

간단하지만 일부 사람들이 말한 것처럼 lambda exp가 더 좋습니다.
알렉스 마르티네즈

0

오래되었지만 내 대답은 누군가를 도울 수 있습니다.

이미 관계를 올바르게 정의한 경우 다음을 사용할 수 있습니다.

        var res = query.Products.Select(m => new
        {
            productID = product.Id,
            categoryID = m.ProductCategory.Select(s => s.Category.ID).ToList(),
        }).ToList();
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.