Search code examples
c#typespropertyinfo

how to pass the correct target when getting value from a PropertyInfo[]


In the first inner loop I was able to pass the correct target object when getting the value from PropertyInfo[], however in the second inner loop it gives an exception that the target object is not correct.

So what I want is to get all of the values of a property with inner properties of this listProperties[j], how do I pass correctly the target object to get all of those values?

Model Data:

public class Foo
{
    public string Name { get; set; }
    public Bar BarProp { get; set; }
}

public class Bar
{
    public string Name { get; set; }
    public decimal Value { get; set; }
}

Method:

private void CreateDataRow(ISheet sheet, IList iList)
{
    for (int i = 0; i < iList.Count; i++)
    {

        var listType = iList[i].GetType();
        var listProperties = listType.GetProperties(BindingFlags.Public | BindingFlags.Instance);

        for (int j = 0; j < listProperties.Count(); j++)
        {
            int columnCount = j;

            if (IsPrimitiveType(listProperties[j].PropertyType))
            {
                var columnData = listProperties[j].GetValue(iList[i], null) ?? "-";
                var dataTypeValue = Convert.ChangeType(columnData, listProperties[j].PropertyType);

                //omitted codes

                continue;
            }

            var innerProperties = listProperties[j].PropertyType.GetProperties().ToArray();

            for (int k = 0; k < innerProperties.Count(); k++)
            {
                //this throws an exception
                var columnData = innerProperties[k].GetValue(listProperties[j], null) ?? "-";

                //omitted codes
            }
        }
    }

}

CreateDataRow is being called here:

private XSSFWorkbook CreateWorkbook(T model)
{
    var workbook = new XSSFWorkbook();

    var type = model.GetType();

    var properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);

    foreach (var property in properties)
    {
        //check if the property is a list
        if (property.PropertyType.IsGenericType &&
            typeof(List<>).IsAssignableFrom(property.PropertyType.GetGenericTypeDefinition()))
        {
            var propertyValueList = (IList)property.GetValue(model, null);

            if (propertyValueList != null && propertyValueList.Count > 0)
            {

                //create data row
                CreateDataRow(sheet, propertyValueList);
            }
        }
    }
    return workbook;
}

The T model uses this Model:

public class ExportModel
{
   public List<Foo> FooList { get; set; }
}

Solution

  • in this line of code:

    var innerProperties = listProperties[j].PropertyType.GetProperties().ToArray(); 
    

    You are getting properties of listProperties[j].PropertyType, but in GetValue:

    var columnData = innerProperties[k].GetValue(listProperties[j], null) ?? "-";
    

    You are sending listProperties[j] as instance argument. Correct one of the following lines:

    var innerProperties = listProperties[j].GetProperties().ToArray();  
    //or  
    var columnData = innerProperties[k].GetValue(listProperties[j].PropertyType, null) ?? "-";
    

    Different is between object instance and its type. The PropertyType represents types of retrieved Property and acts like:

    propertyInfo.GetValue(x,y).GetType();

    You should send exact instance of the target object not its type and not type of retrieved property. and if you want to get value of one of properties of property write:

    var columnData = innerProperties[k].GetValue(listProperties[j].GetValue(iList[i],null) ,null) ?? "-";