How to inject OR condition in Entity Framework linq query? - c#

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

Related

How to create dynamic Multiple And linq conditions in foreach loop

if (rowCount == 1)
{
query =
(from x in partJoinTableRepository.GetPartJoinQuery()
join y in partRepository.GetPartsQuery() on x.PartId equals y.Id
join z in partProductTypeReposiotry.GetPartProductTypesQuery() on x.PartId equals z.PartId
where y.IsSkipped == 0 && (y.IsDisabled != "Y" || y.IsDisabled == null) && z.CreatedDate == x.CreatedDate
&& x.CreatedDate == Convert.ToDateTime(fromDate) && cpaclassids.Contains(x.ProductTypeId.ToString())
select x).Cast<PartJoinTable>().AsQueryable();
predicate = PredicateBuilder.True(query);
}
else
{
query = query.Join(partJoinTableRepository.GetPartJoinQuery(), "PartID", "PartID", "inner", "row1", null).Cast<PartJoinTable>().AsQueryable();
// predicate = PredicateBuilder.True(query);
} //query contains multiple dynamic inner joins
//repids contains the list ,I used the predicate builder for the linq to create AND Queries
foreach(var item in repids)
{
predicate = PredicateBuilder.True(query);
if (typeid == "3")
{
predicate = predicate.And(z => ids.Contains(z.ProductTypeId.ToString()) &&
z.CreatedDate == Convert.ToDateTime(fromDate));
}
}
var count = query.Where(predicate).Distinct().Count();
the above line is taking long time to execute,ids contains the lists and query contains the linq query.basically I need to form a multiple "AND" conditions
//The Query is taking lot of time to execute and multiple and conditions are not working
Remove ToList to improve performance. Because ToList execute your query and retrieve object list to memory. But you need only count. you don't need objects.
var count = query.Where(predicate).Distinct().Count();
If I understood you right, your problem is that this query has a long running time. Let's see your code in the last line:
var count = query.Where(predicate).Distinct().ToList().Count();
In LINQ to SQL (and to entities), your query doesn't execute thought you use ToList(), ToArray() etc.. For example, consider the following query:
var strings = Db.Table
.Where((string s) => s.Contains("A")) // Will convert to something like WHERE s LIKE '%A%'
.Select(s => s.ToUpper()) // Will convert to something like SELECT upper(s)
.ToList(); // Here the query sends to the DB and executes
The final query is SELECT upper(s) FROM [Table] WHERE s LIKE '%A%'.
In you case, first you send the query to the DB and get all the objects corresponding to the condition (.Where()), and then get their count inside your app.
Instead, if you'll get from the DB only the count, the query will be faster:
var count = query.Where(predicate).Distinct().Count(); // No .ToList()! Here, .Count() executes the query.

Non-mandatory fields in the search

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

Filtering data with multiple filter values

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.

The method ‘Skip’ is only supported for sorted input in LINQ to Entities. The method ‘OrderBy’ must be called before the method ‘Skip’

