How to get properties from nested object using ref

2019-06-05 13:10发布

问题:

I have a set of classes, whose properties have data annotations on them. Some of these class properties are of primitive types (and by primitive, I also mean types such as string, double, datetime etc), while others are properties of a custom type.

I would like to be able to iterate through the properties of a class and the properties of the nested objects and pull out the attributes of each property. I’ve played around with reflection and my code works fine, if the class under consideration has only one property of a custom type. However when a class has multiple properties of a custom type and each of those properties have other custom types, I am completely lost on how I’d keep track of the objects/properties that have already been visited.

This is where I have got so far. I have seen a lot of examples on the forum, but they all have a simple nested class, where there is a maximum of one custom type per class. Below is a sample of what I am trying to get done:

Public Class Claim
    <Required()>
    <StringLength(5)>
    Public Property ClaimNumber As String
    <Required()>
    Public Property Patient As Patient
    <Required()>
    Public Property Invoice As Invoice
End Class

Public Class Patient
    <Required()>
    <StringLength(5)>
    Public Property MedicareNumber As String
    <Required()>
    Public Property Name As String
    <Required()>
    Public Property Address As Address
End Class

Public Class Address
    Public Property Suburb As String
    Public Property City As String
End Class

Public Class Invoice
    <Required()>
    Public Property InvoiceNumber As String
    <Required()>
    Public Property Procedure As String
End Class




 Public Shared Function Validate(ByVal ObjectToValidate As Object) As List(Of String)
          Dim ErrorList As New List(Of String)
             If ObjectToValidate IsNot Nothing Then

               Dim Properties() As PropertyInfo = ObjectToValidate.GetType().GetProperties()
                 For Each ClassProperty As PropertyInfo In Properties
                   Select Case ClassProperty.PropertyType.FullName.Split(".")(0)
                      Case "System" 
                        Dim attributes() As ValidationAttribute = ClassProperty.GetCustomAttributes(GetType(ValidationAttribute), False)
                                For Each Attribute As ValidationAttribute In attributes
                                    If Not Attribute.IsValid(ClassProperty.GetValue(ObjectToValidate, Nothing)) Then
                                        ErrorList.Add("Attribute Error Message")
                                    End If
                                Next
                            Case Else
                                Validate(ClassProperty.GetValue(ObjectToValidate, Nothing))
        **** ‘At this point I need a mechanism to keep track of the parent of ClassProperty and also mark ClassProperty as visited, so that I am able to iterate through the other properties of the parent (ObjectToValidate), without revisiting ClassProperty again.**

                        End Select
                    Next
                End If
                Return Nothing
            End Function

回答1:

The most straightforward (and probably easiest) way to approach this is to keep a Dictionary of class property attributes keyed by class name.

If I were approaching this, I would probably create a class to hold the property attributes:

Public Class PropertyAttribute
   Public PropertyName As String
   Public PropertyTypeName As String
   Public Required As Boolean
   Public StringLength As Integer
End Class

Then create a class to hold information about each class' properties:

Public Class ClassAttributes
   Public ClassName As String
   ' You could also use a dictionary here to key properties by name
   Public PropertyAttributes As New List(Of PropertyAttribute)
End Class

Finally, create a dictionary of ClassAttributes to keep track of which custom classes you have already processed:

Public ProcessedClasses As New Dictonary(Of String, ClassAttributes)

The key for the dictionary is the classname.

When you are processing the attributes through reflection, if the property type is custom, check the dictionary for the existence of the class. If it is there, you don't have to process it.

If it is not there, add a new instance to the dictionary immediately (so that nested objects of the same type are safely handled) and then process the attributes of the class.