BlackICE
BlackICE

Reputation: 8926

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"

Upvotes: 3

Views: 218

Answers (2)

Chris
Chris

Reputation: 27619

I've done some playing myself and wonder if this might be your problem...

Sub Main
    PersonEntity.IsValidSearchProperty()
    PatientEntity.IsValidSearchProperty()
End Sub

' Define other methods and classes here

public class BaseEntity(of T)

    public shared sub IsValidSearchProperty ()
        Console.Write(GetType(T))
    end sub

end class

public class PersonEntity
    inherits BaseEntity(of PersonEntity)

end class

public class PatientEntity
    inherits PersonEntity

end class

Here is a quick example of how I think your inheritance is working. I've assumed that the generic parameter passed when constructing BaseEntity is the Entity in question. I'm assuming that PersonEntity is concrete rather than another abstract with a generic parameter.

The problem with the code I've listed is that for PatientEntity when calling IsValidSearchProperty that the type parameter T is still PersonEntity as inherited from the PersonEntity class.

This may or may not be the same as your class but if your GetType is returning PersonEntity instead of PatientEntity then it seems likely that this is your problem.

I assume that if you are about to sort by instances of these classes that you have an instance and you could convert this to an instance method?

Or alternatively you could pass the type explicitly into the recursive function so that rather then using getType on the generic parameter you have worked out the type from the type of the property already and passed that in correctly (you have the property after all so no effort to find its class).

This answer is making a few assumptions but they do fit with the observable situation so I hope they are right and help. If not let me know and I'll edit or delete.

Upvotes: 1

Kratz
Kratz

Reputation: 4340

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

Upvotes: 2

Related Questions