Entity Framework - Use condition statements in query - c#

Good Day Guys,
I have three lists (ICollection of strings). My intention is to fetch results from the db based on the values stored in these lists using linq to entities. Below is my snippet
int entityCriteriaCount = entityCriteria == null ? 0 : entityCriteria.Count();
int operationCriteriaCount = operationCriteria == null ? 0 : operationCriteria.Count();
int roleCriteriaCount = roleCriteria == null ? 0 : roleCriteria.Count();
// Where entityCriteria,operationCriteria and roleCriteria are the above mentioned lists
Data Query:
var auditItems = db.AuditTrails.Where(a => (entityCriteriaCount > 0 ? reportCriteria.EntityTypes.Contains(a.EntityType) : a.EntityType.Contains(""))
&& (roleCriteriaCount > 0 ? reportCriteria.Roles.Contains(a.UserRole) : a.UserRole.Contains(""))
&& (operationCriteriaCount > 0 ? reportCriteria.Operations.Contains(a.UserAction) : a.UserAction.Contains(""))
&& EntityFunctions.TruncateTime(a.TimeStamp) >= startDate
&& EntityFunctions.TruncateTime(a.TimeStamp) <= endDate).OrderByDescending(a => a.TimeStamp).ToList();
The goal of the above query is to check if the list is empty before attempting to fetch records based on its content. The above works perfectly if all the lists have entries. It however fails if any or all of them are empty.
How can I make this work i.e Query the database using the lists if there are entries and fetch all entries for the criteria if the list is empty.
Any help will be appreciated. Thanks guys

You don't have to do the whole query in one go, break it down and only filter when you need to:
var auditItems = db.AuditTrails.Where(EntityFunctions.TruncateTime(a.TimeStamp) >= startDate && EntityFunctions.TruncateTime(a.TimeStamp) <= endDate);
if(entityCriteriaCount > 0)
auditItems = auditItems.Where(a => reportCriteria.EntityTypes.Contains(a.EntityType));
if(roleCriteriaCount > 0)
auditItems = auditItems.Where(a => reportCriteria.Roles.Contains(a.UserRole));
if(operationCriteriaCount > 0)
auditItems = auditItems.Where(a => reportCriteria.Operations.Contains(a.UserAction));
var results = auditItems.OrderByDescending(a => a.TimeStamp).ToList();

Related

Linq Where fails when trying to check a range

When trying to check if a variable is within a range the code does not work.
This is a project in Radzen, Linq is interacting with a mysql database
This does not work:
var QuantQ = dbContext.CnpjCnaes.Where(x => x.CAPITAL_SOCIAL < CapMaximo && x.CAPITAL_SOCIAL > CapMinimo && x.CNAE_FISCAL == Parametros[4] && x.COD_MUNICIPIO == CidadeQ.ToArray()[0].COD_MUNICIPIO.ToString());
But this does:
var QuantQ = dbContext.CnpjCnaes.Where(x => x.CAPITAL_SOCIAL < CapMaximo && x.CNAE_FISCAL == Parametros[4] && x.COD_MUNICIPIO == CidadeQ.ToArray()[0].COD_MUNICIPIO.ToString());
Well I expected to get a range between CapMinimo and CapMaximo, but it seems that when I try to check a range it always fails.

C# / LINQ - conditional LINQ query based on checkbox status

