I am new to LINQ and am trying to create some data points from a table to graph. The three fields of importance in this table are the id, the time and the value. I am writing a query to get the average value over a set time for a chosen id. The LINQ I have written follows:
var value = (from t in _table
where t.Id == id
&& t.Time >= intervalStartTime
&& t.Time <= intervalEndTime
select t.Value).Average();
However this crashes at runtime with:
"The null value cannot be assigned to a member with type System.Decimal which is a non-nullable value type.."
At certain intervals there is no data so the SQL LINQ generates returns null, which I would liked to be COALESCED to 0 but instead crashes the application. Is there a way to write this LINQ query to be able to handle this properly?
The table definition to make things clearer:
[Serializable]
[Table(Name = "ExampleTable")]
public class ExampleTable
{
[Column(Name = "Id")]
public int Id { get; set; }
[Column(Name = "Time")]
public DateTime Time { get; set; }
[Column(Name = "Value")]
public int Value{ get; set; }
}
An object collection such as an IEnumerable<T> can contain elements whose value is null. If a source collection is null or contains an element whose value is null , and your query doesn't handle null values, a NullReferenceException will be thrown when you execute the query. var query1 = from c in categories where c !=
It will return an empty enumerable. It won't be null.
In LINQ, you can find the average of the given sequence by using the Average method. This method returns the average of the elements present in the given sequence or collection. It returns nullable, non-nullable decimal, double, floats, int, etc. values.
I think you want
var value = (from t in _table
where t.Id == id
&& t.Time >= intervalStartTime
&& t.Time <= intervalEndTime
select (int?)t.Value).Average()
This way, you get a double?
back, whereas without the (int?)
cast you need to get a double
back, which cannot be null
.
This is because of the signatures
double Enumerable.Average(IEnumerable<int> source)
double? Enumerable.Average(IEnumerable<int?> source)
Now, to get an average of 0 instead of null, you need to place the coalescing operator at the end
var value = (from t in _table
where t.Id == id
&& t.Time >= intervalStartTime
&& t.Time <= intervalEndTime
select (int?)t.Value).Average() ?? 0.0;
IMHO this is a pretty awful design of the Enumerable
/Queryable
class; why can't Average(IEnumerable<int>)
return double?
, why only for Average(IEnumerable<int?>)
?
EDIT: Complete change :)
Okay, how about this:
var value = (from t in _table
where t.Id == id
&& t.Time >= intervalStartTime
&& t.Time <= intervalEndTime
select t.Value).DefaultIfEmpty().Average()
I believe that's logically what you want - changing {} to {0}, so making all averages achievable. I don't know if it'll do what you want in terms of SQL though.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With