Як виконувати об’єднання між кількома таблицями в лямбда-лінці 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.