question

Hobbyistprogrammer-7674 avatar image
0 Votes"
Hobbyistprogrammer-7674 asked ·

LINQ Specify Property as variable VB.Net

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

dotnet-visual-basic
· 2
10 |1000 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

HI,
yes, its possible. Use Expression Trees.


0 Votes 0 ·

Thanks Peter. I am checking it.

0 Votes 0 ·
Viorel-1 avatar image
0 Votes"
Viorel-1 answered ·

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)))


·
10 |1000 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

Viorel-1 avatar image
0 Votes"
Viorel-1 answered ·

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 ·
10 |1000 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

Hi Viorel, I cant do that since i need to do the same thing with lot of properties. formula is basically same, i just need to change only the the properties

0 Votes 0 ·
Viorel-1 avatar image Viorel-1 Hobbyistprogrammer-7674 ·

If the property is identified by name (a string), then check the other approaches.

0 Votes 0 ·
karenpayneoregon avatar image
0 Votes"
karenpayneoregon answered ·

Hello @Hobbyistprogrammer-7674

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


·
10 |1000 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

Hobbyistprogrammer-7674 avatar image
0 Votes"
Hobbyistprogrammer-7674 answered ·

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

·
10 |1000 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.