Linq : Select Different Object Left Join - entity-framework

I've tow contracts and one query I want to select one of them based on join result , without using where clause,
from a in pContext
join c in vContext
on a.id equals c.id into av
from lav in av.DefaultIfEmpty()
if(lav != null )
{
select new DTO1()
{
a.id,
a.name,
lav.description
}
}
else
{
select new DTO2()
{
a.id,
a.name
}
}

EF Core translates LINQ "left join" to SQL left join and you do not need to handle nulls in this case, because it expects values from DbDataReader. Simple projection should work:
var query =
from a in pContext
join c in vContext on a.id equals c.id into av
from lav in av.DefaultIfEmpty()
select new DTO1
{
a.id,
a.name,
lav.description
};

from a in pContext
join c in vContext
on a.id equals c.id into av
from lav in av.DefaultIfEmpty()
select new DTO()
{
id = a.id,
name = a.name,
description = lav != null ? lav.description : "No Specified"
}
The solution is based on Svyatoslav Danyliv comment using ternary operator but with different condition to avoid the NullReferenceException.

Related

Filter entities with a field from another entity in linq query c#

I wanna join some tables in linq query. The problem is that I can not filter one of entities with field of another entity. In the below code b.createDate is not defiend, how can I do this query in linq?
From a in context.A
Join b in context.B
On a.Id equals b.AId
Join c in context.C.where(x =>
x.createDate >= b.createDate)
On b.Id equals c.BId into g
From result in
g.DefaultIfEmpty()
Select result
You have to use from instead of join in this case. As documented: Collection selector references outer in a non-where case
var query =
from a in context.A
join b in context.B on a.Id equals b.AId
from c in context.C
.Where(x => x.BId == b.Id && x.createDate >= b.createDate)
.DefaultIfEmpty()
select c;

How do you use group by and having clause in EF with parent/child relationship?

How can I write a linq to entities query that includes a group by and a having clause?
For example in SQL:
SELECT * FROM dbo.tblParent p
INNER JOIN
(
SELECT a.ID
FROM dbo.tblParent a
join dbo.tblChild c ON a.ID = c.FkParentID
WHERE a.ColValue = 167
GROUP BY A.ID
HAVING COUNT(c.ID) = 1
) t ON p.ID = t.ID
I found my own answer.
// this is far from pretty but it works as far as I can tell
Apps = (from x in context.tblParents
join t in (
from p in context.tblParents
join c in context.tblChilds
on p.ID equals c.FkParentID
where p.ColValue == 167
group c.ID by p.ID into grouped
where grouped.Count() == 1
select new { grouped.Key }) on x.ID equals t.Key
select x);

How to use DISTINCT ON in ARRAY_AGG()?

I have the following query:
SELECT array_agg(DISTINCT p.id) AS price_ids,
array_agg(p.name) AS price_names
FROM items
LEFT JOIN prices p on p.item_id = id
LEFT JOIN third_table t3 on third_table.item_id = id
WHERE id = 1;
When I LEFT JOIN the third_table all my prices are duplicated.
I'm using DISTINCT inside ARRAY_AGG() to get the ids without dups, but I want the names without dups aswell.
If I use array_agg(DISTINCT p.name) AS price_names, it will return distinct values based on the name, not the id.
I want to do something similar to array_agg(DISTINCT ON (p.id) p.name) AS price_names, but it is invalid.
How can I use DISTINCT ON inside ARRAY_AGG()?
Aggregate first, then join:
SELECT p.price_ids,
p.price_names,
t3.*
FROM items
LEFT JOIN (
SELECT pr.item_id,
array_agg(pr.id) AS price_ids,
array_agg(pr.name) AS price_names
FROM prices pr
GROUP BY pr.item_id
) p on p.item_id = items.id
LEFT JOIN third_table t3 on third_table.item_id = id
WHERE items.id = 1;
Using a lateral join might be faster if you only pick a single item:
SELECT p.price_ids,
p.price_names,
t3.*
FROM items
LEFT JOIN LATERAL (
SELECT array_agg(pr.id) AS price_ids,
array_agg(pr.name) AS price_names
FROM prices pr
WHERE pr.item_id = items.id
) p on true
LEFT JOIN third_table t3 on third_table.item_id = id
WHERE items.id = 1;

How to do left join with group by in NET Core 3.0

