Issues when group join 3 table - c#

Please help me resolve this issue. I still can not understand how it throw exception "system.linq.systemcore_enumerabledebugview evaluation time out" when debuging
var mealDataDetail = ( _dbContext.Meal.Where(m => m.IsDeleted == 0 && m.UserId == userId).ToList()
.GroupJoin(_dbContext.MealImage.Where(i => i.IsDeleted == 0 && i.MealType == (int)mealType).ToList(), p => p.Id, r => r.MealId, (p, rs) => new { p, rs })
.GroupJoin(_dbContext.MealMenu.Where(i => i.IsDeleted == 0 && i.MealType == (int)mealType).ToList(), prs => prs.p.Id, c => c.MealId, (prs, cs) => new MealDataDetail
{
MealId = prs.p.Id,
MealData = new MealData
{
MealImages = prs.rs.Select(im => im.Image),
Description = prs.p.BreakfastDescription,
Time = prs.p.BreakfastTime,
TimeRequired = prs.p.BreakfastTimeRequired,
Appitite = prs.p.BreakfastAppetite,
Status = prs.p.BreakfastStatus == 1,
Calorie = prs.p.BreakfastCalorie,
},
}));

Was it supposed to be a single query?
Keep in mind that your .ToList() is executing the query at that moment.
Try removing the ToList() but I suggest you rewrite it once the database is getting a really monster query. Is better to have a procedure instead

Related

The LINQ expression could not be translated although ToLists is used

I have the following code, where I first create a list object assessmentItems, and then use it inside a LINQ to get the CurrentScore.
List<AssessmentItem> assessmentItems =
_context.AssessmentItems
.Include(ai => ai.Assessment).ThenInclude(assess => assess.Evaluator)
.Where(ai => ai.IsActive &&
ai.Assessment.PeerReviewAssignmentId == peerReviewAssignmentId &&
ai.Assessment.RubricId == rubricId).ToList();
List<RubricDTO> resultToReturn = _context.Rubrics
.Include(r => r.RubricItemCategories)
.Where(r => r.Id == rubricId)
.Select(r => new RubricDTO
{
Ranking = r.Ranking,
Description = r.Description,
RubricItemCategories = r.RubricItemCategories.Select(ric => new RubricItemCategoryDTO
{
Id = ric.Id,
Description = ric.Description,
RubricItems = ric.RubricItems.Select(ri => new RubricItemDTO
{
Id = ri.Id,
Title = ri.Title,
CurrentScore = assessmentItems.Count > 0 ? assessmentItems
.Where(aitem2 => aitem2.RubricItemId == ri.Id && aitem2.Assessment.EvaluatorId == userId)
//.Take(1)
.Select(s => s.CurrentScore)
.FirstOrDefault() : 0,
})
}).OrderBy(ric => ric.Order).ToList()
}).ToList();
However, I receive the following error:
System.InvalidOperationException: The LINQ expression 'aitem2' 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'. See https://go.microsoft.com/fwlink/?linkid=2101038 for
more information.
Any ideas why this might be happenning?
Let me hep you to simplify your query. Includes are not needed because you have custom projection. If you add ToList for subquery elements - it will be not translatable. If you need translatable query - work with IQueryable.
var assessmentItems = _context.AssessmentItems
.Where(ai => ai.IsActive &&
ai.Assessment.PeerReviewAssignmentId == peerReviewAssignmentId &&
ai.Assessment.RubricId == rubricId);
var resultToReturn = _context.Rubrics
.Where(r => r.Id == rubricId)
.Select(r => new RubricDTO
{
Ranking = r.Ranking,
Description = r.Description,
RubricItemCategories = r.RubricItemCategories.Select(ric => new RubricItemCategoryDTO
{
Id = ric.Id,
Description = ric.Description,
RubricItems = ric.RubricItems.Select(ri => new RubricItemDTO
{
Id = ri.Id,
Title = ri.Title,
CurrentScore = assessmentItems
.Where(aitem2 => aitem2.RubricItemId == ri.Id && aitem2.Assessment.EvaluatorId == userId)
.Select(s => s.CurrentScore)
.FirstOrDefault(),
})
}).OrderBy(ric => ric.Order).ToList()
}).ToList();

DefaultIfEmpty() does not handle empty collections

