0

I'm developing an ASP.NET MVC web app in VB and I am required to output a set of data to a table format, and to allow the user to configure the order and presence of columns from an available set. The data set is stored as a list of the object type representing the row model.

Currently, I implement this using CallByName. Iterating over an ordered list of property names and outputting the value from the instance of the row model. However, based on testing this seems to be a major bottleneck in the process.

I've seen a recommendation to store delegates to get the property, against the string representation of the property's name. So, I can presumably do something like this:

Public Delegate Function GetColumn(ByRef RowObj As RowModel) As String

Dim GetPropOne As GetColumn = Function(ByRef RowObj As RowModel) RowObj.Prop1.ToString()

Dim accessors As New Hashtable()

accessors.Add("Prop1", GetPropOne)

Then, loop through and do something like this:

Dim acc As GetColumn = accessors(ColumnName)

Dim val As String = acc.Invoke(currentRow)

It looks faster, but it also looks like more maintenance. If this is indeed faster, is there a way I can dynamically build something like this? I'm thinking:

Public Delegate Function GetObjectProperty(Instance As Object) As Object

For Each prop In GetType(RowModel).GetProperties()
    Dim acc As GetObjectProperty = AddressOf prop.GetValue
    columns.Add(prop.Name, acc)
Next

Dim getColVal As GetObjectProperty = columns(ColumnName)

Dim val As String = getColVal.Invoke(currentRow).ToString()

Open to suggestions for different approaches.

Scott Oliver
  • 507
  • 5
  • 19
  • @VisualVincent Yeah, I think it will work, but I'm unclear on whether there is a performance benefit vs calling CallByName. – Scott Oliver May 09 '17 at 15:48

2 Answers2

0

I do a similar thing to turn a SOAP response into a Data Table

Public Function ObjectToDataSource(objName) As DataSet
    Dim CollName = ""
    Dim ds As New DataSet()
    For Each m As System.Reflection.PropertyInfo In objName.GetType().GetProperties()
        If m.CanRead Then
            If InStr(m.PropertyType.ToString, "[]") <> 0 Then
                CollName = m.Name
                Exit For
            End If
        End If
    Next
    Dim CollObj
    CollObj = CallByName(objName, CollName, CallType.Get)
    If CollObj.length = 0 Then
        Call EndTask("No Supply Chains to display", "Royal Mail failed to return Supply Chain information for these credentials", 3)
    Else
        Dim dt_NewTable As New DataTable(CollName)
        ds.Tables.Add(dt_NewTable)
        Dim ColumnCount = 0
        For Each p As System.Reflection.PropertyInfo In CollObj(0).GetType().GetProperties()
            If p.CanRead Then
                If p.Name <> "ExtensionData" Then
                    dt_NewTable.Columns.Add(p.Name, p.PropertyType)
                    ColumnCount = ColumnCount + 1
                End If
            End If
        Next
        Dim rowcount = CollObj.Length - 1
        For r = 0 To rowcount
            Dim rowdata(ColumnCount - 1) As Object
            For c = 0 To ColumnCount - 1
                rowdata(c) = CallByName(CollObj(r), dt_NewTable.Columns.Item(c).ToString, CallType.Get)
            Next
            dt_NewTable.Rows.Add(rowdata)
            rowdata = Nothing
        Next
    End If
    Return ds
End Function

This is specific to my needs in terms of getting CollName and not requiring ExtensionData

Colster
  • 77
  • 8
0

If ColumnName is the same name as one of the RowModel's properties I don't see why you need the long workaround with delegates...

An extension method which gets only the property you want right now is both faster and consumes less memory.

Imports System.Runtime.CompilerServices

Public Module Extensions

    <Extension()> _
    Public Function GetProperty(ByVal Instance As Object, ByVal PropertyName As String, Optional ByVal Arguments As Object() = Nothing) As Object
        Return Instance.GetType().GetProperty(PropertyName).GetValue(Instance, Arguments)
    End Function
End Module

Example usage:

currentRow.GetProperty("Prop1")
'or:
currentRow.GetProperty(ColumnName)
Visual Vincent
  • 18,045
  • 5
  • 28
  • 75
  • Are you sure `CallByName` is the bottleneck? Because compared to Reflection they both seem to be able to get properties almost equally fast (usually takes less than a millisecond to do so). – Visual Vincent May 09 '17 at 16:42
  • I assumed CallByName and the System.Reflection methods worked the same under the hood, hence why I was thinking about setting up the hashtable of delegate functions to access the properties. My concern is that this creates a maintenance burden when updating the row model. If I can build that set of functions dynamically then that would be better. I guess my concern is where the performance impact from reflection is coming from. Is it when I iterate the properties collection, or is it when I call the GetValue method, or both? I haven't actually tested any of the methods I propose above, yet. – Scott Oliver May 10 '17 at 10:18
  • @ScottOliver : If you use your last code example then the `HashSet` or `Dictionary` will be creating the overhead. This is because your last code block is just a more redundant way of doing what I do above. – Visual Vincent May 10 '17 at 14:12
  • @ScottOliver : I beleive that Reflection will always have a certain overhead (still only about 1 ms per call, though) because there's a lot of calls and evaluations that it has to do. **Therefore** the fastest way of doing this is what you do in your first code block, because it doesn't utilize reflection. So by gaining performance you lose "dynamicness". – Visual Vincent May 10 '17 at 14:17
  • OK, so, looking back at the more verbose but presumably faster method: Is it possible to build something like that dynamically without incurring performance cost due to reflection? EDIT: Hadn't seen the latest comment. Thanks. – Scott Oliver May 10 '17 at 14:18
  • 1
    @ScottOliver : Check the answer in the following question, it might help: http://stackoverflow.com/questions/1027980/improving-performance-reflection-what-alternatives-should-i-consider – Visual Vincent May 10 '17 at 14:24
  • Thanks, I'll check it out. – Scott Oliver May 10 '17 at 14:28