Esko
Esko

Reputation: 4207

VB.Net passing linq-expression as parameter and set object property with that expression

I want to make a function that takes a list of objects in, loops through them and appends customername to each object. The goal is that the user calling this method would tell which property in the object contains the customernumber and which property is used to set the customername fetched from the db.

Now I have the logic pretty much done, but I'm struggling to make it as strongly typed as it can be. So I figured I could use Linq-expressions as parameters. Here is what I have now:

Public Shared Sub FillCustomerNames(Of TModel As Class)(objects As IEnumerable(Of TModel),
                                                      customerNumberExpression As Func(Of TModel, Integer),
                                                      customerNameExpression As Func(Of TModel, String))

    Dim customerNumbers As List(Of Integer) = objects.Select(customerNumberExpression).Distinct().ToList()

    ' Fetch names from the database...
    Dim customerNames = From c In Customers...

    ' Combine original objects with names
    Dim joinResult = From original In objects
                     Let customerNumber As Integer = customerNumberExpression.Invoke(original)
                    Join customer In customerNames On customerNumber Equals customer.CustomerNumber
                    Select original, customer.CustomerName

    For Each item In joinResult
        ' How to set the property in the expression?
        ' item.original.(property wanted) = item.customerName
    Next
End Sub

And I want to be able to call it with something like this:

    FillCustomerNames(Of ADDRESS)(addresses,
                                    Function(a) a.CustomerNumber,
                                    Function(a) a.CustomerName)

Now the Customernumber-expression (that I use to get the customernumber from the object), works fine. But how do you do that setter part? I guess I can not do it with this kind of expression? Do I need an action instead? What is the easiest way to do it.

I know I could use reflection and pass the property name as string, but it's not strongly typed as this would be.

Any help appreciated!

Upvotes: 1

Views: 2205

Answers (1)

AakashM
AakashM

Reputation: 63340

But how do you do that setter part? I guess I can not do it with this kind of expression? Do I need an action instead?

Yes. I'm translating from C# (in my head) to your VB.NET, so apologies if I've got the syntax wrong.

You already know how to pass a getter into your method - as a Func(Of T, Integer), a thing that takes a T and returns an Integer. What you want now is to pass a setter into your method. A setter (in this case) is a thing that takes a T and a String and... does something, and returns nothing. For this you can use Action(Of T, String), like so:

Your method signature becomes

Public Shared Sub FillCustomerNames(Of TModel As Class)(
    objects As IEnumerable(Of TModel),
    customerNumberGetter As Func(Of TModel, Integer),
    customerNameSetter As Action(Of TModel, String))

(note I've removed Expression from the parameter names as that could be confused with the Linq Expression functionality, which isn't being used here)

And this:

For Each item In joinResult
    ' How to set the property in the expression?
    ' item.original.(property wanted) = item.customerName
Next

becomes simply

For Each item In joinResult
    customerNameSetter(item.original, item.customerName)
Next

Remember, customerNameSetter is an Action and so can simply be invoked as if it were a 'real' method.

The call site would become:

FillCustomerNames(Of ADDRESS)(addresses,
                                Function(a) a.CustomerNumber,
                                Sub(a, name) a.CustomerName = name)

(this is where I'm not completely sure of the VB.NET syntax for declaring a lambda Sub)

Upvotes: 1

Related Questions