IN condition in LINQ to replace multiple OR conditions [duplicate] - c#

This question already has answers here:
Linq to SQL how to do "where [column] in (list of values)"
(6 answers)
Closed 6 years ago.
I have an entity framework query as shown below. Here I am trying to find entities for which there is no fax information for primary and secondary users. I am using two person_role_cd conditions using “OR”.
What is the best LINQ way to make the same logic with something like SQL “IN” condition? For example, p.person_role_cd IN (“Primary”, “Secondary” )
query = query
.Where(l => l.loan_documents
.FirstOrDefault(d => d.document_type_cd == “Application”)
.loan_persons.Any(p =>
(
p.fax_no != null
&&
(
p.person_role_cd == “Primary”
||
p.person_role_cd == “Secondary”
)
)
) == false
);

First create an array with all strings you want to check against.
var targetList = new[] {"Primary", "Secondary"};
Then Replace your OR condition with
targetList.Contains(p.person_role_cd);

Caveat: Doing stuff like this causes problems with Linq to Entities
That said, the first thing that comes to mind is use a List with Contains (which is basically what the provided SQL does)
List<string> roles = new List<string>() { "Primary", "Secondary" };
query = query
.Where(l => l.loan_documents
.FirstOrDefault(d => d.document_type_cd == “Application”)
.loan_persons.Any(p => p.fax_no != null && roles.Contains(p.person_role_cd));
Couldn't quite tell where your negation belongs, left as exercise to the reader. The reason it may not work with Linq to Entities is that translating List.Contains (especially off of a local variable) to SQL is the kind of task it likes to give up on. If your types are literally two items, I would just keep the condition as is (apart from considering an enum for my types instead of strings)

Related

How to filter collection in linq based on some subobjects in that list

I want to get only those employees that have at least one service, and which that service is younger than current date (dt)
I tried with .Any() but it returns me all employees with all services (it doesnt check that date)
var employees =
employeeService.GetAllActiveEmployeesForCompanyForLocation(companyId, location.Id)
.Where(x => x.IsCounter && x.Services != null && x.Services.Count > 0 &&
x.Services.Any(u => u.ActiveTo >= dt.Value));
I want to filter just those employees which have at least one service or more where ActiveTo is not in the past (dt is a current datetime.now)
You can chain multiple .Where and .Select statements after one another. Your LINQ query is very hard to read without more specific information about your objects.
To make it more readable, I would suggest splitting your requirements into separate queries, like so:
var employeesWithActiveServices =
employeeService.GetAllActiveEmployeesForCompanyForLocation(companyId, location.Id)
.Where(e => e.IsCounter && e.Services.Count >= 1)
.Select(e => e.Services.Contains(s => s.ActiveTo >= DateTime.Now)).ToList();
Notice how I removed your e.Services != null check. It is redundant when you're already checking e.Services.Count.
This was made quickly of the top of my head, so you may need to tweak it to suit your needs.
It is still a hard LINQ query to read without seeing the objects it is querying, but this at least makes the query itself more readable.
Try to remove any additional null, Count checks, otherwise SQL will be complex and slow:
var employees =
employeeService.GetAllActiveEmployeesForCompanyForLocation(companyId, location.Id)
.Where(x => x.IsCounter &&
x.Services.Any(u => u.ActiveTo >= dt.Value));
EF Core translates your query into the SQL, which do not have NullReference exception.

LINQ to Entities does not recognize the method IsNullOrWhiteSpace

I have the below code:
var countries = from c in db.Countries
where (string.IsNullOrWhiteSpace(searchAlpha2) || (c.Alpha2 ?? string.Empty).ToUpper().Contains(searchAlpha2.ToUpper()))
&& (string.IsNullOrWhiteSpace(searchAlpha2) || (c.Alpha3 ?? string.Empty).ToUpper().Contains(searchAlpha3.ToUpper()))
&& (string.IsNullOrWhiteSpace(searchName) || (c.Name ?? string.Empty).ToUpper().Contains(searchName.ToUpper()))
select c;
This code uses Entity Framework v6 Code First over a SQL database.
Aside from performance, if I don't include the IsNullOrWhitespace I get no results when the filter criteria are blank (I've tested both null and blank values); however when a value is present this works as expected.
I'm getting the error:
LINQ to Entities does not recognize the method 'Boolean IsNullOrWhiteSpace(System.String)' method, and this method cannot be translated into a store expression.
I'm trying to use the searchXXX strings to filter on columns. I've tried using RegEx.IsMatch, SqlMethods.Like, and the code below, but all give me errors saying those functions are not allowed (errors come from either EntityFramework.SqlServer or from Linq to Entities). I've seen numerous posts on here where this has been done successfully though - so wonder if I'm missing something fundamental?
If you want to use your statement in current form you might want to replace
string.IsNullOrWhiteSpace(searchAlpha2)
to
!(searchAlpha2 == null || searchAlpha2.Trim() == string.Empty)
and all the other values too, for it to get translated to working SQL.
Update: Copied from comment by #DavidKempfner
As of EntityFramework.6.2.0 it generated SQL that checked for
!(searchAlpha2.Trim() == string.Empty),
I.E. It ignored the searchAlpha2 == null || part.
Use this instead:
!string.IsNullOrEmpty(entity.searchAlpha2.Trim())
I would suggest a different approach - use the ability to build queries up on the fly, and thus avoid passing optional query parameters to the expressions altogether - this will result in improved query plans when parsed to sql and executed on the database.
Also, if your database (?SqlServer) is not set to case sensitive collation (i.e. xx_CI_xx), you can avoid the casing conversion as well, as it is redundant:
var myQueryable = db.Countries.AsQueryable();
if (!string.IsNullOrWhiteSpace(searchAlpha2))
{
myQueryable = myQueryable.Where(c => c.Alpha2.Contains(searchAlpha2));
}
...
var countries = myQueryable.ToList();
You can get this and a bunch more functionality using PredicateBuilder
Update
JB: based on StuartLC's answer, here's the code amended to use PredicateBuilder:
var predicate = PredicateBuilder.True<Country>();
if (!string.IsNullOrWhiteSpace(searchAlpha2))
predicate = predicate.And(c => c.Alpha2 != null ? c.Alpha2.Contains(searchAlpha2) : false);
if (!string.IsNullOrWhiteSpace(searchAlpha3))
predicate = predicate.And(c => c.Alpha3 != null ? c.Alpha3.Contains(searchAlpha3) : false);
if (!string.IsNullOrWhiteSpace(searchName))
predicate = predicate.And(c => c.Name != null ? c.Name.Contains(searchName) : false);
IQueryable<Country> countries = db.Countries.AsExpandable().Where(predicate);
when you use linq in Entity Framework to get data from DataBase you need to use only with functions that the Entity Framework can convert to sql query.
I know that there an already accepted answer for this question but I got an idea to share.
Instead of putting multiple checks in the LINQ or using if statement to apply where clause on list result, you can use a simple trick. Just declare a bool variable and assign IsNullOrWhitespace of add to linq like:
bool isNull = string.IsNullOrWhiteSpace(searchAlpha2);
var countries = db.Countries.Where(c => isNull || c.Alpha2.Contains(searchAlpha2)).ToList();

