Commit 558c110

mokhan <mokhan@ce5e1baf-6525-42e4-a1b2-857ea38da20a>
2009-03-08 01:57:20
implemented proxy builder and identity map.
git-svn-id: https://svn.xp-dev.com/svn/mokhan-mo.money@57 ce5e1baf-6525-42e4-a1b2-857ea38da20a
1 parent b97df78
trunk/product/MyMoney/Infrastructure/caching/IdentityMapSpecs.cs
@@ -0,0 +1,91 @@
+using jpboodhoo.bdd.contexts;
+using MoMoney.Testing.MetaData;
+using MoMoney.Testing.spechelpers.contexts;
+using MoMoney.Testing.spechelpers.core;
+
+namespace MoMoney.Infrastructure.caching
+{
+    [Concern(typeof (IdentityMap<,>))]
+    public class behaves_like_identity_map : concerns_for<IIdentityMap<int, string>, IdentityMap<int, string>>
+    {
+        public override IIdentityMap<int, string> create_sut()
+        {
+            return new IdentityMap<int, string>();
+        }
+    }
+
+    public class when_getting_an_item_from_the_identity_map_for_an_item_that_has_been_added : behaves_like_identity_map
+    {
+        it should_return_the_item_that_was_added_for_the_given_key = () => result.should_be_equal_to("1");
+
+        because b = () =>
+                        {
+                            sut.Add(1, "1");
+                            result = sut.ItemThatBelongsTo(1);
+                        };
+
+        static string result;
+    }
+
+    public class when_getting_an_item_from_the_identity_map_that_has_not_been_added : behaves_like_identity_map
+    {
+        it should_return_the_default_value_for_that_type = () => result.should_be_equal_to(null);
+
+        because b = () => { result = sut.ItemThatBelongsTo(2); };
+
+        static string result;
+    }
+
+    public class when_checking_if_an_item_has_been_added_to_the_identity_map_that_has_been_added :
+        behaves_like_identity_map
+    {
+        it should_return_true = () => result.should_be_true();
+
+        because b = () =>
+                        {
+                            sut.Add(10, "10");
+                            result = sut.ContainsAnItemFor(10);
+                        };
+
+        static bool result;
+    }
+
+    public class when_checking_if_an_item_has_been_added_to_the_identity_map_that_has_not_been_added :
+        behaves_like_identity_map
+    {
+        it should_return_false = () => result.should_be_false();
+
+        because b = () => { result = sut.ContainsAnItemFor(9); };
+
+        static bool result;
+    }
+
+    public class when_updating_the_value_for_a_key_that_has_already_been_added_to_the_identity_map :
+        behaves_like_identity_map
+    {
+        it should_replace_the_old_item_with_the_new_one = () => result.should_be_equal_to("7");
+
+        because b = () =>
+                        {
+                            sut.Add(6, "6");
+                            sut.UpdateTheItemFor(6, "7");
+                            result = sut.ItemThatBelongsTo(6);
+                        };
+
+        static string result;
+    }
+
+    public class when_updating_the_value_for_a_key_that_has_not_been_added_to_the_identity_map :
+        behaves_like_identity_map
+    {
+        it should_add_the_new_item = () => result.should_be_equal_to("3");
+
+        because b = () =>
+                        {
+                            sut.UpdateTheItemFor(3, "3");
+                            result = sut.ItemThatBelongsTo(3);
+                        };
+
+        static string result;
+    }
+}
\ No newline at end of file
trunk/product/MyMoney/Infrastructure/caching/IIdentityMap.cs
@@ -0,0 +1,53 @@
+using System.Collections.Generic;
+
+namespace MoMoney.Infrastructure.caching
+{
+    public interface IIdentityMap<TKey, TValue>
+    {
+        void Add(TKey key, TValue value);
+        void UpdateTheItemFor(TKey key, TValue newValue);
+        bool ContainsAnItemFor(TKey key);
+        TValue ItemThatBelongsTo(TKey key);
+    }
+
+    public class IdentityMap<TKey, TValue> : IIdentityMap<TKey, TValue>
+    {
+        readonly IDictionary<TKey, TValue> itemsInMap;
+
+        public IdentityMap() : this(new Dictionary<TKey, TValue>())
+        {
+        }
+
+        public IdentityMap(IDictionary<TKey, TValue> itemsInMap)
+        {
+            this.itemsInMap = itemsInMap;
+        }
+
+        public void Add(TKey key, TValue value)
+        {
+            itemsInMap.Add(key, value);
+        }
+
+        public void UpdateTheItemFor(TKey key, TValue newValue)
+        {
+            if (ContainsAnItemFor(key))
+            {
+                itemsInMap[key] = newValue;
+            }
+            else
+            {
+                Add(key, newValue);
+            }
+        }
+
+        public bool ContainsAnItemFor(TKey key)
+        {
+            return itemsInMap.ContainsKey(key);
+        }
+
+        public TValue ItemThatBelongsTo(TKey key)
+        {
+            return ContainsAnItemFor(key) ? itemsInMap[key] : default(TValue);
+        }
+    }
+}
\ No newline at end of file
trunk/product/MyMoney/Infrastructure/Container/Windsor/WindsorContainerFactory.cs
@@ -1,3 +1,4 @@
+using System;
 using Castle.MicroKernel.Registration;
 using Castle.Windsor;
 using MoMoney.Infrastructure.Container.Windsor.configuration;
