Sum of child of child in LINQ - c#

I need help in LINQ query in EF6.
the master table is called EXAM. its child is ExamResult.
each ExamResult has a Question and the selected Answer.
each Answer has power (0 if wrong, 1 if correct).
if I want to know the total of correct answers, I simply run the command:
var examTotal = db.Exams.FirstOrDefault(ex => ex.ExamID == examId).ExamResults.Sum(er => er.Answer.Power);
my problem is when some questions were not answered, and I get NullReferenceException.

There are a couple problems with your "command".
First off, there are at least two queries:
(1) var exam = db.Exams.FirstOrDefault(ex => ex.ExamID == examId);
(2) var examTotal = exam.ExamResults.Sum(er => er.Answer.Power);
Note that the second really executes inside the LINQ to Objects context (eventually including some hidden db calls due to lazy loading).
In that context, there are 2 possible places that can raise NRE
(A) exam.ExamResults if exam is null
(B) er.Answer.Powerif er.Answer is null
You can fix them by including null checks as proposed in other answers.
But better way would be make your command execute a single query inside the LINQ to Entities context where navigation properties have different meaning:
var examTotal = db.Exams.Where(ex => ex.ExamID == examId)
.SelectMany(ex => ex.ExamResults)
.Sum(er => (int?)er.Answer.Power) ?? 0;
The only trick needed is to project the field you want to sum to a nullable type (I've used int? assuming the Power field is of int type, change it to your type if different). This is letting the EF to always return the Sum regardless of whether er.Answer is null or ex.ExamResults is empty.

Some general nullchecks should do the trick
var exam = db.Exams.FirstOrDefault(ex => ex.ExamID == examId);
var examTotal = exam.ExamResults.Sum(er => er.Answer?.Power ?? 0);
...and just in case you're not using C# 6, here's Another version of it:
var exam = db.Exams.FirstOrDefault(ex => ex.ExamID == examId);
var examTotal = exam.ExamResults.Sum(er => er.Answer != null ? er.Answer.Power : 0);

Try this:
var examTotal = db.Exams.FirstOrDefault(ex => ex.ExamID == examId).ExamResults.Where(er => er.Answer != null).Sum(er => er.Answer.Power);

This should work:
var examTotal = db.Exams.FirstOrDefault(ex => ex.ExamID == examId).ExamResults.Count(er => er.Answer.Power == 1);
This will not use the value but instead see if it is equal to 1, thus not generate any NullReferenceException.

Related

Using LINQ to populate a string with a single column value

I'm a newbie both to C# and to LINQ and would appreciate a small push in the right direction.
Firstly, I have an Overrides SQL table (and a corresponding EF DB context), which has a Type, Value, and Override Value. The idea is that for a particular kind ("Type") of override, the code can check a particular value and go see if there is an override value that should be used instead.
var branchOverrides = overridesSqlContext.Overrides
.Where(q => q.Type == "Branch Override")
.Select(s => new
{
s.Value,
s.OverrideValue
});
In this case, I want the list of different override values of the "Branch Override" type. From there, I would like to be able to retrieve a specific override value at a given point within my code. How can I query the branchOverrides variable I've created to be able to say something like:
string readingOverride = select OverrideValue from branchOverrides where Value = "Reading"
My code will need to be able to read various override values for different branches at different points, and being able to query the branchOverrides variable at any point would seem like the ideal approach.
Thank you for any assistance on this.
You can use Single() on the query object you have:
string readingOverride = branchOverrides
.Single(bo => bo.Value == "Reading")
.OverrideValue;
This will throw an exception if an entry doesn't exist though so you probably want to use SingleOrDefault instead and check for a null return.
Also note that the branchOverrides object here is an IQueryable<> which means that every time you use it, it will send a query to the database. You may want to materialise that to a local list by adding .ToList() after the Select(...). Alternatively, you may want to look at caching this data, especially if it's going to be used frequently.
If I understood you right, you want the entry with Value = "Reading" and Type="Branch Override":
var branchOverride = overridesSqlContext.Overrides
.SingleOrdDefault(q => q.Type == "Branch Override"
&& q.Value == "Reading")
.Select(s => new
{
s.Value,
s.OverrideValue
});
if (branchOverride != null)
{
// do whatever ...
}
For performance issue is good to put .ToList() in the end of your LINQ expression if you need to iterante over that list too many times.
var branchOverrides = overridesSqlContext.Overrides
.Where(q => q.Type == "Branch Override")
.Select(s => new
{
s.Value,
s.OverrideValue
}).ToList();
If it you will load the entire list into the memory avoiding to execute the sql query to fetch the data if you need to iterate through your list.
Other thing that you can do is:
string readingOverride = string.Empty;
var branchOverride = branchOverrides.FirstOrDefault(x => x.Value == "Reading");
if(branchOverride != null)
{
readingOverride = branchOverride.OverrideValue;
}
Hope that helps.
If Value is unique within "Branch Override" perhaps you want to turn it to a dictionary for fast lookup
var branchOverrides = overridesSqlContext.Overrides
.Where(q => q.Type == "Branch Override")
.Select(s => new
{
s.Value,
s.OverrideValue
})
.ToDictionary(k => k.Value, v => v.OverrideValue);
Then later on you can find the override value quickly and efficiently
var readingOverride = branchOverrides["Reading"];

Filtering Related Entites with Entity Framework

According to this StackOverflow answer:
Linq to Entities - how to filter on child entities
you should be able to filter down the list of related entities in Entity Framework by utilizing a projection, like I've done here:
Company company = _context.Company
.Where(g => g.CompanyId == id)
.Select(comp => new
{
group = comp,
operators = comp.Operator,
formFamilies = comp.FormFamily.Where(ff => ff.IsActive ?? false)
}).AsEnumerable().Select(i => i.group).FirstOrDefault();
To give a quick overview of what I'm trying to obtain here, I'm trying to get a list of all of the active form families associated with this company object, however, whenever I restrict the results in any way, the result set is empty.
If the line were formFamilies = comp.FormFamily then it returns two results, one active one inactive
If the line is formFamilies = comp.FormFamily.Where(ff => true) then it returns nothing
If the line is formFamilies = comp.FormFamily.OrderBy(ff => ff.FormFamilyId) then it returns nothing.
Any sort of modification that I do to comp.FormFamily means the result set returns nothing, I've dug through the deepest sections of SA to try to find a solution, and tried every solution I've found, but nothing seems to cause this list to return anything.
Assuming that Company and FormFamily entities has one to many relationship I would suggest to use a join statement.Something like this should give you what you are looking for.
var company = from c in _context.Company
join f in _context.FormFamily
on c.Id equals f.CompanyId
where c.Id == id
select new Company()
{
Id = c.Id,
operators = c.Operator.ToList(),
formFamilies = c.FormFamily.Where(x=>x.IsActive ==
false).ToList()
} .FirstOrDefault();
Hope this helps.
I didn't quite understand what is your query is supposed to do. But it seems to me that you cannot just call Select method on another Select result method.
Anyway, you could simply use Include methods instead of projecting.
var company = _context.Company
.Where(c => c.Id == id)
.Include(c => c.FormFamily).Where(ff => ff.IsActive ?? false)
.ToList();
Did not test it. To prove it works or not be sure put an entity model in the question. Then I may produce more accurate answer.

why does LastOrDefault conflicts with IEnumerable<Viewmodel> [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 6 years ago.
Improve this question
I'm trying to merge several query results and it works by using IEnumerable<Viewmodel> as type per query and I use concat to combine but I cannot use LastOrDefault() per query - combine more than two results
But I need to specify that the query will return only the Last value and will return none if it is null , but there is an error.
I already tried First() or FirstOrDefault() or SingleOrDefault() but still it gave me an error.
EDIT:
the three queries have the same structures on the columns returned
IEnumerable<ViewModel> query1= db.Table1
.Where(er => er.ID == Filter1)
.Select(er => new ViewModel
{
//columns here
}).ToList().LastOrDefault(); //the **ToList().LastOrDefault();** shows error but I need this to get only the last row from the query if there is
IEnumerable<ViewModel> query2= db.Table2
.Where(er => er.ID == Filter2)
.Select(er => new ViewModel
{
//columns here
}).ToList().LastOrDefault(); //the **ToList().LastOrDefault();** shows error but I need this to get only the last row from the query if there is
IEnumerable<ViewModel> query3= db.Table3
.Where(er => er.ID == Filter3)
.Select(er => new ViewModel
{
//columns here
}).ToList().LastOrDefault(); //the **ToList().LastOrDefault();** shows error but I need this to get only the last row from the query if there is
var result = query1.Concat(query2).Concat(query3).ToList();//it shows error here if I add the **ToList().LastOrDefault()** per query
if I add the LastOrDefault() on the result statement it will show only one row which is from the query3
How can I fix this? Thank you so much, please be patient to me.
Well, I would say the best way is to override List<T>, but if your project is to small for this and this is the only place where you need it you could make it like this:
List<ViewModel> list1 = new List<ViewModel>();
var query1 = db.Table1.Where(er => er.ID == Filter1)
.Select(er => new ViewModel
{
//columns here
}).ToList().LastOrDefault();
list1.Add(query);
var query2 = db.Table2.Where(er => er.ID == Filter2)
.Select(er => new ViewModel
{
//columns here
}).ToList().LastOrDefault();
list1.Add(query);
var query3 = db.Table3.Where(er => er.ID == Filter3)
.Select(er => new ViewModel
{
//columns here
}).ToList().LastOrDefault();
list1.Add(query);
return list1;
There's no need to concat stuff or or forward-declaring lists and adding elements as you go etc. You can simply select the items one by one and materialize them into a list as follows:
var a = table
.Where(PredicateA)
.Select(Selector)
.FirstOrDefault();
var b = table
.Where(PredicateB)
.Select(Selector)
.FirstOrDefault();
var c = table
.Where(PredicateC)
.Select(Selector)
.FirstOrDefault();
var result = new[] {a, b, c};
Or you can select them all together in a single query (probably better performance-wise, but favor what you think is more readable).
var result = table
.Where(x => PredicateA(x) || PredicateB(x) || PredicateC(x))
.Select(Selector);
LastOrDefault gives you not an IEnumerable but only one element. And you try to assign it to IEnumerable. That is your problem. In your case I would rewrite LastOrDefault (or use another function instead of it) so it returns a IEnumerable with only one element you need.

Order of Execution of Condition in a Where clause

I have the following statement with an Or condition. I am looking what would be the result when both conditions are true.
var result = db.Result.FirstOrDefault(x=>(x.ID==50||x.ID==60)&&x.Name="XYZ");
In a case where I have a row in the result table where both the conditions are true, i.e; x.ID=50 and x.ID=60. What would be the result of the query?
I have already tested to get the result in a test environment. But I wanted to make sure that the order of execution would always remain the same no matter what the size of the database is. As I have read that the where clause uses some sort of indexing for faster retrieval, what would be the course of execution of this statement.
The provided query is just a sample and the name ID has nothing to do with the unique identified of a table.
Question
How would the check be performed on the database? I would expect a result where it first checks if ID ==50 and on failure check if ID==60. If this is my expected result, would the query given above achieve my task?
Update after answer
I find it necessary to give a more clearer example so that the question is more understandable. (If this update makes the existing answers invalid, I am really sorry)
var result = db.result.firstordefault(x=>(x.foreignkeyid == someval|| foreignkeyid == 123)&& x.Name=="XYZ");
And my database sample
ID foreignkeyid Name
1 123 XYZ
2 somevalue XYZ
3 anothervalue XYZ
In this case when the query is executed the result would return the row with ID==1 , but I want row wiht ID==2 to be returned.
Worst case attempt to achieve the result
var result = new Result();
result =db.Result.firstordefault(x=>x.ID==somevalue&&x.name==xyz);
if(result==null)
var result = db.firstordefault(x=>x.ID ==123&& x.name==xyz);
Given this example of yours:
var result = db.result.firstordefault(x=>(x.foreignkeyid == someval|| foreignkeyid == 123)&& x.Name=="XYZ");
In which you want to prioritize the result where fk = someval (fk: foreign key), you can do the following:
db.set.OrderBy(x => x != someval) // false actually comes before true
.ThenBy(x => x != val2ndPriority)
.FirstOrDefault(x => (x.fk == someval ||
x.fk == val2ndPriority ||
x.fk == leastPriorityVal) &&
x.Name == "XYZ");
If you have a lot of "prioritized fk values", or if they are unknown at compile time, you can do:
var orderedEnum = db.set.OrderBy(x => x.Id);
foreach (var fk in fksByPriority)
orderedEnum = orderedEnum.ThenBy(x => x != fk);
var result = orderedEnum.FirstOrDefault(x => fksByPriority.Contains(x.fk) &&
x.Name == "XYZ");
How I would prefer it to look like:
Another different approach would be to get all possibly-relevant records and then run similar logic outside of the DB (your db Linq queries normally run smartly right inside the db):
var results = db.set.Where(x => x.Name == "XYZ" &&
fks.Contains(x.fk)).ToArray();
var highestPriorityResult =
results.OrderBy(x => fksByPriority.IndexOf(x.fk)).FirstOrDefault();
On a final note, I wish to say that your problem indicates a possibly flawed design. I can't imagine why you'd have this filtering-with-priority-foreign-key issue.

Entity Framework (using In and Select Distinct)

I am relatively new to Entity Framework 6.0 and I have come across a situation where I want to execute a query in my C# app that would be similar to this SQL Query:
select * from periods where id in (select distinct periodid from ratedetails where rateid = 3)
Is it actually possible to execute a query like this in EF or would I need to break it into smaller steps?
Assuming that you have in your Context class:
DbSet<Period> Periods...
DbSet<RateDetail> RateDetails...
You could use some Linq like this:
var distincts = dbContext.RateDetails
.Where(i => i.rateId == 3)
.Select(i => i.PeriodId)
.Distinct();
var result = dbContext.Periods
.Where(i => i.Id)
.Any(j => distincts.Contains(j.Id));
Edit: Depending on your entities, you will probably need a custom Comparer for Distinct(). You can find a tutorial here, and also here
or use some more Linq magic to split the results.
Yes, this can be done but you should really provide a better example for your query. You are already providing a bad starting point there. Lets use this one:
SELECT value1, value2, commonValue
FROM table1
WHERE EXISTS (
SELECT 1
FROM table2
WHERE table1.commonValue = table2.commonValue
// include some more filters here on table2
)
First, its almost always better to use EXISTS instead of IN.
Now to turn this into a Lambda would be something like this, again you provided no objects or object graph so I will just make something up.
DbContext myContext = this.getContext();
var myResults = myContext.DbSet<Type1>().Where(x => myContext.DbSet<Type2>().Any(y => y.commonValue == x.commonValue)).Select(x => x);
EDIT - updated after you provided the new sql statement
Using your example objects this would produce the best result. Again, this is more efficient than a Contains which translates to an IN clause.
Sql you really want:
SELECT *
FROM periods
WHERE EXISTS (SELECT 1 FROM ratedetails WHERE rateid = 3 AND periods.id = ratedetails.periodid)
The Lamda statement you are after
DbContext myContext = this.getContext();
var myResults = myContext.DbSet<Periods>()
.Where(x => myContext.DbSet<RateDetails>().Any(y => y.periodid == x.id && y.rateid == 3))
.Select(x => x);
Here is a good starting point for learning about lamda's and how to use them.
Lambda Expressions (C# Programming Guide).
this is your second where clause in your query
var priodidList=ratedetails.where(x=>x.rateid ==3).DistinctBy(x=>x.rateid);
now for first part of query
var selected = periods.Where(p => p.id
.Any(a => priodidList.Contains(a.periodid ))
.ToList();

Categories