LINQ Specify Property as variable VB.Net

Hobbyist_programmer 621 Reputation points
2020-12-08T20:10:39.047+00:00

Hallo,

I have a function like this

Dim test =List.Sum(Function(x) x.Weight)

Where "Weight" is the property in the object. I would like to know if it is possible to make List.Sum(Function(x) x.Variable) so that i can change the properties in the loop.

Thanks

VB
VB
An object-oriented programming language developed by Microsoft that is implemented on the .NET Framework. Previously known as Visual Basic .NET.
2,571 questions
{count} votes

Accepted answer
  1. Viorel 112.1K Reputation points
    2020-12-11T10:40:56.05+00:00

    If you prefer a string variable that contains the property name, then check this approach too:

    Dim variable As String
    
    If SomeCondition Then
        variable = "Weight"
    Else
        variable = "Height"
    End If
    
    Dim test = List.Sum(Function(x) Val(CallByName(x, variable, CallType.Get)))
    
    0 comments No comments

3 additional answers

Sort by: Most helpful
  1. Viorel 112.1K Reputation points
    2020-12-08T20:55:25.537+00:00

    You can use the Func type in this manner:

    Dim variable As Func(Of Class1, Integer)
    
    If SomeCondition Then
        variable = Function(x) x.Weight
    Else
        variable = Function(x) x.Height
    End If
    
    Dim test = List.Sum(variable)
    

    Where Class1 is the type of your objects. It assumes that Weight and Height are integers.


  2. Karen Payne MVP 35,036 Reputation points
    2020-12-10T23:19:32.663+00:00

    Hello @Hobbyist_programmer

    The following uses a language extension originally done in C# (see post), done below in VB.

    The example will use property names as strings against the following class

    Public Class Sample  
    	Public Property Weight() As Double  
    	Public Property Height() As Double  
    	Public Property Group() As Integer  
    End Class  
    

    Here to test a mocked up list for Sample class above.

    Public Class Mocked  
    
    	Public Shared Function SampleList() As List(Of Sample)  
    		Return New List(Of Sample) From {  
    			New Sample() With {.Group = 1, .Height = 12.4D, .Weight = 100.3D},  
    			New Sample() With {.Group = 1, .Height = 22.4D, .Weight = 10.4D}  
    		}  
    	End Function  
    End Class  
    

    Extension method

    Public Module Extensions  
        <Runtime.CompilerServices.Extension>  
        Public Function Sum(source As IQueryable, member As String) As Object  
            If source Is Nothing Then  
                Throw New ArgumentNullException(NameOf(source))  
            End If  
      
            If member Is Nothing Then  
                Throw New ArgumentNullException(NameOf(member))  
            End If  
      
            ' The most common variant of Queryable.Sum() expects a lambda.  
            ' Since we just have a string to a property, we need to create a  
            ' lambda from the string in order to pass it to the sum method.  
      
            ' Lets create a ((TSource s) => s.Price ). First up, the parameter "s":  
            Dim parameter As ParameterExpression = Expression.Parameter(source.ElementType, "s")  
      
            ' Followed by accessing the Price property of "s" (s.Price):  
            Dim [property] As PropertyInfo = source.ElementType.GetProperty(member)  
            Dim getter As MemberExpression = Expression.MakeMemberAccess(parameter, [property])  
      
            ' And finally, we create a lambda from that. First specifying on what  
            ' to execute when the lambda is called, and finally the parameters of the lambda.  
            Dim selector As Expression = Expression.Lambda(getter, parameter)  
      
            ' There are a lot of Queryable.Sum() overloads with different  
            ' return types  (double, int, decimal, double?, int?, etc...).  
            ' We're going to find one that matches the type of our property.  
            Dim sumMethod As MethodInfo = GetType(Queryable).GetMethods().First(  
                Function(m) m.Name = "Sum" AndAlso m.ReturnType = [property].PropertyType AndAlso m.IsGenericMethod)  
      
            ' Now that we have the correct method, we need to know how to call the method.  
            ' Note that the Queryable.Sum<TSource>(source, selector) has a generic type,  
            ' which we haven't resolved yet. Good thing is that we can use copy the one from  
            ' our initial source expression.  
            Dim genericSumMethod = sumMethod.MakeGenericMethod({source.ElementType})  
      
            ' TSource, source and selector are now all resolved. We now know how to call  
            ' the sum-method. We're not going to call it here, we just express how we're going  
            ' call it.  
            Dim callExpression = Expression.Call(Nothing, genericSumMethod, {source.Expression, Expression.Quote(selector)})  
      
            ' Pass it down to the query provider. This can be a simple LinqToObject-data source,  
            ' but also a more complex datasource (such as LinqToSql). Anyway, it knows what to  
            ' do.  
            Return source.Provider.Execute(callExpression)  
      
        End Function  
      
    End Module  
    

    Now let's perform sum against the two double properties.

    Public Class Operations  
    	Public Shared Sub Example1()  
      
    		Dim heightResults = Mocked.SampleList().AsQueryable().Sum("Height")  
    		Dim weightResults = Mocked.SampleList().AsQueryable().Sum("Weight")  
      
    		Debug.WriteLine($"Height sum: {heightResults}")  
    		Debug.WriteLine($"Weight sum: {weightResults}")  
      
    	End Sub  
      
    End Class  
    
    0 comments No comments

  3. Hobbyist_programmer 621 Reputation points
    2020-12-11T10:49:16.933+00:00

    Thanks Viorel. It makes very simple to use. Thanks Karen for the alternative method.

    0 comments No comments