Why is this query raising "SQL statement is nested too deeply"? - c#

Yesterday, a nice person helped me to build a PredicateBuilder for Linq to Entities here.
Seems to work fine, however the complete query generates this hideous 70 000 lines long thing here (too long to be pasted), and raising SQL statement is nested too deeply.
Here is the context :
The user is looking for a list of animals matching his criteria, notably regarding abilities.
In the GUI, for each ability type (ex: "maniability", "agility" etc.), the user can select a modifier (">", "<", or "=") and a value.
For example, he may want to display "All animals that have an ability potential > 3 in agility", or "All animals that have an ability skill < 10 in maniability and ability potential = 2 in agility"
About the database:
Player with columns Id
Animal with columns Id
Ability with columns :
Id
AnimalId
TypeId (representing Enum which can be "Potential", "BirthPotentiel" or "Skill")
AbilityId (representing Enum which can be "Agility", or "Maniability")
Value
Thus, each animal has an AllAbilities property which is an ICollection<Ability>.
Here is the search function (all parameters have previously been entered, or left blank, by the user in the GUI).
public async Task<List<Animal>> Search
(
Player player,
int speciesId,
int breedId,
int coatId,
int genderId,
int minAge,
int maxAge,
int priceModifier, // int representing an Enum Criteria.ModifierE: ">", "<" or "="
int priceValue,
string ownerPseudo,
bool isSearchingOwn,
int minHeight,
int maxHeight,
int minWeight,
int maxWeight,
List<int> character, // representing list of Enum Flags
List<int> abilitySkillModifiers, // representing list of Enum ModifierE: ">", "<" or "="
List<int> abilitySkillValues,
List<int> abilityPotentialModifiers, // representing list of Enum ModifierE: ">", "<" or "="
List<int> abilityPotentialValues
)
{
// You can see "PredicateUtils" class following the first link of this post
var filter = PredicateUtils.Null<Animal>();
filter = filter.And(e => speciesId != -1 ? e.SpeciesId == speciesId : true);
filter = filter.And(e => breedId != -1 ? e.BreedId == breedId : true);
filter = filter.And(e => coatId != -1 ? e.CoatId == coatId : true);
filter = filter.And(e => genderId != -1 ? e.GenderId == genderId : true);
filter = filter.And(e => minAge != -1 ? e.age >= minAge : true);
filter = filter.And(e => maxAge != -1 ? e.age <= maxAge : true);
string pseudo = isSearchingOwn ? player.Pseudo : ownerPseudo;
filter = filter.And(e => !string.IsNullOrEmpty(ownerPseudo) ? e.Owner.Pseudo.Equals(pseudo, StringComparison.InvariantCultureIgnoreCase) : true);
filter = filter.And(e => minHeight > 0 ? e.FinalHeight >= minHeight : true);
filter = filter.And(e => maxHeight > 0 ? e.FinalHeight <= maxHeight : true);
filter = filter.And(e => minWeight > 0 ? e.FinalWeight >= minWeight : true);
filter = filter.And(e => maxWeight > 0 ? e.FinalWeight <= maxWeight : true);
filter = filter.And(e => character.All(c => (e.character & c) == c));
for (int i = 0; i < abilitySkillValues.Count; i++)
{
filter = filter.And(
AbilitySkillFilter
(
(Criteria.ModifierE)abilitySkillModifiers[i], // ">", "<", or "="
i,
abilitySkillValues[i] // value entered by the user for the current ability
)
);
}
for (int i = 0; i < abilityPotentialValues.Count; i++)
{
filter = filter.And(
AbilityPotentialFilter
(
(Criteria.ModifierE)abilityPotentialModifiers[i], // ">", "<", or "="
i,
abilityPotentialValues[i] // value entered by the user for the current ability
)
);
}
return await GetAll(filter);
}
And the abilities filter functions :
static Expression<Func<Animal, bool>> AbilitySkillFilter(Criteria.ModifierE modifier, int abilityId, int userValue)
{
if (modifier == Criteria.ModifierE.More) // User chose ">"
return e => e.AllAbilities.Any(a => a.TypeId == (int)Ability.TypeE.Skill && a.AbilityId == abilityId)
? e.AllAbilities.FirstOrDefault(a => a.TypeId == (int)Ability.TypeE.Skill && a.AbilityId == abilityId).Value >= userValue
: value <= 0;
else if (modifier == Criteria.ModifierE.Equal) // User chose "<"
return e => e.AllAbilities.Any(a => a.TypeId == (int)Ability.TypeE.Skill && a.AbilityId == abilityId)
? e.AllAbilities.FirstOrDefault(a => a.TypeId == (int)Ability.TypeE.Skill && a.AbilityId == abilityId).Value == userValue
: value == 0;
else if (modifier == Criteria.ModifierE.Less) // User chose "<"
return e => e.AllAbilities.Any(a => a.TypeId == (int)Ability.TypeE.Skill && a.AbilityId == abilityId)
? e.AllAbilities.FirstOrDefault(a => a.TypeId == (int)Ability.TypeE.Skill && a.AbilityId == abilityId).Value <= userValue
: value >= 0;
else
return null;
}
static Expression<Func<Animal, bool>> AbilityPotentialFilter(Criteria.ModifierE modifier, int abilityId, int userValue)
{
if (modifier == Criteria.ModifierE.More)
return e => e.AllAbilities.Any(a => a.TypeId == (int)Ability.TypeE.Potential && a.AbilityId == abilityId)
? e.AllAbilities.FirstOrDefault(a => a.TypeId == (int)Ability.TypeE.Potential && a.AbilityId == abilityId).Value >= userValue
: e.AllAbilities.FirstOrDefault(a => a.TypeId == (int)Ability.TypeE.BirthPotential && a.AbilityId == abilityId).Value >= userValue;
else if (modifier == Criteria.ModifierE.Equal)
return e => e.AllAbilities.Any(a => a.TypeId == (int)Ability.TypeE.Potential && a.AbilityId == abilityId)
? e.AllAbilities.FirstOrDefault(a => a.TypeId == (int)Ability.TypeE.Potential && a.AbilityId == abilityId).Value == userValue
: e.AllAbilities.FirstOrDefault(a => a.TypeId == (int)Ability.TypeE.BirthPotential && a.AbilityId == abilityId).Value == userValue;
else if (modifier == Criteria.ModifierE.Less)
return e => e.AllAbilities.Any(a => a.TypeId == (int)Ability.TypeE.Potential && a.AbilityId == abilityId)
? e.AllAbilities.FirstOrDefault(a => a.TypeId == (int)Ability.TypeE.Potential && a.AbilityId == abilityId).Value <= userValue
: e.AllAbilities.FirstOrDefault(a => a.TypeId == (int)Ability.TypeE.BirthPotential && a.AbilityId == abilityId).Value <= userValue;
else
return null;
}
Explanation :
In database, Ability rows with TypeId == Potential or TypeId == Skill may not exist, while TypeId == BirthPotential always do.
In case TypeId == Potential does not exist for the current animal and the current AbilityId, I want to compare user value with TypeId == BirthPotential row value (which always exists).
In case TypeId == Skill does not exist for the current animal and the current AbilityId, I want to compare user value with 0.
If anyone has any suggestion about why this query is producing such an horrible ouput and has an improvement, I would be really grateful.
Don't hesitate if you need more information.
SOLUTION:
It finally works, thanks to juharr proposal (using simple if instead of ternary if to not add the clause if not necessary), combined to Ivan Stoev solution.
With criteria on age, gender, species, pseudo, minheight, maxheight, character, one skill ability and one potential ability, here is the new SQL output: almost 70 000 lines to 60 !
Result here
Thanks a lot !

