1

I'm creating a function to loop over an object and its childs.

But I'm having some issue when i try to map from the original object to the new object, here is the code:

public static bool MatchObjectField<T>(this T obj, string objectRoute, string value)
{

    try
    {
        var objectroutelist = objectRoute.Split('.');
        var objroute = objectroutelist.First();

        var childproperty = typeof(T).GetProperty(objroute);

        if (objectroutelist.Count() == 1)
        {
            if (childproperty.GetValue(obj).ToString().Trim() == value)
            {
                return true;
            }
            return false;
        }
        else
        {
            var instance = Activator.CreateInstance(childproperty.PropertyType);
            //childproperty.SetValue(obj, instance, null);
            //childproperty.SetValue(instance, obj, null);

            instance.MapValues(childproperty);

            instance.MatchObjectField(string.Join(".", objectroutelist.Skip(1)), value);
        }

    }
    catch (Exception e)
    {

        return false;
    }
    return false;
}

And here the class where I do the map and contains the issue.

public static void MapValues<T>(this T destination, PropertyInfo orgproperty)
{


    var orgvalues = orgproperty.GetPropertiesWithValues();

    var instance = Activator.CreateInstance(typeof(T));
    foreach (var property in (typeof(T)).GetProperties())
    {
        try
        {
            var value = orgvalues.FirstOrDefault(a => a.Key == property.Name);
            property.SetValue(instance, value);
        }
        catch (Exception)
        {
            property.SetValue(instance, null);
        }
    }
    destination = (T)(object)instance;
}

The idea of the function is call with objectName.MatchObjectField("parent.child.child.child","MyName")

When I try to compare a field in the parent like objectName.MatchObjectField("Country","Ireland") it works perfectly

But when I try to create the child structure doing a call like objectName.MatchObjectField("Country.Address.City","Dublin") it breaks when I try to map to the new object.

What I noticed is that the property destination into the method MapValues<T> is mapped as Country.Address with all the properties as null which is correct. But (typeof(T)).GetProperties() doesn't return anything. Also i noticed that Activator.CreateInstance(typeof(T)) retunrs type {object} instead of return Country.Address that may be the reason why is not working.

Any idea how to fix this?

EDIT: add get properties with values-> it retuns a Dictionary<string, object>

public static Dictionary<string, object> GetPropertiesWithValues(this Object obj, bool includeValueTypes = true)
{
    return InternalGetProperties(obj, false, includeValueTypes);
}

private static Dictionary<string, object> InternalGetProperties(Object obj, bool withDefaultValue, bool includeValueTypes = true)
{
    Dictionary<string, object> d = new Dictionary<string, object>();
    var res = GetPropertiesR(obj, d, "", withDefaultValue, includeValueTypes);
    return res;
}
private static Dictionary<string, object> GetPropertiesR(Object obj, Dictionary<string, object> d, string parent, bool searchingForDefaults, bool includeValueTypes)
{
    if (obj == null)
        return d;


    var pis = @obj.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public);
    if (pis == null || pis.Length == 0)
        throw new InvalidOperationException("This object doens't have inner properties");

    Func<string, string> formatProperty = (property) => string.Concat(parent, parent == "" ? "" : ".", property);

    foreach (var pi in pis)
    {
        object data = pi.GetValue(@obj, null);

        // check if is value type

        if (pi.PropertyType.IsValueType)
        {
            // data is never null

            if (!includeValueTypes)
                continue;

            Type nullableType = Nullable.GetUnderlyingType(pi.PropertyType);
            object defaultValue = nullableType != null ? GetDefault(nullableType) : GetDefault(data.GetType());


            if (!searchingForDefaults)
            {
                // check default values.
                if (data != null && data.ToString() != defaultValue.ToString())
                    d.Add(formatProperty(pi.Name), data);
            }
            else
            {
                // check default values
                if ((nullableType != null && data == null) || data.ToString() == defaultValue.ToString())
                    d.Add(formatProperty(pi.Name), data);
            }
        }
        else
        {
            //
            // reference types

            if (!searchingForDefaults)
            {
                if (data == default(object))
                    continue;

                if (IsThisPropertyPartofSystemNamespace(pi))
                {
                    // transform list into a string with values.
                    IEnumerable enumeration = data as IList;

                    if (enumeration != null)
                    {
                        StringBuilder sb = new StringBuilder();
                        foreach (var i in enumeration)
                            sb.Append(string.Concat(i.ToString(), ", "));

                        if (sb.Length >= 2)
                            sb.Remove(sb.Length - 2, 2);

                        data = sb.ToString();
                    }

                    d.Add(formatProperty(pi.Name), data);
                }

                else
                {

                    //
                    // user complex type defined
                    string ctxParent = string.Concat(parent, parent == "" ? "" : ".", pi.Name);
                    GetPropertiesR(data, d, ctxParent, searchingForDefaults, includeValueTypes);
                }
            }
            else
            {
                if (data != default(object))
                    continue;

                d.Add(formatProperty(pi.Name), data);
            }
        }

    }

    return d;
}

