LINQ query left join multiple columns with null check failing - c#

pt and emod are both in-memory queries returning a tElementRowValuesDBModel type. Both are not executed until after this statement shown.
If I debug and look at the returned results separately, both run successfully and return values.
Could I use Except or not in sub-query, sure!, but I just want to understand if this is a bug or there's a workaround.
Strangely, if i remove any reference to the tempe object, everything runs OK.
Am I missing something?
var RealNewNodes = from p in pt
join e1 in emod on new{X1 = p.tElementRowsDBModelID, X2 = p.tAttributesDBModelID, X3 = p.AttributeValue} equals new {X1 = e1.tElementRowsDBModelID, X2 = e1.tAttributesDBModelID, X3 = e1.AttributeValue } into tempt
from tempe in tempt.DefaultIfEmpty()
where tempe == null //if i remove this line, works OK
select new tElementRowValuesDBModel{
tElementRowsDBModelID = p.tElementRowsDBModelID,
tAttributesDBModelID = p.tAttributesDBModelID,
AttributeValue = p.AttributeValue,
ID = (tempe!=null ? 1 : 0 )//if i remove this line, works OK
};

Perhaps if you didn't do the join, since your join/where means find pt without matches, it would work:
var RealNewNodes = from p in pt
where !emod.Any(e1 => p.tElementRowsDBModelID == e1.tElementRowsDBModelID && p.tAttributesDBModelID == e1.tAttributesDBModelID && p.AttributeValue == e1.AttributeValue)
select new tElementRowValuesDBModel {
tElementRowsDBModelID = p.tElementRowsDBModelID,
tAttributesDBModelID = p.tAttributesDBModelID,
AttributeValue = p.AttributeValue,
ID = 1
};

Related

C# Linq - Issue with joining3 tables - Error CS1941

I'm trying to fetch the records based on this query
documentList = await (from doc in _dbContext.Documents
join doce in _dbContext.StudentEnrollments on doc.ID equals doce.ID
join enr in _dbContext.Enrollments on doce.EnrollmentID equals enr.Id
where enr.name != null
&& (enr.faiedlattemps = 1 || enr.ReferenceNumber != null)
select doc);
I am getting the
"Correlates the elements of two sequences based on matching key error. The default equality comparer is used to compare keys"
I tried with this as well
var result = from x in _dbContext.Documents
join y in _dbContext.StudentEnrollments
on new { X1 = x.ID } equals new { X1 = y.ID}
join z in _dbContext.Enrollments on new { Z1 = y.EnrollmentID } equals new { Z1 = z.Id}
select new
{
/// Columns
};
Getting the same error

How do you Left join 'Is Not Null' using linq?

