Domanda

This is the extract of a linq expression:

Dim charges As List(Of IndividualCharge) = (From t In totals
                    Group t By t.InvId
                    Into Group
                    Select New IndividualCharge With {
                    .VatRate = Group.FirstOrDefault(Function(x) x.VatRate).VatRate
                    }).ToList()

It appears that when x.VatRate is 0, FirstOrDefault throws an exception:

NullReferenceException

I can confirm this by swapping it out for:

.VatRate = Group.FirstOrDefault(Function(x) 0).VatRate

which ensures an exception every time.

  • Why is a null reference exception thrown (this is a value type, hence no references)?
  • How do I deal with the situation where x.VatRate is actually 0?
È stato utile?

Soluzione

You're misreading the error, compounded by VB.NET's type coercion.

Group.FirstOrDefault(Function(x) x.VatRate).VatRate

FirstOrDefault returns the first value matching the predicate function, or the default value of T if nothing matches. Since your function doesn't evaluate to true or false, VB.NET coerces the int to a bool. In that scenario, 0 is converted to false and any other value is converted to true.

So, when x.VatRate is not 0, then the first IndividualCharge passes the predicate filter. When VatRate is 0, then no IndividualCharge passes the predicate filter and null (the default value of IndividualCharge) is returned. Trying to grab the VatRate then results in your NullReferenceException.

Presumably, you really wanted the first VatRate, whether it was 0 or not. Changing your code to:

Group.Select(Function(x) x.VatRate).First()

should accomplish that.

Altri suggerimenti

When you pass a expression to FirstOrDefault your telling it select the first item that matches the expression (much like a Where clause but for the first object to match the clause). If you changed your statement to the following is it ok?

Dim charges As List(Of IndividualCharge) = (From t In totals
                    Group t By t.InvId
                    Into Group
                    Select New IndividualCharge With {
                    .VatRate = Group.FirstOrDefault().VatRate
                    }).ToList()

If you wanted to get the first value that isn't zero something along these lines should do?

Dim charges As List(Of IndividualCharge) = (From t In totals
                    Group t By t.InvId
                    Into Group
                    Select New IndividualCharge With {
                    .VatRate = if(Group.Any(function(o) o.VatRate > 0),  Group.FirstOrDefault(function(o) o.VatRate > 0).VatRate, -1)
                    }).ToList()

This is caused by VBs implicit type coercion. The FirstOrDefault method expects a function which returns a Boolean. You are passing a function which returns a Decimal, so VB converts it to:

Group.FirstOrDefault(Function(x) x.VatRate <> 0)

If none of the items in the group have a non-zero VatRate, this will return Nothing. You then try to access the VatRate property on the returned object, which results in a NullReferenceException.

Try using:

Group.Where(Function(x) x.VatRate <> 0)
   .Select(Function(x) x.VatRate)
   .FirstOrDefault()

That should return the first non-zero VatRate if there is one, or zero otherwise.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top