Як виконувати об’єднання між кількома таблицями в лямбда-лінці LINQ


91

Я намагаюся виконати об'єднання між кількома таблицями в LINQ. У мене є такі класи:

Product {Id, ProdName, ProdQty}

Category {Id, CatName}

ProductCategory{ProdId, CatId} //association table

І я використовую наступний код (де product, categoryі productcategoryє екземплярами вищевказаних класів):

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}

Де категорія продукту є типом:

ProductProductCategoryClass {product, productcategory}

Я не розумію, де знаходиться об'єднана "таблиця", я очікував єдиного класу, який містить усі властивості задіяних класів.

Моя мета - заповнити інший об'єкт деякими властивостями, що випливають із запиту:

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

як я можу досягти цієї мети?


Я не зрозумів ... чому m.ProdId = ??? замість prodId = m.ProdId ?
— Адріано Репетті

Тому що я не знаю заздалегідь, як орієнтуватися та отримати ProdId
— CiccioMiami

Відповіді:


181

Що стосується об'єднань, я дуже віддаю перевагу синтаксису запитів для всіх деталей, які щасливо приховані (не в останню чергу є прозорі ідентифікатори, що беруть участь у проміжних проекціях на шляху, що видно в еквіваленті крапки-синтаксису). Тим не менш, ти запитав щодо лямбда, я вважаю, що ти маєш усе необхідне - потрібно просто все це зібрати.

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.
— Атакуючий вертоліт "Харамбе"

Здається підозрілим, що ви хочете, p.Id == 1оскільки це скоріше де-фільтр, ніж критерій об’єднання. Те , як ви б зробити приєднатися до більш ніж одним критерієм , як правило, щоб використовувати анонімний тип: join pc in productcategory on new { Id = p.Id, Other = p.Other } equals new { Id = pc.ProdId, Other = pc.Other }. Це працює в Linq-to-Objects, і я припускаю, що те саме буде працювати і з запитами до бази даних. За допомогою Баз даних Ви можете відмовитись від складних запитів на приєднання, визначивши відповідні зовнішні ключі та отримавши доступ до пов’язаних даних через відповідну властивість.
— devgeezer

Дякуємо за чисте рішення.
— Томас Бенц,

У вашому прикладі: у синтаксисі крапок ppc ppc.p є анонімними типами, чи не так? У синтаксисі запиту p.id, який ви використовували під час останнього вибору, все ще є об’єктом продукту, я правильно? Тож із синтаксисом запиту простіше, якщо ви об’єднаєте кілька таблиць, щоб робити операції в остаточному поверненні схеми, як min minby?
— CDrosos

12

Те, що ви побачили, це те, що ви отримуєте - і це саме те, про що ви просили, тут:

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

Це лямбда-вираз, що повертає анонімний тип із цими двома властивостями.

У ваших категорізованих продуктах вам просто потрібно перейти за цими властивостями:

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

Дякую. Я розумію дискусію про анонімний клас, але його властивості містять лише об’єкти класу, які відповідають запиту? І що відбувається після того, як я виконаю 2 приєднання? productproductcategory.product не поєднано з категорією, правда?
— CiccioMiami

@CiccioMiami: Ну, властивості - це посилання на об’єкти, так. Не зовсім зрозуміло, що ви маєте на увазі під словом «не приєднався» - яку інформацію ви не отримуєте з вашого запиту, який ви хочете отримати?
— Джон Скіт,

З першим приєднанням я отримую приєднання між продуктами та категорією товару. З другим я отримую з'єднання між категорією товару (об'єднаний товар) та категорією. Це означає, що інформація про багаторазове приєднання просто міститься у категорії productproductca. Це означає, що товар (і категорія) просто поєднуються з категорією товару.
— CiccioMiami

1
@CiccioMiami: Вибачте, я не стежу за вами - але якщо ви вкажете приєднання, це зробить. Ви пробували використовувати код у моїй відповіді? Чи не робить те, що ти хочеш?
— Джон Скіт,

Вибачте, я хотів отримати ваш код. Доручення CatId робіт прекрасно. Бо ProdIdце має бути m.productproductcategory.product.IdАБО m.productproductcategory.productcategory.ProdId. Ці два завдання різняться, перший - на продукті (приєднаний до productcategory), другий - productcategoryприєднаний до обох 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
Незважаючи на те, що цей фрагмент коду може вирішити питання, включення пояснення дійсно допомагає поліпшити якість вашої публікації. Пам’ятайте, що ви будете відповідати на запитання для читачів у майбутньому, і ці люди можуть не знати причин вашої пропозиції коду.
— доктор Роб Ленг,

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

просто це просто, але краще з лямбда-досвідом, як казали деякі люди.
— Alex Martinez

0

минув деякий час, але моя відповідь може комусь допомогти:

якщо ви вже правильно визначили відношення, ви можете використовувати це:

        var res = query.Products.Select(m => new
        {
            productID = product.Id,
            categoryID = m.ProductCategory.Select(s => s.Category.ID).ToList(),
        }).ToList();
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.