Question

Is there a generic way to retrieve PropertyInfo based on a string value alone, when deeper than one level.

I assume this is probably simple enough, but my search results are only as good as my search criteria, and I think I am having an issue articulating the proper keywords to get search results for what I am after.

I would like to be able to do something like the following (which works perfect if the key is for a direct property / one level - ie key = 'firstName'):

public static PropertyInfo (this HtmlHelper htmlHelper, string key) {

     PropertyInfo pInfo = htmlHelper.ViewData.Model.GetType().GetProperty(key);

     return pInfo;
}

But is there a way for me to return the PropertyInfo based on a string alone when Key equals something more complex, such as nested classes, objects, lists, etc...:

  • key = "somelist[0].myproperty"
  • key = "Items[0].someotherlist[1].someproperty" (where Items is defined as List<Item> Items {get; set;}, someotherlist is defined similarly)

Can the method be generic enough to essentially drill down as many levels as needed (defined)?

Was it helpful?

Solution

So here is what I came up with... this is about to get wordy, and mostly 'stream of thought'

I have custom HtmlHelperExtension, and within it :

 PropertyInfo[] pInfoArray = htmlHelper.ViewData.Model.GetType().GetProperties();

 PropertyInfo pInfo = GetPropertyInfo(pInfoArray, key);

This GetPropertyInfo() method takes the key, and the PropertyInfo array, cycles through the properties, until the keypart (using regex to remove any indication of an array from the string, so I am left with only the property) matches the property name. On Match, determine if this is the first cycle in the loop, and if so assign the matched property to my Temp Type and PropertyInfo variables. If keyParts are remaining to loop through, subsequent loops now use previously set temp variables and the for loop index [i] to iterate / drill down the class structure. Each time setting the pInfoTemp variable, and then pTypeTemp so the next loop can use where it left off.

    private static PropertyInfo GetPropertyInfo(PropertyInfo[] pInfoArray, string key)
    {
        PropertyInfo pInfo = null;
        string[] keyParts = key.Split('.');
        Regex arrayRgx = new Regex("\\[\\d*\\]");
        PropertyInfo pInfoTemp = null;
        Type pTypeTemp = null;

        foreach (PropertyInfo prop in pInfoArray)
        {
            string keyPartsTrimmed = arrayRgx.Replace(keyParts[0], ""); // removes '[#]' from string

            if (keyPartsTrimmed == prop.Name)   // match property name
            {

                for (int i = 0; i < keyParts.Count(); i++)
                {
                    if (i == 0) // initial item [0]
                    {
                        pTypeTemp = prop.PropertyType;  // gets [0]'s type
                        pInfoTemp = prop;               // assigns [0]'s property info
                    }
                    else
                    {
                        pInfoTemp = GetNestedPropertyInfo(pTypeTemp, arrayRgx.Replace(keyParts[i], "")); // gets [i]'s property info for return or next iteration
                        pTypeTemp = pInfoTemp.PropertyType; // gets [i]'s type for next iteration
                    }
                }

                pInfo = pInfoTemp;
                break;
            } 
        }

        return pInfo;
    }

This next method is invoked by the previous for grabbing nested property info, more importantly for detecting whether the passedItemType is a List (without this, it fails to work correctly as it is unable to find the property asked for in a List<> Type. I need to know what the List item Type is.

    private static PropertyInfo GetNestedPropertyInfo(Type passedItemType, string passedProperty)
    {
        PropertyInfo pInfoOut = null;

        if (passedItemType.IsGenericType && passedItemType.GetGenericTypeDefinition() == typeof(List<>))
        {
            Type itemType = passedItemType.GetGenericArguments()[0];
            pInfoOut = itemType.GetProperty(passedProperty);
        }
        else
        {
            pInfoOut = passedItemType.GetProperty(passedProperty);
        }

        return pInfoOut;
    }

This currently suits my requirements as they are today, and I have tested it with the following properties, lists, subclasses, subclasses with lists, etc.. to 4 levels deep, but should function properly no matter the depth:

  • firstName
  • lastName
  • Items[1].sprocket
  • subClass.subClassInt
  • subClass.mySubClassObj.sprocketObj
  • subClass.ItemsInMySubClass[1].sprocket
  • subClass.ItemsInMySubClass[0].mySubClassObj.widgetObj
  • subClass.ItemsInMySubClass[2].mySubClassObj.sprocketObj

If anyone has a better solution, or see any potential issues with what I have, I welcome the feedback.

OTHER TIPS

The best way in your case is to make a parser that split that expression.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top