Looking for simple way to build Linq dynamic / conditional expression

Given tables as:
Table1
id
stringfield1
stringfield2
stringfield3
stringfield4
Table2
id
table1_id
stringfield1
datefield1
Given a UI allow user to make fency query on:
dropdwonlist1 with any and table1.stringfield1 values
dropdwonlist2 with any and table1.stringfield2 values
dropdwonlist3 with any and table1.stringfield3 values
dropdwonlist4 with any and table1.stringfield4 values
dropdwonlist5 with any and table2.stringfield1 values
dropdwonlist6 with [any, the, before, after, between]
calendar1 to link with table2.datefield1
calendar2 to link with table2.datefield1
And as result datagridview with everyfields.
I want to build conditional query as if not "any" add this condition.
Considering that, simple LINQ query is not applicable:
Table2
.Where(x => x.stringfield1 == dropdwonlist1.SelectedValue)
.Where(x => x.stringfield2 == dropdwonlist2.SelectedValue)
.Where(x => x.stringfield3 == dropdwonlist3.SelectedValue)
(...)
There is Expression trees in documentation but that looks too much.
Is there simplest way to build my dynamic query ?
Expression trees look scarier than they are, but you are right, in your situation they are unnecessary: you could use a static condition that is smart enough to ignore dropdowns that have no selection. You can do it like this:
Table2
.Where(x => dropdwonlist1.SelectedValue == null || x.stringfield1 == dropdwonlist1.SelectedValue)
.Where(x => dropdwonlist2.SelectedValue == null || x.stringfield2 == dropdwonlist2.SelectedValue)
.Where(x => dropdwonlist3.SelectedValue == null || x.stringfield3 == dropdwonlist3.SelectedValue)
I've used LINQKit for similar scenarios with great success.
Specifically, you should be able to use the PredicateBuilder to accomplish what you're looking for.
It's common to forget that you can keep building up LINQ expressions across multiple statements. It's one of the great niceties of LINQ. I would simplify dasblinkenlight's answer for the LINQ-to-SQL translation that's going to happen afterward to:
IQueryable<T> query = Table2;
if (dropdownlist1.SelectedValue == null)
query = query.Where(x => x.stringfield1 == dropdownlist1.SelectedValue);
// etc
That way anything with a null value doesn't get mixed up into the where clauses at all, reducing the chances the generated SQL has unnecessary conditions in it.
I like Donut's answer best as a more generalized solution though - for example LINQKit would let you write a loop over the 6 dropdowns that writes each where clause if necessary.