@@ -51,11 +52,31 @@ namespace MoMoney.Infrastructure.Container.Windsor
                     .Pick()
                     .FromAssembly(GetType().Assembly)
                     .WithService
-                    .FirstInterface()
+                    .LastInterface()
+                    //.FirstInterface()
                     .Unless(criteria_to_satisfy.is_satisfied_by)
                     .Configure(x => configuration.configure(x))
                 );
             return the_container;
         }
     }
+
+    public static class e
+    {
+        public static BasedOnDescriptor LastInterface(this ServiceDescriptor descriptor)
+        {
+            return descriptor.Select(delegate(Type type, Type baseType)
+                                         {
+                                             Type first = null;
+                                             var interfaces = type.GetInterfaces();
+
+                                             if (interfaces.Length > 0)
+                                             {
+                                                 first = interfaces[0];
+                                             }
+
+                                             return (first != null) ? new Type[] {first} : null;
+                                         });
+        }
+    }
 }
\ No newline at end of file
trunk/product/MyMoney/Infrastructure/Container/Windsor/WindsorDependencyRegistry.cs
@@ -37,7 +37,7 @@ namespace MoMoney.Infrastructure.Container.Windsor
                                               implementation_type);
         }
 
-        public void register_instance_of<Interface>(Interface instanceOfTheInterface)
+        public void singleton<Interface>(Interface instanceOfTheInterface)
         {
             underlying_container.Kernel.AddComponentInstance<Interface>(instanceOfTheInterface);
         }
