Get null for empty collection in LINQ Group - linq

I am working on LINQ query and part of objective to do SQL database call once to achieve result. I have number of questions which may have collection of answers.
I need to choose all the questions and collection of answer and if there no answer for specific question, I still need it.
for code gives me only question which have answer but not ones without answer
var t3 = (Context.Answers
.Include(answer => answer.AnswerStatusType)
.Where(answer => Context.Questions.Where(q => q.profileId == ProfileId)
.Any(t => t.Id == answer.QuestionId)))
.GroupBy(
x => x.QuestionId,
x => x,
(key, g) => new
{
Question = key,
Answers = g.ToList(),
}
).ToList();

You probably need something like this:
var t3 =
(
from q in Context.Questions
where q.profileId == ProfileId
join a in Context.Answers on q.Id equals a.QuestionId into gas
select new
{
Question = q,
Answers = gas.ToList(),
}
).ToList();

I had the exact same problem and solved it with this giant query, which could probably be optimized, but maybe it will help someone with a similar problem.
// Perform left join to get all the questions (even those that don't have answers)
var results = (from quiz in db.Quizzes
join question in db.Questions on quiz.QuestionID equals question.QuestionID
from answer in db.QuestionAnswers.Where(a => question.QuestionID == a.QuestionID).DefaultIfEmpty()
where quiz.QuizID == quizId
group (answer != null ? new AnswerVM()
{
AnswerId = answer.AnswerID,
AnswerText = answer.AnswerText,
Correct = question.CorrectAnswerID != null ? answer.AnswerID == question.CorrectAnswerID : false,
} : null) // For the questions with no answers
by new
{
question.QuestionID,
question.DisplayOrder,
question.QuestionText,
question.CorrectAnswerID
} into g
select new QuestionVM
{
QuestionId = g.Key.QuestionID,
QuestionText = g.Key.QuestionText,
CorrectAnswerId = g.Key.CorrectAnswerID,
Answers = g.ToList()
})
.AsEnumerable()
.Select(x => {
// If there are no answers then group by will return the list with one value (null),
// then we need to change the answers list to be null (not the list with one null value)
if (x.Answers.Count(a => a != null) <= 0)
{
x.Answers = null;
}
return x;
}).ToList();

Related

LINQ Aggregate results with Many to Many Relationship

I am currently working with this schema
This is how my LINQ currently looks
var regionResults = (
from p in _context.Projects
from pr in p.Regions
where (data.RegionId == null || pr.RegionId == data.RegionId)
group p by pr.RegionId into g
join q in _context.Regions on g.Key equals _context.Regions.First().Id
select new Models.ViewModels.ProjectBreakdownViewModel.Regions
{
RegionName = q.Name,
TotalCount = g.Count(),
RejectedCount = g.Count(e => e.SubmissionStatusId == 2),
DeniedCount = g.Count(e => e.SubmissionStatusId == 3)
});
this is what it is currently producing, albeit incorrect
This is what I need it to be...
I know the problem is with this line, essentially
join q in _context.Regions on g.Key equals _context.Regions.First().Id
I don't know how to do this without the use of .First(), there doesn't seem to be a way to do it. I'm close I just don't know how to finish this.
If you have an collection of ProjectRegions in you Region entity, you can do this:
var result= context.Regions
.Where(r=> data.RegionId == null || r.Id == data.RegionId)
.Select(r=> new
{
RegionName = r.Name,
TotalCount = r.ProjectRegions.Count(),
RejectedCount = r.ProjectRegions.Count(e => e.Project.SubmissionStatusId == 2),
DeniedCount = r.ProjectRegions.Count(e => e.Project.SubmissionStatusId == 3)
});
ProjectRegion entity should have two nav properties, Project and Region, use them to navigate and create the corresponding conditions

How to compare IEnumerable<string> for null in Linq query

For the following query:
var result = from sch in schemeDashboard
join exp in Expenditure on sch.schemeId equals exp.SchemeCode
into SchExpGroup
where sch.SectorDepartmentId == selectedDepartmentId &&
sch.YearCode == StateManager.CurrentYear
orderby sch.ADPId
select new
{
ModifiedAmounts = SchExpGroup.Select(a => a.ModifiedAmounts),
ProjectName = sch.schemeName,
ADPNo = sch.ADPId,
Allocation = sch.CurrentAllocation,
Expenditures = from expend in SchExpGroup
where expend.YearCode == StateManager.CurrentYear &&
expend.DepartmentId == selectedDepartmentId &&
InvStatus.Contains(expend.Status)
orderby expend.ADPId
group expend by expend.InvoiceId
};
I want to filter the above query on a condition so that result gives only those records where "ModifiedAmounts" are not null. I have tried as follow:
if (rbList2.SelectedIndex == 6)
{
result = result.Where(a => a.ModifiedAmounts != null));
}
but this gives error as:
Cannot compare elements of type
'System.Collections.Generic.IEnumerable`1'. Only primitive types,
enumeration types and entity types are supported.
Any suggestions as I am lost as how to rephrase the filtered query.
I think the problem is that ModifiedAmounts will never be null. Select will return an empty list. Unless SchExpGroup is null in which case you will get a null reference exception.
Try changing your code to
result = result.Where(a => a.ModifiedAmounts.Any());
if (rbList2.SelectedIndex == 6)
{
result = result.Where(a => a.!ModifiedAmounts.Any());
}

