Prevent Entity Framework query from being evaluated locally - c#

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

Related

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

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

Cannot implicitly convert type System.Collections.Generic.IEnumerable<> to bool

I'm developing an ASP.NET MVC 4 Application and I'm trying to run this Lambda expression in Entity Framework 5.
var customer = db.GNL_Customer.Where(d => d.GNL_City.FKProvinceID == advancedProvinceID || advancedProvinceID == null)
.Where(d => d.FKCityID == advancedCityID || advancedCityID == null)
.Where(d => d.FKDepartmentStoreID == advancedDepartmentStoreID || advancedDepartmentStoreID == null)
.Where(d => d.GNL_CustomerLaptopProduct.Where(r => String.Compare(r.BrandName, brandID) == 0 || brandID == null));
I get this error :
Cannot implicitly convert type 'System.Collections.Generic.IEnumerable<ITKaranDomain.GNL_CustomerLaptopProduct>' to 'bool'
I know that the last where clause is wrong but I don't know how to correct it.
You might want another .Any instead of a .Where in your .Any clause at the end:
var customer = db.GNL_Customer.Where(d => d.GNL_City.FKProvinceID == advancedProvinceID || advancedProvinceID == null)
.Where(d => d.FKCityID == advancedCityID || advancedCityID == null)
.Where(d => d.FKDepartmentStoreID == advancedDepartmentStoreID || advancedDepartmentStoreID == null)
.Any(d => d.GNL_CustomerLaptopProduct.Any(r => String.Compare(r.BrandName, brandID) == 0 || brandID == null));
Use Where ( Any ) in last statement to select customers which have at least one product satisfying your conditions:
var customer = db.GNL_Customer
.Where(d => d.GNL_City.FKProvinceID == advancedProvinceID || advancedProvinceID == null)
.Where(d => d.FKCityID == advancedCityID || advancedCityID == null)
.Where(d => d.FKDepartmentStoreID == advancedDepartmentStoreID || advancedDepartmentStoreID == null)
.Where(d => brandID == null || d.GNL_CustomerLaptopProduct.Any(r => r.BrandName == brandID));

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

streamline linq query