I'm trying to build a LINQ query that executes as values change, however I only want to bottom 4 statements relating to price and surface area to run on the condition that a certain checkbox on my Windows form is ticked. My code is below
var userSearchQuery =
from sale in saleData
where checkedCities.Contains(sale.City)
&& checkedBedrooms.Contains(sale.Bedrooms)
&& checkedBathrooms.Contains(sale.Bathrooms)
&& checkedHouseTypes.Contains(sale.HouseType)
&& minPrice <= sale.Price
&& maxPrice >= sale.Price
&& minSurfaceArea <= sale.SurfaceArea
&& maxSurfaceArea >= sale.SurfaceArea
select sale;
Can anyone help with the best way to do this please
What you could do is make the base query just as it is. So just remove last 4 conditions that you wish to dinamically add depending on some condition from UI. You will see that your query is of type IQueryable.
var userSearchQuery =
from sale in saleData
where checkedCities.Contains(sale.City)
&& checkedBedrooms.Contains(sale.Bedrooms)
&& checkedBathrooms.Contains(sale.Bathrooms)
&& checkedHouseTypes.Contains(sale.HouseType);
Do not select anything yet. Now add your condition depending on UI.
if(checkBox1.Checked)
userSearchQuery = userSearchQuery.Where(s => minPrice <= s.Price);
if(checkBox2.Checked)
userSearchQuery = userSearchQuery.Where(s => maxPrice => s.Price);
if(checkBox3.Checked)
userSearchQuery = userSearchQuery.Where(s => minSurfaceArea => s.SurfaceArea);
if(checkBox4.Checked)
userSearchQuery = userSearchQuery.Where(s => maxSurfaceArea => s.SurfaceArea);
Finally execute the query by calling ToList().
var results = userSearchQuery.Select(s => s).ToList();
You can stack the queries, so first create an IEnumerable for all cases and then add additional queries to previous IEnumerable only when your checkbox is checked.
var userSearchQuery = from sale in saleData
where checkedCities.Contains(sale.City)
&& checkedBedrooms.Contains(sale.Bedrooms)
&& checkedBathrooms.Contains(sale.Bathrooms)
&& checkedHouseTypes.Contains(sale.HouseType)
select sale;
if (checkbox.IsChecked)
{
userSearchQuery = from sale in userSearchQuery
where minPrice <= sale.Price
&& maxPrice >= sale.Price
&& minSurfaceArea <= sale.SurfaceArea
&& maxSurfaceArea >= sale.SurfaceArea
select sale;
}
You could use the fact that 'true' returns the result as follows:
var userSearchQuery =
from sale in saleData
where checkedCities.Contains(sale.City)
&& checkedBedrooms.Contains(sale.Bedrooms)
&& checkedBathrooms.Contains(sale.Bathrooms)
&& checkedHouseTypes.Contains(sale.HouseType)
&& (*some condition is checked*) ? (minPrice <= sale.Price && maxPrice >= sale.Price && minSurfaceArea <= sale.SurfaceArea && maxSurfaceArea >= sale.SurfaceArea) : true
select sale;
I have tested the syntax, but not the execution so let me know if it doesn't work as expected.
For reading reference about the '?' operator:
?: Operator (C# Reference)
EDITED:
As per apocalypse's comment, there is no need to check the condition multiple times.

??(null-coalescing operator) between EF6 Queries

I am developing an MVC 5 app using EF 6. I want to query my database and store a value in a variable and if there is null as per the the given condition then another query should be executed to return a value. In my method I have as follows: D1 is coming as a parameter and it has the current date.
int otherYear = D1.Year + 1;
lastNo = (db.ABC.ToList().LastOrDefault(x => x.D1.Value.Month <= 6 && x.D1.Value.Year == otherYear).S1) ?? (db.ABC.ToList().LastOrDefault(x => x.D1.Value.Month > 6 && x.D1.Value.Year == D1.Year).S1);
Now the first query before ?? operator executes but when there is null against the condition specified the system throws an error of object reference and does not execute the second query after ?? operator. How can I solve it?
I know that it can be solved by making an if-else condition and within that I should first check that if there is any data using .Any() function. But in that case I have to query my database for minimum of 2 times. Once in .Any() to check the availability of data and second to fetch that data. But I have a hefty database and I don't want to make extra queries.
Regards
You should not call db.ABC.ToList() before applying LastOrDefault() because it will load the whole database to memory and do the processing from there.
You're getting null reference exception because db.ABC.LastOrDefault(x => x.D1.Value.Month <= 6 && x.D1.Value.Year == otherYear) is null so that you cannot get S1 property.
For your question, I think you can use this code:
int otherYear = D1.Year + 1;
lastNo = (db.ABC.LastOrDefault(x => x.D1.Value.Month <= 6 && x.D1.Value.Year == otherYear)?.S1) ?? (db.ABC.LastOrDefault(x => x.D1.Value.Month > 6 && x.D1.Value.Year == D1.Year)?.S1);
To avoid the double query issue, store the result of the query in a list:
int otherYear = D1.Year + 1;
List<T> myABC = db.ABC.ToList();
lastNo = (myABC.LastOrDefault(x => x.D1.Value.Month <= 6 && x.D1.Value.Year == otherYear).S1) ?? (myABC.LastOrDefault(x => x.D1.Value.Month > 6 && x.D1.Value.Year == D1.Year).S1);

Filtering results with Linq

I have two dropdowns on my page. First dropdown shows Authors for books and the other dropdown shows status's i.e Overdue or All.
If they choose Overdue then I need to return all books that have been borrowed more than a week ago so the dueback date (Datetime variable) will be taken into consideration.
I currently have this working correctly filtering on the Author as shown here:
model.ListBooks = (from x in tempModel
where ((x.BookAuthor == model.ListAuthors.SelectedAuthor || model.ListAuthors.SelectedAuthor == null))
select x).ToList(); // Filter the results
But as soon as I pass in the additional search filter I.e status it fails to shows me any books that match the selected Author even though I haven't chosen a status this is what it currently looks like.
model.ListBooks = (from x in tempModel
where (
(x.BookAuthor == model.ListAuthors.SelectedAuthor || model.ListAuthors.SelectedAuthor == null)
&&
(model.BookStatus.SelectedStatusId == (int)Enums.Registration.OverDue && x.DueBack < DateTime.Now.)
)
select x).ToList(); // Filter the results
Can someone see what I'm doing wrong here?
I think your query fails if selected status is not OverDue. In that case you have
where authorFilter && (false && dateFilter)
that gives you false for all books. Thus you have only two statuses, you can just add status.SelectedStatusId != (int)Enums.Registration.OverDue check just as you did with null-check for selected author:
var authors = model.ListAuthors;
var status = model.BookStatus;
model.ListBooks = (from x in tempModel
where (authors.SelectedAuthor == null || x.BookAuthor == authors.SelectedAuthor) &&
(status.SelectedStatusId != (int)Enums.Registration.OverDue || x.DueBack < DateTime.Now)
select x).ToList();
I would use method syntax here to make query more readable:
var books = tempModel; // probably you will need IEnumerable<T> or IQueryable<T> here
if (model.ListAuthors.SelectedAuthor != null)
books = books.Where(b => b.BookAuthor == model.ListAuthors.SelectedAuthor);
if (model.BookStatus.SelectedStatusId == (int)Enums.Registration.OverDue)
books = books.Where(b => b.DueBack < DateTime.Now);
model.ListBooks = books.ToList();
Here:
(model.BookStatus.SelectedStatusId == (int)Enums.Registration.OverDue && x.DueBack < DateTime.Now.)
Should be instead:
(x.BookStatus.SelectedStatusId == (int)Enums.Registration.OverDue && x.DueBack < DateTime.Now.)
Because You like to compare element of LINQ query, not the model.

Conditional LINQ where statement?

I have a linq statement that I want to add an additional where clause to if a drop down index is not 0.
people.Where(n.surname == "surname" || n.forename == "forename" && (dropdown.SelectedIndex > 0) ? n.id = dropdown.SelectedValue : n.id > 0).Select(n => n);
I am not even sure if what I am trying is possible??
I would like to do this rather than having to write two different statements.
Any ideas?
Thanks
Fortunately, this is easy because queries compose:
var query = people.Where(n.surname == "surname" || n.forename == "forename");
if (dropdown.SelectedIndex > 0)
{
query = query.Where(n => n.id.ToString() == dropdown.SelectedValue);
}

Categories