4

its me again! :)

I've been tasked with creating a system to auditing out internal objects, my first iteration was no flexible and very slow so I'm hoping to rewrite it and really make it work how it should.

Performance for this needs to be as perfect as possible, the auditing code is probably going to be run on every object in our system when they are saved..

This code below is what I've done so far - I've profiled it using the visual studio tools and I think I've remove quite a few possible performance hits along the way..

What I really want from you guys is to review this and suggest any possible improvements, Its also worth nothing that CreateObjectFromHistory method doesn't need to be as performant as the rest, its barely going to ever get called.

Also - they keyvaluepair saving is out of my hands.

Any help would be brilliant..

Cheers :)

//Wall o-code coming up..

    public static void AuditObject(ITraceable obj)
    {
        if (obj == null)
            return;

        IEnumerable<PropertyInfo> properties = GetPropertyInfo(obj);

        List<SerializeableKeyValuePair<string, object>> kvpList =
            new List<SerializeableKeyValuePair<string, object>>();

        foreach (PropertyInfo property in properties)
        {
            SerializeableKeyValuePair<string, object> thisValue = new SerializeableKeyValuePair<string, object>();

            thisValue.Key = property.Name;

            thisValue.Value = GetPropertyValue(obj, property);

            if (thisValue.Value != null)
                kvpList.Add(thisValue);
        }

        TestObject o = CreateObjectFromHistory<TestObject>(kvpList);
    }

    public static T CreateObjectFromHistory<T>(List<SerializeableKeyValuePair<string, object>> history)
        where T : class, ITraceable
    {
        T historicalObject = Activator.CreateInstance<T>();

        Dictionary<string, PropertyInfo> propertys = GetPropertysAsDictionary(historicalObject);

        foreach (SerializeableKeyValuePair<string, object> kvp in history)
        {
            if (!propertys.ContainsKey(kvp.Key))
                continue;

            PropertyInfo prop = propertys[kvp.Key];

            if (prop == null)
                continue;

            var value = CoerceValue(prop.PropertyType, kvp.Value);

            prop.SetValue(historicalObject, value, null);

        }

        return historicalObject;

    }

    private static object CoerceValue(Type type, object value)
    {
        if (type == typeof(string))
            return value as string;

        return null;
    }

    private static object GetPropertyValue(ITraceable obj, PropertyInfo property)
    {
        if (property.PropertyType == typeof(string))
            return GetProperyValueByType<string>(property.GetValue(obj, null));
        else if (property.PropertyType == typeof(DateTime))
            return GetProperyValueByType<DateTime>(property.GetValue(obj, null));

        return null;
    }

    private static IEnumerable<PropertyInfo> GetPropertyInfo(ITraceable obj)
    {
        List<PropertyInfo> properties;

        Type objType = obj.GetType();

        if (PropertyDictionary.TryGetValue(objType, out properties) == false)
        {
            properties = obj.GetType().GetProperties(BindingFlags.Public |
                                                     BindingFlags.Instance).ToList();

            properties.RemoveAll(p => IgnoreProperty(p.GetCustomAttributes(typeof(DoNoTraceAttribute), false)));

            PropertyDictionary.Add(objType, properties);
        }

        return properties;
    }

    private static Dictionary<string, PropertyInfo> GetPropertysAsDictionary(ITraceable obj)
    {
        return GetPropertyInfo(obj).ToDictionary(pro => pro.Name);
    }

    private static object GetProperyValueByType<T>(object value)
    {
        T actualType = (T)value;

        if (actualType.Equals(default(T)))
            return default(T);

        //this will need further implementation

        return (T)value;
    }

    private static bool IgnoreProperty(IEnumerable<object> p)
    {
        return p.AsParallel().OfType<DoNoTraceAttribute>().Any();
    }

Updated Code;

   private static IEnumerable<PropertyInfo> GetPropertyInfo(ITraceable obj)
    {
        List<PropertyInfo> properties;

        Type objType = obj.GetType();

        if (PropertyDictionary.TryGetValue(objType, out properties) == false)
        {
            properties = obj.GetType().GetProperties(BindingFlags.Public |
                                                     BindingFlags.Instance).ToList();

            properties.RemoveAll(p => Attribute.IsDefined(p, typeof(DoNoTraceAttribute)));

            PropertyDictionary.Add(objType, properties);
        }

        return properties;
    }

Do this look better ?

Steoates
  • 3,058
  • 5
  • 27
  • 43

1 Answers1

3

If you use PropertyInfo.GetValue() at runtime, the performance will always be slow. To get good performance (especially for looking at lots of objects) you will need to look at something like ILGenerator or Expression - or you could just use something like FastMember and access the values via prop.Name. I really do not think IgnoreProperty is implemented well - you should just look at Attribute.IsDefined here; no need for LINQ, no need for Parallel, and no need to materialize the attributes.

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • could you please provide a sample using FastMember? Also - see updates. – Steoates Feb 02 '12 at 10:24
  • @Stephen - fastmember - this do? http://stackoverflow.com/questions/771524/how-slow-is-reflection/771533#771533 Note that there is also `ObjectAccessor`, if you aren't always dealing with blocks of the same type. It worries about caching per type etc internally, so you don't have to. That `Attribute.IsDefined` usage looks much better. – Marc Gravell Feb 02 '12 at 10:31
  • That looks great - im going to try it now.. How much quicker is your Fastmember ususaly than they way im currently doing it? – Steoates Feb 02 '12 at 10:37
  • @Stephen numbers are here: http://marcgravell.blogspot.com/2012/01/playing-with-your-member.html (see "Mary Mary quite contrary...") – Marc Gravell Feb 02 '12 at 11:01