Estoy tratando de realizar una unión entre varias tablas en LINQ. Tengo las siguientes clases:
Product {Id, ProdName, ProdQty}
Category {Id, CatName}
ProductCategory{ProdId, CatId} //association table
Y uso el siguiente código (donde product
, category
y productcategory
son instancias de las clases anteriores):
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});
Con este código obtengo un objeto de la siguiente clase:
QueryClass { productproductcategory, category}
Donde producproductcategory es de tipo:
ProductProductCategoryClass {product, productcategory}
No entiendo dónde está la "tabla" unida, esperaba una sola clase que contenga todas las propiedades de las clases involucradas.
Mi objetivo es poblar otro objeto con algunas propiedades resultantes de la consulta:
CategorizedProducts catProducts = query.Select(m => new { m.ProdId = ???, m.CatId = ???, //other assignments });
¿Cómo puedo lograr este objetivo?
Respuestas:
Para las uniones, prefiero la sintaxis de consulta para todos los detalles que están felizmente ocultos (entre los que se encuentran los identificadores transparentes involucrados con las proyecciones intermedias en el camino que son evidentes en el equivalente de sintaxis de puntos). Sin embargo, preguntaste sobre Lambdas, que creo que tienes todo lo que necesitas, solo necesitas juntarlo todo.
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 es necesario, puede guardar la combinación en una variable local y reutilizarla más tarde, sin embargo, al carecer de otros detalles al contrario, no veo ninguna razón para introducir la variable local.
Además, podría lanzar el
Select
en la última lambda del segundoJoin
(nuevamente, siempre que no haya otras operaciones que dependan de los resultados de la combinación) que darían: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 });
... y haciendo un último intento de venderle la sintaxis de consulta, esto se vería así:
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 };
Sus manos pueden estar atadas sobre si la sintaxis de consulta está disponible. Sé que algunas tiendas tienen tales mandatos, a menudo basados en la noción de que la sintaxis de consulta es algo más limitada que la sintaxis de puntos. Hay otras razones, como "¿por qué debería aprender una segunda sintaxis si puedo hacer todo y más con la sintaxis de puntos?" Como muestra esta última parte, hay detalles que la sintaxis de consulta oculta que pueden hacer que valga la pena adoptarla con la mejora en la legibilidad que aporta: todas esas proyecciones intermedias e identificadores que tiene que cocinar felizmente no están al frente y al centro. etapa en la versión de sintaxis de consulta: son pelusas de fondo. Fuera de mi caja de jabón ahora, de todos modos, gracias por la pregunta. :)
fuente
JOIN
declaración? ¿Como hacemos eso? Por ejemplo, en lajoin pc in productcategory on p.Id equals pc.ProdId
línea, necesitamos agregarand p.Id == 1
.p.Id == 1
ya que es más un filtro de dónde que un criterio de unión. La manera en que se hace una combinación de más de un criterio general es el uso de un tipo anónimo:join pc in productcategory on new { Id = p.Id, Other = p.Other } equals new { Id = pc.ProdId, Other = pc.Other }
. Esto funciona en Linq-to-Objects, y supongo que lo mismo funcionará también con consultas de base de datos. Con las bases de datos, es posible que pueda prescindir de consultas de combinación complicadas definiendo claves externas según corresponda y accediendo a los datos relacionados a través de la propiedad relacionada.Lo que ha visto es lo que obtiene, y es exactamente lo que pidió, aquí:
(ppc, c) => new { productproductcategory = ppc, category = c}
Esa es una expresión lambda que devuelve un tipo anónimo con esas dos propiedades.
En sus CategorizedProducts, solo necesita pasar por esas propiedades:
CategorizedProducts catProducts = query.Select( m => new { ProdId = m.productproductcategory.product.Id, CatId = m.category.CatId, // other assignments });
fuente
CatId
obras funciona bien. PorqueProdId
debería serm.productproductcategory.product.Id
ORm.productproductcategory.productcategory.ProdId
. Las dos asignaciones difieren, la primera es sobre el producto (unida conproductcategory
) la segunda es conproductcategory
unida a ambosproduct
ycategory
. ¿Sigues mi razonamiento?mira este código de muestra de mi proyecto
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(); }
en este código me uní a 3 tablas y escupí la condición de unión desde la cláusula where
nota: las clases de servicios simplemente se deforman (encapsulan) las operaciones de la base de datos
fuente
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); }
fuente
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}"); }
fuente
ha pasado un tiempo pero mi respuesta puede ayudar a alguien:
si ya definió la relación correctamente, puede usar esto:
var res = query.Products.Select(m => new { productID = product.Id, categoryID = m.ProductCategory.Select(s => s.Category.ID).ToList(), }).ToList();
fuente