trunk/product/MyMoney/Infrastructure/proxies/Interceptors/IMethodCallTracker.cs
@@ -0,0 +1,11 @@
+using System.Collections.Generic;
+using Castle.Core.Interceptor;
+
+namespace MoMoney.Infrastructure.proxies.Interceptors
+{
+    public interface IMethodCallTracker<TypeToProxy> : IInterceptor
+    {
+        TypeToProxy target { get; }
+        IEnumerable<string> methods_to_intercept();
+    }
+}
\ No newline at end of file
trunk/product/MyMoney/Infrastructure/proxies/Interceptors/MethodCallTracker.cs
@@ -0,0 +1,44 @@
+using System;
+using System.Collections.Generic;
+using Castle.Core.Interceptor;
+
+namespace MoMoney.Infrastructure.proxies.Interceptors
+{
+    public class MethodCallTracker<TypeToProxy> : IMethodCallTracker<TypeToProxy>
+    {
+        readonly IList<string> the_name_of_each_method_to_intercept;
+
+        public MethodCallTracker() : this(new List<string>())
+        {
+        }
+
+        public MethodCallTracker(IList<string> the_name_of_each_method_to_intercept)
+        {
+            this.the_name_of_each_method_to_intercept = the_name_of_each_method_to_intercept;
+        }
+
+        public TypeToProxy target { get; set; }
+
+        public void Intercept(IInvocation invocation)
+        {
+            set_return_value_for(invocation);
+            if (the_name_of_each_method_to_intercept.Contains(invocation.Method.Name))
+            {
+                return;
+            }
+            the_name_of_each_method_to_intercept.Add(invocation.Method.Name);
+        }
+
+        public IEnumerable<string> methods_to_intercept()
+        {
+            return the_name_of_each_method_to_intercept;
+        }
+
+        static void set_return_value_for(IInvocation invocation)
+        {
+            var return_type = invocation.Method.ReturnType;
+            if (return_type == typeof (void)) return;
+            invocation.ReturnValue = (return_type.IsValueType ? Activator.CreateInstance(return_type) : null);
+        }
+    }
+}
\ No newline at end of file
trunk/product/MyMoney/Infrastructure/proxies/Interceptors/MethodCallTrackerSpecs.cs
@@ -0,0 +1,49 @@
+using System.Collections.Generic;
+using System.Linq;
+using Castle.Core.Interceptor;
+using jpboodhoo.bdd.contexts;
+using MoMoney.Testing.MetaData;
+using MoMoney.Testing.spechelpers.contexts;
+using MoMoney.Testing.spechelpers.core;
+
+namespace MoMoney.Infrastructure.proxies.Interceptors
+{
+    [Concern(typeof (MethodCallTracker<IAnInterface>))]
+    public class behaves_like_method_call_tracker :
+        concerns_for<IMethodCallTracker<IAnInterface>, MethodCallTracker<IAnInterface>>
+    {
+        public override IMethodCallTracker<IAnInterface> create_sut()
+        {
+            return new MethodCallTracker<IAnInterface>();
+        }
+    }
+
+    public class when_tracking_the_calls_to_intercept_on_a_type : behaves_like_method_call_tracker
+    {
+        static IInvocation invocation;
+        static IEnumerable<string> result;
+
+        context c = () =>
+                        {
+                            invocation = an<IInvocation>();
+                            invocation
+                                .is_told_to(s => s.Method)
+                                .it_will_return(typeof (IAnInterface).GetMethod("ValueReturningMethodWithAnArgument"));
+                        };
+
+        because b = () =>
+                        {
+                            sut.Intercept(invocation);
+                            result = sut.methods_to_intercept();
+                        };
+
+        it should_return_all_the_methods_that_are_specified_for_interception =
+            () => result.should_contain(typeof (IAnInterface).GetMethod("ValueReturningMethodWithAnArgument").Name);
+
+        it should_only_contain_the_methods_specified_for_interception = () => result.Count().should_be_equal_to(1);
+
+        it should_specify_the_default_return_value_for_the_method_to_intercept =
+            //() => invocation.was_told_to(i => i.ReturnValue = 0);
+            () => invocation.ReturnValue.should_be_equal_to(0);
+    }
+}
\ No newline at end of file
trunk/product/MyMoney/Infrastructure/proxies/Interceptors/RunOnBackgroundThreadInterceptor.cs
@@ -0,0 +1,25 @@
+using Castle.Core.Interceptor;
+using MoMoney.Infrastructure.Threading;
+using MoMoney.Utility.Core;
+
+namespace MoMoney.Infrastructure.proxies.Interceptors
+{
+    public class RunOnBackgroundThreadInterceptor<CommandToExecute> : IInterceptor
+        where CommandToExecute : IDisposableCommand
+    {
+        readonly IBackgroundThreadFactory thread_factory;
+
+        public RunOnBackgroundThreadInterceptor(IBackgroundThreadFactory thread_factory)
+        {
+            this.thread_factory = thread_factory;
+        }
+
+        public virtual void Intercept(IInvocation invocation)
+        {
+            using (thread_factory.create_for<CommandToExecute>())
+            {
+                invocation.Proceed();
+            }
+        }
+    }
+}
\ No newline at end of file
trunk/product/MyMoney/Infrastructure/proxies/Interceptors/RunOnBackgrounThreadInterceptorSpecs.cs
@@ -0,0 +1,46 @@
+using Castle.Core.Interceptor;
+using jpboodhoo.bdd.contexts;
+using MoMoney.Infrastructure.Threading;
+using MoMoney.Testing.MetaData;
+using MoMoney.Testing.spechelpers.contexts;
+using MoMoney.Testing.spechelpers.core;
+using MoMoney.Utility.Core;
+
+namespace MoMoney.Infrastructure.proxies.Interceptors
+{
+    [Concern(typeof (RunOnBackgroundThreadInterceptor<>))]
+    public class behaves_like_background_thread_interceptor :
+        concerns_for<IInterceptor, RunOnBackgroundThreadInterceptor<IDisposableCommand>>
+    {
+        context c = () => { thread_factory = the_dependency<IBackgroundThreadFactory>(); };
+
+        protected static IBackgroundThreadFactory thread_factory;
+    }
+
+    public class when_intercepting_a_call_to_a_method_that_takes_a_long_time_to_complete :
+        behaves_like_background_thread_interceptor
+    {
+        static IInvocation invocation;
+        static IBackgroundThread background_thread;
+
+        context c = () =>
+                        {
+                            invocation = an<IInvocation>();
+                            background_thread = an<IBackgroundThread>();
+                            thread_factory
+                                .is_told_to(f => f.create_for<IDisposableCommand>())
+                                .it_will_return(background_thread);
+                        };
+
+        because b = () => sut.Intercept(invocation);
+
+        it should_display_a_progress_bar_on_a_background_thread =
+            () => thread_factory.was_told_to(f => f.create_for<IDisposableCommand>());
+
+        it should_proceed_with_the_orginal_invocation_on_the_actual_object =
+            () => invocation.was_told_to(i => i.Proceed());
+
+        it should_hide_the_progress_bar_when_the_invocation_is_completed =
+            () => background_thread.was_told_to(b => b.Dispose());
+    }
+}
\ No newline at end of file
trunk/product/MyMoney/Infrastructure/proxies/Interceptors/SelectiveInterceptor.cs
@@ -0,0 +1,27 @@
+using System.Collections.Generic;
+using Castle.Core.Interceptor;
+
+namespace Ec.AuditTool.Infrastructure.Proxies.Interceptors
+{
+    public class SelectiveInterceptor : IInterceptor
+    {
+        private readonly IList<string> methods_to_intercept;
+        private readonly IInterceptor underlying_interceptor;
+
+        public SelectiveInterceptor(IEnumerable<string> methods_to_intercept, IInterceptor underlying_interceptor)
+        {
+            this.methods_to_intercept = new List<string>(methods_to_intercept);
+            this.underlying_interceptor = underlying_interceptor;
+        }
+
+        public void Intercept(IInvocation invocation)
+        {
+            if (methods_to_intercept.Contains(invocation.Method.Name))
+            {
+                underlying_interceptor.Intercept(invocation);
+                return;
+            }
+            invocation.Proceed();
+        }
+    }
+}
\ No newline at end of file
trunk/product/MyMoney/Infrastructure/proxies/IConstraintSelector.cs
@@ -0,0 +1,7 @@
+namespace MoMoney.Infrastructure.proxies
+{
+    public interface IConstraintSelector<TypeToPutConstraintOn>
+    {
+        TypeToPutConstraintOn InterceptOn { get; }
+    }
+}
\ No newline at end of file
trunk/product/MyMoney/Infrastructure/proxies/IInterceptorConstraint.cs
@@ -0,0 +1,30 @@
+using System.Collections.Generic;
+using MoMoney.Infrastructure.proxies.Interceptors;
+
+namespace MoMoney.Infrastructure.proxies
+{
+    public interface IInterceptorConstraint<TypeToPutConstraintOn> : IConstraintSelector<TypeToPutConstraintOn>
+    {
+        IEnumerable<string> methods_to_intercept();
+    }
+
+    public class InterceptorConstraint<TypeToPutConstraintOn> : IInterceptorConstraint<TypeToPutConstraintOn>
+    {
+        private readonly IMethodCallTracker<TypeToPutConstraintOn> call_tracker;
+
+        public InterceptorConstraint(IMethodCallTracker<TypeToPutConstraintOn> call_tracker)
+        {
+            this.call_tracker = call_tracker;
+        }
+
+        public TypeToPutConstraintOn InterceptOn
+        {
+            get { return call_tracker.target; }
+        }
+
+        public IEnumerable<string> methods_to_intercept()
+        {
+            return call_tracker.methods_to_intercept();
+        }
+    }
+}
\ No newline at end of file
trunk/product/MyMoney/Infrastructure/proxies/IInterceptorConstraintFactory.cs
@@ -0,0 +1,26 @@
+namespace MoMoney.Infrastructure.proxies
+{
+    public interface IInterceptorConstraintFactory
+    {
+        IInterceptorConstraint<Type> CreateFor<Type>();
+    }
+
+    public class InterceptorConstraintFactory : IInterceptorConstraintFactory
+    {
+        readonly IMethodCallTrackerFactory factory;
+
+        public InterceptorConstraintFactory() : this(new MethodCallTrackerFactory())
+        {
+        }
+
+        public InterceptorConstraintFactory(IMethodCallTrackerFactory factory)
+        {
+            this.factory = factory;
+        }
+
+        public IInterceptorConstraint<Type> CreateFor<Type>()
+        {
+            return new InterceptorConstraint<Type>(factory.create_for<Type>());
+        }
+    }
+}
\ No newline at end of file
trunk/product/MyMoney/Infrastructure/proxies/IMethodCallTrackerFactory.cs
@@ -0,0 +1,32 @@
+using Castle.DynamicProxy;
+using MoMoney.Infrastructure.proxies.Interceptors;
+
+namespace MoMoney.Infrastructure.proxies
+{
+    public interface IMethodCallTrackerFactory
+    {
+        IMethodCallTracker<TypeToProxy> create_for<TypeToProxy>();
+    }
+
+    public class MethodCallTrackerFactory : IMethodCallTrackerFactory
+    {
+        private readonly ProxyGenerator generator;
+
+        public MethodCallTrackerFactory() : this(new ProxyGenerator())
+        {
+        }
+
+        public MethodCallTrackerFactory(ProxyGenerator generator)
+        {
+            this.generator = generator;
+        }
+
+        public IMethodCallTracker<TypeToProxy> create_for<TypeToProxy>()
+        {
+            var call_tracker_interceptor = new MethodCallTracker<TypeToProxy>();
+            var target = generator.CreateInterfaceProxyWithoutTarget<TypeToProxy>(call_tracker_interceptor);
+            call_tracker_interceptor.target = target;
+            return call_tracker_interceptor;
+        }
+    }
+}
\ No newline at end of file
trunk/product/MyMoney/Infrastructure/proxies/InterceptorConstraintFactorySpecs.cs
@@ -0,0 +1,29 @@
+using jpboodhoo.bdd.contexts;
+using MoMoney.Testing.MetaData;
+using MoMoney.Testing.spechelpers.contexts;
+using MoMoney.Testing.spechelpers.core;
+
+namespace MoMoney.Infrastructure.proxies
+{
+    [Concern(typeof (InterceptorConstraintFactory))]
+    public class behaves_like_constraint_factory :
+        concerns_for<IInterceptorConstraintFactory, InterceptorConstraintFactory>
+    {
+        context c = () => { method_call_tracker_factory = the_dependency<IMethodCallTrackerFactory>(); };
+
+        protected static IMethodCallTrackerFactory method_call_tracker_factory;
+    }
+
+    public class when_creating_a_constraint_for_a_type_to_intercept_on : behaves_like_constraint_factory
+    {
+        static IInterceptorConstraint<string> result;
+
+        it should_create_a_method_call_tracker_for_the_type_to_place_a_constraint_on =
+            () => method_call_tracker_factory.was_told_to(f => f.create_for<string>());
+
+        it should_return_an_instance_of_an_interceptor_constraint =
+            () => result.should_be_an_instance_of<InterceptorConstraint<string>>();
+
+        because b = () => { result = sut.CreateFor<string>(); };
+    }
+}
\ No newline at end of file
trunk/product/MyMoney/Infrastructure/proxies/InterceptorConstraintSpecs.cs
@@ -0,0 +1,56 @@
+using System.Collections.Generic;
+using jpboodhoo.bdd.contexts;
+using MoMoney.Infrastructure.proxies.Interceptors;
+using MoMoney.Testing.MetaData;
+using MoMoney.Testing.spechelpers.contexts;
+using MoMoney.Testing.spechelpers.core;
+
+namespace MoMoney.Infrastructure.proxies
+{
+    [Concern(typeof (InterceptorConstraint<string>))]
+    public class behaves_like_constraint : concerns_for<IInterceptorConstraint<string>, InterceptorConstraint<string>>
+    {
+        context c = () => { method_call_tracker = the_dependency<IMethodCallTracker<string>>(); };
+
+        protected static IMethodCallTracker<string> method_call_tracker;
+    }
+
+    public class when_asking_for_all_the_methods_to_intercept : behaves_like_constraint
+    {
+        static IEnumerable<string> result;
+        static IList<string> methods_to_intercept;
+
+        it should_return_all_the_recorded_method_calls_from_the_call_tracker =
+            () => result.should_be_equal_to(methods_to_intercept);
+
+        context c = () =>
+                        {
+                            methods_to_intercept = new List<string>();
+                            method_call_tracker
+                                .is_told_to(t => t.methods_to_intercept())
+                                .it_will_return(methods_to_intercept);
+                        };
+
+        because b = () => { result = sut.methods_to_intercept(); };
+    }
+
+    [Concern(typeof (InterceptorConstraint<int>))]
+    public class when_asking_for_the_target_of_the_interception_constrain :
+        concerns_for<IInterceptorConstraint<int>, InterceptorConstraint<int>>
+    {
+        static IMethodCallTracker<int> method_call_tracker;
+        static int result;
+        const int target_of_interception = 7;
+
+        context c = () =>
+                        {
+                            method_call_tracker = the_dependency<IMethodCallTracker<int>>();
+                            method_call_tracker.is_told_to(t => t.target).it_will_return(target_of_interception);
+                        };
+
+        because b = () => { result = sut.InterceptOn; };
+
+        it should_return_the_target_of_the_method_call_tracker =
+            () => result.should_be_equal_to(target_of_interception);
+    }
+}
\ No newline at end of file
trunk/product/MyMoney/Infrastructure/proxies/IProxyFactory.cs
@@ -0,0 +1,33 @@
+using System.Collections.Generic;
+using System.Linq;
+using Castle.Core.Interceptor;
+using Castle.DynamicProxy;
+
+namespace Ec.AuditTool.Infrastructure.Proxies
+{
+    public interface IProxyFactory
+    {
+        TypeToProxy CreateProxyFor<TypeToProxy>(TypeToProxy implementation, IEnumerable<IInterceptor> interceptors);
+    }
+
+    public class ProxyFactory : IProxyFactory
+    {
+        private readonly ProxyGenerator generator;
+
+        public ProxyFactory() : this(new ProxyGenerator())
+        {
+        }
+
+        public ProxyFactory(ProxyGenerator generator)
+        {
+            this.generator = generator;
+        }
+
+        public TypeToProxy CreateProxyFor<TypeToProxy>(TypeToProxy implementation,
+                                                       IEnumerable<IInterceptor> interceptors)
+        {
+            return generator
+                .CreateInterfaceProxyWithTarget<TypeToProxy>(implementation, interceptors.ToArray());
+        }
+    }
+}
\ No newline at end of file
trunk/product/MyMoney/Infrastructure/proxies/ProxyBuilder.cs
@@ -0,0 +1,63 @@
+using System.Collections.Generic;
+using System.Linq;
+using Castle.Core.Interceptor;
+using Ec.AuditTool.Infrastructure.Proxies;
+using Ec.AuditTool.Infrastructure.Proxies.Interceptors;
+
+namespace MoMoney.Infrastructure.proxies
+{
+    public interface IProxyBuilder<TypeToProxy>
+    {
+        IConstraintSelector<TypeToProxy> AddInterceptor<Interceptor>() where Interceptor : IInterceptor, new();
+        TypeToProxy CreateProxyFor(TypeToProxy an_implementation_of_the_interface);
+    }
+
+    public class ProxyBuilder<TypeToProxy> : IProxyBuilder<TypeToProxy>
+    {
+        readonly IDictionary<IInterceptor, IInterceptorConstraint<TypeToProxy>> constraints;
+        readonly IProxyFactory proxy_factory;
+        readonly IInterceptorConstraintFactory constraint_factory;
+
+        public ProxyBuilder() : this(new ProxyFactory(), new InterceptorConstraintFactory())
+        {
+        }
+
+        public ProxyBuilder(IProxyFactory proxy_factory, IInterceptorConstraintFactory constraint_factory)
+        {
+            this.proxy_factory = proxy_factory;
+            this.constraint_factory = constraint_factory;
+            constraints = new Dictionary<IInterceptor, IInterceptorConstraint<TypeToProxy>>();
+        }
+
+
+        public IConstraintSelector<TypeToProxy> AddInterceptor<Interceptor>() where Interceptor : IInterceptor, new()
+        {
+            var constraint = constraint_factory.CreateFor<TypeToProxy>();
+            constraints.Add(new Interceptor(), constraint);
+            return constraint;
+        }
+
+        public TypeToProxy CreateProxyFor(TypeToProxy an_implementation_of_the_interface)
+        {
+            return proxy_factory.CreateProxyFor(an_implementation_of_the_interface,
+                                                AllInterceptorsWithTheirConstraints());
+        }
+
+        IEnumerable<IInterceptor> AllInterceptorsWithTheirConstraints()
+        {
+            foreach (var pair in constraints)
+            {
+                var constraint = pair.Value;
+                var interceptor = pair.Key;
+                if (constraint.methods_to_intercept().Count() > 0)
+                {
+                    yield return new SelectiveInterceptor(constraint.methods_to_intercept(), interceptor);
+                }
+                else
+                {
+                    yield return interceptor;
+                }
+            }
+        }
+    }
+}
\ No newline at end of file
trunk/product/MyMoney/Infrastructure/proxies/ProxyBuilderSpecs.cs
@@ -0,0 +1,169 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using Castle.Core.Interceptor;
+using Ec.AuditTool.Infrastructure.Proxies;
+using jpboodhoo.bdd.contexts;
+using MoMoney.Testing.MetaData;
+using MoMoney.Testing.spechelpers.contexts;
+using MoMoney.Testing.spechelpers.core;
+
+namespace MoMoney.Infrastructure.proxies
+{
+    [Concern(typeof (ProxyBuilder<IAnInterface>))]
+    public class behaves_like_proxy_builder : concerns_for<IProxyBuilder<IAnInterface>, ProxyBuilder<IAnInterface>>
+    {
+        public override IProxyBuilder<IAnInterface> create_sut()
+        {
+            return new ProxyBuilder<IAnInterface>();
+        }
+    }
+
+    public class when_building_a_proxy_for_a_type : behaves_like_proxy_builder
+    {
+        it should_make_sure_the_original_call_gets_forwarded_to_the_item_to_proxy =
+            () =>
+                {
+                    an_implementation_of_the_interface.was_told_to(i => i.OneMethod());
+                    an_implementation_of_the_interface.was_told_to(i => i.SecondMethod());
+                };
+
+        it should_allow_each_intercepter_to_intercept_the_call =
+            () =>
+                {
+                    SomeInterceptor.MethodsCalled.Count().should_be_equal_to(2);
+                    AnotherInterceptor.MethodsCalled.Count().should_be_equal_to(2);
+                };
+
+        context c = () => { an_implementation_of_the_interface = an<IAnInterface>(); };
+
+        because b = () =>
+                        {
+                            sut.AddInterceptor<SomeInterceptor>();
+                            sut.AddInterceptor<AnotherInterceptor>();
+                            var proxy = sut.CreateProxyFor(an_implementation_of_the_interface);
+                            proxy.OneMethod();
+                            proxy.SecondMethod();
+                        };
+
+        after_each_observation ae = () =>
+                                        {
+                                            SomeInterceptor.Cleanup();
+                                            AnotherInterceptor.Cleanup();
+                                        };
+
+        static IAnInterface an_implementation_of_the_interface;
+    }
+
+    [Integration]
+    public class when_building_a_proxy_to_target_certain_methods_on_a_type : behaves_like_proxy_builder
+    {
+        it should_only_intercept_calls_on_the_method_that_was_specified =
+            () =>
+                {
+                    SomeInterceptor.MethodsCalled.Count().should_be_equal_to(1);
+                    SomeInterceptor.MethodsCalled.First().Name.should_be_equal_to("OneMethod");
+                };
+
+        context c = () => { an_implementation = an<IAnInterface>(); };
+
+        because b = () =>
+                        {
+                            var constraint = sut.AddInterceptor<SomeInterceptor>();
+                            constraint.InterceptOn.OneMethod();
+
+                            var proxy = sut.CreateProxyFor(an_implementation);
+                            proxy.OneMethod();
+                            proxy.SecondMethod();
+                        };
+
+        after_each_observation ae = () =>
+                                        {
+                                            SomeInterceptor.Cleanup();
+                                            AnotherInterceptor.Cleanup();
+                                        };
+
+        static IAnInterface an_implementation;
+    }
+
+    public interface IAnInterface
+    {
+        string GetterAndSetterProperty { get; set; }
+        void OneMethod();
+        void SecondMethod();
+        int FirstValueReturningMethod();
+        int ValueReturningMethodWithAnArgument(int number);
+    }
+
+    public class SomeInterceptor : IInterceptor
+    {
+        public static bool WasCalled;
+        public static IList<MethodInfo> MethodsCalled;
+
+        static SomeInterceptor()
+        {
+            MethodsCalled = new List<MethodInfo>();
+        }
+
+        public void Intercept(IInvocation invocation)
+        {
+            WasCalled = true;
+            MethodsCalled.Add(invocation.Method);
+            invocation.Proceed();
+        }
+
+        public static void Cleanup()
+        {
+            WasCalled = false;
+            MethodsCalled.Clear();
+        }
+    }
+
+    public class AnotherInterceptor : IInterceptor
+    {
+        public static bool WasCalled;
+        public static IList<MethodInfo> MethodsCalled;
+
+        static AnotherInterceptor()
+        {
+            MethodsCalled = new List<MethodInfo>();
+        }
+
+        public void Intercept(IInvocation invocation)
+        {
+            WasCalled = true;
+            MethodsCalled.Add(invocation.Method);
+            invocation.Proceed();
+        }
+
+        public static void Cleanup()
+        {
+            WasCalled = false;
+            MethodsCalled.Clear();
+        }
+    }
+
+    public class SomeImplementation : IAnInterface
+    {
+        public string GetterAndSetterProperty { get; set; }
+
+        public void OneMethod()
+        {
+        }
+
+        public void SecondMethod()
+        {
+        }
+
+        public int FirstValueReturningMethod()
+        {
+            return 1;
+        }
+
+        public int ValueReturningMethodWithAnArgument(int number)
+        {
+            return number + 1;
+        }
+    }
+}
\ No newline at end of file
trunk/product/MyMoney/Testing/MetaData/IntegrationAttribute.cs
@@ -0,0 +1,8 @@
+using System.ComponentModel;
+
+namespace MoMoney.Testing.MetaData
+{
+    public class IntegrationAttribute : CategoryAttribute
+    {
+    }
+}
\ No newline at end of file
trunk/product/MyMoney/MyMoney.csproj
@@ -189,6 +189,8 @@
     <Compile Include="Domain\Core\range.cs" />
     <Compile Include="Domain\Core\range_specs.cs" />
     <Compile Include="Domain\repositories\company_repository_extensions.cs" />
