0

I can't figure out why this is failing to cast. It says it is null, but that should never be the case because I'm checking if it's null and replacing it with 0, right?

        var BF = (double)filtered.Sum(s => s.fees
           .Where(w => w.status == "B")
           .Sum(su => su.amount ?? 0));

ERR: The cast to value type 'System.Decimal' failed because the materialized value is null. Either the result type's generic parameter or the query must use a nullable type.

I have a list of objects that each contain a list of fees which contain a nullable decimal amount and I just need to sum up the amounts. What am I missing?

Blue Eyed Behemoth
  • 3,692
  • 1
  • 17
  • 27
  • 1
    Try: .Sum(su => su.amount ?? 0D)); – Kevin Mar 23 '16 at 20:06
  • Sorry, I forgot to mention it was a decimal. – Blue Eyed Behemoth Mar 23 '16 at 20:08
  • 1
    Did you try searching for that error? There are no rows, causing `Sum()` (or rather the materialization from the query result to a variable of the requested type) to fail with this cryptic exception. – CodeCaster Mar 23 '16 at 20:08
  • 1
    If su.amount is nullable use: Sum(su => su.amount ? su.amount.Value : 0m)); – Kevin Mar 23 '16 at 20:08
  • 1
    So are you actually casting to `double` or to `decimal`? The code you've shown doesn't match the error you're showing... – Jon Skeet Mar 23 '16 at 20:09
  • From `decimal?` to `double` – Blue Eyed Behemoth Mar 23 '16 at 20:10
  • @CodeCaster So if there are no rows, Sum returns `null`? – Blue Eyed Behemoth Mar 23 '16 at 20:10
  • Yes. So use `Sum(su => su.amount) ?? 0` instead, as explained in [The cast to value type 'Int32' failed because the materialized value is null](http://stackoverflow.com/questions/6864311/the-cast-to-value-type-int32-failed-because-the-materialized-value-is-null), which will be translated to `ISNULL(SUM(...), 0)`, which EF _will_ be able to translate to a non-nullable decimal or double. – CodeCaster Mar 23 '16 at 20:14
  • 2
    Unrelated, but is there a reason you're casting a perfectly good decimal to a perfectly lousy double for a value that appears to be financial? – Anthony Pegram Mar 23 '16 at 20:16
  • @AnthonyPegram Because I'm building off of existing code that someone else started and instead of changing everything back to decimal like it should be, I'm just following in their footsteps. Accuracy isn't a large factor luckily lol – Blue Eyed Behemoth Mar 23 '16 at 20:18

2 Answers2

2

Since .Sum() will return null when done on an empty set, I would suggest using .DefaultIfEmpty(0) in order to ensure that you avoid encountering null in this scenario.

To limit the set, I would also suggest to filter out the nulls.

var BF = (double)filtered.Sum(s => s.fees
       .Where(w => w.status == "B" && w.amount != null)
       .Select(su => su.amount)
       .DefaultIfEmpty(0)
       .Sum());
Travis J
  • 81,153
  • 41
  • 202
  • 273
  • I don't think that'll work. This LINQ gets translated to SQL, and the response back to the `decimal`, the latter part is failing because there are no rows. – CodeCaster Mar 23 '16 at 20:12
  • @CodeCaster - It should return 0 if you sum an empty set. I don't believe that the no rows aspect (if there really are no rows) plays a role here. – Travis J Mar 23 '16 at 20:15
  • 1
    `SELECT SUM(f) WHERE 1=0` returns `NULL`. It's that `NULL` that can't be translated back to the requested non-nullable `decimal`. – CodeCaster Mar 23 '16 at 20:17
  • @CodeCaster is right, I got it to work with a combo of suggestions. – Blue Eyed Behemoth Mar 23 '16 at 20:19
  • @CodeCaster - I see, you are correct. I will make an edit. – Travis J Mar 23 '16 at 20:19
  • @TravisJ You were closer before lol `var BF = (double)filtered.Sum(s => s.fees.Where(w => w.status == "B" && w.amount != null).Sum(su => su.amount) ?? 0);` worked just fine. – Blue Eyed Behemoth Mar 23 '16 at 20:23
  • @BlueEyedBehemoth - If you have an answer or solution to your problem that differs from mine or others here feel free to post it as an answer. There is nothing wrong with doing that. May even get some upvotes ;) – Travis J Mar 23 '16 at 20:24
0

try this:

var BF = (double)filtered.Sum(s => s.fees.Where(w => w.status == "B").Sum(su => su.amount ?? 0) ?? 0);

Maybe the internal lambda "s.fees.Where(w => w.status == "B").Sum(su => su.amount ?? 0)" return null