I have this piece of the code intended to search a database. A user should have 3 options here: to type surname only, the first name and the user can search using both of them - surname and the first name.
This code retrieves the records from my db if I provide both strings - surname and the first name. But if I type only one of them, my resulting list is always empty.
var query = from x in db.people
where (txtSurname == null || x.Surname== txtSurname.Text)
&& (txtFirstName == null || x.FirstName == txtFirstName.Text)
select x;
var data = query.ToList();
peopleBindingSource.DataSource = data;
Remember that an Entity Framework query doesn't get sent to the database until you materialise the data wth ToList or iterating over it for example. This means you can build up the query in code like this:
var query = db.people.AsQueryable();
if(!string.IsNullOrEmpty(txtSurname.Text))
{
query = query.Where(p => p.Surname == txtSurname.Text);
}
if(!string.IsNullOrEmpty(txtFirstName.Text))
{
query = query.Where(p => p.FirstName == txtFirstName.Text);
}
peopleBindingSource.DataSource = query.ToList();
Related
I have two and more if conditions to inject my main query but if the condition has no value (is nullable) I don't want to inject to my query.
For example this is my AND query injection:
// first initialize query can have where or not
var query = context.QuestionInfoes.Include(x =>x.RelationsInfoes).AsQueryable();
// first if condition to inject query
if (filterQuestionInfo.ToProfileId.HasValue)
{
query = (from q in query
join qr in context.QuestionRelationsInfoes on q.Id equals qr.QuestionId
where q.BrodcastType == QuestionBrodcastType.All || filterQuestionInfo.ToProfileId == qr.ToProfileId
select q);
}
// second if condition to inject AND query and i want to this be OR injection
if (filterQuestionInfo.ProfileId.HasValue)
{
query = (from q in query
where q.ProfileId == filterQuestionInfo.ProfileId
select q);
}
Now I want to create "OR" injection and when I call .ToList(), I see just queries in SQL that I needed. In top example if ToProfileId and ProfileId have values, I see questions where sent to ToProfileId value and 0 questions from profile id in "ProfileId" value because second query is "And" condition to first query. But I want both of them when I fill both values.
when two values are null: I filtered all of questions (works now)
when one value of ToProfileId or ProfileId is null: I filtered all of questions on that value is not null (works now)
When both value are filled, I want both question list (does not work now)
Note: I don't want to create one query and inject all of my condition in to that query.
Assuming QuestionRelationsInfoes exists on QuestionInfoes as a navigation property called QuestionRelationsInfoes, then you dont need the join.
Unfortunately, you will have to construct the query based on the 3 scenarios (Both filters set, only 1st filter set, only 2nd filter set).
var query = context.QuestionInfoes.Include(x => x.RelationsInfoes);
if (filterQuestionInfo.ToProfileId.HasValue && filterQuestionInfo.ProfileId.HasValue)
{
query = query.Where(q =>
q.BrodcastType == QuestionBrodcastType.All ||
q.QuestionRelationsInfoes.ToProfileId == filterQuestionInfo.ToProfileId ||
q.ProfileId == filterQuestionInfo.ProfileId);
}
else if (filterQuestionInfo.ToProfileId.HasValue)
{
query = query.Where(q => q.BrodcastType == QuestionBrodcastType.All || filterQuestionInfo.ToProfileId == q.QuestionRelationsInfoes.ToProfileId);
}
else if (filterQuestionInfo.ProfileId.HasValue)
{
query = query.Where(q.ProfileId == filterQuestionInfo.ProfileId);
}
As an alternative, if you dislike the code repetition, you can push the check to SQL, by first checking if the filter is not null:
var query = context.QuestionInfoes.Include(x => x.RelationsInfoes);
if (filterQuestionInfo.ToProfileId.HasValue || filterQuestionInfo.ProfileId.HasValue)
{
query = query.Where(q =>
(filterQuestionInfo.ToProfileId.HasValue && (q.BrodcastType == QuestionBrodcastType.All || filterQuestionInfo.ToProfileId == q.QuestionRelationsInfoes.ToProfileId)) ||
(filterQuestionInfo.ProfileId.HasValue && q.ProfileId == filterQuestionInfo.ProfileId));
}
I have a query that returns a list of currencies and joins to a lookup table. The result is then put into a class object (which works fine):
var queryforobject = from x in db.CurrencyExchangeRates.AsNoTracking()
join c in db.CurrencyTypes.AsNoTracking() on x.CurrencyTypeID equals c.ID
orderby x.ID
select new CurrencyExchangeRateObject
{
ID = x.ID,
CurrencyID = c.ID,
Currency = c.Description,
ExchangeRate = x.ExchangeRate,
LastEditedDate = x.LastEditedDate,
LastEditedBy = x.LastEditedBy,
Active = x.Active
};
I want to make this more dynamic, so if no CurrencyTypeID is supplied then it will return the full list (as it does already) - otherwise if a CurrencyTypeID is supplied it will only show where X.CurrencyTypeID = ID.
Something along the lines of an inline if?
There are a few options for filtering the query based on CurrencyTypeID if a search value (named currencyTypeID in this answer) is supplied, but return all data if no currencyTypeID is supplied.
First option: You could add a where clause to your existing query expression. The WHERE clause below will return every record in the data set if null is passed in for the currencyTypeID variable, otherwise it will filter the results.
from x in db.CurrencyExchangeRates.AsNoTracking()
join c in db.CurrencyTypes.AsNoTracking() on x.CurrencyTypeID equals c.ID
where (currencyTypeID == null || x.CurrencyTypeID == currencyTypeID)
orderby x.ID
select new CurrencyExchangeRateObject {
ID = x.ID,
CurrencyID = c.ID,
Currency = c.Description,
ExchangeRate = x.ExchangeRate,
LastEditedDate = x.LastEditedDate,
LastEditedBy = x.LastEditedBy,
Active = x.Active
};
Alternatively: Since queryforobject is of type IQueryable<T>, you can use LINQ's fluent API to append a WHERE clause to the query inside an if statement. You need to be more careful about timing on this one though as it needs to be done before you force evaluation of the IQueryable with a foreach loop, .ToList(), .Select() or other LINQ methods that force evaluation.
if(currencyTypeID != null)
queryforobject = queryforobject.Where(cerObj => cerObj.CurrencyID == currencyTypeID);
I have a form with multiple search criteria that a user can use to search for employee data, e.g. FirstName, LastName, HireDate, Department, etc.
I am using LINQ and am wondering what method could I use to query a collection of Employes given any of of the search criteria, i.e. a user does not have to enter all, but they do have to enter at least one of the search parameters.
So far, while testing my LINQ statement with two search parameters in place, it seems that I have to see if the search parameter is entered or not.
If this is the case, then this can get quite unwieldy for many search parameters.
// only FirstName is entered
if (!string.IsNullOrEmpty(FirstName) && string.IsNullOrEmpty(LastName))
{
var employees = DB.Employees
.Where(emp => emp.FirstName.Contains(fName));
}
// only LastName is entered
else if (string.IsNullOrEmpty(FirstName) && !string.IsNullOrEmpty(LastName))
{
var employees = DB.Employees
.Where(emp => emp.LastName.Contains(lName));
}
// both parameters are entered
else if (!string.IsNullOrEmpty(FirstName) && !string.IsNullOrEmpty(LastName))
{
var employees = DB.Employees
.Where(emp => emp.FirstName.Contains(fName))
.Where(emp => emp.LastName.Contains(lName));
}
FYI, I initially thought that I could just append Where() statements to my LINQ statement with the pertinent search parameters but I noticed that not all records were being returned that should and thus the above logic of if-then statements.
What about something like this:
IQueryable<Employee> employees = DB.Employees;
if (!string.IsNullOrEmpty(FirstName))
{
employees = employees
.Where(emp => emp.FirstName.Contains(fName));
}
if (!string.IsNullOrEmpty(LastName))
{
employees = employees
.Where(emp => emp.Last.Contains(lName));
}
You can write it like this:
var employees = DB.Employees.AsQueryable();
if (!string.IsNullOrEmpty(fName)
employees = employees.Where(emp => emp.FirstName.Contains(fName));
if (!string.IsNullOrEmpty(lName)
employees = employees.Where(emp => emp.LastName.Contains(lName));
I encountered a similar challenge where a user could select 0, 1 or many values for about 10 searchable fields and needed to construct that query at runtime.
I ended up using LINQKit:
http://www.albahari.com/nutshell/linqkit.aspx
In particular I used it's predicate builder, which is described here:
http://www.albahari.com/nutshell/predicatebuilder.aspx
In your example above, you've encompassed the query multiple within if statements.
The alternative is to build the query as you go.
If you were to declare var employees = DB.Employees outside of those if statements (Assuming that it's always relevant), then you could just tack on your where statements within your if statements if they're applicable.
LINQ gives you deferred execution, so you don't have to have the entire expression in a single block (Even though it feels most natural to do so and in many cases you will).
Things get a bit more complicated if you want to mix in OR's with ANDs, but that's where the previously mentioned predicate builder comes in.
Unfortunately I don't have any examples to share, but those links should get you off to a good start.
var resultData = (from data in db.Abc
where !string.IsNullOrEmpty(firstName) ? data.FirstName == firstName : true
&& data.UserType == userTypeValue
&& !string.IsNullOrEmpty(lastName) ? data.LastName == lastName : true
&& !string.IsNullOrEmpty(gender) ? data.Gender == gender : true
&& !string.IsNullOrEmpty(phone) ? data.CellPhone == phone : true
&& !string.IsNullOrEmpty(fax) ? data.Fax == fax : true
&& !string.IsNullOrEmpty(emailAddress) ? data.Email == emailAddress : true
&& !string.IsNullOrEmpty(address1) ? data.Address == address1 : true
select new
{
UserName = data.UserName,
FirstName = data.FirstName,
Address = data.Address,
CellPhone = data.CellPhone,
Fax = data.Fax,
Email = data.Email
}).ToList();
Hello fellow stackoverflowers,
I'm currently working on a project which gives me a bit of trouble concerning filtering data from a database by using multiple filter values. The filter happens after selecting the filters and by clicking a button.
I have 5 filters: Region, Company, Price, and 2 boolean values
Note that Region and Company are special dropdownlist with checkboxes which means the user can select one or more regions and company names.
I already made a few tests and came up with a incomplete code which works a bit but not to my liking.
Problems arise when one of my filters is NULL or empty. I don't really know how to process this. The only way i thought of was using a bunch of IF ELSE statements, but i'm starting to think that this will never end since there are so much possibilities...
I'm sure there is a far more easier way of doing this without using a bunch of IF ELSE statements, but i don't really know how to do it. If anyone could steer me in the right direction that would be appreciated. Thanks
Here is what i have right now (I haven't added the Price to the query for now):
protected void filterRepeater(List<int> regionIDs, string[] companyArray,
string blocFiltValue, bool bMutFunds, bool bFinancing)
{
DatabaseEntities db = new DatabaseEntities();
PagedDataSource pagedDsource = new PagedDataSource();
IQueryable<Blocs> query = (from q in db.Blocs
where q.isActive == true
orderby q.date descending
select q);
IQueryable<Blocs> queryResult = null;
//if some filters are NULL or Empty, it create a null queryResult
queryResult = query.Where(p => companyArray.Contains(p.company) &&
regionIDs.Contains((int)p.fkRegionID) &&
(bool)p.mutual_funds == bMutFunds &&
(bool)p.financing == bFinancing);
if (queryResult.Count() > 0)
{
//Bind new data to repeater
pagedDsource.DataSource = queryResult.ToArray();
blocRepeater.DataSource = pagedDsource;
blocRepeater.DataBind();
}
}
Only add the relevant filters to query:
IQueryable<Blocs> query =
from q in db.Blocs
where q.isActive == true
orderby q.date descending
select q;
if (companyArray != null)
{
query = query.Where(p => companyArray.Contains(p.company));
}
if (regionIDs != null)
{
query = query.Where(p => regionIDs.Contains((int)p.fkRegionID));
}
// ...
// etc
// ...
if (query.Any()) // Any() is more efficient than Count()
{
//Bind new data to repeater
pagedDsource.DataSource = query.ToArray();
blocRepeater.DataSource = pagedDsource;
blocRepeater.DataBind();
}
If you want to filter only by the filter values that are not null or empty then you can construct the query by appending the where clauses one by one:
if(companyArray != null && companyArray.Length > 0) {
query = query.Where(p => companyArray.Contains(p.company));
}
if(regionIDs!= null && regionIDs.Length > 0) {
query = query.Where(p => regionIDs.Contains((int)p.fkRegionID));
}
if (!String.IsNullOrEmpty(blocFiltValue)) {
query = query.Where(p => p.Block == blocFiltValue);
}
Also you can use nullable values for value types, if you need to filter them optionally
bool? bMutFunds = ...; // Possible values: null, false, true.
...
if(bMutFunds.HasValue) {
query = query.Where(p => (bool)p.mutual_funds == bMutFunds.Value);
}
Maybe you can create a string for the SQL sentence, and dynamically add parts to this sentence like if something was selected or checked you add something to this string when thh selection was completed by the user you can execute this SQL sentence.
I have a view with 3 textboxes which bind to properties in the ViewModel SupplierName, Contact, Address and one button which bind to SearchCommand property in my ViewModel.
My requirement is to filter Supplier records based on the above properties. I used EntityFramework.
The user can enter any of the above textboxes which lead me to write 9 different queries.
For instance if the user inputs data only on the SupplierName textbox then I need to run one query with SupplierName as parameter. If the user enters SupplierName and Contact textboxes then I need to run another query. And so on.
Here is my code:
public IEnumerable<Model.Supplier> GetAllSuppliersBySearch(string nameMatch, string contactMatch, string phoneMatch)
{
if(nameMatch!=null)
{
var q = from f in Context.Suppliers
where f.SupplierName==nameMatch
select f;
}
else if(contactMatch!=null)
{
var q = from f in Context.Suppliers
where f.ContactName==contactMatch
select f;
}
else if(phoneMatch!=null)
{
var q = from f in Context.Suppliers
where f.ContactName==contactMatch
select f;
}
return q.AsEnumerable();
}
Instead of writing multiple queries, how to accomplish this with one query or in any optimized way?
Compose query with lambda syntax:
IQueryable<Supplier> query = Context.Suppliers;
if (!String.IsNullOrEmpty(nameMatch))
query = query.Where(s => s.SupplierName == nameMatch);
if (!String.IsNullOrEmpty(contactMatch))
query = query.Where(s => s.ContactName == contactMatch);
// etc
return query.AsEnumerable();
Another option is adding parameter-checking conditions to query
var query =
from s in Context.Suppliers
where (String.IsNullOrEmpty(nameMatch) || s.SupplierName == nameMatch) &&
(String.IsNullOrEmpty(contactMatch) || s.ContactName == contactMatch)
// etc
select s;