Reputation: 4207
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
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