BuilderAware attribute

Topics: ObjectBuilder
Mar 24, 2008 at 11:13 PM
Edited Mar 24, 2008 at 11:18 PM
I read the a discussion regarding performance an ObjectBuilder. That got me thinking along the lines of a builder aware attribute.

Often what I do when I implement a new strategy is that I've created an attribute that I wish should have semantics.

The process of the strategy is usually: iterate over all member infos, locate my attribute, perform action (depending on buildup or teardown).

I aimed to encapsulate this process in a builder aware attribute strategy. The benefit is also that since this strategy knows its working with builder aware attributes it scans and caches those that it finds. So the next time the same type is encountered it just loops over an array calling the buildup and teardown methods on the attribute. This should give an increased performance over having seperate strategies for each attribute.

I don't think this is a one size fits all solution to all strategies and this is only a rough beginning. However I thought it turned out rather nicely so I though I shared this with you.

My simplistic test case.


   /// <summary>
   /// MyBuilderAwareAttribute implements IBuilderAwareAttribute, it then gets buildup and teardown
   /// </summary>
   [AttributeUsage(AttributeTargets.Property)]
   public class MyBuilderAwareAttribute : Attribute, IBuilderAwareAttribute
   {
      public object BuildUp(IBuilderContext context, object buildKey, object existing, object attribute, MemberInfo memberInfo)
      {
         // Inject "Test"
         var l_pi = (PropertyInfo)memberInfo;
 
         l_pi.SetValue(existing, "Test", null);
 
         return existing;
      }
 
      public object TearDown(IBuilderContext context, object item, object attribute, MemberInfo memberInfo)
      {
         return item;
      }
   }
 
   public class MyClass
   {
      /// <summary>
      /// Inject "Test"
      /// </summary>
      [MyBuilderAware]
      public string Name
      {
         get;
         set;
      }
   }
 
   public static class Assert2
   {
      public static void True(
         Expression<Func<bool>> assert)
      {
         if (!assert.Compile()())
         {
            Assert.True(
               false,
               assert.Body.ToString());
         }
      }
   }
 
   public class BuilderAwareAttributeStrategyTest
   {
      [Fact]
      public void BuildCallsClassWithInterface()
      {
         var strategy = new BuilderAwareAttibuteStrategy();
         var context = new MockBuilderContext();
 
         context.Strategies.Add(strategy);
 
         var l_object = new MyClass();
 
         context.HeadOfChain.BuildUp(context, typeof(MyClass), l_object);
 
         // Validate property value indeed is "Test"
         Assert2.True(() => l_object.Name == "Test");
      }
 
   } 
The interface

   public interface IBuilderAwareAttribute
   {
      object BuildUp(
         IBuilderContext context, 
         object buildKey, 
         object existing,
         object attribute,
         MemberInfo memberInfo);
 
      object TearDown(
         IBuilderContext context, 
         object item,
         object attribute,
         MemberInfo memberInfo);
      }

The strategy

   public class BuilderAwareAttibuteStrategy : BuilderStrategy
   {
      struct CachedAttributeData
      {
         public IBuilderAwareAttribute Attribute;
         public MemberInfo MemberInfo;
      }
 
      readonly Dictionary<
         Type,
         CachedAttributeData[]> m_cache = new Dictionary<
            Type,
            CachedAttributeData[]>();
 
      static CachedAttributeData[] BuildCacheElement(Type t)
      {
         var l_result = new List<CachedAttributeData>();
 
         var l_member_infos = t.GetMembers(
            BindingFlags.Instance
            |  BindingFlags.NonPublic
            |  BindingFlags.Public
            );
 
         for (var l_iter = 0; l_iter < l_member_infos.Length; ++l_iter)
         {
            var l_member_info = l_member_infos[l_iter];
            var l_attributes = l_member_info.GetCustomAttributes(true);
 
            for (var l_ita = 0; l_ita < l_attributes.Length; ++l_ita)
            {
               var l_attribute = l_attributes[l_ita] as IBuilderAwareAttribute;
 
               if (l_attribute != null)
               {
                  l_result.Add(new CachedAttributeData
                     {
                        Attribute = l_attribute,
                        MemberInfo = l_member_info,
                     });
               }
            }
         }
 
         return l_result.ToArray();
      }
 
      CachedAttributeData[] Lookup(Type t)
      {
         lock (m_cache)
         {
            CachedAttributeData[] l_result;
            if (m_cache.TryGetValue(
               t,
               out l_result))
            {
               return l_result;
            };
         }
 
         var l_new = BuildCacheElement(t);
 
         lock (m_cache)
         {
            // Potentially overwrites a duplicate value but it 
            // should be identical due to the immutability of Type
            m_cache[t] = l_new;
         }
 
         return l_new;
      }
 
      public override object BuildUp(IBuilderContext context, object buildKey, object existing)
      {
         var l_cached = Lookup(existing.GetType());
 
         for (var l_iter = 0; l_iter < l_cached.Length; ++l_iter)
         {
            var l_item = l_cached[l_iter];
 
            l_item.Attribute.BuildUp(
               context,
               buildKey,
               existing,
               l_item.Attribute,
               l_item.MemberInfo);
         }
 
         return base.BuildUp(context, buildKey, existing);
      }
 
      public override object TearDown(IBuilderContext context, object item)
      {
         var l_cached = Lookup(item.GetType());
 
         for (int l_iter = 0; l_iter < l_cached.Length; ++l_iter)
         {
            var l_item = l_cached[l_iter];
 
            l_item.Attribute.TearDown(
               context,
               item,
               l_item.Attribute,
               l_item.MemberInfo);
         }
 
         return base.TearDown(context, item);
      }
   }
Comments?