Joining to a GROUP'ed query

I'm a LINQ Newb and I've got this query, which returns the pages in a survey. (These values are not materialized into a table, for whatever reason.)
//Group all of this data by page
var pages = from fq in db.FormQuestions
where (fq.FormId == id) && fq.Disabled == false
group fq by fq.PageNumber into p
select new DTOs.PageDTO { PageNumber = p.Key.Value };
And then I have this query, which projects all of the leaf-data into DTOs.
var questions = from fq in db.FormQuestions
join q in db.Questions on fq.QuestionId equals q.QuestionId
where (fq.FormId == id) && fq.Disabled == false
//where (fq.FormId == id) && fq.Disabled == false && fq.PageNumber == page
orderby fq.DisplayOrder
select new DTOs.FormQuestionDTO()
{
DisplayOrder = (fq.DisplayOrder.HasValue ? fq.DisplayOrder.Value : 0),
PageNumber = (fq.PageNumber.HasValue ? fq.PageNumber.Value : 0),
QuestionId = q.QuestionId,
QuestionSelectionMode = q.vts_tbQuestionSelectionMode.Description,
QuestionText = q.QuestionText,
Answers =
from answer in q.Answers
join at in db.AnswerTypes on answer.AnswerTypeId equals at.AnswerTypeID
where answer.Disabled == false
orderby answer.DisplayOrder
select new DTOs.AnswerDTO()
{
AnswerId = answer.AnswerId,
AnswerText = answer.AnswerText,
DisplayOrder = answer.DisplayOrder,
AnswerType = at.Description
}
};
Is there a way to join these two neatly? I.e., under each page DTO I want to see the QUestion DTOs, then inside of that Answer DTOs, and onward...
Also, even if I could do this all in one LINQ statement, is it preferable to build the LINQ statements separately and then merge them? This feels a bit like building temp variables in SQL in that it may be possible to build one giant query but it's a pain to maintain.
So why wouldn't something like this work?
//Group all of this data by page
var pages = from fq in db.FormQuestions
where (fq.FormId == id) && fq.Disabled == false
group fq by fq.PageNumber into p
select new DTOs.PageDTO {
PageNumber = p.Key.Value
questions = from fq in db.FormQuestions
join q in db.Questions on fq.QuestionId equals q.QuestionId
where (fq.FormId == id) && fq.Disabled == false
orderby fq.DisplayOrder
select new DTOs.FormQuestionDTO()
{
DisplayOrder = (fq.DisplayOrder.HasValue ? fq.DisplayOrder.Value : 0),
// etc as you 2nd code sample

Entity Framework/ Linq - groupby and having clause

Given the query below
public TrainingListViewModel(List<int> employeeIdList)
{
this.EmployeeOtherLeaveItemList =
CacheObjects.AllEmployeeOtherLeaves
.Where(x => x.OtherLeaveDate >= Utility.GetToday() &&
x.CancelDate.HasValue == false &&
x.OtherLeaveId == Constants.TrainingId)
.OrderBy(x => x.OtherLeaveDate)
.Select(x => new EmployeeOtherLeaveItem
{
EmployeeOtherLeave = x,
SelectedFlag = false
}).ToList();
}
I want to put in the employeeIdList into the query.
I want to retrieve all of the x.OtherLeaveDate values where the same x.OtherLeaveDate exists for each join where x.EmployeeId = (int employeeId in employeeIdList)
For example if there are EmployeeIds 1, 2, 3 in employeeIdList and in the CacheObjects.AllEmployeeOtherLeaves collection there is a date 1/1/2001 for all 3 employees, then retreive that date.
If I read you well it should be something like
var grp = this.EmployeeOtherLeaveItemList =
CacheObjects.AllEmployeeOtherLeaves
.Where(x => x.OtherLeaveDate >= Utility.GetToday()
&& x.CancelDate.HasValue == false
&& x.OtherLeaveId == Constants.TrainingId
&& employeeIdList.Contains(x.EmployeeId)) // courtesy #IronMan84
.GroupBy(x => x.OtherLeaveDate);
if (grp.Count() == 1)
{
var result = g.First().Select(x => new EmployeeOtherLeaveItem
{
EmployeeOtherLeave = x,
SelectedFlag = false
})
}
First the data is grouped by OtherLeaveDate. If the grouping results in exactly one group, the first (and only) IGrouping instance is taken (which is a list of Leave objects) and its content is projected to EmployeeOtherLeaveItems.
To the where statement add "&& employeeIdList.Contains(x.EmployeeId)"
I need to thank #IronMan84 and #GertArnold for helping me along, and I will have to admonish myself for not being clearer in the question. This is the answer I came up with. No doubt it can be improved but given no one has responded to say why I will now tick this answer.
var numberOfEmployees = employeeIdList.Count;
var grp = CacheObjects.AllEmployeeOtherLeaves.Where(
x =>
x.OtherLeaveDate >= Utility.GetToday()
&& x.CancelDate.HasValue == false
&& x.OtherLeaveId == Constants.TrainingId
&& employeeIdList.Contains(x.EmployeeId))
.GroupBy(x => x.OtherLeaveDate)
.Select(x => new { NumberOf = x.Count(), Item = x });
var list =
grp.Where(item => item.NumberOf == numberOfEmployees).Select(item => item.Item.Key).ToList();

How can I define a List to add results of a query in a loop?

I have an array filled with long type values and for each value in the array I need to implement a query. I used foreach loop as you can see from the code below:
var result;
foreach(long id in PrdIdArr)
{
var mainQuery = (from o in db.OPERATIONs
join u in db.UNITs on o.OP_UNIT_ID equals u.UNIT_ID into smt
from s in smt
join x in db.XIDs on s.UNIT_ID equals x.UNIT_ID
where o.OP_OT_CODE == OtCode
where x.IDTYP_CD == "BSN"
where s.START_PRD_ID == id
where o.OP_UPD_DATE >= _StartDate
where o.OP_UPD_DATE <= _EndDate
select new
{
o.OP_ID,
o.OP_UPD_DATE,
x.EXTERNAL_ID,
o.OP_OS_CODE,
o.OP_START,
o.OP_ST_STATION,
s.START_PRD_ID
}).Take(_RowNumber);
//var result = mainQuery.ToList();
result.add(mainQuery.ToList());
}
data = this.Json(result);
data.JsonRequestBehavior = JsonRequestBehavior.AllowGet;
return data;
However, I have a problem in my code; I have to define a main list just before the foreach loop so that I could add results of each query to the that main list. my question is: How can I define this list as you can see at the beginning of my code? Thanks for the help...
How can I define this list as you can see at the beginning of my code?
Make
new {
o.OP_ID,
o.OP_UPD_DATE,
x.EXTERNAL_ID,
o.OP_OS_CODE,
o.OP_START,
o.OP_ST_STATION,
s.START_PRD_ID
}
into a concrete type (say QueryResult, although something a little more specific than that), and then just declare
var result = new List<QueryResult>();
Also, you should consider turning
foreach(long id in PrdIdArr)
and
where s.START_PRD_ID == id
into
where PrdIdArr.Contains(s.Start_PRD_ID)
var result = new List<object>();
foreach(long id in PrdIdArr)
{
....
result.Add(mainQuery.ToList());
}
You could do this:
var result = PrdIdArr.Select(id =>
from o in db.OPERATIONs
join u in db.UNITs on o.OP_UNIT_ID equals u.UNIT_ID into smt
from s in smt
join x in db.XIDs on s.UNIT_ID equals x.UNIT_ID
where o.OP_OT_CODE == OtCode
where x.IDTYP_CD == "BSN"
where s.START_PRD_ID == id
where o.OP_UPD_DATE >= _StartDate
where o.OP_UPD_DATE <= _EndDate
select new
{
o.OP_ID,
o.OP_UPD_DATE,
x.EXTERNAL_ID,
o.OP_OS_CODE,
o.OP_START,
o.OP_ST_STATION,
s.START_PRD_ID
}
.Take(_RowNumber)
.ToList()
).ToList();
I highly recommend performing some Extract Method refactorings, as the code is pretty complex and hard to understand/mange this way.
Just create the anonymous type outside with the same property names and the correct type
var result = Enumerable.Range(0, 0).Select(x => new
{
OP_ID = 1,
OP_UPD_DATE = DateTime.Now,
EXTERNAL_ID = 1,
OP_OS_CODE = 1,
OP_START = DateTIme.Now,
OP_ST_STATION = "",
START_PRD_ID = 1,
}).ToList();
And in your loop call AddRange
result.AddRange(mainQuery.ToList());

Resources