private static bool IsThisPropertyPartofSystemNamespace(PropertyInfo pi)
{
    var systemNames = new HashSet<string>
                    {
                        "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken={TOKENKEY}",
                        "System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken={TOKENKEY}"
                    };

    var isSystemType = systemNames.Contains(pi.PropertyType.Assembly.FullName);
    return isSystemType;
}

private static object GetDefault(Type type)
{
    if (type.IsValueType)
    {
        return Activator.CreateInstance(type);
    }

    return null;
}
5
  • Where is GetPropertiesWithValues defined? Commented Jun 7, 2018 at 11:08
  • Any reason why you're not just using AutoMapper? Commented Jun 7, 2018 at 11:14
  • @EvdzhanMustafa I added the functions, but basically is returning a Dictionary<string, object> Commented Jun 7, 2018 at 11:19
  • @LasseVågsætherKarlsen It is not installed in the app, and I only used a couple of times, but honestly I was not expecting this error when i first think about the method. Do you think that automapper could fix this issue? Commented Jun 7, 2018 at 11:21
  • From my understanding you want to match a specific property value in a specific property structure, am i right? Then why do you create an instance on each recursion step and doing all sorts of wild things instead of recursively searching for the specific property and then doing the value match? Commented Jun 7, 2018 at 11:23

2 Answers 2

1

TL;DR: Take a step back. Describe the responsibility of each of the methods separately, and write tests for them that way. Start with the "lowest level" method and work your way up. That will make it a lot easier to see what's going wrong.


There are multiple problems here. First look at these two lines of code:

var instance = Activator.CreateInstance(childproperty.PropertyType);
instance.MapValues(childproperty);

The return type of Activator.CreateInstance is object, so this is effectively:

var instance = Activator.CreateInstance(childproperty.PropertyType);
instance.MapValues<object>(childproperty);

That's not what you want - you want to use childproperty.PropertyType as the type argument to MapValues. You don't know that at compile-time, so it's not a good fit as a type parameter.

But beyond that, your MapValues method has a bigger problem: it basically ignores its parameter. The only time it uses destination is in the last line, when it assigns a new value to it:

destination = (T)(object)instance;

That parameter is a value parameter, so assigning it at the end of the method is pointless.

You should decide what the purpose of MapValues is:

  • Is it to create an instance and populate it, then return it?
  • Is it to accept an existing instance and populate that?

Either is simple enough to implement, but at the moment you're half way between the two. Also note that you're only passing in a single PropertyInfo - consider how you'd expect to assign multiple properties with that.

Finally, there's the matter of where the values come from. You're currently calling GetPropertiesWithValues on the PropertyInfo - that's not going to do what I think you expect it to. You need to provide the source object itself, otherwise there's nowhere to get values from.

Sign up to request clarification or add additional context in comments.

1 Comment

thanks for the tips, quite clear everything, I was over complicating everything; i saw this stackoverflow.com/questions/2535287/… and its enough to what i want. Thanks.
0

I found what i need to do in this post: Getting Nested Object Property Value Using Reflection getting the nested object is an easy way of what i was pretending to do.

public static object GetPropertyValue(object src, string propName)
{
    if (src == null) throw new ArgumentException("Value cannot be null.", "src");
    if (propName == null) throw new ArgumentException("Value cannot be null.", "propName");

    if(propName.Contains("."))//complex type nested
    {
        var temp = propName.Split(new char[] { '.' }, 2);
        return GetPropertyValue(GetPropertyValue(src, temp[0]), temp[1]);
    }
    else
    {
        var prop = src.GetType().GetProperty(propName);
        return prop != null ? prop.GetValue(src, null) : null;
    }
}

public static bool MatchObjectField<T>(this T obj, string objectRoute, string value)
{

    try
    {
        var propvalue = GetPropertyValue(obj, objectRoute);

        return ( propvalue.ToString().Trim() == value.Trim());
    }
    catch (Exception) {
       throw;   
    }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.