EF cannot translate query - c#

I have query for getting data
await _dbContext.VwJobSupplierWithScores
.Where(x => x.JobId == jobId && x.SupplierKey == supplierKey)
.OrderBy(x => x.SpendCurrencyJob)
.Select((x, i) => new {item = x, index = i})
.FirstOrDefaultAsync())!,
But for some reasons EF cannot translate it and I get this error
The LINQ expression 'DbSet()
.Where(x => (Guid?)x.JobId == __jobId_0 && x.SupplierKey == __supplierKey_1)
.OrderBy(x => x.PurchaseOrderValueCurrencyJob)
.Select((x, i) => new {
item = x,
index = i
})' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to 'AsEnumerable', 'AsAsyncEnumerable', 'ToList', or 'ToListAsync'.
How I can write it correctly, that LINQ can translate it?

One option, do as the error told you:
await _dbContext.VwJobSupplierWithScores
.Where(x => x.JobId == jobId && x.SupplierKey == supplierKey)
.OrderBy(x => x.SpendCurrencyJob)
.AsEnumerable()
.Select((x, i) => new {item = x, index = i})
.FirstOrDefaultAsync())!
Another option:
var dbObject = await _dbContext.VwJobSupplierWithScores
.Where(x => x.JobId == jobId && x.SupplierKey == supplierKey)
.OrderBy(x => x.SpendCurrencyJob)
.FirstOrDefaultAsync();
var yourmodel = new { item = dbObject, index = 0 };
Please note that some parts of your original code are completely pointless. Using the "!" after calling FirstOrDefault yourself? If you know there will always be one, just call First. The saving of the index? What do you think the index of the first element will be? So... there is actually no point in having your anonymous object, because the data it holds is redundant. I have shown two ways to make your compiler happy, but you should think about why the code does these weird things in the first place.

Such Select overload is not supported by EF and I actually do not see reason to use that. FirstOrDefaultAsync will return one entity and Index will be always ZERO:
await _dbContext.VwJobSupplierWithScores
.Where(x => x.JobId == jobId && x.SupplierKey == supplierKey)
.OrderBy(x => x.SpendCurrencyJob)
.Select(x => new {item = x, index = 0})
.FirstOrDefaultAync();

Your Problem is, the select not matching the objects in VmJobSupplierQithScores. EF tries to map your Query to that type. Because you do a select with a different type, it can not translate that query.
You can split your query into something like this.
var item = _dbContext.VwJobSupplierWithScores
.Where(x => x.JobId == jobId && x.SupplierKey == supplierKey)
.OrderBy(x => x.SpendCurrencyJob).First();
var result = new {item = x, index = 0};

Related

(Entity) Linq Group By statement type conversion

