views:

56

answers:

1

I am trying to create a List of properties for objects. I can create a list for basic objects just fine, but if I have a situation where type object A contains a property of type object B which contains a property of type object A.... well.. then I get infinite recursion. What I've done is added a static property to all of my objects called "RecursivePropertyList" which is a List<string> of each property that has recursion.

So, for example: I have a Person class with a property called Vendor of type Vendor. The Vendor class has a property called People which is of type List<Person>.

In the Person class I set the value of RecursivePropertyList to myList.Add("Vendor.People"). In the Vendor class, I set the value of RecursivePropertyList to myList.Add("People.Vendor").

Then in my function that creates the property list, I check to see if the current objectname.propertyName is in the RecursivePropertyList and if so, I don't add it to my list of properties and don't drill down into it to get its properties.

This works great in the situation described above.

However, it begins to fail when I have the following situation: An Application class that has a property of type List<ApplicationFunction_Application>. An ApplicationFunction class that has a property of type List<ApplicationFunction_Application>. An ApplicationFunction_Application class that has two properties, one of type Application, and another of type ApplicationFunction. The purpose of the ApplicationFunction_Application class is to define a many-to-many relationship between Application and ApplicationFunction.

In the RecursivePropertyList of Application I put:

myList.Add("ApplicationFunctions.Application");

In the RecursivePropertyList of ApplicationFunction I put:

myList.Add("Applications.ApplicationFunction");

In the RecursivePropertyList of ApplicationFunction_Application I put:

myList.Add("ApplicationFunction.Applications");

myList.Add("Application.ApplicationFunctions");

But, my script just keeps looping forever and never stops.

Here is the code used by the function:

First, the function that is called that begins the whole thing:

public static EquatableList<PropertyState> FillPropertyStateList(System.Type myObjectType, ref EquatableList<PropertyState> myPropertyStateList)
        {
            List<string> myRecursivePropertyList = FillDefaultRecursivePropertyList(myObjectType);
            return FillPropertyStateList(myObjectType, ref myPropertyStateList, string.Empty, string.Empty, myRecursivePropertyList);
        }

Then the function that does the meat of the work and that calls itself recursively to generate the property list for all child objects.

public static EquatableList<PropertyState> FillPropertyStateList(System.Type myObjectType, ref EquatableList<PropertyState> myPropertyStateList, string myParentPrefix, string myObjectName, List<string> myRecursivePropertyList)
        {
            if (myPropertyStateList == null)
            {
                myPropertyStateList = new EquatableList<PropertyState>();
            }
            if (string.IsNullOrEmpty(myParentPrefix))
            {
                myParentPrefix = string.Empty;
            }
            else if (!myParentPrefix.EndsWith("."))
            {
                myParentPrefix = myParentPrefix + ".";
            }
            if (string.IsNullOrEmpty(myObjectName))
            {
                myObjectName = string.Empty;
            }
            else
            {
                myObjectName = myObjectName + ".";
            }

            foreach (System.Reflection.PropertyInfo info in myObjectType.GetProperties())
            {
                if (info.PropertyType.BaseType == typeof(BOBase))
                {
                    if (!myRecursivePropertyList.Exists(delegate(string x) { return x.Equals(myObjectName + info.Name); }))
                    {
                        myPropertyStateList.Add(new PropertyState(myParentPrefix + myObjectName + info.Name, true, PropertyType.BOBase));
                        List<string> myChildRecursivePropertyList = FillDefaultRecursivePropertyList(info.PropertyType);
                        myChildRecursivePropertyList.AddRange(myRecursivePropertyList.FindAll(delegate(string x) { return Regex.IsMatch(x, info.Name + ".*"); }));
                        FillPropertyStateList(info.PropertyType, ref myPropertyStateList, myParentPrefix + myObjectName, info.Name, myChildRecursivePropertyList);
                    }
                }
                else if (info.PropertyType.IsGenericType
                    && (info.PropertyType.BaseType.GetGenericTypeDefinition() == typeof(List<>) || info.PropertyType.BaseType.GetGenericTypeDefinition() == typeof(Library.EquatableList<>)))
                {
                    if (!myRecursivePropertyList.Exists(delegate(string x) { return x.Equals(myObjectName + info.Name); }))
                    {
                        myPropertyStateList.Add(new PropertyState(myParentPrefix + myObjectName + info.Name, true, PropertyType.BOBaseCollection));
                        List<string> myChildRecursivePropertyList = FillDefaultRecursivePropertyList(info.PropertyType.BaseType.GetGenericArguments()[0]);
                        myChildRecursivePropertyList.AddRange(myRecursivePropertyList.FindAll(delegate(string x) { return Regex.IsMatch(x, info.Name + ".*"); }));
                        FillPropertyStateList(info.PropertyType.BaseType.GetGenericArguments()[0], ref myPropertyStateList, myParentPrefix + myObjectName, info.Name, myChildRecursivePropertyList);
                    }
                }
                else
                {
                    myPropertyStateList.Add(new PropertyState(myParentPrefix + myObjectName + info.Name, true, PropertyType.Standard));
                }

            }
            return myPropertyStateList;
        }