I have tried using Left outer join using linq. It gives me the same result anytime i change my report parameters.
var _result = (from ls in SessionHandler.CurrentContext.LennoxSurveyResponses
from ml
in SessionHandler.CurrentContext.MailingListEntries
.Where(mle => mle.SurveyCode == ls.SurveyCode).DefaultIfEmpty()
from lists
in SessionHandler.CurrentContext.MailingLists
.Where(m => m.MailingListId == ml.MailingListId).DefaultIfEmpty()
from channel
in SessionHandler.CurrentContext.Channels
.Where(ch => ch.ChannelId == lists.ChannelId).DefaultIfEmpty()
from tmChannelGroup
in SessionHandler.CurrentContext.ChannelGroups
.Where(tcg => tcg.ChannelGroupId == channel.ChannelGroupId).DefaultIfEmpty()
from dmChannelGroup
in SessionHandler.CurrentContext.ChannelGroups
.Where(dcg => dcg.ChannelGroupId == tmChannelGroup.ParentChannelGroupId).DefaultIfEmpty()
from amChannelGroup
in SessionHandler.CurrentContext.ChannelGroups
.Where(acg => acg.ChannelGroupId == dmChannelGroup.ParentChannelGroupId).DefaultIfEmpty()
where (model.ChannelId != 0 && channel.ChannelId == model.ChannelId ||
model.TMId != 0 && channel.ChannelGroupId == model.TMId ||
model.DistrictId != 0 && dmChannelGroup.ChannelGroupId == model.DistrictId ||
model.AreaId != 0 && amChannelGroup.ChannelGroupId == model.AreaId ||
model.AreaId == 0 && amChannelGroup.ChannelGroupId == model.LoggedChannelGroupId ||
model.DistrictId == 0 && dmChannelGroup.ChannelGroupId == model.LoggedChannelGroupId ||
model.TMId == 0 && tmChannelGroup.ChannelGroupId == model.LoggedChannelGroupId ||
model.ChannelId == 0 && tmChannelGroup.ChannelGroupId == model.LoggedChannelGroupId)
&& (ml.EmailDate != null || ml.LetterDate != null || ml.EmailBounce == null)
select ls).ToList();
I have this LINQ query which is repeated (sort of) based on the model value. How would I be able to shorten this query..if I could just use one var object instead of using a bunch for different parameters..as you see this code is repeated.
if (model.ChannelId != 0)
{
var _result =
(from ls in SessionHandler.CurrentContext.LennoxSurveyResponses
join ml in SessionHandler.CurrentContext.MailingListEntries on ls.SurveyCode equals ml.SurveyCode
join m in SessionHandler.CurrentContext.MailingLists on ml.MailingListId equals m.MailingListId
join ch in SessionHandler.CurrentContext.Channels on m.ChannelId equals ch.ChannelId
where ch.ChannelId == model.ChannelId
&& ml.EmailBounce == null || ml.EmailBounce.Equals(false)
select ls).ToList();
var _SentSurveys =
(from ml in SessionHandler.CurrentContext.MailingListEntries
join m in SessionHandler.CurrentContext.MailingLists on ml.MailingListId equals m.MailingListId
join ch in SessionHandler.CurrentContext.Channels on m.ChannelId equals ch.ChannelId
where ch.ChannelId == model.ChannelId
&& (ml.EmailDate != null || ml.LetterDate != null || ml.EmailBounce == null)
select ml).ToList();
model.SentSurveys = _SentSurveys.Count() > 0 ? _SentSurveys.Count() : 0;
model.CompletedSurveys = _result.Count() > 0 ? _result.Count() : 0;
model.PercentageComplete = model.SentSurveys != 0 ? model.CompletedSurveys / model.SentSurveys : 0;
//model.Referring = _result.Average(m => Convert.ToInt32(m.Question1Answer));
model.Referring = Math.Round(_result.Select(m => string.IsNullOrEmpty(m.Question1Answer) ? 0 : Double.Parse(m.Question1Answer)).Average());
model.ServicePerformance = Math.Round(_result.Select(m => string.IsNullOrEmpty(m.Question2Answer) ? 0 : Double.Parse(m.Question2Answer)).Average());
model.InstallPerformance = Math.Round(_result.Select(m => string.IsNullOrEmpty(m.Question3Answer) ? 0 : Double.Parse(m.Question3Answer)).Average());
model.ReferringLennox = Math.Round(_result.Select(m => string.IsNullOrEmpty(m.Question4Answer) ? 0 : Double.Parse(m.Question4Answer)).Average());
}
else if (model.TMId != 0)
{
var _result =
(from ls in SessionHandler.CurrentContext.LennoxSurveyResponses
join ml in SessionHandler.CurrentContext.MailingListEntries on ls.SurveyCode equals ml.SurveyCode
join m in SessionHandler.CurrentContext.MailingLists on ml.MailingListId equals m.MailingListId
join ch in SessionHandler.CurrentContext.Channels on m.ChannelId equals ch.ChannelId
where ch.ChannelGroupId == model.TMId
select ls).ToList();
var _SentSurveys =
(from ml in SessionHandler.CurrentContext.MailingListEntries
join m in SessionHandler.CurrentContext.MailingLists on ml.MailingListId equals m.MailingListId
join ch in SessionHandler.CurrentContext.Channels on m.ChannelId equals ch.ChannelId
where ch.ChannelGroupId == model.TMId
&& (ml.EmailDate != null || ml.LetterDate != null || ml.EmailBounce == null)
select ml).ToList();
model.SentSurveys = _SentSurveys.Count() > 0 ? _SentSurveys.Count() : 0;
model.CompletedSurveys = _result.Count() > 0 ? _result.Count() : 0;
model.PercentageComplete = model.SentSurveys != 0 ? model.CompletedSurveys / model.SentSurveys : 0;
model.Referring = _result.Select(m => string.IsNullOrEmpty(m.Question1Answer) ? 0 : Double.Parse(m.Question1Answer)).Average();
model.ServicePerformance = _result.Select(m => string.IsNullOrEmpty(m.Question2Answer) ? 0 : Double.Parse(m.Question2Answer)).Average();
model.InstallPerformance = _result.Select(m => string.IsNullOrEmpty(m.Question3Answer) ? 0 : Double.Parse(m.Question3Answer)).Average();
model.ReferringLennox = _result.Select(m => string.IsNullOrEmpty(m.Question4Answer) ? 0 : Double.Parse(m.Question4Answer)).Average();
}
and there are 5 additional model parameters and for each parameters, a new var _result and _SentSurveys are created..i just want to streamline this code.
I think refactoring this first can make writing these queries easier would be beneficial. If this isn't an option, you can use some CompiledQueries to cut down on the repetition of the big queries. But doing so doesn't make it "streamlined" in terms of efficiency, just makes your code slightly cleaner. Additionally, your post-processing in both cases look nearly identical with a lot of unnecessary checks. No need to repeat the common stuff. With some heavy refactoring, you could probably do something like this:
// need to set up the compiled queries first
static readonly Func<MyDataContextType,
MyModelType,
Func<MyDataContextType, MailingListEntry, MailingList, Channel, MyModelType, bool>,
IQueryable<LennoxSurveyResponse>>
GetResult = CompiledQuery.Compile(
(MyDataContextType ctx, MyModelType mod,
Func<MyDataContextType, MailingListEntry, MailingList, Channel, MyModelType, bool> pred) =>
from lsr in ctx.LennoxSurveyResponses
join mle in ctx.MailingListEntries on lsr.SurveyCode equals mle.SurveyCode
join ml in ctx.MailingLists on mle.MailingListId equals ml.MailingListId
join ch in ctx.Channels on ml.ChannelId equals ch.ChannelId
where pred(ctx, mod, mle, ml, ch)
select lsr);
static readonly Func<MyDataContextType, MyModelType, IQueryable<MailingListEntry>>
GetSentSurveys = CompiledQuery.Compile(
(MyDataContextType ctx, MyModelType mod) =>
from mle in ctx.MailingListEntries
join ml in ctx.MailingLists on mle.MailingListId equals ml.MailingListId
join ch in ctx.Channels on ml.ChannelId equals ch.ChannelId
where ch.ChannelId == mod.ChannelId
&& (mle.EmailDate != null || mle.LetterDate != null || mle.EmailBounce == null)
select mle);
static readonly Func<MyDataContextType, MyModelType, MailingListEntry, MailingList, Channel, bool>
ChannelPredicate = CompiledQuery.Compile(
(MyDataContextType ctx, MyModelType mod,
MailingListEntry mle, MailingList ml, Channel ch) =>
ch.ChannelId == mod.ChannelId && ml.EmailBounce == null || !ml.EmailBounce.Value);
static readonly Func<MyDataContextType, MyModelType, MailingListEntry, MailingList, Channel, bool>
TMPredicate = CompiledQuery.Compile(
(MyDataContextType ctx, MyModelType mod,
MailingListEntry mle, MailingList ml, Channel ch) =>
ch.ChannelGroupId == mod.TMId);
static void UpdateModel(MyModelType model)
{
if (model.ChannelId == 0 && model.TMId == 0) return;
var currentContext = SessionHandler.CurrentContext;
var predicate = (model.ChannelId != 0) ? ChannelPredicate : TMPredicate;
var results = GetResults(currentContext, model, predicate).ToList();
var sentSurveys = GetSentSurveys(currentContext, model).ToList();
model.SentSurveys = sentSurveys.Count();
model.CompletedSurveys = results.Count();
model.PercentageComplete = model.SentSurveys != 0 ? model.CompletedSurveys / model.SentSurveys : 0;
model.Referring = _result.Average(m => string.IsNullOrEmpty(m.Question1Answer) ? 0 : Double.Parse(m.Question1Answer));
model.ServicePerformance = _result.Average(m => string.IsNullOrEmpty(m.Question2Answer) ? 0 : Double.Parse(m.Question2Answer));
model.InstallPerformance = _result.Average(m => string.IsNullOrEmpty(m.Question3Answer) ? 0 : Double.Parse(m.Question3Answer));
model.ReferringLennox = _result.Average(m => string.IsNullOrEmpty(m.Question4Answer) ? 0 : Double.Parse(m.Question4Answer));
if (model.ChannelId != 0)
{
// should be rounded
model.Referring = Math.Round(model.Referring);
model.ServicePerformance = Math.Round(model.ServicePerformance);
model.InstallPerformance = Math.Round(model.InstallPerformance);
model.ReferringLennox = Math.Round(model.ReferringLennox);
}
}

