110 votes

Comment effectuer une jointure entre plusieurs tables dans une lambda LINQ ?

J'essaie d'effectuer une Jointure entre plusieurs tables dans LINQ. J'ai les classes suivantes :

Product {Id, ProdName, ProdQty}

Category {Id, CatName}

ProductCategory{ProdId, CatId} //association table

J'utilise le code suivant (où product , category y productcategory sont des instances des classes susmentionnées) :

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

Avec ce code, j'obtiens un objet de la classe suivante :

QueryClass { productproductcategory, category}

Lorsque producproductcategory est de type :

ProductProductCategoryClass {product, productcategory}

Je ne comprends pas où se trouve la "table" jointe, je m'attendais à ce qu'il y ait une "table" jointe. classe unique qui contient toutes les propriétés des classes concernées.

Mon objectif est de remplir un autre objet avec certaines propriétés résultant de la requête :

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

comment puis-je atteindre cet objectif ?

228voto

devgeezer Points 1447

Pour les jointures, je préfère de loin la syntaxe des requêtes pour tous les détails qui sont heureusement cachés (notamment les identificateurs transparents impliqués dans les projections intermédiaires en cours de route qui sont apparents dans l'équivalent en syntaxe des points). Cependant, vous avez posé une question sur les Lambdas et je pense que vous avez tout ce qu'il vous faut - il vous suffit de les assembler.

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

Si nécessaire, vous pouvez enregistrer la jointure dans une variable locale et la réutiliser ultérieurement. Cependant, en l'absence d'autres détails contraires, je ne vois aucune raison d'introduire la variable locale.

Vous pouvez également lancer la fonction Select dans le dernier lambda du second Join (encore une fois, à condition qu'il n'y ait pas d'autres opérations qui dépendent des résultats de la jointure), ce qui donnerait :

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

...et pour tenter une dernière fois de vous vendre la syntaxe des requêtes, cela ressemblerait à ceci :

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

Il se peut que vous n'ayez pas les mains libres pour déterminer si la syntaxe des requêtes est disponible. Je sais que certains ateliers ont de telles obligations - souvent fondées sur l'idée que la syntaxe de requête est un peu plus limitée que la syntaxe de point. Il y a d'autres raisons, comme "pourquoi devrais-je apprendre une deuxième syntaxe si je peux faire tout et plus dans la syntaxe pointée ?" Comme le montre cette dernière partie, il y a des détails que la syntaxe de requête cache et qui valent la peine d'être adoptés avec l'amélioration de la lisibilité qu'ils apportent : toutes ces projections intermédiaires et ces identificateurs que vous devez cuisiner ne sont heureusement pas au premier plan dans la version de la syntaxe de requête - ils sont en arrière-plan. Je quitte mon banc de savon maintenant - en tout cas, merci pour la question :)

12voto

Jon Skeet Points 692016

Ce que vous avez vu est ce que vous obtenez - et c'est exactement ce que vous avez demandé, ici :

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

Il s'agit d'une expression lambda renvoyant un type anonyme avec ces deux propriétés.

Dans vos CategorizedProducts, il vous suffit de passer par ces propriétés :

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

7voto

Basheer AL-MOMANI Points 5565

Regardez cet exemple de code de mon projet

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

Dans ce code, j'ai joint 3 tables et j'ai séparé la condition de jointure de la clause where.

Note : les classes de services ne font qu'encapsuler les opérations de la base de données.

5voto

saktiprasad swain Points 131
 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);

       }

0voto

Alex Martinez Points 21
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}");
        }

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X