Using Entity Framework 6.0.2 and .NET 4.5.1 in Visual Studio 2013 Update 1 with a DbContext connected to SQL Server:
I have a long chain of filters I am applying to a query based on the caller's desired results. Everything was fine until I needed to add paging. Here's a glimpse:
IQueryable<ProviderWithDistance> results = (from pl in db.ProviderLocations
let distance = pl.Location.Geocode.Distance(_geo)
where pl.Location.Geocode.IsEmpty == false
where distance <= radius * 1609.344
orderby distance
select new ProviderWithDistance() { Provider = pl.Provider, Distance = Math.Round((double)(distance / 1609.344), 1) }).Distinct();
if (gender != null)
{
results = results.Where(p => p.Provider.Gender == (gender.ToUpper() == "M" ? Gender.Male : Gender.Female));
}
if (type != null)
{
int providerType;
if (int.TryParse(type, out providerType))
results = results.Where(p => p.Provider.ProviderType.Id == providerType);
}
if (newpatients != null && newpatients == true)
{
results = results.Where(p => p.Provider.ProviderLocations.Any(pl => pl.AcceptingNewPatients == null || pl.AcceptingNewPatients == AcceptingNewPatients.Yes));
}
if (string.IsNullOrEmpty(specialties) == false)
{
List<int> _ids = specialties.Split(',').Select(int.Parse).ToList();
results = results.Where(p => p.Provider.Specialties.Any(x => _ids.Contains(x.Id)));
}
if (string.IsNullOrEmpty(degrees) == false)
{
List<int> _ids = specialties.Split(',').Select(int.Parse).ToList();
results = results.Where(p => p.Provider.Degrees.Any(x => _ids.Contains(x.Id)));
}
if (string.IsNullOrEmpty(languages) == false)
{
List<int> _ids = specialties.Split(',').Select(int.Parse).ToList();
results = results.Where(p => p.Provider.Languages.Any(x => _ids.Contains(x.Id)));
}
if (string.IsNullOrEmpty(keyword) == false)
{
results = results.Where(p =>
(p.Provider.FirstName + " " + p.Provider.LastName).Contains(keyword));
}
Here's the paging I added to the bottom (skip and max are just int parameters):
if (skip > 0)
results = results.Skip(skip);
results = results.Take(max);
return new ProviderWithDistanceDto { Locations = results.AsEnumerable() };
Now for my question(s):
As you can see, I am doing an orderby in the initial LINQ query, so why is it complaining that I need to do an OrderBy before doing a Skip (I thought I was?)...
I was under the assumption that it won't be turned into a SQL query and executed until I enumerate the results, which is why I wait until the last line to return the results AsEnumerable(). Is that the correct approach?
If I have to enumerate the results before doing Skip and Take how will that affect performance? Obviously I'd like to have SQL Server do the heavy lifting and return only the requested results. Or does it not matter (or have I got it wrong)?
I am doing an orderby in the initial LINQ query, so why is it complaining that I need to do an OrderBy before doing a Skip (I thought I was?)
Your result starts off correctly as an ordered queryable: the type returned from the query on the first line is IOrderedQueryable<ProviderWithDistance>, because you have an order by clause. However, adding a Where on top of it makes your query an ordinary IQueryable<ProviderWithDistance> again, causing the problem that you see down the road. Logically, that's the same thing, but the structure of the query definition in memory implies otherwise.
To fix this, remove the order by in the original query, and add it right before you are ready for the paging, like this:
...
if (string.IsNullOrEmpty(languages) == false)
...
if (string.IsNullOrEmpty(keyword) == false)
...
result = result.OrderBy(r => r.distance);
As long as ordering is the last operation, this should fix the runtime problem.
I was under the assumption that it won't be turned into a SQL query and executed until I enumerate the results, which is why I wait until the last line to return the results AsEnumerable(). Is that the correct approach?
Yes, that is the correct approach. You want your RDBMS to do as much work as possible, because doing paging in memory defeats the purpose of paging in the first place.
If I have to enumerate the results before doing Skip and Take how will that affect performance?
It would kill the performance, because your system would need to move around a lot more data than it did before you added paging.

LINQ Syntax for Current Revision

How can I write this SQL statement using C# and LINQ? I am quering an Oracle database and the table has multiple revisions of the records. Therefore, I want onyl the current revision of each record contained in the table.
The SQL looks like this:
select TP_ID, TP_TEXT, TP_DEFN_SAKEY
from TP_DEFN tp1
where tp1.TP_ACTIVE_FLAG = 'Y' and
tp1.FAMILY_ID = 1 and
tp1.TP_DEFN_REV_DTS = (select max(TP_DEFN_REV_DTS)
from TP_DEFN tp2
where tp2.family_id = tp1.family_id and tp2.tp_id = tp1.tp_id )
order by TP_ID
TP_DEFN_REV_DTS is the date time field that stores the current revision.
I am a beginner with LINQ and have been struggling to find an workable solution. Every time that I try grouping in the LINQ query I get an error
GroupBy is not supported
Try something like this:
var res =
from tp1 in TP_DEFN
where tp1.TP_ACTIVE_FLAG == "Y" &&
tp1.FAMILY_ID == 1 &&
tp1.TP_DEFN_REV_DTS == (from tp2 in TP_DEFN
where tp2.FAMILY_ID == tp1.FAMILY_ID &&
tp2.TP_ID == tp1.TP_ID
select tp2.TP_DEFN_REV_DTS).Max()
orderby tp1.TP_ID
select new
{
tp1.TP_ID,
tp1.TP_TEXT,
tp1.TP_DEFN_SAKEY
};
Off the top of my head, and not knowing which LINQ provider you're using...
var q = from tp1 in Context.TP_DEFN
where tp1.TP_ACTIVE_FLAG == "Y"
&& tp1.FAMILY_ID == 1
&& tp1.TP_DEFN_REV_DTS
== Context.TP_DEFN.Where(tp2 => tp2.FAMILY_ID == tp1.FAMILY_ID
&& tp2.TP_ID == tp1.TP_ID)
.Max(tp2 => tp2.TP_DEFN_REV_DTS)
orderby tp1.TP_ID
select new
{
tp1.TP_ID,
tp1.TP_TEXT,
tp1.TP_DEFN_SAKEY
};
If you're using entity framework or linq-to-sql, you can just pass the direct sql if you want (although that'll prevent change tracking, at least by default).
For EF, use ObjectContext.ExecuteStoreQuery: http://msdn.microsoft.com/en-us/library/dd487208.aspx
For L2S, use DataContext.ExecuteQuery: http://msdn.microsoft.com/en-us/library/system.data.linq.datacontext.executequery.aspx

Categories