Problem with LINQ query

The following works fine:
(from e in db.EnquiryAreas
from w in db.WorkTypes
where
w.HumanId != null &&
w.SeoPriority > 0 &&
e.HumanId != null &&
e.SeoPriority > 0 &&
db.Enquiries.Where(f =>
f.WhereId == e.Id &&
f.WhatId == w.Id &&
f.EnquiryPublished != null &&
f.StatusId != EnquiryMethods.STATUS_INACTIVE &&
f.StatusId != EnquiryMethods.STATUS_REMOVED &&
f.StatusId != EnquiryMethods.STATUS_REJECTED &&
f.StatusId != EnquiryMethods.STATUS_ATTEND
).Any()
select
new
{
EnquiryArea = e,
WorkType = w
});
But:
(from e in db.EnquiryAreas
from w in db.WorkTypes
where
w.HumanId != null &&
w.SeoPriority > 0 &&
e.HumanId != null &&
e.SeoPriority > 0 &&
EnquiryMethods.BlockOnSite(db.Enquiries.Where(f => f.WhereId == e.Id && f.WhatId == w.Id)).Any()
select
new
{
EnquiryArea = e,
WorkType = w
});
+
public static IQueryable<Enquiry> BlockOnSite(IQueryable<Enquiry> linq)
{
return linq.Where(e =>
e.EnquiryPublished != null &&
e.StatusId != STATUS_INACTIVE &&
e.StatusId != STATUS_REMOVED &&
e.StatusId != STATUS_REJECTED &&
e.StatusId != STATUS_ATTEND
);
}
I get the following error:
base {System.SystemException}: {"Method 'System.Linq.IQueryable1[X.Enquiry]
BlockOnSite(System.Linq.IQueryable1[X.Enquiry])' has no supported translation to SQL."}
Linq to Sql only translates certain method calls to SQL, and yours (BlockOnSite) is not one of them. Hence the error. The fact that your method takes an IQueryable<T> and returns an IQueryable<T> doesn't make it special.
Ok I solved it using:
IQueryable<Enquiry> visibleOnSite = EnquiryMethods.VisibleOnSite(db.Enquiries);
var combinations = (from e in db.EnquiryAreas
from w in db.WorkTypes
where
w.HumanId != null &&
w.SeoPriority > 0 &&
e.HumanId != null &&
e.SeoPriority > 0 &&
visibleOnSite.Where(f => f.WhereId == e.Id && f.WhatId == w.Id).Any()
select
new
{
EnquiryArea = e,
WorkType = w
});

Categories