.netvb.netreflectionstatic-methodsinvokemember

How can I dynamically call a static method on a derived class


In my ASP.Net MVC pages I can click on column headers to sort by that column, but this involves "magic strings" in the aspx, which can result in runtime issues. I am trying to check at runtime whether the values passed to sort by are valid. I have a base class that all my entities inherit from:

Public MustInherit Class BaseEntity(Of T)
'Some Property and method definitions...'

    Public Shared Function IsValidSearchProperty(name As String) As Boolean
        Dim rootPart As String = name
        Dim nested As Boolean = False
        If rootPart.Contains(".") Then
            rootPart = rootPart.Split("."c)(0)
            nested = True
        End If
        Dim properties As PropertyInfo() = GetType(T).GetProperties()
        For Each prop As PropertyInfo In properties
            If prop.Name = rootPart Then
                If nested Then
                    'This is where my issue is'
                    Return Convert.ToBoolean(
                    prop.PropertyType.InvokeMember("IsValidSearchProperty",
                                                   BindingFlags.InvokeMethod Or BindingFlags.Public Or BindingFlags.Static Or BindingFlags.FlattenHierarchy,
                                                   Nothing, Nothing, New Object() {name.Substring(name.IndexOf(".") + 1)})
                                            )
                Else
                    Return True
                End If
            End If
        Next
        Return False
    End Function
End Class

This works great, unless I'm trying to validate a nested property that is more than 1 layer deep in a class hierarchy. For Example:

'Pseudocode Hierarchy
BaseEntity(of T)
    PersonEntity : Inherits BaseEntity(Of PersonEntity)
        Property FirstName as string
    PatientEntity : Inherits PersonEntity
        Property PatientType as int
    VisitEntity : Inherits BaseEntity(Of VisitEntity)
        Property Patient as PatientEntity

Sorting Visits by Patient.FirstName works fine, the property is found recursively, but when I try to sort Visits based on Patient.PatientType, this fails to find the PatientType property. The IsValidSearchProperty is initially called from a VisitEntity, which Finds the Patient property, and it even shows as being of type PatientEntity, but when this method uses InvokeMember to recursively call itself (This is how I am attempting to call it using the property Type), in the second call the GetType(T) is of type PersonEntity, which does not have a PatientType. Any suggestions on how to make this correctly resolve the Type in the nested call?

This method would be called like this:

VisitEntity.IsValidSearchProperty("Patient.FirstName") 
VisitEntity.IsValidSearchProperty("Patient.PatientType")  '* This one doesn't work
PatientEntity.IsValidSearchProperty("PatientType")
PatientEntity.IsValidSearchProperty("FirstName")

Update

Here is some more on how I'm using this:

                Dim sorts() As String = SortExpression.Split(";")

                For Each sort As String In sorts
                    Dim sortParts() As String = sort.Split(" ")

                    If VisitEntity.IsValidSearchProperty(sortParts(0)) Then
                        If sortParts(1).ToLower = "true" Then
                            visits = visits.OrderBy(Of VisitEntity)(sortParts(0).ToString(), SortDirection.Ascending)
                        Else
                            visits = visits.OrderBy(Of VisitEntity)(sortParts(0).ToString(), SortDirection.Descending)
                        End If
                    Else
                        _log.WarnFormat("Found invalid sort property {0}", sortParts(0))
                    End If
                Next

SortExpression would be something like "Patient.PatientType True;Patient.FirstName True"


Solution

  • I don't know why InvokeMember will call the base type and not the current type. But, I would change the function to work around that behavior. The below uses a private overload of the function that takes the type to check as a parameter. When the function drills down, it can just call this overload and pass it the type it wants it to check. This should eliminate the issue of what class the method is called on and what value GetType(T) would return for that class.

    Public Shared Function IsValidSearchProperty(name As String) As Boolean
        Dim CurrentType = GetType(T).GetProperties()
        Return IsValidSearchProperty(name, CurrentType)
    End Function 
    
    Private Shared Function IsValidSearchProperty(name As String, CurrentType as Type) As Boolean
        Dim rootPart As String = name
        Dim nested As Boolean = False
        If rootPart.Contains(".") Then
            rootPart = rootPart.Split("."c)(0)
            nested = True
        End If
        Dim properties As PropertyInfo() = CurrentType.GetProperties()
        For Each prop As PropertyInfo In properties
            If prop.Name = rootPart Then
                If nested Then
                    'This is where my issue is'
                    Return IsValidSearchProperty(name.Substring(name.IndexOf(".") + 1), prop.PropertyType)
    
                Else
                    Return True
                End If
            End If
        Next
        Return False
    End Function