I've been trying for a couple of days to write to "translate" this query in LINQ with no success so far. Could you guys please help me? I would also appreciate some explanation to learn actually something out of it.
Here is the T-SQL query:
SELECT R.ResourceID, R.DefaultValue
FROM Resources as R
JOIN
(SELECT [t0].[NameResourceID] AS [ResourceID]
FROM [dbo].[Sectors] AS [t0]
LEFT OUTER JOIN [dbo].[LocalizedResources] AS [t1] ON [t0].[NameResourceID] = [t1].[ResourceID] and [t1].[LanguageID] = 2
WHERE t1.Value IS NULL) AS subQ
ON R.ResourceID = subQ.ResourceID
Thank's
Try something like that :
from r in db.Resources
join subQ in (from t0 in db.Sectors
join t1 in db.LocalizedResources on t0.NameResourceID equals t1.ResourceID
where t1.LanguageId
&& t1.Value == null
select new { ResourceID = t0.NameResourceID }) on r.ResourceID equals subQ.ResourceID
select new { r.ResourceId, r.DefaultValue };
Related
Ok, I have such SQL code:
DECLARE #documentId INT = 8
SELECT
i.[status] AS [status],
i.[barcode] AS [barcode],
i.[containerId] AS [containerTypeId],
(
SUM(ISNULL(lp.[Weight], 0) * il.[factCount]) +
ISNULL(lpt.[Weight], 0)
) AS [weight],
i.[comment] AS [comment]
FROM [Items] AS i WITH(NOLOCK)
LEFT JOIN [dbo].[ItemsLines] AS il WITH(NOLOCK) ON i.[id] = il.[itemsId]
LEFT JOIN [dbo].[LagerOptions] AS lp WITH(NOLOCK) ON lp.[lagerId] = il.[lagerId]
LEFT JOIN [dbo].[LagerOptions] AS lpt WITH(NOLOCK) ON lpt.[lagerId] = i.[containerId]
WHERE #documentId = i.[documentId]
GROUP BY i.[status], i.[barcode], i.[containerId], i.[comment], lpt.[Weight]
The most similar code I've written in LINQ is:
var lots = await (
from i in _dbContext.Items
join lpt in _dbContext.LagerOptions on i.ContainerId equals lpt.LagerId into lptTable
from lpt in lptTable.DefaultIfEmpty()
// The sum of all lagers for current lot
let lotWeight = (
from il in _dbContext.ItemsLines
join lp in _dbContext.LagerOptions on il.LagerId equals lp.LagerId into lpTable
from lp in lpTable.DefaultIfEmpty()
where il.ItemsId == i.Id
select (il.FactCount * lp.Weight.GetValueOrDefault(0))
).Sum()
where i.DocumentsId == documentId
select new
{
Status = i.Status,
Barcode = i.Barcode,
ContainerTypeId = i.ContainerId,
// total weight with container Weight
Weight = lotWeight + lpt.Weight.GetValueOrDefault(0),
//
Comment = i.Comment
}
).ToListAsync();
For SQL query the results can contain empty lots or lots with null-weight lagers (problem lagers) that makes "Weight" field of lot is equal NULL that helps detect problem lots.
But when I check the C# LINQ code EntityFramework create a COALESCE() function over SUM() function
and the query converted to SQL looks like this:
SELECT [t].[status] AS [Status], [t].[barcode] AS [Barcode], [t].[containerId] AS [ContainerTypeId], (
SELECT COALESCE(SUM([t0].[factCount] * COALESCE([l0].[Brutto], 0.0)), 0.0)
FROM [dbo].[ItemsLines] AS [t0]
LEFT JOIN [dbo].[LagerOptions] AS [l0] ON [t0].[lagerId] = [l0].[lagerId]
WHERE [t0].[itemsId] = [t].[id]) + COALESCE([l].[weight], 0.0) AS [Weight], [t].[comment] AS [Comment]
FROM [dbo].[Items] AS [t]
LEFT JOIN [dbo].[LagerOptions] AS [l] ON [t].[containerId] = [l].[lagerId]
WHERE [t].[documentId] = #__documentId_0
As a result, the weight of the problem lot will be equal to the container weight of this lot.
I can solve the problem with crutch methods, but I'm sure that there is a simple solution, which, unfortunately, I did not find.
I tryed to add different checks on null and rewrite the query for different JOIN patterns, but the main problem - EntityFramework create a COALESCE() function over SUM() function. And I don't know how to fix it in root. I work with C# EF approximately 1 month. EF 7.0.2
Help me, please.
!!EDITED
Correct LINQ query looks like this (Thank, Svyatoslav Danyliv):
var query =
from i in _dbContext.Items
join il in _dbContext.ItemsLines on i.Id equals il.ItemsId into ilj
from il in ilj.DefaultIfEmpty()
join lp in _dbContext.LagerOptions on il.LagerId equals lp.LagerId into lpj
from lp in lpj.DefaultIfEmpty()
join lpt in _dbContext.LagerOptions on i.ContainerId equals lpt.LagerId into lptj
from lpt in lptj.DefaultIfEmpty()
where i.DocumentsId == documentId
group new { lp, il, lpt } by new { i.Status, i.Barcode, i.ContainerId, i.Comment, lpt.Weight } into g
select new
{
g.Key.Status,
g.Key.Barcode,
ContainerTypeId = g.Key.ContainerId,
Weight = g.Sum(x => ((decimal?)x.lp.Weight) ?? 0 * x.il.FactCount) + g.Key.Weight ?? 0
g.Key.Comment,
};
But the query converted to SQL looks like this:
SELECT
[t].[status] AS [Status],
[t].[barcode] AS [Barcode],
[t].[containerId] AS [ContainerTypeId],
COALESCE(SUM(COALESCE([l].[weight], 0.0) * [t0].[factCount])), 0.0) +
COALESCE([l0].[weight], 0.0) AS [Weight],
[t].[comment] AS [Comment]
FROM [dbo].[Items] AS [t]
LEFT JOIN [dbo].[ItemsLines] AS [t0] ON [t].[id] = [t0].[itemsId]
LEFT JOIN [dbo].[LagerOptions] AS [l] ON [t0].[lagerId] = [l].[lagerId]
LEFT JOIN [dbo].[LagerOptions] AS [l0] ON [t].[containerId] = [l0].[lagerId]
WHERE [t].[documentsId] = #__documentId_0
GROUP BY [t].[status], [t].[barcode], [t].[containerId], [t].[comment], [l0].[weight]
EntityFramework create a COALESCE() function over SUM() function.
How to remove this?
This should equivalent LINQ query to your SQL. I do not understand why instead of grouping you have forced LINQ Translator to generate another query.
var query =
from i in _dbContext.Items
join il in _dbContext.ItemsLines on i.Id equals il.ItemsId into ilj
from il in ilj.DefaultIfEmpty()
join lp in _dbContext.LagerOptions on il.LagerId equals lp.LagerId into lpj
from lp in lpj.DefaultIfEmpty()
join lpt in _dbContext.LagerOptions on i.ContainerId equals lpt.LagerId into lptj
from lpt in lptj.DefaultIfEmpty()
where i.DocumentsId == documentId
group new { lp, il, lpt } by new { i.Status, i.Barcode, i.ContainerId, i.Comment, lpt.Weight } into g
select new
{
g.Key.Status,
g.Key.Barcode,
ContainerTypeId = g.Key.ContainerId,
Weight = g.Sum(x => ((double?)x.lp.Weight) ?? 0 * x.il.FactCount) + g.Key.Weight ?? 0
g.Key.Comment,
};
I'm using SqlKata in my project and it's necessary to connect several tables with the help of nested join. I expect to see something like that:
SELECT * FROM t1
LEFT JOIN (t2 LEFT JOIN t3 ON t3.id = t2.id)
ON t2.id = t1.id
In Join/LeftJoin/RigthJoin methods, I did not find any overloads that would accept anything other than a join or other request.
Wouldn't want to manually make such connections, maybe someone has already faced such a problem? That would be great, I would really appreciate a hint.
Defining JOIN precedence is not available in SqlKata at the moment.
But you can achieve same result by using a Sub Query.
var query = new Query("t1")
.LeftJoin(
new Query("t2").LeftJoin("t3", "t3.id", "t2.id").As("tmp"),
j => j.On( "tmp.id", "t1.id")
);
This query would result in the following sql:
SELECT * FROM [t1] LEFT JOIN (
SELECT * FROM [t2] LEFT JOIN [t3] ON [t3].[id] = [t2].[id]
) AS [tmp] ON ([tmp].[id] = [t1].[id])
How to convert store procedure to linq in nopCommerce c#
My store procedure query
SELECT p.Id
FROM Product p WITH (NOLOCK)
LEFT JOIN Discount_AppliedToProducts dap WITH (NOLOCK)
ON p.Id = dap.Product_Id
LEFT JOIN Product_Category_Mapping pcm WITH (NOLOCK)
ON p.Id = pcm.ProductId
LEFT JOIN Discount_AppliedToCategories dac WITH (NOLOCK)
ON pcm.CategoryId = dac.Category_Id
AND dac.Category_Id IN (1 ,2 ,3 ,4 ,5 ,6)
LEFT JOIN Product_Manufacturer_Mapping pmm WITH (NOLOCK)
ON p.Id = pmm.ProductId
LEFT JOIN Discount_AppliedToManufacturers dam WITH (NOLOCK)
ON pmm.ManufacturerId = dam.Manufacturer_Id
WHERE dap.Discount_Id IN (3)
OR dac.Discount_Id IN (3)
OR dam.Discount_Id IN (3)
My linq query
var productlist = (from q in _productRepository.Table
select q).ToList();
var discount_AppliedToProductIds = (from dp in _discountRepository.Table
from p in dp.AppliedToProducts
select p).ToList().DistinctBy(d => d.Id).ToList();
var discount_AppliedToCategorieIds = (from dp in _discountRepository.Table
from c in dp.AppliedToCategories
select c).ToList().DistinctBy(d => d.Id).ToList();
var discount_AppliedToManufacturerIds = (from dp in _discountRepository.Table
from m in dp.AppliedToManufacturers
select m).ToList().DistinctBy(d => d.Id).ToList();
var product_Manufacturer_Mapping = (from dp in productlist
from pm in dp.ProductManufacturers
select pm).ToList().DistinctBy(d => d.Id).ToList();
var product_Category_Mapping = (from dp in productlist
from pc in dp.ProductCategories
select pc).ToList().DistinctBy(d => d.Id).ToList();
var ss = (from p in productlist
join dap in discount_AppliedToProductIds on p.Id equals dap.Id
join pcm in product_Category_Mapping on p.Id equals pcm.ProductId
//join dac in discount_AppliedToCategorieIds on pcm.CategoryId equals dac.Id
from dac in discount_AppliedToCategorieIds
join pmm in product_Manufacturer_Mapping on p.Id equals pmm.ProductId
join dam in discount_AppliedToManufacturerIds on pmm.ManufacturerId equals dam.Id
from dapd in dap.AppliedDiscounts
from pacd in dac.AppliedDiscounts
from damd in dam.AppliedDiscounts
where discountIds.Any(d => dapd.Id == d || d == pacd.Id || d == damd.Id)
// innner join condition
where categoryIds.Any(d => d == dac.Id) && dac.Id == pcm.CategoryId
select p).ToList();
I have write this code into c# but this code not provide proper result. Now I don't know what is problem into this code. If I run this code into sql server, then I get proper result, but in c# code I don't get proper result.
It's been a long time since I wrote a query in LINQ, but I seem to recall that if you wish to model a LEFT JOIN, you have to use DefaultIfEmpty(), otherwise you end up with an INNER JOIN.
See this, it's answer shows where to apply DefaultIfEmpty:
Linq join iquery, how to use defaultifempty
Obviously if you don't correctly model a LEFT JOIN expression, you'll end up with results only when all 3 inputs produce values.
I would also suggest not using .ToList() on each of your source queries, because that's going to manifest data into memory and use LINQ to Objects for your final query. If you remove the .ToList(), LINQ will construct a single database query for the entire process.
Mark
I am fairly new to LINQ and I am struggling to make a multiple JOIN.
So, this is how my database structure looks like:
Now, how should my query look like, if I have a particular Grade and I want to select
{Student.IndexNo, GradeValue.Value}, but if there is no grade value for a particular grade and particular user, null should be returned (Left join)?
The trick to get a LEFT join is to use the DefaultIfEmpty() method:
var otherValue = 5;
var deps = from tbl1 in Table1
join tbl2 in Table2
on tbl1.Key equals tbl2.Key into joinGroup
from j in joinGroup.DefaultIfEmpty()
where
j.SomeProperty == "Some Value"
&& tbl1.OtherProperty == otherValue
select j;
Deliberately posting this in 2015 for newbies looking for solution on google hits. I managed to hack and slash programming my way into solution.
var projDetails = from r in entities.ProjekRumah
join d in entities.StateDistricts on r.ProjekLocationID equals d.DistrictID
join j in entities.ProjekJenis on r.ProjekTypeID equals j.TypeID
join s in entities.ProjekStatus on r.ProjekStatusID equals s.StatusID
join approvalDetails in entities.ProjekApproval on r.ProjekID equals approvalDetails.ProjekID into approvalDetailsGroup
from a in approvalDetailsGroup.DefaultIfEmpty()
select new ProjectDetailsDTO()
{
ProjekID = r.ProjekID,
ProjekName = r.ProjekName,
ProjekDistrictName = d.DistrictName,
ProjekTypeName = j.TypeName,
ProjekStatusName = s.StatusName,
IsApprovalAccepted = a.IsApprovalAccepted ? "Approved" : "Draft",
ProjekApprovalRemarks = a.ApprovalRemarks
};
Produces following SQL code internally
{SELECT [Extent1].[ProjekID] AS [ProjekID]
,[Extent1].[ProjekName] AS [ProjekName]
,[Extent2].[DistrictName] AS [DistrictName]
,[Extent3].[TypeName] AS [TypeName]
,[Extent4].[StatusName] AS [StatusName]
,CASE
WHEN ([Extent5].[IsApprovalAccepted] = 1)
THEN N'Approved'
ELSE N'Draft'
END AS [C1]
,[Extent5].[ApprovalRemarks] AS [ApprovalRemarks]
FROM [dbo].[ProjekRumah] AS [Extent1]
INNER JOIN [dbo].[StateDistricts] AS [Extent2] ON [Extent1].[ProjekLocationID] = [Extent2].[DistrictID]
INNER JOIN [dbo].[ProjekJenis] AS [Extent3] ON [Extent1].[ProjekTypeID] = [Extent3].[TypeID]
INNER JOIN [dbo].[ProjekStatus] AS [Extent4] ON [Extent1].[ProjekStatusID] = [Extent4].[StatusID]
LEFT JOIN [dbo].[ProjekApproval] AS [Extent5] ON [Extent1].[ProjekID] = [Extent5].[ProjekID]
}
I'd like to reproduce the following SQL into C# LinqToSql
SELECT TOP(10) Keywords.*
FROM Keywords
LEFT OUTER JOIN IgnoreWords
ON Keywords.WordID = IgnoreWords.ID
WHERE (DomainID = 16673)
AND (IgnoreWords.Name IS NULL)
ORDER BY [Score] DESC
The following C# Linq gives the right answer.
But I can't help think I'm missing something (a better way of doing it?)
var query = (from keyword in context.Keywords
join ignore in context.IgnoreWords
on keyword.WordID equals ignore.ID into ignored
from i in ignored.DefaultIfEmpty()
where i == null
where keyword.DomainID == ID
orderby keyword.Score descending
select keyword).Take(10);
the SQL produced looks something like this:
SELECT TOP (10)
[t0].[DomainID]
, [t0].[WordID]
, [t0].[Score]
, [t0].[Count]
FROM [dbo].[Keywords] AS [t0]
LEFT OUTER JOIN
( SELECT 1 AS [test]
, [t1].[ID]
FROM [dbo].[IgnoreWords] AS [t1]
) AS [t2]
ON [t0].[WordID] = [t2].[ID]
WHERE ([t0].[DomainID] = 16673)
AND ([t2].[test] IS NULL)
ORDER BY [t0].[Score] DESC
How can I get rid of this redundant inner selection?
It's only slightly more expensive but every bit helps!
I think you can do something like this to eliminate the left join and maybe get more efficiency:
var query = (from keyword in context.Keywords
where keyword.DomainID == ID
&& !(from i in context.IgnoreWords select i.ID).Contains(keyword.WordID)
orderby keyword.Score descending
select keyword)
.Take(10);