The helper function that gets the default recursive properties list:

private static List<string> FillDefaultRecursivePropertyList(System.Type myObjectType)
    {
        List<string> myRecursivePropertyList = new List<string>();
        if (myObjectType.BaseType == typeof(BOBase))
        {
            System.Reflection.PropertyInfo pi = myObjectType.GetProperty("RecursivePropertyList", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static);
            if (pi != null)
            {
                myRecursivePropertyList = (List<string>)pi.GetValue(null, null);
            }
        }
        return myRecursivePropertyList;
    }

Any ideas on what I'm doing wrong?

A: 

I haven't dug into the code, yet, but have you considered using an Attribute to denote which properties are recursive? That might save you the headache of having to maintain the separate static list of properties. It seems like the list approach could be prone to potentially difficult synchrnonization issues.

ETA: Sample code:

If I'm understanding this correctly, something like this would probbaly work for a property-level attribute. It includes a helper method to retrieve the properties marked as Recursive from the given type, which might help when building the tree.

[AttributeUsage(AttributeTargets.Property)]
public class RecursivePropertyAttribute
    : Attribute
{
    private static Dictionary<Type, List<PropertyInfo>> _Cache = new Dictionary<Type, List<PropertyInfo>>();

    public IEnumerable<PropertyInfo> GetRecursiveProperties(Type t)
    {
        // Check the cache for the type
        if (!_Cache.ContainsKey(t))
        { 
            // Create the entry
            _Cache.Add(t, new List<PropertyInfo>());

            // Add properties that have the attribute
            foreach (PropertyInfo p in t.GetProperties())
            {
                if (p.IsDefined(typeof(RecursivePropertyAttribute), true))
                    _Cache[t].Add(p);
            }
        }

        return _Cache[t];
    }
}
Steven
The problem with this is that a property isn't determined to be recursive within it's own class. For instance, the Vendor property of Person is not a recursive property in itself, but the Vendor property of the Person objects in the Vendor object's People property IS recursive... does that makes sense? So I need to be able to set the recursive properties of "child" ckasses from the parent instantiating class.
Amanda Myer
After reading your comment to the post above, I think I'm understanding your problem a bit better. If the potential "recursive" properties are fairly limited, would having overloaded ctors make sense? In the example you cited, would having something like Vendor() vs. Vender(Person) allow you to either pre-populate the property value with the supplied argument, or leave it null to be lazy-loaded at the time it's needed?
Steven
Unless I'm misunderstanding your scenario (which, I most likely am), wouldn't the fact that the recursiveness of the property is dependent on the chain of events conflict with the static nature of the list? Could that be the problem? Or is the list just a definition of potential recursive properties?
Steven