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"
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