When doing dynamic filtering, try to do more static evaluation outside of the expressions. This way you'll get better queries because currently EF does not optimize constant expressions except for building static IN (...) list criteria.
But the main problem with your current code are the usages of FirstOrDefault inside your ability filters. In general try to avoid using any query construct type that can lead to SQL subquery, because as you saw, for some reason EF is nesting all the subqueries, hence you get that monstrosity SQL and the error. The safe construct is to use Any which is translated to SQL EXISTS subquery w/o nesting.
So try this and see what you'll get:
static Expression<Func<Animal, bool>> AbilitySkillFilter(Criteria.ModifierE modifier, int abilityId, int userValue)
{
Expression<Func<GameAnimal, bool>> filter = null;
bool includeMissing = false;
if (modifier == Criteria.ModifierE.More) // User chose ">"
{
filter = e => e.AllAbilities.Any(a => a.TypeId == (int)Ability.TypeE.Skill && a.AbilityId == abilityId && a.Value >= userValue);
includeMissing = userValue <= 0;
}
else if (modifier == Criteria.ModifierE.Equal) // User chose "="
{
filter = e => e.AllAbilities.Any(a => a.TypeId == (int)Ability.TypeE.Skill && a.AbilityId == abilityId && a.Value == userValue);
includeMissing = userValue == 0;
}
else if (modifier == Criteria.ModifierE.Less) // User chose "<"
{
filter = e => e.AllAbilities.Any(a => a.TypeId == (int)Ability.TypeE.Skill && a.AbilityId == abilityId && a.Value >= userValue);
includeMissing = userValue >= 0;
}
if (filter != null && includeMissing)
filter = filter.Or(e => !e.AllAbilities.Any(a => a.TypeId == (int)Ability.TypeE.Skill && a.AbilityId == abilityId));
return filter;
}
static Expression<Func<Animal, bool>> AbilityPotentialFilter(Criteria.ModifierE modifier, int abilityId, int userValue)
{
if (modifier == Criteria.ModifierE.More)
return e => e.AllAbilities.Any(a => a.TypeId == (int)Ability.TypeE.Potential && a.AbilityId == abilityId)
? e.AllAbilities.Any(a => a.TypeId == (int)Ability.TypeE.Potential && a.AbilityId == abilityId && a.Value >= userValue)
: e.AllAbilities.Any(a => a.TypeId == (int)Ability.TypeE.BirthPotential && a.AbilityId == abilityId && a.Value >= userValue);
else if (modifier == Criteria.ModifierE.Equal)
return e => e.AllAbilities.Any(a => a.TypeId == (int)Ability.TypeE.Potential && a.AbilityId == abilityId)
? e.AllAbilities.Any(a => a.TypeId == (int)Ability.TypeE.Potential && a.AbilityId == abilityId && a.Value == userValue)
: e.AllAbilities.Any(a => a.TypeId == (int)Ability.TypeE.BirthPotential && a.AbilityId == abilityId && a.Value == userValue);
else if (modifier == Criteria.ModifierE.Less)
return e => e.AllAbilities.Any(a => a.TypeId == (int)Ability.TypeE.Potential && a.AbilityId == abilityId)
? e.AllAbilities.Any(a => a.TypeId == (int)Ability.TypeE.Potential && a.AbilityId == abilityId && a.Value <= userValue)
: e.AllAbilities.Any(a => a.TypeId == (int)Ability.TypeE.BirthPotential && a.AbilityId == abilityId && a.Value <= userValue);
else
return null;
}