As we know in .NET Core 3.0 a lot has changed and I'm building queries which don't run on client side memory.
I want to achieve a left join which count the items from child table keeping the parent intact if there no children against it.
Desired output query:
SELECT [c].[Type] AS [Name], COUNT([c0].Id) AS [Count]
FROM [CustomerTypes] AS [c]
LEFT JOIN [Customers] AS [c0] ON [c].[Id] = [c0].[CustomerTypeId]
WHERE [c0].[Id] IS NULL OR (([c0].[CompanyId] = 1) AND [c0].[CompanyId] IS NOT NULL)
GROUP BY [c].[Id], [c].[Type]
Entity Framework Queries
from types in _dbManager.CustomerTypes
join customers in _dbManager.Customers
on types.Id equals customers.CustomerTypeId into tempJoin
from leftJoined in tempJoin.DefaultIfEmpty()
where leftJoined == null || leftJoined.CompanyId == 1
group leftJoined by new { types.Id, types.Type } into grouped
select new TabCountBindingModel()
{
Name = grouped.Key.Type,
Count = grouped.Count(c => c != null)
}
Output Query
SELECT [c].[Type] AS [Name], COUNT(*) AS [Count]
FROM [CustomerTypes] AS [c]
LEFT JOIN [Customers] AS [c0] ON [c].[Id] = [c0].[CustomerTypeId]
WHERE [c0].[Id] IS NULL OR (([c0].[CompanyId] = 1) AND [c0].[CompanyId] IS NOT NULL)
GROUP BY [c].[Id], [c].[Type]
No matter what I try I always end up with query above, either this or an exception.
Tried these too
from types in _dbManager.CustomerTypes
join customers in _dbManager.Customers
on types.Id equals customers.CustomerTypeId into tempJoin
from leftJoined in tempJoin.DefaultIfEmpty()
where leftJoined == null || leftJoined.CompanyId == 1
group leftJoined by new { types.Id, types.Type } into grouped
select new TabCountBindingModel()
{
Name = grouped.Key.Type,
Count = grouped.Where(c=>c != null).Count()
}
Results in exception.

linq subquery join and group by

Hi is it possible to translate the below queries into linq ? ...
I am using entity frameworks core and tried to use the stored procedure but it seems like i have to create a model that applies to the metadata of the stored procedure. So i am trying to understand whether this kinda such query can be translated into linq so i don't have to create a separate db model.
SELECT
Stock.stockID ProductID,
stockName ProductName,
categoryName ProductCategory,
typeName ProductType,
sizeName ProductSize,
currentQuantity CurrentQuantity,
standardQuantity QuantityPerBox,
CONVERT(VARCHAR(255),CONVERT(INT,Stock.price)) AvgUnitCost,
CONVERT(VARCHAR(255),CONVERT(INT,x.lastUnitCost)) LastOrderUnitCost,
CONVERT(VARCHAR(10),CONVERT(DATE,x.lastOrderDate)) LastOrderDate
FROM dbo.Stock
LEFT JOIN
(
SELECT stockID,unitPrice lastUnitCost ,orderDate lastOrderDate, ROW_NUMBER() OVER (PARTITION BY stockID ORDER BY orderDate DESC) rn FROM dbo.SalesOrder
JOIN dbo.SalesOrderDetail ON SalesOrderDetail.salesOrderID = SalesOrder.salesOrderID
WHERE customerID = #customerID AND salesStatus = 'S'
) x ON x.stockID = Stock.stockID AND rn = 1
LEFT JOIN dbo.StockCategory ON StockCategory.stockCategoryID = Stock.stockCategoryID
LEFT JOIN dbo.StockType ON StockType.stockTypeID = Stock.stockTypeID
LEFT JOIN dbo.StockSize ON StockSize.stockSizeID = Stock.stockSizeID
WHERE disStock = 0
Almost everything is possible. you just need to be careful with performance.
var query =
from stock in db.Stocks
join x in (
from grp in (
from so in db.SalesOrders
join sod in db.SalesOrderDetails on so.SalesOrderId equals sod.SalesOrderId
where so.CustomerId == customerId && so.SalesStatus == "S"
orderby so.OrderDate descending
select new {
sod.StockId,
LastUnitCost = sod.UnitPrice,
LastOrderDate = so.OrderDate
} into inner
group inner by inner.StockId)
select grp.Take(1)) on x.StockId equals stock.StockId into lastStockSales
from x in lastStockSales.DefaultIfEmpty()
join sc in db.StockCategories on stock.StockCatergotyId equals sc.StockCategoryId into scLeft
from sc in scLeft.DefaultIfEmpty()
join st in db.StockTypes on stock.StockTypeId equals st.StockTypeId into stLeft
from st in stLeft.DefaultIfEmpty()
join ss in db.StockSizes on stock.StockSizeId equals ss.StockSizeId into ssLeft
from ss in ssLeft.DefaultIfEmpty()
where stock.DisStock == 0
select new MyDTO {
ProductId = stock.StockId,
ProductName = stock.StockName,
ProductType = st.TypeName,
ProductSize = ss.SizeName,
CurrentQuantity = stock.CurrentQuantity,
QuantityPerBox = stock.StandardQuantity,
AvgUnitCost = stock.Price,
LastOrderUnitCost = x.LastUnitCost,
LastOrderDate = x.LastOrderDate
};
As you can see is easy to rewrite these queries, I had to change a little bit the logic on how to get the latest sales for a stock item since ROW_NUMBER() OVER (PARTITION... is not supported from a LINQ perspective. Again, you would have to consider performance when rewriting queries.
Hope this helps.