Search code examples
vb.netlinqlinq-to-entities

LINQ SUM Nullable


I have a simple linq query

(From a In db.payments Group Join b In db.interestcharges On  a.pid Equals b.pid Into totalinterest = Group, TotalInterestReceived = Sum(b.interest)) select a, TotalInterestReceived).toList()

b.interest is type decimal in DB.

Throws

"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."

This is because InterestCharges table may not have any interest records for pid.

I have tried sum(if(b.interest=nothing,0,b.interest) but this is translated by LINQ to if(b.interest=0, 0, b.interest) hence it never checks for null. Nothing else seems to work instead of nothing. I have tried vbNull, isDBNull(), no success. query works fine when the sum is not null. defaultifempty(0) may work but not sure how to use it in this scenario. Any pointers?


Solution

  • The GROUP JOIN statement indicates that you're attempting a LEFT OUTER JOIN. This is why you're receiving a NULL issue. By using JOIN for an INNER JOIN, then you won't stumble on this, but it also means that you will only see those items that have values for interest.

    (FROM a in db.Payments 
    Join b in db.InterestCharges ON a.pid Equals b.Pid 
    SELECT a, TotalInterestReceived = SUM(b.Interest) 
    ).toList()
    

    It is possible to generate a sub-select which may get the values you're hoping for. The purpose here is that you get all payments, and then basically add in the Interest charges (or 0 if there are none).

    (From a In db.Payments 
    Select a 
    , TotalInterestRecieved = (From b in db.InterestCharges 
                               Where b.pid = a.pid
                               select b.Interest).DefaultIfEmpty(0).Sum() 
    ).ToList
    

    EDIT:

    Another option would be to bypass EF entirely. Build a view in the database and query that view directly rather than attempting to access the underlying data via LINQ.

    Most other suggestions I would have would involve iterating through the initial list of "Payments" and populating the values as needed. Which is fine for a small number of "Payments" but that is a O(n) solution.