I've been trying to left join the table and they are in a one-to-many relationship.
I have written a SQL query and trying to convert it into LINQ for my ASP.NET Core application.
My sql query is as follows:
SELECT ap.SystemId,
ap.AccessRequiredToId,
cb.AccessAreaManagementId,
ap.EquipmentTagId,
COUNT(ap.Name) [Count]
FROM ApplicationForms ap LEFT JOIN AccessAreaCheckBoxes cb
ON n ap.RecordId = cb.RecordId
WHERE EndDate IS NULL AND (Checked IS NULL OR Checked = 1)
GROUP BY ap.SystemId, ap.AccessRequiredToId, cb.AccessAreaManagementId, ap.EquipmentTagId
SQL Result
And my LINQ is as follows:
var active = _context.ApplicationForms
.Where(w => w.EndDate == null)
.GroupJoin(_context.AccessAreaCheckBoxes
.Where(w => (w.AccessAreaManagement == null || w.Checked == true)),
x => x.RecordId,
y => y.RecordId,
(x, y) => new { ApplicationForms = x, AccessAreaCheckBoxes = y })
.SelectMany(x => x.AccessAreaCheckBoxes.DefaultIfEmpty(),
(x, y) => new { x.ApplicationForms, AccessAreaCheckBoxes = y })
.GroupBy(g => new { g.ApplicationForms.System, g.ApplicationForms.AccessRequiredTo, g.AccessAreaCheckBoxes.AccessAreaManagement, g.ApplicationForms.EquipmentTag })
.Select(s => new RecordViewModel
{
System = s.Key.System.Name,
AccessRequiredTo = s.Key.AccessRequiredTo.Name,
AccessArea = s.Key.AccessAreaManagement.Name,
EquipmentTag = s.Key.EquipmentTag.Name,
Count = s.Count()
}).ToList();
Everything is working well except it doesn't show the rows with the NULL value.
Did I miss out something in my LINQ?
Any help would be greatly appreciated!
This is what I do in the end, post here for your reference.
var active = (from ap in _context.ApplicationForms
join cb in _context.AccessAreaCheckBoxes
on ap.RecordId equals cb.RecordId into j1
from j2 in j1.DefaultIfEmpty()
where ap.EndDate == null
&& (j2.AccessAreaManagement == null || j2.Checked == true)
group new { ap.System, ap.AccessRequiredTo, j2.AccessAreaManagement, ap.EquipmentTag }
by new { System = ap.System.Name, Building = ap.AccessRequiredTo.Name, AccessArea = j2.AccessAreaManagement.Name, Equipment = ap.EquipmentTag.Name } into grp
select new RecordViewModel
{
System = grp.Key.System,
AccessRequiredTo = grp.Key.Building,
AccessArea = grp.Key.AccessArea,
EquipmentTag = grp.Key.Equipment,
Count = grp.Count()
}).ToList();

Convert c# foreach to Linq expression

I have following code snippet in c# which is working fine
foreach (var customer in customers.OrderBy(x => x.CustomerId))
{
if (customer.CustomerId != customerId)
{
winningRate = Helper.Utility.WinningRate(settledCustomers, customer.CustomerId).Status;
numberOfBets = customers.Count(x => x.CustomerId == customer.CustomerId);
numberOfWinnings = customers.Count(x => x.CustomerId == customer.CustomerId && x.Win > 0);
averageBet = Helper.Utility.CustomerAverageBet(settledCustomers, customer.CustomerId);
}
var risky = Helper.Utility.CheckRiskyBet(winningRate, numberOfWinnings, numberOfBets, averageBet, customer.Stake, customer.Win);
customer.Status = risky.Status;
customer.Message = risky.Message;
customerId = customer.CustomerId;
}
return customers;
Now I want to convert above code into LINQ expression and for the same I have written following code which is not giving the same output as
var r = from c in customers
let winningRate1 = Helper.Utility.WinningRate(settledCustomers, c.CustomerId).Status
let numberOfBets1 = customers.Count(x => x.CustomerId == c.CustomerId)
let numberOfWinnings1 = customers.Count(x => x.CustomerId == c.CustomerId && x.Win > 0)
let averageBet1 = Helper.Utility.CustomerAverageBet(settledCustomers, c.CustomerId)
let risky1 = Helper.Utility.CheckRiskyBet(winningRate, numberOfWinnings, numberOfBets, averageBet, c.Stake, c.Win)
where c.CustomerId != customerId
orderby c.CustomerId
select new Customer
{
Status = risky1.Status,
Message = risky1.Message,
CustomerId = c.CustomerId
};
where c.CustomerId != customerId
This clearly is wrong because it reduces the number of times the loop runs.
Many let clauses and control flow in the loop can get ugly when converting to query expressions. A multi-statement lambda often is easier and gets you the same functional code benefits:
customers
.OrderBy(x => x.CustomerId)
.Select(c => {
if (customer.CustomerId != customerId)
{
winningRate = Helper.Utility.WinningRate(settledCustomers, customer.CustomerId).Status;
numberOfBets = customers.Count(x => x.CustomerId == customer.CustomerId);
numberOfWinnings = customers.Count(x => x.CustomerId == customer.CustomerId && x.Win > 0);
averageBet = Helper.Utility.CustomerAverageBet(settledCustomers, customer.CustomerId);
}
var risky = Helper.Utility.CheckRiskyBet(winningRate, numberOfWinnings, numberOfBets, averageBet, customer.Stake, customer.Win);
return new Customer
{
Status = risky.Status,
Message = risky.Message,
CustomerId = c.CustomerId
};
});
This code is just a sketch, it has some issues that you can easily fix. For examples I'm assigning to non-local variables. Your code did not explain what those are so I left them alone.

Translating my SQL Query to c# linq/lambda. Multiple parameter GroupBy

