I am left joining expected records to a returned set and attempting to determine if the expected column was updated correctly. The Column to be updated is determined by a string in the expected row.
Problem: I have a compile error I don't understand.
Cannot resolve symbol dbRow
(where bold/ bracketed by ** in QtyUpdated field).
var x = from addRow in expected.AsEnumerable()
join dbRow in dtDB.AsEnumerable()
on
new { key1= addRow[0], key2=addRow[1] ,key3=addRow[3] }
equals
new { key1=dbRow["TransactionID"],
key2=dbRow["TransactionItemID"],
key3=dbRow["DeliverDate"]
}
into result
from r in result.DefaultIfEmpty()
select new {TID = addRow[0], ItemID = addRow[1], DeliveryDate= addRow[3],
QtyUpdated= (
addRow[6].ToString() == "Estimated" ? **dbRow**["EstimatedQuantity"] == (decimal)addRow[5] :
addRow[6].ToString() == "Scheduled" ? **dbRow**["ScheduledQuantity"]==(decimal)addRow[5] :
addRow[6].ToString() == "Actual" ? **dbRow**["ActualQuantity"]== (decimal)addRow[5] : false)
};
I know this seems wonky, its a tool for Q/A to test that the Add function in our API actually worked.
Yes, dbRow is only in scope within the equals part of the join. However, you're not using your r range variable - which contains the matched rows for the current addRow... or null.
Just change each dbRow in the select to r. But then work out what you want to happen when there aren't any matched rows, so r is null.
Related
I'm a newcomer in Linq C#.
I have a scenario where I need to check part of a sentence is equal to another value of the field.
I use IndexOf to get part of the sentence in the left join condition. The result is good when any data match between 'a' and 'c'. But when data does not exist in 'c', then the value of 'test' is all data of table dbData.Data3.
Can anyone know what I'm missing here?
var test = (from a in dbData.Data2
let COLODescIndexOfSpace = a.LongKeywordDesc .IndexOf(' ') < 0 ? 0 : a.LongKeywordDesc .IndexOf(' ')
join c in dbData.Data3 on
new
{
KeywordDesc = a.LongKeywordDesc .Substring(0, COLODescIndexOfSpace),
Stsrc = true
}
equals new
{
KeywordDesc = c.KeywordDesc,
Stsrc = (c.Stsrc != AppConstant.StrSc.Deactive)
}
into c_leftjoin
from c in c_leftjoin.DefaultIfEmpty()
where lmsCourseOutlineIds.Contains(a.CourseOutlineID)
select new
{
data = a.KeywordID,
data2 = c.KeywordID,
data3 = c.KeywordDesc,
}).ToList();
this is some example of data
here
and this is what I expect as result
here
Here is one way to solve the problem (I will leave it to my Linq betters figure out a solution purely with Linq). In short, just create a SQL View in the database and call it to get your desired results.
For example, in the database:
create view dbo.GetSentencesByKeywords
as
select
d2.ID,
d2.longKeywordDesc,
d3.keywordDesc,
d3.AdditionalInfo
from
dbo.Data2 d2
left join
(
select
ID,
keyWordDesc,
AdditionalInfo,
len(keyWordDesc) as keyWordLength
from dbo.Data3 data3
) d3
on substring(d2.longKeywordDesc, 0, d3.keyWordLength + 1) = d3.keywordDesc
Now the linq is nice and simple (assuming you can re-scaffold the dbContext to add it to your models, or otherwise can get it in there one way or another):
var x = dbData.GetSentencesByKeywords.Select(c => c);
SELECT
FW1.id, count(*)
FROM
firmware FW1
LEFT JOIN
firmware FW2 ON FW1.firmware_group_id = FW2.firmware_group_id
AND FW1.br_date < FW2.br_date
AND FW2.[public]= '1'
GROUP BY
FW1.id
I am looking to convert into linq query. As I know less than symbol cannot be converted into Linq query. Please suggest how to do it. I have a string date and I need to compare into linq.
As you said, Linq does not support other types of join outside of EquiJoin. Docs is pretty clear on what you can do to bypass this:
you could use a simple cross join (a cartesian product), then applying in the where clause the conditions for your non-equijoin.
Or you could use a temporary variable to store a new table with only the attributes you need for your query and, like before, applying the conditions in the where clause.
In your case, a possible Linq query could be this one:
from f1 in firmwares
from f2 in firmwares
let f1_date = DateTime.Parse(f1.Dt)
let f2_date = DateTime.Parse(f2.Dt)
where f1.Group_Id == f2.Group_Id && f1_date < f2_date
group f1 by f1.Id into fres
select new {
Id = fres.Key,
Count = fres.Count()
};
However I am still thinking how to emulate the LEFT JOIN without casting it to a group join.
Of course the < symbol can be used. Just use method syntax instead of query syntax!
Every FW1 has zero or more FW2s. Every FW2 belongs to exactly one FW1. This one-to-many is implemented using foreign key firmware_group_id.
Apparently you want all FW1s, each with the number of its FW2s, that have a property public with a value equal to 1 and a property br-date larger than the value of the br-date of the FW1.
Whenever you want an item with its many sub-items (using a foreign key), like s School with its Students, a Customer with his Orders, a Book with his Pages, you'll need Enumerable.GroupJoin
var result = FW1.GroupJoin(FW2, // GroupJoin FW1 and FW2
fw1 => fw1.firmware_group_id, // from fw1 take the primary key firmware_group_id
fw2 => fw2.firmware_group_id, // from fw2 take the foreing key firmware_group_id
(fw1, fw2s) => new // from every fw1, with all its matching fw2s
{ // make one new object containing the following properties:
Id = fw1.Id,
// Count the fw2s of this fw1 that have public == 1
// and a date larger than fw1.date
Count = fw2.Where(fw2 => fw2.Public == 1 && fw1.br_date < fw2.br_date)
.Count(),
});
Note:
I am writing a C# LINQ to perform a left join as below
from pt in context.PD
join label in context.Labels on new { pt.LabelId, pt.SubLabelId } equals
new { label .LabelId, label.SubLabelId }
into clientLabel
from status in clientLabel
select new LabelDto{
LastStatus = pt.dubs> 0?
clientLabel.FirstOrDefault(s => s.type== 1).Label
: clientLabel.FirstOrDefault(s => s.type== 2).Label
LastSubStatus = pt.dubs> 0 ?
clientLabel.FirstOrDefault(s=>s.type==1).SubLabel
: clientLabel.FirstOrDefault(s => s.type== 2).SubLabel
};
right table(Labels) may have more than one matching rows in which case row to be returned will be selected based on type(1 or 2) but the above query returns duplicate row one with type = 1 and other with type = 2 where I want only one row either type = 1 or 2
What do I change to get the desired output?
I want to bring back a set of results based on the characters typed in by a user.
I've created the following query which does what I want inside of LINQpad4
LinqPad
var PostCodes = (from OA in OrganisationAddresses
join OV in OpportunityVersions on OA.ID equals OV.LocationID
where OA.CurrentVersion.PostCode.Contains("LH")
select OV.ID).ToList();
PostCodes.Dump();
The user enters the string of "LH" and I get 13 results back
Now when I place a very similar query into my production environment, if I type in "LH" I get zero results. it only returns matches when the full string is entered such as "LH1 1HP"
Production
Builder = Builder.And(o =>
(from OA in Context.OrganisationAddresses
join OV in Context.OpportunityVersions on OA.ID equals OV.LocationID
where Options.PostCode.Contains(OA.CurrentVersion.PostCode)
&& OV.ID == o.CurrentVersionID select OV.ID).Any());
I am using SQLServer2012 and LINQ to Entities. I would like to know what could be causing this and how to fix it.
Thanks
It seems you swapped some code by mistake
Builder = Builder.And(o =>
(from OA in Context.OrganisationAddresses
join OV in Context.OpportunityVersions on OA.ID equals OV.LocationID
where OA.CurrentVersion.PostCode.Contains(Options.PostCode) // fix in this string
&& OV.ID == o.CurrentVersionID select OV.ID).Any());
And also you have additional condition in prod:
&& OV.ID == o.CurrentVersionID
I am trying to join tables using LINQ by matching columns where a column in the joined table is equal to a variable or the variable is null (at which point the join still needs to happen just not on that field).
My LINQ is something like:
var data = (
from lt in cxt.CmsPageRow
join page in cxt.CmsPage on new { lt.CmsPageID, cmsSiteID.Value } equals new { page.CmsPageID, page.CmsSiteID }
...
cmsSiteID is a nullable INT.
I cannot compile my code as it is complaining about "Type inference failed in the call to 'Join'."
On top of that I need to only join on page.CmsSiteID when cmsSiteID is not null. If cmsSiteID is null then the join on lt.CmsPageID still needs to happen.
* EDIT *
The question has kind of changed now. I can get it to do what I want by using a WHERE clause on the join in my LINQ.
join page in cxt.CmsPage.Where(p=>(cmsSiteID==0||p.CmsSiteID==cmsSiteID)) on lt.CmsPageID equals page.CmsPageID
However, this still runs slow. If I change the parameter passed through to a literal it executes instantly.
Slow runner
(#p__linq__1 = 0 OR [Extent2].[CmsSiteID] = #p__linq__1)
Fast runner
(267 = 0 OR [Extent2].[CmsSiteID] = 267)
Is there a way to speed this up?
join in LINQ assumes an inner join (no nulls). Try pulling the null stuff out into separate where clauses. I think something along these lines should work for what you're describing.
from lt in cxt.CmsPageRow
join page in cxt.CmsPage on lt.CmsPageID == page.CmsPageID
where cmsSiteID == null ||
(cmsSiteID != null && (page.CmsSiteID == null || page.CmsSiteId == cmsSiteID.Value))
select ...
Update
I didn't realize that performance was an issue for you. In that case, I'd suggest creating a different query structure based on values that are known at run-time and don't depend on individual rows:
var rows =
from lt in cxt.CmsPageRow
join page in cxt.CmsPage on lt.CmsPageID == page.CmsPageID
select new {lt, page};
if (cmsSiteID != null)
{
rows = rows.Where(r => r.page.CmsSiteID == null ||
r.page.CmsSiteId == cmsSiteID.Value));
}
var data = rows.Select(...);
Also, if your data context is set up right, you should be able to use navigation properties to simplify your code somewhat.
var rows = ctx.CmsPageRow;
if (cmsSiteID != null)
{
rows = rows.Where(r => r.CmsPage.Any(p => p.CmsSiteID == null ||
p.CmsSiteId == cmsSiteID.Value));
}
var data = rows.Select(...);