I am having trouble converting the list type from group by statement back to the type I can use.
I know group by adds a key to the list but how do I get rid of it and it my case convert it back to List<UserAnswers> ? Can anybody provide some insights or point me in the right direction?
public async Task<List<UserAnswers>>
GetQuestionsWellAsync(string UserName) // get questions
{
return await ctx.UserAnswers.Where(x => x.UserId == UserName && x.Correct == 1).
GroupBy(c => c.QuestionId).Where(grp => grp.Count() > 2).ToListAsync();
You can use .SelectMany() to flatten your groupings back out into a single aggregated list.
public async Task<List<UserAnswers>> GetQuestionsWellAsync(string UserName)
{
return await ctx.UserAnswers
.Where(x => x.UserId == UserName && x.Correct == 1)
.GroupBy(c => c.QuestionId)
.Where(grp => grp.Count() > 2)
.SelectMany(grp => grp)
.ToListAsync();
}
Based on your update, it seems like EF cannot translate the GroupBy + Where Count() expression. You may need to project the grouped data into memory first and then filter.
public async Task<List<UserAnswers>> GetQuestionsWellAsync(string UserName)
{
return (await userAnswers
.Where(x => x.UserId == UserName && x.Correct == 1)
.GroupBy(c => c.QuestionId)
.ToListAsync())
.Where(grp => grp.Count() > 2)
.SelectMany(grp => grp)
.ToList();
}

ANY with ALL in Entity Framework evaluates locally

I have the following Entity Framework 2.0 query:
var user = context.Users.AsNoTracking()
.Include(x => x.UserSkills).ThenInclude(x => x.Skill)
.Include(x => x.UserSkills).ThenInclude(x => x.SkillLevel)
.FirstOrDefault(x => x.Id == userId);
var userSkills = user.UserSkills.Select(z => new {
SkillId = z.SkillId,
SkillLevelId = z.SkillLevelId
}).ToList()
Then I tried the following query:
var lessons = _context.Lessons.AsNoTracking()
.Where(x => x.LessonSkills.All(y =>
userSkills.Any(z => y.SkillId == z.SkillId && y.SkillLevelId <= z.SkillLevelId)))
.ToList();
This query evaluates locally and I get the message:
The LINQ expression 'where (([y].SkillId == [z].SkillId) AndAlso ([y].SkillLevelId <= [z].SkillLevelId))' could not be translated and will be evaluated locally.'.
I tried to solve it using userSkills instead of user.UserSkills but no luck.
Is there a way to run this query on the server?
You should try limiting the usage of in-memory collections inside LINQ to Entities queries to basically Contains on primitive value collection, which currently is the only server translatable construct.
Since Contains is not applicable here, you should not use the memory collection, but the corresponding server side subquery:
var userSkills = context.UserSkills
.Where(x => x.UserId == userId);
var lessons = context.Lessons.AsNoTracking()
.Where(x => x.LessonSkills.All(y =>
userSkills.Any(z => y.SkillId == z.SkillId && y.SkillLevelId <= z.SkillLevelId)))
.ToList();
or even embed the first subquery into the main query:
var lessons = context.Lessons.AsNoTracking()
.Where(x => x.LessonSkills.All(y =>
context.UserSkills.Any(z => z.UserId == userId && y.SkillId == z.SkillId && y.SkillLevelId <= z.SkillLevelId)))
.ToList();
Use Contains on the server then filter further on the client:
var userSkillIds = userSkills.Select(s => s.SkillId).ToList();
var lessons = _context.Lessons.AsNoTracking()
.Where(lsn => lsn.LessonSkills.All(lsnskill => userSkillIds.Contains(lsnskill.SkillId)))
.AsEnumerable() // depending on EF Core translation, may not be needed
.Where(lsn => lsn.LessonSkills.All(lsnskill => userSkills.Any(uskill => uskill.SkillId == lsnskill.SkillId && lsnskill.SkillLevelId <= uskill.SkillLevelId)))
.ToList();

The Include path expression must refer to a navigation property defined on the type.

My linq query
model.Questions = db.Questions
.Where (x => x.CategoriesID == categoryId)
.Include (qc => qc.QuestionCounters.Where(x => x.MemberID == User.Identity.GetUserId()))
.Include (qf => qf.QuestionFavorites.Where(x => x.MemberId == User.Identity.GetUserId()))
.Include (qt => qt.QuestionTags)
.ToList();
produces the error
'The Include path expression must refer to a navigation property
defined on the type. Use dotted paths for reference navigation
properties and the Select operator for collection navigation
properties.'
Any ideas why is this happening?
As some people commented, you cannot use Where method in Include.
Disclaimer: I'm the owner of the project Entity Framework Plus
EF+ Query IncludeFilter feature allow filtering related entities.
model.Questions = db.Questions
.Where (x => x.CategoriesID == categoryId)
.IncludeFiler (qc => qc.QuestionCounters.Where(x => x.MemberID == User.Identity.GetUserId()))
.IncludeFiler (qf => qf.QuestionFavorites.Where(x => x.MemberId == User.Identity.GetUserId()))
.IncludeFiler (qt => qt.QuestionTags)
.ToList();
Wiki: EF+ Query IncludeFilter
Solution #2
Another technique is by using projection (which is what my library do under the hood)
bd.Questions
.Select(q = new {
Question = q,
QuestionCounters = q.QuestionCounters.Where(x => x.MemberID == memberId),
QuestionFavorites = q.QuestionFavorites.Where(x => x.MemberId == memberId),
QuestionTags = q.QuestionTags
})
.ToList()
.Select(x => x.Question)
.ToList();
Ok. Ended up with
IQueryable<HomeViewModel> test = db.Questions
.Where(x => x.CategoriesID == categoryId)
.Select(q => q.ToHomeViewModel(User.Identity.GetUserId()));
and
public static HomeViewModel ToHomeViewModel(this Question q, string memberId)
{
return new HomeViewModel()
{
QuestionCounters = q.QuestionCounters.Where(x => x.MemberID == memberId),
QuestionFavorites = q.QuestionFavorites.Where(x => x.MemberId == memberId),
QuestionTags = q.QuestionTags
};
}
How needs include after all? ;)
Thanks for commenting #jle

