views:

65

answers:

2

I'm trying to use a nested multi-line lambda Function in VB.NET and am getting an error. Here's what my code looks like:

cartItems = cartItems.Select(Function(ci) New With {.CartItem = ci, .Discount = discountItems.FirstOrDefault(Function(di) di.SKU = ci.SKU)}) 
.Select(Function(k)
            If k.Discount Is Not Nothing Then
                k.CartItem.Discount = minNumberOfItemsDiscounted * k.Discount.DiscountAmount
            End If
            Return k.CartItem
        End Function)

And here's the long error message:

Error   1   Overload resolution failed because no accessible 'Select' can be called with these arguments:
    Extension method 'Public Function Select(Of TResult)(selector As System.Func(Of <anonymous type>, Integer, TResult)) As System.Collections.Generic.IEnumerable(Of TResult)' defined in 'System.Linq.Enumerable': Nested function does not have a signature that is compatible with delegate 'System.Func(Of <anonymous type>, Integer, TResult)'.
    Extension method 'Public Function Select(Of TResult)(selector As System.Func(Of <anonymous type>, Integer, TResult)) As System.Collections.Generic.IEnumerable(Of TResult)' defined in 'System.Linq.Enumerable': Data type(s) of the type parameter(s) cannot be inferred from these arguments. Specifying the data type(s) explicitly might correct this error.
    Extension method 'Public Function Select(Of TResult)(selector As System.Func(Of <anonymous type>, TResult)) As System.Collections.Generic.IEnumerable(Of TResult)' defined in 'System.Linq.Enumerable': 'Is' operator does not accept operands of type 'Integer'. Operands must be reference or nullable types.
    Extension method 'Public Function Select(Of TResult)(selector As System.Func(Of <anonymous type>, TResult)) As System.Collections.Generic.IEnumerable(Of TResult)' defined in 'System.Linq.Enumerable': Data type(s) of the type parameter(s) cannot be inferred from these arguments. Specifying the data type(s) explicitly might correct this error. C:\_Dev Projects\CMS2000\Components\NET\HBCatalogPromo\CatalogPromotion\CatalogPromotion.ConsoleTest\Module1.vb 88  21  CatalogPromotion.ConsoleTest

It feels like I have something wrong with my syntax because I've fixed other lines by collapsing in line Functions on to one line when possible. However, I can't do that in this case.

+1  A: 

Your select is throwing because the compiler can't determine the appropriate type due to your usage of a anonymous type.

That being said, it's a bad idea, in general, to use Select() to run code that causes a side effect, which you're doing in this case.

I would, personally, abandon doing this in a single LINQ statement, in this instance. The LINQ is, in my opinion, causing this to be more complex than it ought to be. My preference, in a case like this, would be to make a single function that does your logic, and returns an appropriate, filled in (known, non-anonymous type). You could then just refactor your LINQ statement into a single Select which returns your known type.

This would avoid the potential side effects, since you could construct a new instance of the type in the method, and simplify the code (including making it much more easily testable, by refactoring the logic into a separate method that worked on a single "instance").

Reed Copsey
I think I understand what you're saying. If the intent is to change data, separate that step from the LINQ query. That way you don't get a side effect from the process.
Ben McCormack
@Ben: Exactly. It also means the method that converts the data can work on a single element, and be **testable**. Putting it into a LINQ statement muddles the issue - since you're "querying" while modifying. It can work, but it has the same smell as trying to modify a collection while iterating over it in a foreach statement, etc...
Reed Copsey
A: 

Reactive Extension .NET's Do() extension method might be a good candidate if you expecting only side-effect. After that, you can use Select() projection method as normal.

Kthurein