How to create dynamic query with for loop? - c#

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

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 can I filter a simple LINQ query and use it in a more complexe LINQ query

i have this logic in one of my function but it does 3 roundtrips to the database. How can i convert the query to make only one query to the database?
var saisonTouristiqueId = 1;
var currentSaison = this.appContext.SaisonTouristiques.FirstOrDefault(x => x.SaisonTouristiqueId == saisonTouristiqueId);
var nextSaison = (from saison in this.appContext.SaisonTouristiques
where saison.DebutSaison > currentSaison.FinSaison
orderby saison.DebutSaison
select saison).FirstOrDefault();
if (nextSaison != null)
{
var forfaits = from forfait in this.appContext.Forfaits
where forfait.ComposantForfaits.Any(x => x.SaisonTouristiqueId == nextSaison.SaisonTouristiqueId)
select forfait;
return forfaits.ToList();
}
Only use FirstOrDefault when in the subsequent query, to allow all of the execution to be deferred.
var saisonTouristiqueId = 1;
var currentSaison = this.appContext.SaisonTouristiques
.Where(x => x.SaisonTouristiqueId == saisonTouristiqueId);
var nextSaison = from saison in this.appContext.SaisonTouristiques
where saison.DebutSaison > currentSaison.FirstOrDefault().FinSaison
orderby saison.DebutSaison
select saison;
var forfaits = from forfait in this.appContext.Forfaits
where forfait.ComposantForfaits.Any(x => x.SaisonTouristiqueId == nextSaison.FirstOrDefault().SaisonTouristiqueId)
select forfait;
return forfaits.ToList();

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

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

Combine results from multiple queries to as single queryable

I want to search through a user database and order my results according to how precise the match is. Exact matches on a users name should appear in the result before single word matches, as an example.
This is what i have (the variable 'value' contains a search term and 'query' contains an initial queryable i want to modify)
var values = value.Split(new [] {' ','\t', '\n', '\r'});
var q1 = query.Where(u => u.Id == valueAsInt || u.ExternalId == valueAsInt);
var q2 = query.Where(u => u.Name.Contains(value) || u.Username.Contains(value));
var q3 = query.Where(u => values.All(i => u.Name.Contains(i)) || values.All(i => u.Username.Contains(i)));
var q4 = query.Where(u => values.Any(i => u.Name.Contains(i)) || values.Any(i => u.Username.Contains(i)));
However, I now want to combine the results of q1 through q4 and have a new queryable which i can pass along. I also want to preserve the order of my queries, and frankly I have no idea how to go about doing this..
Answered by Silas Hansen in the comments
You should use ranking. e.g.
var result = query.Select(u =>
{
if (u.Id == valueAsInt || u.ExternalId == valueAsInt)
return new {Rank = 1, Item = u};
if (u.Name.Contains(value) || u.UserName.Contains(value) )
return new {Rank = 2, Item = u};
//Add the other conditions in here
return new {Rank = 3, Item = u};
}).OrderBy(u => u.Rank).Select(u => u.Item);

LINQ: How can I shorten my code?

I have done some LINQ, it works great but I'm not a fan of this type of coding, I would like to shorten it down, but not quite sure how to.
Does anyone know how I can shorten this section of code? I've heard of predicates before but not quite sure how to implement them?
List<Voucher> list = new List<Voucher>();
if (String.IsNullOrEmpty(Search.SearchText) && Search.Status == 0)
{
list = (from voucherslist in db.Vouchers
//where voucherslist.Status != (int)VoucherStatus.Removed
select voucherslist)
.Take(100)
.ToList();
}
if (!String.IsNullOrEmpty(Search.SearchText) && Search.Status ==0)
{
list = (from voucherslist in db.Vouchers
where voucherslist.Title.Contains(Search.SearchText)
select voucherslist).Take(100).ToList();
}
if (String.IsNullOrEmpty(Search.SearchText) && Search.Status > 0)
{
list = (from voucherslist in db.Vouchers
where voucherslist.Status == Search.Status
select voucherslist).Take(100).ToList();
}
if (!String.IsNullOrEmpty(Search.SearchText) && Search.Status > 0)
{
list = (from voucherslist in db.Vouchers
where voucherslist.Status == Search.Status
&& voucherslist.Title.Contains(Search.SearchText)
select voucherslist).Take(100).ToList();
}
// Convert
ret = VouchersConverter.Convert(list);
// Get Business Details
foreach (ENT_Voucher item in ret)
item.BusinessDetails = this._businessesBLL.GetBusinessDataByID(item.BusinessID);
// Refine and sort
ret = ret.Where(x=>x.BusinessDetails.Accept == true)
.OrderByDescending(x => x.Status.Equals(1))
.ThenByDescending(x => x.StartDate).ToList();
To remove the repetition, first set up your list.
list = (from voucherslist in db.Vouchers
//where voucherslist.Status != (int)VoucherStatus.Removed
select voucherslist);
Then add the title search if you need it:
if (!String.IsNullOrEmpty(Search.SearchText))
{
list = list.Where(x => x.Title.Contains(Search.SearchText));
}
And the status search:
if (Search.Status > 0)
{
list = list.Where(x => x.Status == Search.Status);
}
And finally, take your 100 and flatten it to a list.
list = list.Take(100).ToList();
The thing to bear in mind is that this will not actually construct and execute the SQL query until the .ToList() call, and the SQL that will be executed will contain all of the filtering you have concatenated together.
Your current logic looks a bit broken to me, but I suspect you want:
var query = db.Vouchers;
if (...)
{
query = query.Where(v => v.Title.Contains(Search.SearchText);
}
if (...)
{
query = query.Where(v => v.Status == Search.Status);
}
// etc
List<Voucher> list = query.Take(100).ToList();
Using multiple calls to Where will effectively apply an "AND" on all the filters.

Categories