Search code examples
c#reflectionauditaudit-logging

"Deep compare" dictionaries


I'm doing "deep comparison" of data structures for audit logging (who changed what members, when). I'm using reflection for this, recursing into the structures and comparing them. I've hit a problem though with those containing dictionaries.

I can detect that a member is a dictionary by typeof(IDictionary).IsAssignableFrom(memberType). My plan is then to collect the keys which are present in both objects, and continue recursion on those. However, IDictionary.Keys is an ICollection, which is not extended by LINQ. Not knowing the type of the keys, how could I achieve this?

Maybe this method is suboptimal (I'm not that experienced in the Generics/Reflection combo), should I do this in another way?


Solution

  • Found a solution myself. Here ChangedProperties is a type containing property name, and values pre/post change.

    if (typeof(IDictionary).IsAssignableFrom(propertyType))
    {
        Type keyType = propertyType.GetGenericArguments()[0],
             valueType = propertyType.GetGenericArguments()[1];
        Type hashsetType = typeof(HashSet<>).MakeGenericType(keyType);
        var hashsetCtor = hashsetType.GetConstructor(new[] { typeof(IEnumerable<>).MakeGenericType(keyType) });
    
        dynamic aDict = a;
        dynamic bDict = b;
        dynamic aKeys = hashsetCtor.Invoke(new object[] { aDict.Keys });
        dynamic bKeys = hashsetCtor.Invoke(new object[] { bDict.Keys });
    
        List<ChangedProperty> changes = new List<ChangedProperty>();
        foreach (var key in Enumerable.Intersect(aKeys, bKeys))
        {
                // Present in both dictionaries. Recurse further
        }
        foreach (var key in Enumerable.Except(aKeys, bKeys))
        {
              // Key was removed
        }
        foreach (var key in Enumerable.Except(bKeys, aKeys))
        {
              // Key was added
        }
    
        return changes;
    }