Related

Get filter criteria from function in c# LINQ to SQL

I have the following LINQ query. Is there a way to outsource the central part into a method?
IQueryable<vwResCache> clientDataset = db.vwResCaches.Where(r =>
clientResources.Contains(r.ID_Resource) &&
r.StartDate < prospectDate &&
(
r.StartDate >= retrospectDate ||
r.StartDate < retrospectDate && (
(clientConfig.FinishReq_FinishedPA && clientConfig.FinishReq_DeliveryNotesPrinted && (!r.FinishedPA.Value || !r.DeliveryNotesPrinted.Value)) ||
(clientConfig.FinishReq_FinishedPA && !clientConfig.FinishReq_DeliveryNotesPrinted && !r.FinishedPA.Value) ||
(!clientConfig.FinishReq_FinishedPA && clientConfig.FinishReq_DeliveryNotesPrinted && !r.DeliveryNotesPrinted.Value)
)
));
As you can see, the must indented part depends on the local object clientConfig. I want to exclude that into s separate method like so:
IQueryable<vwResCache> clientDataset = db.vwResCaches.Where(r =>
clientResources.Contains(r.ID_Resource) &&
r.StartDate < prospectDate &&
(
r.StartDate >= retrospectDate ||
r.StartDate < retrospectDate && GetFilterCriteria(clientConfig, r)
));
This method should look something like:
FilterExpression GetFilterExpression(ClientConfig clientConfig, c)
{
if (clientConfig.FinishReq_FinishedPA && clientConfig.FinishReq_DeliveryNotesPrinted)
return new FilterExpression(c, r => !r.FinishedPA.Value || !r.DeliveryNotesPrinted.Value);
if (clientConfig.FinishReq_FinishedPA)
return new FilterExpression(c, r => !r.FinishedPA.Value);
if (clientConfig.FinishReq_DeliveryNotesPrinted)
return new FilterExpression(c, r => !r.DeliveryNotesPrinted.Value);
}
Is such a thing possible? Or would the effort not justify the benefit?
Calling .Where(...) multiple times has the same effect as combining the individual filters with &&.
Unfortunately, there's no simple equivalent for combining filters with ||, so your filter function would need to include the StartDate logic for each branch.
Something like this should work:
static Expression<Func<vwResCache, bool>> BuildClientConfigFilter(
DateTime retrospectDate,
ClientConfig clientConfig)
{
if (clientConfig.FinishReq_FinishedPA
&& clientConfig.FinishReq_DeliveryNotesPrinted)
{
return r => r.StartDate >= retrospectiveDate
|| (r.StartDate < retrospectiveDate
&& (!r.FinishedPA.Value || !r.DeliveryNotesPrinted.Value));
}
if (clientConfig.FinishReq_FinishedPA
&& !clientConfig.FinishReq_DeliveryNotesPrinted)
{
return r => r.StartDate >= retrospectiveDate
|| (r.StartDate < retrospectiveDate
&& !r.FinishedPA.Value);
}
if (!clientConfig.FinishReq_FinishedPA
&& clientConfig.FinishReq_DeliveryNotesPrinted)
{
return r => r.StartDate >= retrospectiveDate
|| (r.StartDate < retrospectiveDate
&& !r.DeliveryNotesPrinted.Value;
}
return r => r.StartDate >= retrospectiveDate;
}
Usage:
IQueryable<vwResCache> clientDataset = db.vwResCaches
.Where(r => clientResources.Contains(r.ID_Resource))
.Where(r => r.StartDate < prospectDate)
.Where(BuildClientConfigFilter(retrospectDate, clientConfig));

Prevent Entity Framework query from being evaluated locally

I have this query that is being partially evaluated locally and I am wondering how to prevent it.
It seems to be the conditional select that is causing the problem.
var fetchQuery = (
from udg in _entities.UberDirectGroups.AsNoTracking()
join uber in _entities.Ubers.AsNoTracking()
on udg.Id equals uber.Id
let memberCount = (
from t in _entities.UberDirects.AsNoTracking()
join u in _entities.Ubers.AsNoTracking()
on t.UberToId equals u.Id
where t.UberFromId == udg.Id && !u.Deleted
select u.UberTypeId == (byte)UberType.User ? 1 :
u.UberTypeId == (byte)UberType.Department ? u.Department.Users.Where(user => user.Deleted == false).Count() :
u.UberTypeId == (byte)UberType.Office ? u.tblOffice.tblDepartment.SelectMany(d => d.Users).Where(user => user.Deleted == false).Count() :
u.UberTypeId == (byte)UberType.ProjectGroup ? u.Group.GroupMembers.Select(pm => pm.User).Where(user => user.Deleted == false).Count() :
u.UberTypeId == (byte)UberType.Role ? _entities.Roles.Where(r => r.RoleDataId == u.tblRoleData.Id).Select(r => r.tblUser).Where(user => user.Deleted == false).Count() : 0
).Sum()
where
udg != null &&
uber != null &&
uber.InstanceId == instanceId &&
(!isSearch || udg.Name.Contains(searchText))
select new TargetGroupProjection
{
id = udg.Id,
name = udg.Name,
created = uber.Date,
toCount = memberCount
}
);

EntityFremework: could a select before a where optimize this?

I'm trying to gain performance on this query, and I'd like to know if calling a Select() before the Where() I could have some improvement:
public async Task<List<PostValues>> GetValuesToTheDashboard(DataFilter filter, CancellationToken cancellationToken) {
long startTimestanp = Helpers.UnixTimeNow(filter.StartDate);
long endTimestanp = Helpers.UnixTimeNow(filter.EndDate);
return await
_context.CatchDetails.Where(
x => x.Monitoring.Client.Id == filter.CustomerId && x.Data.published >= startTimestanp
&& x.Data.published <= endTimestanp
&& ((filter.Sentiment == Sentiments.ALL) || x.Sentiment_enum == filter.Sentiment)
&& (filter.MonitoringId == 0 || x.Monitoring.id == filter.MonitoringId)
&& (filter.KeywordId == 0 || x.Keyword.Id == filter.KeywordId)
&& (filter.MotiveId == 0 || x.Motive.Id == filter.MotiveId)
&& (filter.SocialNetwork.Count == 0 || filter.SocialNetwork.Any(s => x.Data.social_media == s))
&& (filter.Busca == "" || x.Data.content_snippet.Contains(filter.Busca))
&& (filter.Gender.Count == 0 || filter.Gender.Any(g => x.Data.extra_author_attributes.gender_enum == g)))
.Select(s => new PostValues() {
CatchDetailsId=s.Id,
Monitoring=s.Monitoring.name,
Keyword=s.Keyword.Text,
Motive=s.Motive.Name,
Sentiment=s.Sentiment_enum,
Gender=s.Data.extra_author_attributes.gender_enum,
SocialMedia=s.Data.social_media,
Country=s.Data.extra_author_attributes.world_data.country_code,
State=s.Data.extra_author_attributes.world_data.region,
Published=s.Data.published
}).ToListAsync(cancellationToken);
}
There might be a way how to improve performance, but it won't be with switching Select and Where (as Chetan mentioned in the comment).
You could build the query in a sequence and based on the filter get a simpler query in the end. This would go like this:
var query = _context.CatchDetails.Where(
x => x.Monitoring.Client.Id == filter.CustomerId && x.Data.published >= startTimestanp
&& x.Data.published <= endTimestanp);
if (filter.Sentiment != Sentiments.ALL)
{
query = query.Where(x => x.Sentiment_enum == filter.Sentiment);
}
if (filter.MonitoringId != 0)
{
query = query.Where(x => x.Monitoring.id == filter.MonitoringId);
}
[...]
return await
query.Select(s => new PostValues() {
[...]
}).ToListAsync(cancellationToken);
Do not forget, the variable query is already on memory of the application when the SQL returns data. If there is many results it could throw memory exception.
I suggest that you limit the range of date on that search.

Filtering an Iqueryable object by a group of rules

I've been filtetring an Iqueryable object by some rules, using successive Where clauses to do && filtering, how can i do the same for || filtering?
Anyone has a suggestion?
Using another words, i want to apply for all rules || filtering.
I'm going to show a portion of the code to exemplify what im asking.
Where i get the pages object:
public IQueryable<gbd_Pages> getAllPagesByOrderAndDir(int template_id, string name = "", int sortOrder = 3, string sortDirection = "asc")
{
IQueryable<gbd_Pages> Listpages;
if (sortDirection == "asc")
{
Listpages = from p in _db.gbd_Pages
from pc in (from c in p.gbd_Content
where c.IsActive == true && c.IsDeleted == false && c.Template_Id == template_id
&& !string.IsNullOrEmpty(name) ? c.Content.ToLower().Contains(name.ToLower()) : true
&& c.gbd_Template_Fields.SortOrder == sortOrder
select c).Take(1)
orderby pc.Content ascending
where p.IsActive == true && p.IsDeleted == false
select p;
}
else
{
Listpages = from p in _db.gbd_Pages
from pc in (from c in p.gbd_Content
where c.IsActive == true && c.IsDeleted == false && c.Template_Id == template_id
&& !string.IsNullOrEmpty(name) ? c.Content.ToLower().Contains(name.ToLower()) : true
&& c.gbd_Template_Fields.SortOrder == sortOrder
select c).Take(1)
orderby pc.Content descending
where p.IsActive == true && p.IsDeleted == false
select p;
}
return Listpages;
}
The function where i do the filtering. Actually it's only doing && filtering for all rules:
public IEnumerable<gbd_Pages> PagesFiltered(List<Rule> newRules, string search, int sortColumnId, string sortColumnDir, out int count)
{
GBD.FrontOffice.Controllers.SegmentsController segmentCtrl = new GBD.FrontOffice.Controllers.SegmentsController();
var Pages = controller.getAllPagesByOrderAndDir(1, search, sortColumnId, sortColumnDir);
if (newRules.Count > 0)//FILTRA A LISTAGEM DE ACORDO COM AS REGRAS
{
foreach (var rule in newRules)
{
if (rule.Value!=null)
{
gbd_Template_Fields templateField = controller.getTemplateFieldById(1, rule.Template_Field_Id);
gbd_Segment_Operators segOperator = segmentCtrl.getOperatorById(rule.Operator_Id);
if (segOperator.Value == "==")//IGUALDADE
if (rule.Value.Trim() != "")
{
if (templateField != null && templateField.Field_Id == 3) //tipo data
{
try
{
DateTime test_datetime = DateTime.Parse(rule.Value); // testa se a data introduzida é valida
Pages = Pages.Where(p => p.gbd_Content.Any(c => c.Templates_Fields_Id == rule.Template_Field_Id && c.IsActive == true && c.IsDeleted == false && (!string.IsNullOrEmpty(c.Content) && c.Content.Trim() == rule.Value.Trim())));
}
catch (Exception ex)
{
}
}
else if (templateField != null && templateField.Field_Id == 12 && !string.IsNullOrEmpty(rule.Value))//Lista múltipla de valores
{
rule.Value = rule.Value.Replace(',', '|');
Pages = Pages.Where(p => p.gbd_Content.Any(c => c.Templates_Fields_Id == rule.Template_Field_Id && c.IsActive == true && c.IsDeleted == false && (!string.IsNullOrEmpty(c.Content) && c.Content == rule.Value)));
}
else
Pages = Pages.Where(p => p.gbd_Content.Any(c => c.Templates_Fields_Id == rule.Template_Field_Id && c.IsActive == true && c.IsDeleted == false && (c.Content != null && c.Content.ToLower().Trim() == rule.Value.ToLower().Trim())));
}
else
{
Pages = Pages.Where(p => !p.gbd_Content.Any(c => c.Templates_Fields_Id == rule.Template_Field_Id) || p.gbd_Content.Any(c => c.Templates_Fields_Id == rule.Template_Field_Id && c.IsActive == true && c.IsDeleted == false && (c.Content == null || c.Content.ToLower().Trim() == rule.Value.ToLower().Trim())));
}
else if (segOperator.Value == ">")//MAIOR QUE
{
if (templateField != null && templateField.Field_Id == 3) //tipo data
{
try
{
DateTime test_datetime = DateTime.Parse(rule.Value); // testa se a data introduzida é valida
Pages = Pages.Where(p => p.gbd_Content.Any(c => c.Templates_Fields_Id == rule.Template_Field_Id && c.IsActive == true && c.IsDeleted == false && (!string.IsNullOrEmpty(c.Content) && (c.Content.Substring(6, 4).CompareTo(rule.Value.Trim().Substring(6, 4)) > 0 || c.Content.Substring(6, 4).CompareTo(rule.Value.Trim().Substring(6, 4)) == 0 && c.Content.Substring(3, 2).CompareTo(rule.Value.Trim().Substring(3, 2)) > 0 || c.Content.Substring(6, 4).CompareTo(rule.Value.Trim().Substring(6, 4)) == 0 && c.Content.Substring(3, 2).CompareTo(rule.Value.Trim().Substring(3, 2)) == 0 && c.Content.Substring(0, 2).CompareTo(rule.Value.Trim().Substring(0, 2)) > 0))));
}
catch (Exception ex)
{
}
}
else if (templateField != null && templateField.Field_Id == 5)//tipo número
Pages = Pages.Where(p => p.gbd_Content.Any(c => c.Templates_Fields_Id == rule.Template_Field_Id && c.IsActive == true && c.IsDeleted == false && (c.Content != null && c.Content != "" && (c.Content.Trim().CompareTo(rule.Value.Trim()) > 0 || (rule.Value.Trim().StartsWith("-") && c.Content.Trim().CompareTo(rule.Value.Trim()) > 0 || (c.Content.Trim().CompareTo("0") > 0))))));//procurar outra solução https://stackoverflow.com/questions/7740693/big-issue-in-converting-string-to-datetime-using-linq-to-entities
else if (templateField != null && templateField.Field_Id == 6) //tipo telefone //ESTA CONDIÇÃO NÃO FOI ADICIONADA AINDA A BD
{
try
{
Pages = Pages.AsEnumerable().Where(p => p.gbd_Content.Any(c => c.Templates_Fields_Id == rule.Template_Field_Id && c.IsActive == true && c.IsDeleted == false && (c.Content != null && c.Content != "" && (Convert.ToInt32(c.Content.Split(' ')[1]) > Convert.ToInt32(rule.Value))))).AsQueryable();//TODO: procurar outra solução
}
catch (Exception ex) { }
}
}
many more rules...
else if (segOperator.Value == "StartsWith")//COMEÇA COM
{
if (rule.Value.Trim() != "")
Pages = Pages.Where(p => p.gbd_Content.Any(c => c.Templates_Fields_Id == rule.Template_Field_Id && c.IsActive == true && c.IsDeleted == false && (c.Content != null && c.Content.ToLower().Trim().StartsWith(rule.Value.ToLower().Trim()))));
}
else if (segOperator.Value == "EndsWith")//ACABA COM
{
if (rule.Value.Trim() != "")
Pages = Pages.Where(p => p.gbd_Content.Any(c => c.Templates_Fields_Id == rule.Template_Field_Id && c.IsActive == true && c.IsDeleted == false && (c.Content != null && c.Content.ToLower().Trim().EndsWith(rule.Value.ToLower().Trim()))));
}
}
}
}
count = Pages.Select(d => d.Id).Count();
return Pages;
}
The Where statement is implicitly using AND in all cases. To get an OR statement equivalent, you'll need to use the double pipe operator (||) inside of a Where statement with something like:
.Where(p => p.A == 'b' || p.C == 'd')
Unfortunately there is no simple way to add successive optional statements the .NET framework. It would be great to have something like .Where(p => p.A == 'b').Or(p => p.C == 'd') but generating a query from this seems impractical - if there was an another Where statement, then how would Linq know how to group it relative to the other two? (A || B && C) versus ((A || B) && C).
That said, there is an external library (LINQKit) that can help fill the gap. See Dynamic where clause (OR) in Linq to Entities for reference.
You can work around the limitations of linq by just adding all the results into a List<>
List<gbd_Pages> allPages = new List<gdb_Pages>();
foreach (var rule in newRules)
{
IEnumerable<gbd_Pages> rulePages;
// current logic (except you need to not overwrite the Pages variable)
// instead of the pattern "Pages = Pages.Where( ..." use "rulePages = Pages.Where( ..."
allPages.AddRange(rulePages);
}
return allPages;

LINQ to Entities: nullable datetime in where clause

I have a where clause that looks up child objects on an entity:
var Lookup = row.Offenses.Where(x => x.Desc == co.Desc && x.Action == co.Action && x.AppealYN == co.AppealYN && x.OffDate == co.OffDate).ToList();
Sometimes the co.OffDate can be null, which will cause an exception. Right now, the only way I can think of to get around that, is to use an if statement:
if (co.OffDate.HasValue)
{
var Lookup = row.Offenses.Where(x => x.Desc == co.Desc && x.Action == co.Action && x.AppealYN == co.AppealYN && x.OffDate == co.OffDate).ToList();
}
else
{
var Lookup = row.Offenses.Where(x => x.Desc == co.Desc && x.Action == co.Action && x.AppealYN == co.AppealYN).ToList();
}
Is there anyway I can re-write the linq query to accomplish what the if statement does? I still want to do a lookup, even if the co.OffDate is null.
You could insert a ternary into your Where filter:
var Lookup = row.Offenses
.Where(x =>
x.Desc == co.Desc
&& x.Action == co.Action
&& x.AppealYN == co.AppealYN
&& (co.OffDate.HasValue ? x.OffDate == co.OffDate : true)
).ToList();
I would rewrite it to be more readable (in my opinion):
var query = row.Offenses.Where(x => x.Desc == co.Desc
&& x.Action == co.Action
&& x.AppealYN == co.AppealYN)
if (co.OffenseDate.HasValue)
{
query = query.Where(x.OffDate == co.OffenseDate);
}
var Lookup = query.ToList();

Categories