Unity - Modification to support circular reference

The following source code changes to the Unity, ObjectBuilder and Tests projects will permit circular references during DI.   All Unit Test passed to include the new unit test below (last code snippet).  The affected areas of code are commented with the following: "// BillKrat.2008.07.22 ID: Circular Reference"


Src\ObjectBuilder\BuilderContext.cs 

        // BillKrat.2008.07.22 ID: Circular Reference
        // New Property
        private IBuilderContext _parentContext;
        /// <summary>
        /// The context that spawned the current context
        /// </summary>
        public IBuilderContext ParentContext
        {
            get { return _parentContext; }
            set { _parentContext = value; }
        }


Src\ObjectBuilder\IBuilderContext.cs

        // BillKrat.2008.07.22 ID: Circular Reference
        // New Property
        /// <summary>
        /// Gets / Sets the parent of the current context
        /// </summary>
        IBuilderContext ParentContext { get; set; }


Src\ObjectBuilder\Strategies\BuildPlan\BuildPlanStrategy.cs

    /// <summary>
    /// A <see cref="BuilderStrategy"/> that will look for a build plan
    /// in the current context. If it exists, it invokes it, otherwise
    /// it creates one and stores it for later, and invokes it.
    /// </summary>
    public class BuildPlanStrategy : BuilderStrategy
    {
        /// <summary>
        /// Called during the chain of responsibility for a build operation.
        /// </summary>
        /// <param name="context">The context for the operation.</param>
        public override void PreBuildUp(IBuilderContext context)
        {
            IBuildPlanPolicy plan = context.Policies.Get<IBuildPlanPolicy>(context.BuildKey);
            if(plan == null)
            {
                IBuildPlanCreatorPolicy planCreator =
                    context.Policies.Get<IBuildPlanCreatorPolicy>(context.BuildKey);

                if(planCreator != null)
                {
                    plan = planCreator.CreatePlan(context, context.BuildKey);
                    context.PersistentPolicies.Set(plan, context.BuildKey);
                }
            }
            // BillKrat.2008.07.22 ID: Circular Reference
            // Added IsCircularReference(context)
            if(plan != null && !IsCircularReference(context))
            {
                plan.BuildUp(context);
            }
        }

        // BillKrat.2008.07.22 ID: Circular Reference
        // New method
        /// <summary>
        /// If the current context matches it's grandparents
        /// then we're in a circular reference
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        private bool  IsCircularReference(IBuilderContext context)
        {
            if(context.ParentContext==null)
                return false;;
            if(context.ParentContext.ParentContext==null)
                return false;

            IBuildKey current = (IBuildKey)context.BuildKey;
            IBuildKey parent = (IBuildKey)context.ParentContext.ParentContext.BuildKey;

            return current.Type == parent.Type;

        }
    }


Src\Unity\ObjectBuilder\NamedTypeDependencyResolverPolicy.cs

        public object Resolve(IBuilderContext context)
        {
            Guard.ArgumentNotNull(context, "context");
            NamedTypeBuildKey key = new NamedTypeBuildKey(type, name);
            IBuilderContext recursiveContext = context.CloneForNewBuild(key, null);
            // BillKrat.2008.07.22 ID: Circular Reference
            // Set new ParentContext property
            recursiveContext.ParentContext = context;
            return recursiveContext.Strategies.ExecuteBuildUp(recursiveContext);
        }   



Tests\ObjectBuilder\Utility\MockBuilderContext.cs
Tests\ObjectBuilder.BuildPlan\TestDoubles\TestBuilderContext.cs
Tests\Unity.Tests\TestDoubles\TestingBuilderContext.cs

        // BillKrat.2008.07.22 ID: Circular Reference
        // New Property
        private IBuilderContext _parentContext;
        /// <summary>
        /// Get / Set parent context
        /// </summary>
        public IBuilderContext ParentContext
        {
            get { return _parentContext; }
            set { _parentContext = value; }
        }


NEW UNIT TEST FOLLOWS 

        // BillKrat.2008.07.22 ID: Circular Reference
        // The following is new code

        public interface IFooA  
        { 
            IFooB B { get; set; }
            IFooB BInstance { get; }
            int GetFooCount();
        }
        public interface IFooB
        {
            IFooA A { get; set; }
            IFooA AInstance { get; }
        }

        public class FooA : IFooA
        {
            public List<FooB> blist = new List<FooB>();

            private IFooB _bInstance;
            public IFooB BInstance
            {
                get { return _bInstance; }
            }

            public FooA(IFooB bInjected)
            {
                _bInstance = bInjected;
                blist.Add(new FooB());
                blist.Add(new FooB());
            }

            private IFooB _b;
            [Dependency]
            public IFooB B
            {
                get { return _b; }
                set { _b = value;}
            }

            public int GetFooCount()
            {
                return blist.Count;
            }
        }

        public class FooB : IFooB
        {
            private IFooA _aInstance;
            public IFooA AInstance
            {
                get { return _aInstance; }
            }

            public FooB() { }
            public FooB(IFooA fooA)
            {
                _aInstance = fooA;
            }

            private IFooA _a;
            [Dependency]
            public IFooA A
            {
                get{ return _a;}
                set{ _a = value;}
            }
        }

        [TestMethod]
        public void CanResolveCircularReference()
        {
            IUnityContainer container = new UnityContainer()
                .RegisterType<IFooA, FooA>()
                .RegisterType<IFooB, FooB>();

            IFooA fooA = container.Resolve<IFooA>();
            IFooB fooB = container.Resolve<IFooB>();

            Assert.IsNull(fooA.B.A);
            Assert.IsNull(fooB.A.B);

            Assert.IsNotNull(fooA.BInstance);
            Assert.IsNotNull(fooB.AInstance);

            Assert.AreEqual(2, fooA.GetFooCount());
            Assert.AreEqual(2, fooB.A.GetFooCount());
        }

Related CodePlex link: http://www.codeplex.com/unity/Thread/View.aspx?ThreadId=29302
Tags: ,
Categories: Unity


Actions: E-mail | Permalink |  Grammar/Typo/Better way? Please let me know