I have to fix a query which was already written in the LINQ Lambda, I found the fix in a Simple SQL Query but now I have some trouble in converting it to LINQ Query,
Here is my SQL Query
select * from RequestItem_SubRequestItem x
where x.RequestItem_key = 1 and x.SubRequestItem_key in (
select o.SubRequestItem_key
from SubRequestItem_Entitlement o
inner join SubRequestItem sr on sr.SubRequestItem_key = o.SubRequestItem_key
where o.Entitlement_key = 2 and sr.Action = 'Add' )
And below is my LINQ C# code where I am trying to insert my fixes which include inner join.
z.Entitlements = ARMContext.Context.SubRequestItem_Entitlement
.Where(o => o.Entitlement_key == z.AccessKey && !o.Role_key.HasValue && o.Entitlement.EntitlementConfiguration.UserVisible == true
&& (ARMContext.Context.RequestItem_SubRequestItem
.Where(x => x.RequestItem_key == requestItemKey)
.Select(y => y.SubRequestItem_key)
.Contains(o.SubRequestItem_key)))
.Join(ARMContext.Context.SubRequestItems, subrq => subrq.SubRequestItem_key, temp => requestItemKey, (subrq, temp) => subrq == temp)
Where as previously the C# LINQ code looked like this
z.Entitlements = ARMContext.Context.SubRequestItem_Entitlement
.Where(o => o.Entitlement_key == z.AccessKey && !o.Role_key.HasValue && o.Entitlement.EntitlementConfiguration.UserVisible == true
&& (ARMContext.Context.RequestItem_SubRequestItem
.Where(x => x.RequestItem_key == requestItemKey)
.Select(y => y.SubRequestItem_key)
.Contains(o.SubRequestItem_key)))
When I try to insert the JOIN in the LINQ as per my conditions then I get to see this error.
What is my mistake? Can anybody tell me a correct way to do it?
I think this should Suffice your need, although you might have to make changes to the other code which are dependent on your SubRequestItem_Entitlement table with {user, add}
please have a look at that. As I am sure you will have to make those changes.
.Join(ARMContext.Context.SubRequestItems, user => user.SubRequestItem_key, subreqItems => subreqItems.SubRequestItem_key, (user, subreqItems) => new { user, subreqItems })
.Where(Action => Action.subreqItems.Action == z.ApprovalAction)
you can use this query. I exactly matched the SQL query
var query = ARMContext.Context.RequestItem_SubRequestItem
.Where(a => a.RequestItem_key == 1 && a.RequestItem_key == (ARMContext.Context.SubRequestItem_Entitlement
.Join(ARMContext.Context.SubRequestItems,
right => right.SubRequestItem_key,
left => left.SubRequestItem_key,
(right, left) => new
{
right = right,
left = left
})
.Where(x => x.right.Entitlement_key == 2 && x.left.Action == "Add" && x.right.SubRequestItem_key == a.RequestItem_key).Select(y => y.right.SubRequestItem_key)).FirstOrDefault());
Related
I've looked at several possible solutions to this problem, and the ones I have tried do not seem to work. One solution was to use if statements for the optional filters, which doesn't work because I have multiple joins and the where clause is in the last join.
The optional parameters are: roleId, disciplineId, resourceId, and projectName.
try
{
IQueryable<ProjectPlanHeader> bob =
(
from h in context.ProjectPlanHeaders
join r in context.ProjectPlanRevisions on h.ProjectPlanHeaderId equals r.ProjectPlanHeaderId
join a in context.PlanActivityLineItems on r.PlanRevisionId equals a.PlanRevisionId
where ((roleId == null || a.RequiredRoleId == roleId) &&
(disciplineId == null || a.DisciplineId == disciplineId) &&
(resourceId == null || a.ActualResourceId == resourceId) &&
(h.ProjectPlanName.ToLower().Contains(projectName.ToLower()) || projectName == String.Empty))
select h
)
.Include(x => x.ProjectPlanRevisions)
.ThenInclude(y => y.PlanActivityLineItem)
.ThenInclude(z => z.PlannedHours)
.Include(x => x.ActualPlanRevisions)
.ThenInclude(y => y.ActualPlanActivities)
.ThenInclude(z => z.ActualHours);
var john = bob.ToList();
return bob;
}
catch (Exception ex)
{
return null;
}
I added the try/catch so I could see what was happening, as it was silently failing. What I found was a "Object not set to an instance of an object". That's never helpful, because I don't know what object it's talking about. Can someone please show me how to do this the right way?
UPDATE: Thanks for the responses I got, but unfortunately they don't work. The problem is that I end up getting multiple headers back when I filter. This happens because there are multiple revisions for each header, and I really only need the max rev. I tried changing the initial query so that only the max rev was included, and that still did not help. There does not appear to be a solution for this issue, so I will have to do it another way.
Rewrite query to do not use explicit joins, because you have navigation properties. Also because of JOINS you have duplicated records, you will discover it later.
var query = context.ProjectPlanHeaders
.Include(x => x.ProjectPlanRevisions)
.ThenInclude(y => y.PlanActivityLineItem)
.ThenInclude(z => z.PlannedHours)
.Include(x => x.ActualPlanRevisions)
.ThenInclude(y => y.ActualPlanActivities)
.ThenInclude(z => z.ActualHours)
.AsQueryable();
if (!string.IsNullOrEmpty(projectName))
{
// here we can combine query
query = query
.Where(h => h.ProjectPlanName.ToLower().Contains(projectName.ToLower()));
}
// check that we have to apply filter on collection
if (roleId != null || disciplineId != null || resourceId != null)
{
// here we have to do filtering as in original query
query = query
.Where(h => h.ProjectPlanRevisions
.Where(r => roleId == null || r.PlanActivityLineItem.RequiredRoleId == roleId)
.Where(r => disciplineId == null || r.PlanActivityLineItem.DisciplineId == disciplineId)
.Where(r => resourceId == null || r.PlanActivityLineItem.ActualResourceId == resourceId)
.Any()
);
}
var result = query.ToList();
Let me clarify my comment with an example:
So: An IQueryable is used to build up an expression tree. They are not evaluated until enummerated (e.g. ToList or FirstOrDefault). I.e. you can conditional add Where and Includes with little to no cost before ennumerating`
Thus you could do this,
IQueryable<ProjectPlanHeader> bob =
context.ProjectPlanHeader
.Include(x => x.ProjectPlanRevisions)
.ThenInclude(y => y.PlanActivityLineItem);
if (roleId != null) {
bob =
from h in bob
join r in context.ProjectPlanRevisions on h.Id equals r.ProjectPlanHeaderId
join a in context.PlanActivityLineItems on r.Id equals a.ProjectPlanRevisionId
where a.RequiredRoleId == roleId
select h;
}
// same for the rest.
var john = bob.ToList();
writing the chained filer is not easiest, but it works
I have a pretty complicated linq statement that gets a list of people (using Entity Framework) and I want to add an OrderBy clause to the end, depending on which column the user has clicked on for sorting. I DON'T want to get all the people and then sort as there are potentially alot of people and we also do paging, so getting the data and then sorting/paging is not an option. It must therefore be done using LINQ to EF.
I have managed to get the search criteria that filters based on the status of the user's current vaccination status, but I am unable to "convert" that to an OrderBy statement
The data I am getting relates to COVID vaccinations and whether the person's vaccination status is Full, Partial, Not Disclosed or None.
The Entity Framework LINQ statement with the Where clause looks like this and It is an IQueryable<Person>, not a List<Person>:
people.Where(p => p.Encounters.Where(e =>
e.EncounterItems.Any(ei => ei.PersonAssessments.Any(pa =>
pa.Assessment.Questions.Any(q => q.questioncode.Equals("qIDateF", StringComparison.InvariantCultureIgnoreCase) || q.questioncode.Equals("qIDateP", StringComparison.InvariantCultureIgnoreCase)))))
.OrderByDescending(e => e.servicedt ?? e.planneddt).FirstOrDefault()
.EncounterItems.Where(ei =>
ei.PersonAssessments.Any(pa => pa.Answers.Any(a => a.adate.HasValue && DbFunctions.AddMonths(a.adate, procedureCycleDays) < DateTime.Today &&
(a.Question.questioncode.Equals("qIDateF", StringComparison.InvariantCultureIgnoreCase) || (a.Question.questioncode.Equals("qIDateP", StringComparison.InvariantCultureIgnoreCase)
&& (!pa.Answers.Any(aa => aa.adate.HasValue && aa.Question.questioncode.Equals("qIDateF", StringComparison.InvariantCultureIgnoreCase)))
))))).FirstOrDefault()
!= null)
From the above it will filter the people where their vaccination status is "Overdue". i.e they have done either Partial or Full Vaccination but the cycle for this vaccination has been exceeded. There are 2 questions with questioncode's "qIDateP" (partial) and "qIDateF" (full).
I know the below OrderBy is completly wrong, but I want to do something like this so that all the people with overdue vaccination status's are at the top. I will then add several other OrderBy clauses such as "Current" using the same clause, just chainging the date expression e.g. DbFunctions.AddMonths(a.adate, procedureCycleDays) >= DateTime.Today
people.OrderBy(p => p.Encounters.Where(e =>
e.EncounterItems.Any(ei => ei.PersonAssessments.Any(pa =>
pa.Assessment.Questions.Any(q => q.questioncode.Equals("qIDateF", StringComparison.InvariantCultureIgnoreCase) || q.questioncode.Equals("qIDateP", StringComparison.InvariantCultureIgnoreCase)))))
.OrderByDescending(e => e.servicedt ?? e.planneddt).FirstOrDefault()
.EncounterItems.Where(ei =>
ei.PersonAssessments.Any(pa => pa.Answers.Any(a => a.adate.HasValue && DbFunctions.AddMonths(a.adate, procedureCycleDays) < DateTime.Today &&
(a.Question.questioncode.Equals("qIDateF", StringComparison.InvariantCultureIgnoreCase) || (a.Question.questioncode.Equals("qIDateP", StringComparison.InvariantCultureIgnoreCase)
&& (!pa.Answers.Any(aa => aa.adate.HasValue && aa.Question.questioncode.Equals("qIDateF", StringComparison.InvariantCultureIgnoreCase)))
))))).FirstOrDefault()
!= null)
The Relationships for the EF Models is as follows:
Person => Encounter => EncounterItem => PersonAssessment => Answer
A person can answer multiple Assessments over their life and can change their mind as to whether they want to disclose their vaccination status or not.
NOTE: We are using the latest Entity Framework 6.4.4
I hope someone can help me with the OrderBy clause as Im at a complete loss as to how to achieve this.
------UPDATE 1-------
I have used this so far.
people.OrderBy(p => p.Encounters.Where(
e => e.EncounterItems.Any(
ei => ei.PersonAssessments.Any(
pa => pa.Assessment.Questions.Any(
q => q.questioncode.Equals("qIDateF", StringComparison.InvariantCultureIgnoreCase)
|| q.questioncode.Equals("qIDateP", StringComparison.InvariantCultureIgnoreCase))))).OrderByDescending(e => e.servicedt ?? e.planneddt).FirstOrDefault() // you have 1 Encounters item
.EncounterItems.DefaultIfEmpty().FirstOrDefault(
ei => ei.PersonAssessments.Any(
pa => pa.Answers.Any(
a => a.adate.HasValue
&& DbFunctions.AddMonths(a.adate, procedureCycleDays) < DateTime.Today
&& (a.Question.questioncode.Equals("qIDateF", StringComparison.InvariantCultureIgnoreCase)
|| (a.Question.questioncode.Equals("qIDateP", StringComparison.InvariantCultureIgnoreCase)
&& (!pa.Answers.Any(aa => aa.adate.HasValue && aa.Question.questioncode.Equals("qIDateF", StringComparison.InvariantCultureIgnoreCase)))))))).Encounter.planneddt)
The issue is that all the "Overdue" records are at the bottom, not at the top. If I use OrderByDescending it seems correct. How can I now put all those records at the top with OrderBy instead of OrderByDescending.
------ UPDATE 2 Final Solution ------
After a couple of changes based on Margus answer below I have the final updated OrderBy. I had to OrderBydescending for some reason in order to get the records that I wanted at the top.
people.OrderByDescending(p => p.Encounters.Where(
e => e.EncounterItems.Any(
ei => ei.PersonAssessments.Any(
pa => pa.Assessment.Questions.Any(
q => q.questioncode.Equals("qIDateF", StringComparison.InvariantCultureIgnoreCase)
|| q.questioncode.Equals("qIDateP", StringComparison.InvariantCultureIgnoreCase))))).OrderByDescending(e => e.servicedt ?? e.planneddt).FirstOrDefault() // you have 1 Encounters item.EncounterItems.DefaultIfEmpty().FirstOrDefault(
ei => ei.PersonAssessments.Any(
pa => pa.Answers.Any(
a => a.adate.HasValue
&& DbFunctions.AddMonths(a.adate, procedureCycleDays) < DateTime.Today
&& (a.Question.questioncode.Equals("qIDateF", StringComparison.InvariantCultureIgnoreCase)
|| (a.Question.questioncode.Equals("qIDateP", StringComparison.InvariantCultureIgnoreCase)
&& (!pa.Answers.Any(aa => aa.adate.HasValue && aa.Question.questioncode.Equals("qIDateF", StringComparison.InvariantCultureIgnoreCase)))))))).Encounter.planneddt)
Now Im concerned about the performance lol... But that will be another stackoverflow search :)
Well as far as I can tell you want to use orderBy and then simply fetch the first element, while you could simply fetch the first element with the same predicate dropping O(nlogn) complexity
var result = people.Where(
p => p.Encounters.Where(
e => e.EncounterItems.Any(
ei => ei.PersonAssessments.Any(
pa => pa.Assessment.Questions.Any(
q => q.questioncode.Equals("qIDateF", StringComparison.InvariantCultureIgnoreCase)
|| q.questioncode.Equals("qIDateP", StringComparison.InvariantCultureIgnoreCase)))))
.FirstOrDefault(e => e.servicedt ?? e.planneddt) // you have 1 Encounters item
.EncounterItems.FirstOrDefault(
ei => ei.PersonAssessments.Any(
pa => pa.Answers.Any(
a => a.adate.HasValue
&& DbFunctions.AddMonths(a.adate, procedureCycleDays) < DateTime.Today
&& (a.Question.questioncode.Equals("qIDateF", StringComparison.InvariantCultureIgnoreCase)
|| (a.Question.questioncode.Equals("qIDateP", StringComparison.InvariantCultureIgnoreCase)
&& (!pa.Answers.Any(aa => aa.adate.HasValue && aa.Question.questioncode.Equals("qIDateF", StringComparison.InvariantCultureIgnoreCase)))))))));
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();
I am trying to remove an item from a list. It finds the item with the above query, but it doesn't remove it from the list. I don't know why is it so?
var qry = db.AssemblyListItems
.AsNoTracking()
.Where(x => x.ProductionPlanID == (long)_currentPlan.ProductionPlan)
.ToList();
var hasbeenAssembled = db.Assembleds
.AsNoTracking()
.Where(x => x.ProductionPlanId == (long)_currentPlan.ProductionPlan)
.ToList();
foreach (var item in hasbeenAssembled)
{
qry = qry.RemoveAll(X => X.DocumentNo == item.DocumentId &&
X.ItemCode == item.KitHeaderId &&
X.ProductionPlanID == item.ProductionPlanId );
}
olvData.SetObjects(qry);
Above is a listView where i want the items to appear. The main query "qry" is on the top.
You can handle this all in one query by excluding the assembled items from the list in a subquery:
var productionPlan = (long)_currentPlan.ProductionPlan;
var qry = db.AssemblyListItems
.AsNoTracking()
.Where(item => item.ProductionPlanID == productionPlan
&& !db.Assembleds
.Any(x => x.ProductionPlanId == item.ProductionPlanID
&& x.DocumentNo == item.DocumentId
&& x.ItemCode == item.KitHeaderId))
The advantage is (as also said by others) that you don't pull AssemblyListItems into memory that you're going to discard again later. Entity Framework will be able to translate this into one SQL statement, so everything is handled efficiently by he database.
Don't include the unwanted items in the results of the query. Don't prematurely bring over query results from the database when it might be able to process the query for you.
var hasBeenAssembled = db.Assembleds
.AsNoTracking()
.Where(x => x.ProductionPlanId == (long)_currentPlan.ProductionPlan);
var qry = db.AssemblyListItems
.AsNoTracking()
.Where(x => x.ProductionPlanID == (long)_currentPlan.ProductionPlan)
.Where(ali => !hasBeenAssembled.Any(hba => hba.DocumentId == ali.DocumentNo && hba.KitHeaderId == ali.ItemCode && hba.ProductionPlanId == ali.ProductionPlanID))
.ToList();
olvData.SetObjects(qry);
Easier way to do this. Items in the first list does not exist in the second list.
from item in hasbeenAssembled
where !(qry.Any(X => X.DocumentNo == item.DocumentId &&
X.ItemCode == item.KitHeaderId &&
X.ProductionPlanID == item.ProductionPlanId))
select item;
Im having a problem with Linq using Lambda Expressions. Im trying to do this select
SELECT L.IDLLAMADO FROM LLAMADOS L WHERE EXISTS (SELECT D.IDLLAMADO FROM DIAGNOSTICO D WHERE D.DESCRIPCION LIKE '%SOME VALUE%')
Some notes:
Im using IQueryable because Im appending joins and where depending on
the parameters of the method.
In the example I mapped only a property in my complex class because the rest are not useful in the example.
diagonostico is a string parameter.
My code is:
DbSet<Llamados> llamados = context.Set<Llamados>();
IQueryable<ComplexLlamadosAfil> query = llamados
.Select(e => new ComplexLlamadosAfil { IdLlamado = e.IdLlamado });
//If some conditions
query = query.Where(e => diagnosticos.Any(d => d.IdLlamado == e.IdLlamado &&
d.Descripcion.Contains(diagnostico)) == true);
There is not compilation error but when i run this i keep getting this error:
The object type
'System.Data.Objects.ObjectQuery`1[GAMP.MO.VW_DIAGNOSTICOS_LLAMADO]'
cannot be converted into
'System.Data.Entity.DbSet`1[GAMP.MO.VW_DIAGNOSTICOS_LLAMADO]'.
I also tried using .TakeWhile() instead of .Where() but no success.
Thanks for reading guys,
Pablo.
well, the message is rather clear, no ?
I would do
//Don't tell that it's a DbSet<Llamados>. You want an IQueryable<Llamados>
IQueryable<Llamados> llamados = context.Set<Llamados>().AsQueryable();
IQueryable<ComplexLlamadosAfil> query = llamados
.Select(e => new ComplexLlamadosAfil { IdLlamado = e.IdLlamado });
//If some conditions
query = query.Where(e => diagnosticos.Any(d => d.IdLlamado == e.IdLlamado &&
d.Descripcion.Contains(diagnostico)) == true);
or just use var
var llamados = context.Set<Llamados>().AsQueryable();
var query = llamados
.Select(e => new ComplexLlamadosAfil { IdLlamado = e.IdLlamado });
//If some conditions
query = query.Where(e => diagnosticos.Any(d => d.IdLlamado == e.IdLlamado &&
d.Descripcion.Contains(diagnostico)) == true);