I've been puzzling over this problem all morning and can't figure out how to do it in C#.
My SQL query as follows:
select a.CourseID,
a.UserID
from audit a
inner join results r on a.UserID = r.UserID
inner join Course c on a.CourseID = c.CourseID
where c.CourseType = 9 and a.Guid = 'A123F123D123AS123123'
and a.Result = 'Passed' and r.Class = 'Maths'
group by a.CourseID, a.UserID
order by a.UserID
returns exactly what I want, but I can't seem to translate it into linq format. (the format being used here is what is required in my job at the moment so please advise on this format)
So far I have the following:
var audits = auditRepository.Get(a => a.Course.CourseType == 9 && a.GUID == this.Company.GUID && a.Result == "Passed", null, null,
a => a.Course, a => a.User)
.Join(resultsRepository.Get(r => r.GUID == this.Company.GUID && r.Class == class),
a => a.UserID,
r => r.UserID,
(a, r) => new Audit
{
User = a.User,
Course = a.Course,
Result = a.Result,
Timestamp = a.Timestamp,
AuditID = a.AuditID,
UserID = a.UserID
}
)
.OrderByDescending(o => o.Timestamp)
.GroupBy(u => new { u.User, u.Course })
.Select(grp => grp.ToList())
.ToList();
However this returns duplicates.
Any advice is appreciated, thanks
H
Instead of
.Select(grp => grp.ToList())
Select only the first element from each group to exclude duplicates:
.Select(grp => grp.First())
If you need a count also:
.Select(t => new{grp = t.First(),cnt = t.Count()} )
Fix:
.Select(t => new { grp = t.First(), cnt = t.Select(s => s.AuditID).Distinct().Count() })

Group by and MIN() in LINQ

Trying to convert the below SQL query to LINQ, but I'm stuck at grouping by ClientCompany.
SELECT TOP 300 ClientCompany,
CASE WHEN MIN(FeatureID) = 12 THEN 1 ELSE 0 END as Sort
FROM Ad
LEFT JOIN AdFeature
ON Ad.ID = AdFeature.AdID
WHERE (AdFeature.FeatureID = 13 OR AdFeature.FeatureID = 12)
AND SiteID = 2
GROUP BY ClientCompany
ORDER BY Sort DESC
My attempt to convert this to LINQ:
(from a in Ads
join af in AdFeatures
on new {
join1 = a.ID,
join3 = 2
} equals new {
join1 = af.AdID,
join3 = af.SiteID
}
let sort = (
af.FeatureID == 12 ? 1 : 0
)
orderby sort descending
where af.FeatureID == 13 || af.FeatureID == 12
select new { a.ClientCompany, sort } ).Take(300)
How would I use MIN(FeatureID) and GROUP BY ClientCompany in LINQ, so that I only get a single row per ClientCompany back?
EDIT
This worked! Based on Daniel Hilgarth's answer. Is there anything that can go horribly wrong with this solution?
Ads.Join(AdFeatures, x => x.ID, x => x.AdID,
(a, af) => new { Ad = a, AdFeature = af })
.Where(x => x.AdFeature.FeatureID == 12 || x.AdFeature.FeatureID == 13)
.Where(x => x.AdFeature.SiteID == 2)
.GroupBy(x => x.Ad.ClientCompany)
.Select(g => new { ClientCompany = g.Key, Sort = g.Min(x => x.AdFeature.FeatureID) == 12 ? 1 : 0 })
.OrderByDescending(x => x.Sort)
.Take(300)
Try this:
Ads.Join(AdFeatures, x => x.FeatureID, x => x.FeatureID,
(a, af) => new { Ad = a, AdFeature = af })
.Where(x => x.AdFeature.FeatureID == 12 || x.AdFeature.FeatureID == 13)
.Where(x => x.AdFeature.SiteID == 2)
.GroupBy(x => x.Ad.ClientCompany)
.Select(g => new { ClientCompany = g.Key,
Sort = g.Min(x => x.AdFeature.FeatureID) == 12 ? 1 : 0 });
Please note, I changed the left outer join into an inner join, because your original query accesses AdFeature unconditionally, making it effectively an inner join .
hi I would write it like that
context.Ads.Where(ad => ad.AdFeatures.Any(feature => (feature.FeatureID == 13 || feature.FeatureID == 12) && feature.SiteID == 2))
.GroupBy(ad => ad.ClientCompany)
.Select(ads => new
{
cc = ads.Key, sort = ads.SelectMany(ad => ad.AdFeatures)
.Select(feature => feature.FeatureID)
.Min() == 12
})
.OrderBy(arg => arg.sort).Take(300);
Try this:
(from a in ads
join af in AdFeatures on a.ID equals af.AdID into g
from x in g.DefaultIfEmpty()
where x.FeatureID == 13 || x.FeatureID == 12
where x.SiteID == 2
orderby a.Sort descending
group a by a.ClientCompany into g2
from x2 in g2
let sort = g2.Select(T => T.FeatureID).Min() == 12 ? 1 : 0
select new { a.ClientCompany, Sort = sort }).Take(300);
Why do you need grouping anyway?

Categories

Resources