1
votes

I want to know the best way to get the prop info and value using reflection for a nested class by its custom attribute name. With below code I can get the prop info via recursion. But is there a better way or using LINQ. Note that I do not want to hard code the class type as similar to other solution

I also want to get the property value by custom attribute

e.g var propValue = ?????

 public class PlanetRoot

    {

        public void GetNeighborMoon()

        {

            Planet planet = new Planet();

            Product product = new Product();

            Neighbor neighbor = new Neighbor();

            neighbor.Moons = 10;

            neighbor.RingColor = "Red";

            product.Neighbors = new List<Neighbor>();

            product.Neighbors.Add(neighbor);

            planet.Product = product;



            //1. Get the RingColor property info of neighbor with attribute MyDBField(Name = "NeighborRing") . Is there a better way

            PropertyInfo propInfo = null;
            DoRecursiveGetProperty(planet.GetType(), "NeighborRing", out propInfo );



            //2. Get the RingColor property value of neighbor with attribute MyDBField(Name = "NeighborRing")

            //var propValue = GetPropertyValue(????);

        }

    }



    private static PropertyInfo DoRecursiveGetProperty(Type type, string attribName, out PropertyInfo propInfo)

    {

        PropertyInfo[] pi = type.GetProperties();

        propInfo= null;

        foreach (PropertyInfo p in pi)

        {

            var dbFieldAttribute = (MyDBFieldAttribute)Attribute.GetCustomAttribute(p, typeof(MyDBFieldAttribute));

            if (dbFieldAttribute != null && attribName.ToUpper() == dbFieldAttribute.Name.ToUpper())

            {

                propInfo= p;

                //Console.WriteLine(p.Name + " : " + (dbFieldAttribute != null && dbFieldAttribute.Name != null ? dbFieldAttribute.Name : "****"));

                return true;

            }



            if (p.PropertyType.IsClass && !p.PropertyType.IsValueType && !p.PropertyType.IsPrimitive

            && p.PropertyType.FullName != "System.String")
                if (propInfo != null) return true;
                else DoRecursiveGetProperty(p.PropertyType, attribName, out propInfo);



        }



        return false;



    }

    public class Planet

    {

        public string PlanetId { get; set; }

        public string Name { get; set; }

        public string Description { get; set; }

        public Product Product { get; set; }



        [MyDBField(Name="PubDate")]

        public string Publishdate { get; set; }



    }



    public class Product

    {

        public string ProductId { get; set; }

        public List<Neighbor> Neighbors { get; set; }



    }



    public class Neighbor

    {

        [MyDBField(Name = "NeighborRing")]

        public string RingColor { get; set; }

        public int Moons { get; set; }

    }



    public class MyDBFieldAttribute : System.Attribute

    {

        public string Name { get; set; }

    }
1
Your point 2 seems unclear - the attribute "NeighborRing" applies to every instance of Neighbor so every member of the Neighbors list has that attribute - which value do you want? IOTW, you already know Planet.Product.Neighbors[].RingColor is the property in question - why do you need to use Reflection?NetMage
While I agree with @NetMage, if you just want a property's value while holding a PropertyInfo instance and the object you want the property of, you can just use PropertyInfo.GetValue: object propValue = propInfo.GetValue(planet); under your line with 2..Sean Skelly
Do you want to iterate through the properties of a class, and then the properties of each property value, and so forth, and then return just a single PropertyInfo? That's a little confusing. What if that hierarchy contains more than one property with the attribute? (Sorry, I changed my comment after you replied to it. I had initially pointed out that there were no nested classes.)Scott Hannen
@ScottHannen By nested classes, it is meant classes that contain members that are instances of other classes, e.g. Product is the type of a member of Planet, and hence nested.NetMage
@SeanSkelly Perhaps the issue is there is no object reference since the PropertyInfo references a nested member of the original class and following that trail isn't obvious?NetMage

1 Answers

0
votes

In order to get the value for a nested member that might be in a collection, you need to iterate the collections, and track the current object.

Assuming you don't need the resulting PropInfo for other reasons, just try to get the value:

private static bool TryRecursiveGetValueWithMyDBFieldName(object startObject, string attribName, out object propValue) {
    PropertyInfo[] pi = startObject.GetType().GetProperties();

    foreach (PropertyInfo p in pi) {
        var dbFieldAttribute = (MyDBFieldAttribute)Attribute.GetCustomAttribute(p, typeof(MyDBFieldAttribute));
        if (dbFieldAttribute != null && dbFieldAttribute.Name.Equals(attribName, StringComparison.CurrentCultureIgnoreCase)) {
            //Console.WriteLine(p.Name + " : " + (dbFieldAttribute != null && dbFieldAttribute.Name != null ? dbFieldAttribute.Name : "****"));
            propValue = p.GetValue(startObject);
            return true;
        }

        if (p.PropertyType.IsClass && !p.PropertyType.IsValueType && !p.PropertyType.IsPrimitive &&
            !p.PropertyType.FullName.StartsWith("System.")) {
            var tryObject = p.GetValue(startObject);
            if (tryObject != null && TryRecursiveGetValueWithMyDBFieldName(tryObject, attribName, out propValue))
                return true;
        }

        if (p.PropertyType.IsClass && p.GetValue(startObject) is IEnumerable ip) {
            foreach (var obj in ip) {
                if (obj != null && TryRecursiveGetValueWithMyDBFieldName(obj, attribName, out propValue))
                    return true;
            }
        }
    }

    propValue = default;
    return false;
}

To use it, call with the initial object:

var foundAttrib = TryRecursiveGetValueWithMyDBFieldName(planet, "NeighborRing", out var propValue);

NOTE: This will return the value of the first object with a matching attribute, as e.g. every member of the List<Neighbor> member will have the MyDBField attribute with the Name property of NeighborRing.