I am having some issues getting my LINQ statement to work. I am left joining a table, secondTable, where one of the columns can be null but I only need the records where this column is not null. I'm not sure how to get the following into a LINQ expression
LEFT JOIN secondTable b ON a.ID = b.oneTableID AND b.name IS NOT NULL
So far my LINQ is:
var list = await (from one in dbRepository.oneTable
join two in dbRepository.secondTable
on new { name = one.name, phone = one.phone, height = { is not null} } equals new
{ name = two.name, phone = two.phone, height = two.height
into temp
from two in temp.DefaultIfEmpty()
select new.....
Any Ideas?
EDIT 1: I was able to find a solution.
var list = await (from one in dbRepository.oneTable
join two in dbRepository.secondTable
on new { name = one.name, phone = one.phone, height = false } equals new
{ name = two.name, phone = two.phone, height = string.IsNullOrEmpty(two.height)}
into temp
from two in temp.DefaultIfEmpty()
select new.....
You have to use SelectMany possibility to create LEFT JOIN:
var query =
from one in dbRepository.oneTable
from two in dbRepository.secondTable
.Where(two => two.name = one.name && two.phone == one.phone
&& two.height != null)
.DefaultIfEmpty()
select new.....
Try this one:
var list = await (from one in dbRepository.oneTable
join two in dbRepository.secondTable
on new { name = one.name, phone = one.phone}
equals new
{ name = two.name, phone = two.phone}
into temp
from two in temp.DefaultIfEmpty()
where one.height == null || one.height = two.height
select new.....

Linq to SQL grouping with embedded lists causes too many queries

I am developing a query to grab and join some SQL tables in C# and am having some trouble with grouping and enumerables within the dataset. My query is below. This gives me the data in the format I'm looking for, but it takes way too long when I try to add the enumerated list as indicated below. When I look under the hood I can see it is executing way too many SQL queries. I'd like to get it to just one. Using LinqPad:
void Main()
{
var nightlyRuns = (from a in LoadTestSummaries
join b in LoadTestTestSummaryData
on a.LoadTestRunId equals b.LoadTestRunId
where a.TargetStack == "LoadEnv" &&
a.TestGuid != null &&
a.StartTime != null &&
a.LoadTestRunId != null
orderby a.StartTime
group new {a, b} by new
{
a.TestGuid,
a.Name,
a.Description,
a.StartTime,
a.Duration,
a.NumAgents,
a.NumHosts,
a.PassFail,
a.ResultsFilePath,
a.Splunk
}
into g
let scenarioStart = g.Min(s => s.a.StartTime) ?? g.Min(s => s.a.DateCreated)
let testCases = g.Select(s => s.b)
orderby scenarioStart
select new
{
TestGuid = g.Key.TestGuid,
ScenarioRun = new
{
Name = g.Key.Name,
Description = g.Key.Description,
StartTime = scenarioStart,
Duration = g.Key.Duration,
NumAgents = g.Key.NumAgents,
NumHosts = g.Key.NumHosts,
Result = g.Key.PassFail,
ResultsFilePath = g.Key.ResultsFilePath,
SplunkLink = g.Key.Splunk,
// PROBLEM: Causes too many queries:
TestRuns = from t in testCases select t.TestCaseId
}
}).ToLookup(g => g.TestGuid, g => g.ScenarioRun);
nightlyRuns["ba593f66-695f-4fd1-99c3-71253a2e4981"].Dump();
}
The "TestRuns" line is causing the excessive queries. Any idea what I am doing wrong here?
Thanks for any insight.
Tough answer to test but I think we can avoid the grouping and multiple queries with something like this: (https://msdn.microsoft.com/en-us/library/bb311040.aspx)
var nightlyRuns = (from a in LoadTestSummaries
join b in LoadTestTestSummaryData
on a.LoadTestRunId equals b.LoadTestRunId
where a.TargetStack == "LoadEnv" &&
a.TestGuid != null &&
a.StartTime != null &&
a.LoadTestRunId != null
into testGroup
select new
{
TestGuid = a.TestGuid,
ScenarioRun = new
{
Name = a.TestGuid,
Description = a.Description,
StartTime = a.StartTime ?? a.DateCreated,
Duration = a.Duration,
NumAgents = g.Key.NumAgents,
NumHosts = a.NumHosts,
Result = a.PassFail,
ResultsFilePath = a.ResultsFilePath,
SplunkLink = a.Splunk,
// PROBLEM: Causes too many queries:
TestRuns =testGroup
}
}).OrderBy(x=>x.StartTime).ToLookup(x => x.TestGuid, x => x.ScenarioRun);
nightlyRuns["ba593f66-695f-4fd1-99c3-71253a2e4981"].Dump();

ASP.NET C# Linq to SQL appears to be doing OR instead of AND

I have a Linq to SQL query that appears to be producing the right SQL but the results are not the same as if I run the query directly in SQL Management Studio Express.
My C# code is:
using (DataClasses1DataContext dc = new DataClasses1DataContext())
{
var q = from s in dc.tbContentDetails
where s.Active == true && s.SupervisorApproved == true
select s;
foreach (string st in result2)
{
q = from s in q
where s.Fuzzy.Contains("|" + st + "|")
select s;
}
lblResults.Text = q.Count().ToString();
repResults.DataSource = q.Take(10);
repResults.DataBind();
}
The SQL generated is (field list replaced with "..."):
SELECT [t0].[ContentDetailId], ... FROM [dbo].[tbContentDetail] AS [t0]
WHERE ([t0].[Fuzzy] LIKE #p0) AND ([t0].[Fuzzy] LIKE #p1)
AND ([t0].[Fuzzy] LIKE #p2) AND ([t0].[Active] = 1)
AND ([t0].[SupervisorApproved] = #p3)
If I run this query directly I get the expected results but when it runs in Linq I get many more search results, exactly the same as if all the "Fuzzy LIKE" in the where clause are joined with OR not AND.
If I put all the "Fuzzy.Contains" in to one Linq statement it does work fine but as I need to allow for any number of keywords I chose this way of building my query. Perhaps this is not the right way to do this or is there some other problem here?
It seems, like delayed execution strikes back. The filter is executed, after loop is completed. Your's filter uses variable ST during execution, that, at the moment of execution will have value, equals to last value in the result2 array.
Quick fix would be to copy st variable to local variable:
using (DataClasses1DataContext dc = new DataClasses1DataContext())
{
var q = from s in dc.tbContentDetails
where s.Active == true && s.SupervisorApproved == true
select s;
foreach (string st in result2)
{
var tmp = st;
q = from s in q
where s.Fuzzy.Contains("|" + tmp + "|")
select s;
}
lblResults.Text = q.Count().ToString();
repResults.DataSource = q.Take(10);
repResults.DataBind();
}
This explains, why foreach statement behaves like this, inside closure.
You are setting q inside the loop so essentially when you are setting the data source, q is set just by the last iteration of the loop making all other iterations useless. I'm not sure if that's what you desire. I'm just guessing but I think you need something like this
var q = from s in dc.tbContentDetails
where s.Active == true && s.SupervisorApproved == true &&
results2.Any(r => s.Fuzzy.Contains("|" + r + "|")
select s;
EDIT: not very optimal but for linq to sql, try this
var q = from s in dc.tbContentDetails
where s.Active == true && s.SupervisorApproved == true &&
select s;
q = q.ToList().Where(s => results2.Any(r => s.Fuzzy.Contains("|" + r + "|"));
I think "q" gets reused which is causing the behaviour.
try changing q = from s in q to var q2 = from s in q
EDIT 2 (after reading the comment) - remove the foreach and try this:
var Result = from s in dc.tbContentDetails
from st in result2
where s.Active == true and s.SupervisorApproved == true
and s.Fuzzy.Contains("|" + st + "|")
select s;

Help creating a LINQ to SQL query

I'm populating a FormView by setting the datasource to an IQueryable object that I get by doing a LINQ query. Basically it returns the number of employees that hold a certain "Position" within a certain "Shift".
int shiftID = 1;
var shiftCount = from x in context.Employees.Take(1)
select new
{
ManagerCount = ((from p in context.Persons
where p.PositionID == 1 && p.ShiftID == shiftID && p.IsEmployee == true
select p.PersonId).Count(),
PartTimeCount = ((from p in context.Persons
where (p.PositionID == 2 || p.PositionID == 3) && p.ShiftID == shiftID && p.IsEmployee == true
select p.PersonId).Count(),
etc, etc...
};
That part works fine. However, when I want to get the number of employees for all shifts, I can't quite figure out how to do it:
//Get all shifts 1, 2, and 3
var shiftCount = from x in context.Employees.Take(1)
select new
{
ManagerCount = ((from p in context.Persons
where p.PositionID == 1 && (p.ShiftID == 1 || p.ShiftID == 2 || p.ShiftID == 3) && p.IsEmployee == true
select p.PersonId).Count()
};
That doesn't work though because it of course returns 3 values and gives the Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression. error.
So I need to get the sum of the three values returned (it's not always three though, it depends on the position).
I've looked at various ways using Sum and LINQ grouping, but can't quite seem to work it out. Can anybody point me in the right direction?
I've got a few answers for you, but first it seems like you have something wrong with your first part of your query. You're doing .Take(1) on Employees which will give you only one x value. Since you don't use x in the remainder of your query then using Employees is redundant.
Now, since all of your queries are quite similar I've tried to remove repetition. The first thing to do is to get a common filter for the shifts you are filtering on.
If you have a single shift, use this:
int shiftID = 1;
var shifts = new [] { shiftID, };
If you have multiple shifts, use this:
var shifts = new [] { 1, 2, 3, };
Either way you end up with an array of integers representing the shifts you want to filter against. All of the below answers require this shifts array.
Then define a query for the employees in those shift, regardless of position for now.
var employeesInShifts =
from p in context.Persons
where p.IsEmployee
where shifts.Contains(p.ShiftID)
select p;
So you can then get the shift counts like so:
var shiftCount =
new
{
ManagerCount = employeesInShifts
.Where(p => p.PositionID == 1)
.Count(),
PartTimeCount = employeesInShifts
.Where(p => p.PositionID == 2 || p.PositionID == 3)
.Count(),
// etc
};
Perhaps a better alternative for you though, would be to turn the employeesInShifts query into a dictionary and then just pluck the values from the dictionary.
var employeesInShifts =
(from p in context.Persons
where p.IsEmployee
where shifts.Contains(p.ShiftID)
group p by p.PositionID into gps
select new
{
PositionID = gps.Key,
Count = gps.Count(),
})
.ToDictionary(pc => pc.PositionID, pc.Count);
var shiftCount =
new
{
ManagerCount = employeesInShifts[1],
PartTimeCount = employeesInShifts[2] + employeesInShifts[3],
// etc
};
The downside to this approach is that you really should check that the dictionary has values for each PositionID before getting the values.
That can be fixed by introducing an array of position ids that you want the dictionary to have and joining your results on that.
var positionIDs = new [] { 1, 2, 3, };
var employeesInShifts =
(from p in context.Persons
where p.IsEmployee
where shifts.Contains(p.ShiftID)
where positionIDs.Contains(p.PositionID)
select p).ToArray();
var allPositionEmployeesInShifts =
from pid in positionIDs
join p in employeesInShifts on pid equals p.PersonId into gps
select new
{
PositionID = pid,
Count = gps.Count(),
};
var countOfPositionID =
allPositionEmployeesInShifts
.ToDictionary(x => x.PositionID, x => x.Count);
var shiftCount =
new
{
ManagerCount = countOfPositionID[1],
PartTimeCount = countOfPositionID[2] + countOfPositionID[3],
// etc
};
Now that guarantees that your final dictionary will contain the counts of all of the position ids that you are wanting to query.
Let me know if this works or if you really needed to join on the Employees table, etc.
I would recommend looking at the Group By examples here:

Categories