Generate Linq dynamically but throw exception when string[] is null - c#

int id = 1;
string[] employerNos = null;
var query = from y in mydb.myTable
where y.ID == id
&& (employerNos == null ? true : employerNos.Contains(y.EmpNo))
select y;
var result = query.ToList();
The string array is null so I think the statement should be true. I don't know why it throws System.NotSupportedException.

Because your query is not LINQ to Objects, so your LINQ query provider has to translate both parts of conditional statement into correct SQL code.
Instead of including condition within query take it out of it:
int id = 1;
string[] employerNos = null;
var query = mydb.MyTable.Where(y => y.ID == id)
if(employerNos != null)
query = query.Where(y => employerNos.Contains(y.EmpNo))
var result = query.ToList();

It's trying to translate that expression into SQL. I believe the problem is that you can't pass a list of strings as a parameter.
so
var query = from y in mydb.myTable
where y.ID == id
&& employerNos.Contains(y.EmpNo))
select y;
Would translate to something like
select * from myTable
where ID=#ID
and EmpNo in ('1','3','5') -- employerNos evaluated before translation to sql
But if you try your query with the null check, you get something like
select * from myTable
where ID=#ID
and (#list is null
or EmpNo in #list) -- this bit is not valid SQL
You can do something like this instead.
var query =
from y in mydb.myTable
where y.ID == id
select y;
if (employerNos != null)
query = query.Where(y => employerNos.Contains(y.EmpNo))

Related

How to search with Linq query in multiples columns and bind into datagridview?

I am using C# and Entity Framework and I would like to select in database with some filter condition. Which comes from a simple SQL query like this:
SELECT *
FROM EMPLOYEE
WHERE ACTIVE = 1 (FNAME LIKE '%KEY%' OR LNAME LIKE '%KEY%' OR ADDRESS LIKE '%KEY%')
ORDER BY LASTUPDAATE DESC;
I using in Linq query as below:
var query = (from e in db.TBLEMPLOYEE
where (e.ACTIVE == 1 AND
(e.FNAME.Contains(text.ToString().Trim())
|| e.LNAME.Contains(text.ToString().Trim())
|| e.ADDRESS.Contains(text.ToString().Trim())))
select e).OrderByDescending(e => c.LASTUPDATE);
if (query.Any())
{
int i = 0;
foreach (EMPLOYEE item in query)
{
i += 1;
int newrow = grid.Rows.Add();
grid.Rows[newrow].Cells[0].Value = item.ID.ToString();
grid.Rows[newrow].Cells[1].Value = i.ToString();
grid.Rows[newrow].Cells[2].Value = item.FNAME.ToString();
grid.Rows[newrow].Cells[3].Value = item.LNAME.ToString();
grid.Rows[newrow].Cells[4].Value = item.ACTIVE.ToString();
}
}
But I get an error in the linq query while running:
The function evaluation requires all threads to run.
Unable to evaluate the expression. Operation not supported. Unknown error: 0x80070057.
Any suggestions please?
Thank you in advance.
Ada.
Fix your query:
var seachText=text.Trim();
var query =db.TBLEMPLOYEE
.Where( e=> (e.ACTIVE == 1)
&& ( ( EF.Functions.Like(e.FNAME,$"%{seachText}%")
|| ( EF.Functions.Like(e.LNAME,$"%{seachText}%")
|| ( EF.Functions.Like(e.ADDRESS,$"%{seachText}%") ) )
.OrderByDescending(c => c.LASTUPDATE)
.ToList();
Here is the working.
var query = (from e in db.TBLEMPLOYEE
where (e.ACTIVE == 1 AND
(e.FNAME.Contains(text.ToString().Trim())
|| e.LNAME.Contains(text.ToString().Trim())
|| e.ADDRESS.Contains(text.ToString().Trim())))
select e).OrderByDescending(e => c.LASTUPDATE).ToList();
Thank you very much all.

How to create dynamic query with for loop?

I want to create query dynamically based on entered number and make it to array to get data from view, so I can create condition to filter it, but my query only works for 3 entered numbers.
var query = from bs in dc.VwResourceAssignments select bs;
var listReqNumber = new[] {123, 456, 789};
My current query is:
if (listReqNumber.Length == 1)
{
query = query.Where(p => p.RequisitionNumber.Contains(listReqNumber[0]));
}
else if (listReqNumber.Length == 2)
{
query = query.Where(p => p.RequisitionNumber.Contains(listReqNumber[0]) ||
p.RequisitionNumber.Contains(listReqNumber[1]));
}
else if (listReqNumber.Length == 3)
{
query = query.Where(p => p.RequisitionNumber.Contains(listReqNumber[0]) ||
p.RequisitionNumber.Contains(listReqNumber[1]) ||
p.RequisitionNumber.Contains(listReqNumber[2]));
}
Is there any way to make it dynamically so I can input requisition number as many as I want?
Let's generalize the problem: if we have an arbitrary listReqNumber array we want to implement
query = query.Where(
p => p.RequisitionNumber.Contains(listReqNumber[0]) ||
p.RequisitionNumber.Contains(listReqNumber[1]) ||
...
p.RequisitionNumber.Contains(listReqNumber[listReqNumber.Length - 1])
);
or - let's get rid of || - we want any item req withing listReqNumber be contained in p.RequisitionNumber
// doesn't compile - just the idea
query = query.Where(p => p.RequisitionNumber.Contains(any req in listReqNumber));
Pity, we can't put any req in listReqNumber but we can swap listReqNumber and p.RequisitionNumber and finally have a valid query:
query = query.Where(p => listReqNumber.Any(req => p.RequisitionNumber.Contains(req)));
How about if you use directly like this:
var result = from p in query where listReqNumber.Contains(p.RequisitionNumber) select p;
var data = query.Where(q =>
q.RequisitionNumber.Any(w => listReqNumber.Contains(w))
);
You can use dynamic linq. Link is here:
https://weblogs.asp.net/scottgu/dynamic-linq-part-1-using-the-linq-dynamic-query-library
then have a function like below:
string GetQuery(int num)
{
return $"p.RequisitionNumber.Contains(listReqNumber[{num}])";
}
Create a query like below in Main:
string testQuery = string.Empty; // or use StringBuilder then convert to string.
int length = 4;
for (int temp = 1; temp <= length; temp ++)
testQuery += testQuery.Length == 0 ? GetQuery(temp - 1) : " || " + GetQuery(temp);
then use dynamic linq like:
var query = northwind.Products
.Where(testQuery).OrderBy("SupplierID");

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();

how to use Linq " NOT IN"

I'm using Entity Framework
So I want to write a sql command using two tables - tblContractor and tbSiteByCont tables.
It looks like this in SQL
SELECT PKConID, Fname, Lname
FROM tblContractor
WHERE (PKConID NOT IN
(SELECT FKConID
FROM tbSiteByCont
WHERE (FKSiteID = 13)))
but I don't know how to write in Linq.
I tried like this
var query1 = from s in db.tblSiteByConts
where s.FKSiteID == id
select s.FKConID;
var query = from c in db.tblContractors
where c.PKConID != query1.Any()
select Contractor;
But this doesn't work.
So how should I write it? What is the procedure? I'm new to Linq.
var _result = from a in tblContractor
where !(from b in tbSiteByCont
where FKSiteID == 13
select b.FKConID)
.Contains(a.PKConID)
select a;
or
var siteLst = tbSiteByCont.Where(y => y.FKSiteID == 13)
.Select(x => x.FKConID);
var _result = tblContractor.Where(x => !siteLst.Contains(x.PKConID));
I'd use a HashSet, it ensures you only evaluate the sequence once.
var result = from p in tblContractor
let hasht = new HashSet<int>((from b in tbSiteByCont
where b.FKSiteID == 13
select b.PKConID).Distinct())
where !hasht.Contains(p.PKConID)
select p;
may this work too
var _result = from a in tblContractor
.Where(c => tbSiteByCont
.Count(sbc => sbc.FKSiteID == 13 && c.PKConID == sbc.FKConID) == 0)

How to write Linq depending if a value is provided or not

I am trying to write a LINQ statement with some optional where clauses. This is for a search. The user can select a specific site to search or search against all sites:
var query =
_db.STEWARDSHIP
.OrderBy(r => r.SITE.SITE_NAME)
.Where(r => r.SITE_ID == SiteId)
.Where(r => r.VISIT_TYPE_VAL.VISIT_TYPE_ID == VisitTypeId)
.Select(r => new
{
id = r.STEWARDSHIP_ID,
name = r.SITE.SITE_NAME,
visit_type = r.VISIT_TYPE_VAL.VISIT_TYPE_DESC,
visit_date = r.VISIT_DATE
});
return query;
So when the method gets SiteId = 14, for instance, no problem. However, when it gets SiteId = null, then that where clause should not be considered.
Thanks
Eric
That's easy:
var query = _db.STEWARDSHIP.OrderBy(r => r.SITE.SITE_NAME);
if (SiteId != null)
{
query = query.Where(r => r.SITE_ID == SiteId);
}
query = query.Where(r => r.SITE.SITE_TYPE_VAL.SITE_TYPE_ID == SiteTypeId)
.Select(r => new
{
id = r.STEWARDSHIP_ID,
name = r.SITE.SITE_NAME,
visit_type = r.VISIT_TYPE_VAL.VISIT_TYPE_DESC,
visit_date = r.VISIT_DATE
});
return query;
This works because queries compose nicely - and they really only represent queries; it's only when you try to fetch data from them that the query is actually executed.
Can't you just edit the where clause to something like
.Where(r=>SiteId == null || r.SiteId == SiteId)
you can use where clause in one statement ..like this ..
.Where(r => SiteID == null || r.SITE_ID == SiteID)
I'm stealing a trick from TSQL. Just check for the null value as well.
...
.Where(r => SiteID == null || r.SITE_ID == SiteID)
...
The SQL example is this:
WHERE (SITE_ID = #given OR #given IS NULL) --return matches or all
Though if that value is mutable and you want the value at the time the query was built, try this instead:
var localSiteID = SiteID;
...
.Where(r => localSiteID == null || r.SITE_ID == SiteID)
...

Categories