LINQ Equivalent of and SQL query with two inner joins and a left join - linq

I would be grateful for help with a LINQ equivalent of the following SQL Query (which works). Below this SQL query I give some description of my simple data base and the problem I want to solve.
Select a.Name, a.OrderID,
b.ProductIDFirst, c1.productName ProductNameFirst,
b.ProductIDSecond , c2.productName ProductNameSecond
from Customers a
INNER JOIN ORDERS b ON a.OrderID = b.OrderID
left join products c1 on b.productidfirst = c1.productid
left join products c2 on b.ProductIDSecond = c2.productid
Background information on the database structure:
I have a simple SQL Server Database with three Tables named Products, Orders and Customers.
The business model is such that each order can have only two products (not more).
The Orders table has two foreign keys, though they both come from the Products table. These Foreign Key field Names in the Orders Table are ProductIDFirst and ProductIDSecond. These two Foreign Keys in the orders table correspond to two products that each order can have. Customers table has one Foreign Key which comes from the Orders Table.
Now I need help with an LINQ query that will return me all customers such that I get five fields - CustomerName, OrderID and Names of each of the two products that match the OrderID in the customer product.

Your links are non-existent, so here's a best attempt without seeing anything.
Assuming you have the following containers (you'll need to change them for your scenario):
var customers = new List<Customer>();
var orders = new List<Order>();
var products = new List<Product>();
You can do the following:
var query =
from a in customers
join b in orders
on a.OrderId equals b.OrderId
join c1 in products
on b.ProductIdFirst equals c1.ProductId into c1a
join c2 in products
on b.ProductIdSecond equals c2.ProductId into c2a
from p1 in c1a.DefaultIfEmpty()
from p2 in c2a.DefaultIfEmpty()
select new
{
Name = a.Name,
OrderId = a.OrderId,
ProductIdFirst = p1 == null ? null : p1.ProductIdFirst,
ProductNameFirst = p1 == null ? null : p1.ProductNameFirst,
ProductIdSecond = p2 == null ? null : p1.ProductIdSecond,
ProductNameSecond = p2 == null ? null : p1.ProductNameSecond,
};
In short, where you want a left join, project the join into something else (e.g. c1a, c2a) then call from on them using DefaultIfEmpty() which will set null when no matching item exists on the right-hand-side.

Related

LINQ Left Outer Join only the first record

I'm working on a LINQ query that joins three tables. For the Orders and OrderInfo table I expect a single record in each table for a given order id. However for the ShipRate table, there could be 0, 1 or more records for a given order id. So for this table I am using a left outer join. The query shown below is working if 0 or 1 records exist in the ShipRate table, but for instances where the number of records is > 1, I need to select only the most recent ShipRate record. I tried to do this by replacing the line:
from shipRate in sr.DefaultIfEmpty()
with this:
from shipRate in sr.OrderByDescending(r => r.CreateDate).Take(1).DefaultIfEmpty()
but the query takes forever, as if it is loading the entire ShipRate table. Where have I gone wrong?
var query = (from order in db.Orders
join info in db.OrderInfo
on order.OrderId equals info.OrderId
join shipRate in db.ShipRate
on info.OrderId equals shipRate.OrderId
into sr
from shipRate in sr.DefaultIfEmpty()
where order.OrderId == orderId
select new
{
OrderId = order.OrderId,
OrderDetail = info.OrderDetail,
Carrier = shipRate.Carrier
}).SingleOrDefault();
With a proper model definition your query would be like:
var query = (from order in db.Orders
where order.OrderId == orderId
select new
{
OrderId = order.OrderId,
OrderDetail = order.OrderInfo.OrderDetail,
Carrier = order.OrderInfo.ShipRates.OrderBy(sr =>sr.CreateDate).FirstOrDefault()
}).SingleOrDefault();
I can't be sure though, because you didn't supply sample data and model.
Cetin Basoz's answer is a good one: ideally you'd set up your model in a way that allows you to use navigation properties. If you're using a model generated from your database schema, that typically means setting up foreign and primary keys properly.
If you can't do that, you should still be able to get a similar effect by writing SQL like this:
var query = (from order in db.Orders
where order.OrderId == orderId
let orderInfo = db.OrderInfo.FirstOrDefault(info => order.OrderId == info.OrderId)
let currentShipRate = db.ShipRate
.Where(shipRate => info.OrderId == shipRate.OrderId)
.OrderByDescending(shipRate => shipRate.CreateDate)
.FirstOrDefault()
select new
{
OrderId = order.OrderId,
OrderDetail = orderInfo.OrderDetail,
Carrier = currentShipRate.Carrier
}).SingleOrDefault();
However, LINQ to SQL isn't nearly as good at building advanced queries as Entity Framework, and the symptoms you're describing might be an indication that it's actually doing multiple database round-trips instead of a join. I'd recommend logging the query that you're producing (prior to the .SingleOrDefault()) either by calling .ToString() on the query or by executing your query in LINQPad and clicking on the SQL tab. That might give you a clue as to why the query is misbehaving.
There seems to be a one-to-one relation between Orders and OrderInfos: every Order has exactly one OrderInfo, and every OrderInfo is the info of exactly one Order, namely the Order that the foreign key OrderId refers to.
On the other hand, there seems to be a one-to-many relation between Orders and ShipRates. Every Order has zero or more ShipRates, every ShipRate is a ShipRate of exactly one Order, namely the Order that the foreign key OrderId refers to.
You want several properties of "Orders, each Order with its one and only OrderInfo and its zero or more ShipRates"
Whenever you have a one-to-many relation, and you want "items with their zero or more sub-items", like Schools with their Students, Customers with their Orders, or in your case: Orders with their ShipRates, consider to use one of the overloads of Queryable.GroupJoin
In the other direction: if you want an item with its one and only other item that the foreign key refers to, like Student with the School he attends, Order with the Customer who created the Order, or Order with its one and only OrderInfo, use Queryable.Join.
I mostly use the overload of GroupJoin that has a parameter resultSelector, so I can select exactly what properties I want.
int orderId = ...
var ordersWithShipRates = dbContext.Orders.GroupJoin(dbContext.ShipRates,
order => order.Id, // from every Order take the primary key
shipRate => shipRate.OrderId, // from every ShipRate take the foreign key to Order
// parameter resultSelector: from every Order, with its zero or more ShipRates
// make one new
(order, shipRatesOfThisOrder) => new
{
// Select the Order properties that you plan to use:
Id = order.Id,
Date = order.Date,
...
ShipRates = shipRatesOfThisOrder.Select(shipRate => new
{
// Select the ShipRate properties that you plan to use:
Id = shipRate.Id,
Value = shipRate.Value,
...
})
.ToList(),
// A simple join to get the one and only OrderInfo
OrderInfo = dbContext.OrderInfos.Where(orderInfo => orderInfo.Id == order.Id)
.Select(orderInfo => new
{
// Select the orderInfo properties that you plan to use
Name = orderInfo.Name,
...
})
.FirstOrDefault(),
});

How to return values from query when joining multiple tables in Linq?

I have a Linq queries that have tables join and couple of tables inner join together. Sometimes I got an error from the query when table is empty. What I trying to do is I am tryting to get a value from table even if other table is empty.
Thanks in Advance.
You need to do left join
Assuming left join between customer and order table.
var query =
from customer in dc.Customers
from order
in dc.Orders
.Where(o => customer.CustomerId == o.CustomerId)
.DefaultIfEmpty()
select new { Customer = customer, Order = order }
Also refer below link
http://forums.asp.net/t/1792428.aspx/1

Using LINQ to get distinct items that do not join

I'm having problems running a LINQ query between two tables and returning an answer set that doesen't match.
TB_AvailableProducts
-Prod_ID
-Name
....
TB_Purchases
-Cust_ID
-Prod_ID
Is there a way to get all distinct products that a customer has not purchased by using 1 LINQ query, or do I have to be doing two separate queries, 1 for all products and 1 for purchased products, and compare the two?
This query will return all products, which do not have related record in purchases table.
int customerID = 1;
var query = from ap in context.TB_AvailableProducts
join p in context.TB_Purchases.Where(x => x.Cust_ID == customerID)
on ap.Prod_ID equals p.Prod_ID into g
where !g.Any()
select ap;
I don't think you need Distinct here if you don't have duplicated records in your products table.
Generated SQL query will look like:
SELECT ap.Prod_ID, ap.Name
FROM TB_AvailableProducts AS ap
WHERE NOT EXISTS (SELECT
1 AS C1
FROM TB_Purchases AS p
WHERE (1 = p.Cust_ID) AND (ap.Prod_ID = p.Prod_ID)
)

LINQ Joining 2 tables

I have two tables in the database one contains a list of all possible grocery values. For Example
Milk
Cheese
Bread
Meat
the second table contains Items from Grocery that are selected. For Example:
Milk
Cheese
I want a result that has all possible grocery items with Milk and Cheese selected.
Any ideas?
Here are the tables.
The GroceryList Table:
ID INT PK
Description Varchar(50)
The ShoppingList Table:
ID INT PK
GroceryListID int FK to GroceryList.ID
So the resulting Entity would be all items from GroceryList and if they exist in ShoppingList then selected is marked as true:
ShoppingList.ID
Grocerylists.Description
Selected
Based on understanding you can do something like this
//first get the list of product which satisfy your condition
var ids = (from p ShoppingList
select p.GroceryListID ).ToList();
//second filter the Grocery products by using contains
var myProducts = from p in GroceryList
where ids.Contains(p.ID)
Select p;
or
if you want to get info about join than this image would help you
Inner Join
Outer Join
Try to understand and which may help you to resolve your query
Edit: Still sounds like you want to do a left join. In your case:
var LINQResult = from g in Datacontext.GroceryList
from s in DataContext.ShoppingList
.Where(c=>c.ID == g.ID)
.DefaultIfEmpty()
select new {
g.ID,
g.Description,
s.ID // Will be null if not selected.
};
For more examples:
Left Join on multiple tables in Linq to SQL

Linq To Entity Framework selecting whole tables

I have the following Linq statement:
(from order in Orders.AsEnumerable()
join component in Components.AsEnumerable()
on order.ORDER_ID equals component.ORDER_ID
join detail in Detailss.AsEnumerable()
on component.RESULT_ID equals detail.RESULT_ID
where orderRestrict.ORDER_MNEMONIC == "MyOrderText"
select new
{
Mnemonic = detail.TEST_MNEMONIC,
OrderID = component.ORDER_ID,
SeqNumber = component.SEQ_NUM
}).ToList()
I expect this to put out the following query:
select *
from Orders ord (NoLock)
join Component comp (NoLock)
on ord .ORDER_ID = comp.ORDER_ID
join Details detail (NoLock)
on comp.RESULT_TEST_NUM = detail .RESULT_TEST_NUM
where res.ORDER_MNEMONIC = 'MyOrderText'
but instead I get 3 seperate queries that select all rows from the tables. I am guessing that Linq is then filtering the values because I do get the correct values in the end.
The problem is that it takes WAY WAY too long because it is pulling down all the rows from all three tables.
Any ideas how I can fix that?
Remove the .AsEnumerable()s from the query as these are preventing the entire query being evaluated on the server.

Resources