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
Related
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)
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();
I have code like this:
var modele = from model in ds.Tables["modele"].AsEnumerable()
where model.Field<string>("KRAJ_PRODUKCJI") == krajText
&& model.Field<string>("FABRYKA") == fabrykaText
// && model.Field<string>("NAZWA") == itemId
orderby model.Field<string>("NAZWA")
select model;
In the commented out line I need to dynamically generate a where clause or check that the field NAZWA is equal to one of multiple keyword in itemId which is a list with a few keyword to check with || between.
Is it possible to do this?
Have you tried something like:
where itemId.Contains(model.Field<string>("NAZWA"))
I'm not sure if that's what you're looking for, but it might be.
#korchev's won't work because you want an "||".
Mam nadzieje ze to pomocne.
Might not work straight out-of-the-box, but I'm guessing you should look for your answer in this direction:
var keywords = itemId.split("||");
var model = from m in ds.Tables["modele"].AsQueryable();
model = model.Where(m => keywords.Contains(m.Field<string>("NAZWA"));
This question already has answers here:
Multiple WHERE clause in Linq
(3 answers)
Closed 2 years ago.
I am trying the to query my Status Update repository using the following
var result = (from s in _dataContext.StatusUpdates
where s.Username == "friend1" && s.Username == "friend2" etc...
select s).ToList();
Instead of using s.Username == "friendN" continuously is there anyway I can pass a list or array or something like that rather that specifying each one, or can I use a foreach loop in the middle of the query.
Thanks
If you only need to check whether the Username property has some specified value, you can create a list of the values and then use method such as All or Any to check if some condition holds for any/all elements of the array.
Your example looks a bit suspicious though - the user name s.Username cannot be equal to multiple different strings. Did you want to check whether it is equal to any of the (specified) names? That could be written like this:
var friends = new[] { "friend1", "friend2", ... };
var result =
from s in dc.StatusUpdates
where friends.Any(fr => s.Username == fr)
select s;
This returns all status updates such that the Username property is equal to any of the specified friend names (specified as an array, but you could use any IEnumerable<string>).
Yo could do it like this:
IQueryable<s> query= _dataContext.StatusUpdates;
foreach (var item in names)
{
query = query.Where(p=>p.Username == item);
}
List<s> result = query.ToList();
I think I mucked with some data types of yours but this should be close:
var names = new List<string>();
// populate names
var updates = new List<StatusUpdate>();
// populate updates
var result = (from s in updates
where names.Contains(s.ToString())
select s).ToList();
I've a project which ask me to do such a BIG search engine but which is all dynamic. I mean I can have about 0 to 9 main "group" which have inside something like an infinite possibility of "where" with "OR" or "AND". First thing we think was to use Dynamic Linq which provide a good alternative to build dynamic query. All this using EF with an homemade wrapper.
Probleme :
I'm not able to access to a "Collection". I mean, I can easly access to a referenced object (Like Customer.State.StateName = "New-York" OR Custoemr.State.StateName = "Quebec" ) but I can't find a way to acces to something like : "Customer.Orders.OrderID = 2 OR Customer.Orders.OrderID = 3". I can easly figure out this its because its a collection, but how can I do this?
Please help me out!!
** Sorry for my english !!
Update
I'm not clear enought I think, sorry its because im french...
My problem its because nothing is static. Its a candidat search engine for a recruting compagny that place candidats into an enterprise. In a page where manager can search candidat, he can "parse" by : Domain(s) (Jobs), City(ies) or many other that user have filled up when he register. All this in format (if it were in SQL) :
[...] WHERE (domaine.domainID = 3 OR domaine.domainID = 5 OR domaine.domainID = 23) AND (cities.cityID = 4, cities.city = 32) [...]
So i can't do this with a normal LINQ format like :
Candidat.Domaines.Where(domain => domain.DomainID == 3 || domain.DomainID == 5 || domain.DomainID == 23);
Even the operator in the paretheses are dynamic ("AND" or "OR")! That why we trying to use Dynamic Linq because its a lot more flexible.
Hope its more easy to understand my problem ...
Update 2
Here's my method
private string BuildDomainsWhereClause() {
StringBuilder theWhere = new StringBuilder();
if (this.Domaines.NumberOfIDs > 0) {
theWhere.Append("( ");
theWhere.Append(string.Format("Domaines.Where( "));
foreach (int i in this.Domaines.ListOfIDs) {
if (this.Domaines.ListOfIDs.IndexOf(i) > 0) {
theWhere.Append(string.Format(" {0} ", this.DispoJours.AndOr == AndOrEnum.And ? "&&" : "||"));
}
theWhere.Append(string.Format("DomaineId == {0}", i));
}
theWhere.Append(" ))");
}
return theWhere.ToString();
}
It works great instead that it "Not return a boolean". So how should I?
Error : "Expression of type 'Boolean' expected".
At the end, it returns something like : "( Domaines.Where( DomaineId == 2 && DomaineId == 3 && DomaineId == 4 && DomaineId == 5 ))." which is added to my LINQ Query :
var queryWithWhere = from c in m_context.Candidats.Where(WHERE)
select c;
Dont forget that there's like 7 or 8 more "possible" added things to search in ... Any ideas?
What you need to do here, is build a LambdaExpression (more specifically an Expression<Func<T, bool>>). You cannot use a string. You can build a simple expression like this:
ParameterExpression p = Expression.Parameter(typeof(Domaine), "domaine");
Expression<Func<Domaine, bool>> wherePredicate =
Expression.Lambda<Func<Domaine, bool>>(
Expression.Or(
Expression.Equal(
Expression.Property(p, "DomainID"),
Expression.Constant(10)),
Expression.Equal(
Expression.Property(p, "DomainID"),
Expression.Constant(11))
), p);
i.e.,
domaine.DomainID = 10 || domaine.DomainID = 11
Not very readable if you need to do this by hand.
There's a sample of a fully operational expression parser that will actually do this for you based on a string in C# Samples for Visual Studio 2008 at MSDN Code Gallery, under DynamicQuery. (The LinqDataSource control uses a slightly modified version of this sample internally.)
Finaly i've got it exactly the way I want.
private string BuildDomainsWhereClause() {
StringBuilder theWhere = new StringBuilder();
if (this.Domains.NumberOfIDs > 0) {
theWhere.Append("( ");
foreach (int i in this.Domains.ListOfIDs) {
if (this.Domains.ListOfIDs.IndexOf(i) > 0) {
theWhere.Append(string.Format(" {0} ", this.Domains.AndOr == AndOrEnum.And ? "&&" : "||"));
}
theWhere.Append(string.Format("Domains.Any(IdDomaine== {0})", i));
}
theWhere.Append(" )");
}
return theWhere.ToString();
}
Which produce something like : "( DispoJours.Any(IdDispo == 3) && DispoJours.Any(IdDispo == 5) )".
All my other "Where builder" will do the same things with a "&&" between which give the correct result.
And later :
var queryWithWhere = from c in m_context.Candidats.Where(WHERE)
select c;
WHOOOHOOO !! Thanks folks. Were very usefull! Love this website!
Update
Don't forget that i use Dynamic Linq on this query. It's not a normal LINQ query.
Assuming that Customer.Orders returns a collection, this is exactly why you can't just call a property of it.
In order to use LINQ to get the Order you're looking for, you'd need to know the OrderID (Or some other property) in which case you can do:
Customer.Orders.Find(order => order.OrderID == 2);
Edit: To add the expression to find id 2 or 3 in this way:
Customer.Orders.FindAll(order => order.OrderID == 2 || order.OrderID == 3);
Do I understand that right, that both Customers is a collection and Orders is a collection while State (obviously) is a Property?
var q = from a in Customer
from b in a.Orders
where b.ID == 2
|| b.ID == 3
select b;
Would work I guess.
Edit:
I did partly something like that. It's been too long to be exactly sure how I did it, but I can tell you, that I was using
public static IQueryable<T> Where<T>(this IQueryable<T> source, string predicate, params object[] values);
From DynamicQueryable class.
this.CountrySitesObject.Sites.AsQueryable().Where(w.WhereQuery, w.WhereParameters)
(copied from my code).
If you step back and ask what does the customer want to do.
Filter bug information.
Why not export the data to excel or point excel to the SQL Table. It is not as much fun to build, but you would be done in a couple of hours, instead of days or weeks. :)