I want to convert this foreach loop to a LINQ statement

I am not an great at linq by any means but I usually have no issues with a problem of this sort. I want to convert this foreach statement to a LINQ statement:
var existingKeys = new List<int>();
foreach (var taskKey in request.Keys)
{
existingKeys.AddRange(_context.WebTaskGroups
.Where(x => x.TaskGroupNameKey == key && x.TaskKey == taskKey)
.Select(x => x.TaskGroupNameKey));
}
I thought this would do it:
var existingKeys = request.Keys.ForEach(taskKey => _context.WebTaskGroups
.Where(x => x.TaskGroupNameKey == key && x.TaskKey == taskKey)
.Select(x => x.TaskGroupNameKey));
That apparently returns a void not a list...
This:
var existingKeys = request.Keys.Select(taskKey =>
_context.WebTaskGroups
.Where(x => x.TaskGroupNameKey == key && x.TaskKey == taskKey)
.Select(keys => keys.TaskGroupNameKey));
Gives me an "IEnumerable<IQueryable<int>>. So what is the secret sauce that I am missing here?
You shouldn't be performing N database queries in the first place. Using LINQ to perform those N queries instead of a foreach loop doesn't fix that core problem.
You need to re-conceptualize your query so that you have just one query that gets all of the data that you need. In this case that means getting all of the items that match your collection of keys rather than trying to match a single key and then performing N of those queries.
var requestedKeys = request.Keys;
var existingKeys = _context.WebTaskGroups
.Where(x => x.TaskGroupNameKey == key &&
requestedKeys.Contains(x.TaskKey))
.Select(x => x.TaskGroupNameKey))
.ToList();
var existingKeys = request
.SelectMany(r => r.Keys)
.SelectMany(tk =>
_context.WebTaskGroups
.Where(x.TaskGroupNameKey == key && x.TaskKey == tk)
.Select(x => x.TaskGroupNameKey))
.ToList();
var existingKeys = _context.WebTaskGroups
.Where(x => x.TaskGroupNameKey == key && request.Keys.Contains(x.TaskKey))
.Select(x => x.TaskGroupNameKey)
.ToList();
ForEach return a void: http://msdn.microsoft.com/en-us/library/bwabdf9z(v=vs.110).aspx
ForEch: Performs the specified action on each element of the List.
So what to do, is for each item in the list of request.Keys to perform the action to add to the list of existingKeys.
For example:
request.Keys.ForEach(taskKey =>
existingKeys.AddRange(_context.WebTaskGroups
.Where(x => x.TaskGroupNameKey == key && x.TaskKey == taskKey)
.Select(x => x.TaskGroupNameKey));

If/else selected count from sql database

What I'm doing now is getting my SQL database table: IsAcross varchar(45) into my if/else statement. My IsAcross table only consist of YES and NO.
So now I want to take out only select YES statement from SQL Server, so therefore I have put the thingy in the whole list first. Then I use if/else statement to extract out the YES but how am I supposed to do that?
Example: I have a total of 7 items in a list, 4 yes 3 no. I want to take out the all the 4 yes only. Something like that:
ViewModels.WordSearchVM wsvm = new ViewModels.WordSearchVM();
wsvm.ActivityID = id;
var results = db.CrossPuzzles.Where(m => m.ActivityID == id)
.Select(m => m.IsAcross)
.AsEnumerable()
.ToList();
if (results = "yes")
{
else
}
As far as I understand your problem, you can forget your if statement and simply extend the .Where part:
.Where(m => m.ActivityID == id && m.results=="yes")
var results = db.CrossPuzzles.Where(m => m.ActivityID == id)
.Where(m => m.IsAcross)
.AsEnumerable()
.ToList();
//OR
var results = db.CrossPuzzles.Where(m => m.ActivityID == id)
.Where(m => m.IsAcross == "YES")
.AsEnumerable()
.ToList();
var results = db.CrossPuzzles
.Where(m => m.ActivityID == id)
.Select(m => m.IsAcross)
.Where(x => x == "YES") // filter to "YES"
.ToList();
if (results.Count > 0)
// YES
else
// NO
This what you're looking for?

Categories