+    <Compile Include="Infrastructure\caching\IdentityMapSpecs.cs" />
+    <Compile Include="Infrastructure\caching\IIdentityMap.cs" />
     <Compile Include="Infrastructure\cloning\BinarySerializer.cs" />
     <Compile Include="Infrastructure\cloning\BinarySerializerSpecs.cs" />
     <Compile Include="Infrastructure\cloning\ISerializer.cs" />
@@ -219,6 +221,21 @@
     <Compile Include="Infrastructure\interceptors\UnitOfWorkInterceptor.cs" />
     <Compile Include="Infrastructure\interceptors\UnitOfWorkInterceptorSpecs.cs" />
     <Compile Include="Infrastructure\Logging\ILoggable.cs" />
+    <Compile Include="Infrastructure\proxies\IConstraintSelector.cs" />
+    <Compile Include="Infrastructure\proxies\IInterceptorConstraint.cs" />
+    <Compile Include="Infrastructure\proxies\IInterceptorConstraintFactory.cs" />
+    <Compile Include="Infrastructure\proxies\IMethodCallTrackerFactory.cs" />
+    <Compile Include="Infrastructure\proxies\InterceptorConstraintFactorySpecs.cs" />
+    <Compile Include="Infrastructure\proxies\InterceptorConstraintSpecs.cs" />
+    <Compile Include="Infrastructure\proxies\Interceptors\IMethodCallTracker.cs" />
+    <Compile Include="Infrastructure\proxies\Interceptors\MethodCallTracker.cs" />
+    <Compile Include="Infrastructure\proxies\Interceptors\MethodCallTrackerSpecs.cs" />
+    <Compile Include="Infrastructure\proxies\Interceptors\RunOnBackgroundThreadInterceptor.cs" />
+    <Compile Include="Infrastructure\proxies\Interceptors\RunOnBackgrounThreadInterceptorSpecs.cs" />
+    <Compile Include="Infrastructure\proxies\Interceptors\SelectiveInterceptor.cs" />
+    <Compile Include="Infrastructure\proxies\IProxyFactory.cs" />
+    <Compile Include="Infrastructure\proxies\ProxyBuilder.cs" />
+    <Compile Include="Infrastructure\proxies\ProxyBuilderSpecs.cs" />
     <Compile Include="Infrastructure\registries\default_registry.cs" />
     <Compile Include="Infrastructure\registries\default_registry_specs.cs" />
     <Compile Include="Domain\accounting\billing\Bill.cs" />
@@ -473,6 +490,7 @@
     <Compile Include="Tasks\infrastructure\FileSystemTasks.cs" />
     <Compile Include="Tasks\application\IncomeTasks.cs" />
     <Compile Include="Tasks\infrastructure\UpdateTasks.cs" />
+    <Compile Include="Testing\MetaData\IntegrationAttribute.cs" />
     <Compile Include="Testing\spechelpers\contexts\concerns_for.cs" />
     <Compile Include="Testing\spechelpers\contexts\behaves_like_a_repository.cs" />
     <Compile Include="Testing\spechelpers\core\empty_fixture.cs" />