Próbuję wykonać sprzężenie między wieloma tabelami w LINQ. Mam następujące zajęcia:
Product {Id, ProdName, ProdQty}
Category {Id, CatName}
ProductCategory{ProdId, CatId} //association table
I użyć kodu następujące (gdzie product
, category
i productcategory
są przypadki powyższych zajęć):
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});
Za pomocą tego kodu otrzymuję obiekt z następującej klasy:
QueryClass { productproductcategory, category}
W przypadku gdy kategoria produktów jest typu:
ProductProductCategoryClass {product, productcategory}
Nie rozumiem, gdzie jest połączona „tabela”, spodziewałem się pojedynczej klasy zawierającej wszystkie właściwości z zaangażowanych klas.
Moim celem jest wypełnienie innego obiektu niektórymi właściwościami wynikającymi z zapytania:
CategorizedProducts catProducts = query.Select(m => new { m.ProdId = ???, m.CatId = ???, //other assignments });
jak mogę osiągnąć ten cel?
Odpowiedzi:
W przypadku złączeń zdecydowanie wolę składnię zapytania dla wszystkich szczegółów, które są szczęśliwie ukryte (z których nie najmniejszym są przezroczyste identyfikatory związane z rzutami pośrednimi po drodze, które są widoczne w odpowiedniku składni kropkowej). Jednak pytałeś o Lambdy, które myślę, że masz wszystko, czego potrzebujesz - wystarczy to wszystko poskładać.
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 });
Jeśli zajdzie taka potrzeba, możesz zapisać sprzężenie w zmiennej lokalnej i użyć jej później, jednak z powodu braku innych szczegółów, nie widzę powodu, aby wprowadzać zmienną lokalną.
Możesz również wrzucić the
Select
do ostatniej lambda sekundyJoin
(ponownie, pod warunkiem, że nie ma innych operacji zależnych od wyników łączenia), co dałoby: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 });
... i podejmując ostatnią próbę sprzedania Cię na podstawie składni zapytania, wyglądałoby to tak:
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 };
Twoje ręce mogą być związane, czy składnia zapytania jest dostępna. Wiem, że niektóre sklepy mają takie uprawnienia - często oparte na założeniu, że składnia zapytań jest nieco bardziej ograniczona niż składnia kropkowa. Istnieją inne powody, takie jak „po co mam się uczyć drugiej składni, skoro mogę zrobić wszystko, a nawet więcej, używając składni kropkowej?” Jak pokazuje ta ostatnia część - istnieją szczegóły, które ukrywa składnia zapytań, które mogą sprawić, że warto ją uwzględnić dzięki poprawie czytelności, jaką przynosi: wszystkie te pośrednie projekcje i identyfikatory, które musisz ugotować, na szczęście nie są z przodu i na środku. etap w wersji składni zapytania - są one puszystym tłem. Teraz z mojej mydelniczki - w każdym razie dzięki za pytanie. :)
źródło
JOIN
wyciągu? Jak to zrobimy? Na przykład wjoin pc in productcategory on p.Id equals pc.ProdId
linii musimy dodaćand p.Id == 1
.p.Id == 1
ponieważ jest to bardziej kryterium gdzie-filtru niż jest to kryterium łączenia. Sposób, w jaki chcesz zrobić sprzężenia na więcej niż jedno kryterium ogólnie jest użycie typu anonimowego:join pc in productcategory on new { Id = p.Id, Other = p.Other } equals new { Id = pc.ProdId, Other = pc.Other }
. Działa to w Linq-to-Objects i przypuszczam, że to samo zadziała również z zapytaniami do bazy danych. W przypadku baz danych można zrezygnować ze skomplikowanych zapytań łączących, definiując odpowiednie klucze obce i uzyskując dostęp do powiązanych danych za pośrednictwem powiązanej właściwości.To, co widziałeś, jest tym, co otrzymujesz - i dokładnie o to prosiłeś, tutaj:
(ppc, c) => new { productproductcategory = ppc, category = c}
To wyrażenie lambda zwracające anonimowy typ z tymi dwiema właściwościami.
W swoich skategoryzowanych produktach wystarczy przejść przez te właściwości:
CategorizedProducts catProducts = query.Select( m => new { ProdId = m.productproductcategory.product.Id, CatId = m.category.CatId, // other assignments });
źródło
CatId
prac w porządku. BoProdId
to powinno byćm.productproductcategory.product.Id
LUBm.productproductcategory.productcategory.ProdId
. Te dwa przypisania różnią się, pierwszy dotyczy produktu (połączony zproductcategory
), drugi dotyczyproductcategory
połączenia z obydwomaproduct
icategory
. Czy podążasz za moim rozumowaniem?spójrz na ten przykładowy kod z mojego projektu
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(); }
w tym kodzie dołączyłem 3 tabele i wyplułem warunek złączenia z klauzuli where
uwaga: klasy Services są po prostu wypaczone (hermetyzujące) operacje na bazie danych
źródło
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); }
źródło
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}"); }
źródło
minęło trochę czasu, ale moja odpowiedź może komuś pomóc:
jeśli już poprawnie zdefiniowałeś relację, możesz użyć tego:
var res = query.Products.Select(m => new { productID = product.Id, categoryID = m.ProductCategory.Select(s => s.Category.ID).ToList(), }).ToList();
źródło