Design: List storing objects; access information associated with that object [duplicate]

This question already has answers here:
Linq Contains method for a object
(4 answers)
Determine if LINQ Enumerable contains object based on a condition? [duplicate]
(4 answers)
Closed 10 years ago.
I have two classes that are pertinent to what I'm trying to do.
Class 1: Person
Class 2: Personal Profiles
Person has properties such as address, name, phone #, etc...
Inside of my Personal Profiles class I have a List that stores the information for the Person's that have been created.
My question: If I'm trying to find out whether a Person with address: 999 Candy Lane exists within the List do I need to create a a new Person with default's for everything except the specified address and then use that in my .Exists or .Contains? Or should I not be creating a new object just for a searching function.
Why don't you use Linq:
theList.Where(x => x.address == "999 Candy Lane").First();
If you use .net 3.5+ you can use a linq query:
i.e.
var result = (from p in Profiles where p.Address=="bla bla" select p).FirstOrDefault();
The result will be null if no matching person is found.
Given the high probability of missing values it is better to use FirstOrDefault.
Of course First and FirstOrDefault takes a predicate so there is no need to use Where
var result = List.FirstOrDefault(x => x.Address == "999 Candy Lane");
if(result != null)
{
......
}
This is the exact use case for Any.
It is used like this:
if(People.Any(p => p.Address.Equals("999 Candy Lane")))
{
//.....
}
No you don't have to create a new object for searching:
Person person = profiles.PersonsList.Where(p => p.Address == "Address here")
.Select(p => p);
You probably want to use linq and a lambda:
var candyLane = Persons.Where(x => x.Address == "999 Candy Lane").ToList();
What this does is supply a lambda predicate to be used as a selector. Think of it like an anonymous function specified like this:
public bool CandyLaneChecker(x){
return x.Address == "999 Candy Lane";
}
Where the braces and return keyword are replaced by a => and the public bool CandyLaneChecker is omitted because this is an anonymous function. That would leave (x) => x.Address == "999 Candy Lane" and we could've left the parenthesis in our original predicate, but they're not needed.
Alternately, one could use a linq expression such as:
var candyLane = (from p in persons
where p.Profile.Address == "999 Candy Lane"
select p).ToList();
Which looks a lot more like a database query and is easier for some people to understand.
The great part about the linq expression is that you could even use that same expression with XML.

Multiple where conditions in EF [duplicate]

This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Conditional Linq Queries
Using Entity Framework 4.0
I have a search condition like this
There are four fields that allow the users to filter their search. The conditions are all AND. The result has to omit the corresponding filter if the textbox value is String.Empty or the dropdownlist value is All. Could do this in a Stored Procedure but I am unable to mimic that at all in a Linq2SQL/ Entity Framework scenario.
My question is this, how to omit IEnumerable.Where in the Linq according to some entered values?
You can chain your where clauses. You just need an IQueryable datasource.
var filteredData = _repository.GetAll();
//If your data source is IEnumerable, just add .AsQueryable() to make it IQueryable
if(keyWordTextBox.Text!="")
filteredData=filteredData.Where(m=>m.Keyword.Contains(keyWordTextBox.Text));
if(LocationDropDown.SelectedValue!="All")
filteredData=filteredData.Where(m=>m.Location==LocationDropDown.SelectedValue));
... etc....
Because it is IQueryable, the data is not fetched until you bind it so it only pulls the data you need.
Assuming that Location and Category are identified in your code by ids (id is the value attribute in the comboboxes items), you can do something similar to
function GetItems(string keyword, string consultant, int? locationId, int categoryId){
using(MyContextEntities context = new MyContextEntities()){
return context.Items.Where(item =>
(string.IsNullOrEmpty(keyword) || item.Text.Contains(keyword))
&& (string.IsNullOrEmpty(consultant) || item.Consultant.Contains(consultant))
&& (!locationId.HasValue || item.Location.Id == locationId.Value)
&& (!categoryId.HasValue || item.Category.Id == categoryId.Value)
);
}
}
Take a look at PredicateBuilder. It will allow you to do something like this:
IQueryable<??> SearchProducts (params string[] keywords)
{
var predicate = PredicateBuilder.True<??>();
foreach (string keyword in keywords)
{
string temp = keyword;
if(temp != String.Empty || temp != "All")
predicate = predicate.And(e => e.???.Contains (temp));
}
return dataContext.??.Where (predicate);
}
Note:
The flexible way to do this is to build up the where clause separately.
This article shows you how to do that. It takes a bit of work to initially set it up. But its worth it.
You can do something like this.
var abc = from al in myEntity.a
where (field == string.Empty ? al.field == string.Empty : al.field == field)
select new { al.field1, al.field2, al.field3 };
I think Skip While and Take While may help in your situation
http://msdn.microsoft.com/en-us/vcsharp/aa336757#SkipWhileSimple

Categories