Order Of Operation

Jun 9, 2008 at 2:51 PM
Hello,

Is there any way to delay setter injection until a get is requests the injected value?

I converted a project to Unity from a customized DI implementation based on something that Brad Wilson. I ran into problems with curcular references that weren't a problem before.  My work around was to add an internal get that resolved those objects on first touch. I really don't like my objects to have to know anything about where they come from but this seemed like the most expedient solution. My first attempt was to use setter injection for the circularly referenced instances but it appears as if objects are built up and then setters injected one after the other rather than building all objects and then then applying setter injection.

Maybe circular references are just bad and I should refactor the code? Or possibly they are just bad when using DI? I would be willing to refactor if I could find a decent pattern. My classes map data objects so for example, a customer can have orders and orders have customers.

Thanks,

David Morris
Jul 9, 2008 at 10:22 PM

Back Again,

I am still wondering how others use Unity where circular references exist I refactored my code twice to work around this problem. Using Unity within a class that is contained in a Unity container seems like a bad thing to do but that worked OK. Next, I refactored the code to add a relationship interface and implemented the relationships in seperate classes. That makes the code more complex. All of the patterns I have seen for factoring out circular dependencies do so at the cost of added complexity. Here is a better description of my problem:

I have classes that map Entity objects to a database. An entity could be something like a Customer with Orders. The mappers load entities using the Data Access Application Block. This is pretty clean and simple since the majority of code can exist in the EntityMapper super class. Each class does have to set up the parameters for DAAB and load related data. To load related data, the CustomerMapper needs an OrderMapper to load and persist Order Entities for a Customer. And likewise, the OrderMapper needs a CustomerMapper to load an Order's Customer.

Adding IChild<TParent, TChild> and IParent<TParent, TChild> allowed me to work around this problem (I ran into the same problem when I tried to create an IParentChild<TParent, TChild>). However, I have the same code now with more interfaces and classes and I really don't see how it is any better except that there are no circular references.

Is there any way to build an object with Unity where the injection happens after I Resolve the object?

Thanks again,

David Morris

Jul 22, 2008 at 5:36 AM
Edited Jul 22, 2008 at 5:41 AM
The following is a new Unity Unit Test  that has a circular reference (and passes) - added to the existing UnityContainerFixture.cs. 


dmmorris wrote:

I am still wondering how others use Unity where circular references exist I refactored my code twice to work around this problem.

It is the first of many that I'll be creating to support circular references.   I chose this scenario (circular reference) to study the new object builder and dynamic buildplans; later as I understand them better the code could change; it is to early in the game to see if this is as efficient as it could be.   

At this point I can only say that *ALL* unit test pass to include the following; I can't say if injection, other than setter, will cause issues.  When I complete all possible scenarios all be sure to update this message thread.

There are only a few code changes/additions required to make it work - the details are HERE.


        public interface IFooA  
        { 
            IFooB B { get; set; } 
        }
        public class FooA : IFooA
        {
            public List<FooB> blist = new List<FooB>();
            public FooA()
            {
                blist.Add(new FooB());
                blist.Add(new FooB());
            }

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

        public interface IFooB  
        { 
            IFooA A { get; set; } 
        }
        public class FooB : IFooB
        {
            List<FooA> alist = new List<FooA>();
            private IFooA _a;

            [Dependency]
            public IFooA A
            {
                get{ return _a;}
                set{ _a = value;}
            }
        }
        [TestMethod]
        public void CanRegisterCircularReference()
        {
            IUnityContainer container = new UnityContainer()
                .RegisterType<IFooA, FooA>()
                .RegisterType<IFooB, FooB>();

            IFooA fooA = container.Resolve<IFooA>();
            
            // TODO: cleanup - casting cause it's late ;)
            Assert.AreEqual(((FooA)fooA).blist.Count, 2);
        }

 

Jul 22, 2008 at 2:02 PM

Hi Bill,

Thanks for taking the time to work on this. The tests you provided cover my situation. I will let you know how these changes work in my codebase.

--David Morris

Jul 23, 2008 at 2:02 AM

Your welcome David,  I was pleasantly surprised to find that a circular reference with constructor injection worked as well as it did with Setter.  I updated the unit test and did some minor refactoring to make things technically correct (nothing that changed any logic).  The new bInstance and aInstance properties utilize constructor injection, the A and B properties use setter; the asserts tell the rest of the story.  

        [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());
        }



Visit my Blog for the details - the rest of the unit test follows:

        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());
        }

Jul 23, 2008 at 5:51 AM
Hi Bill,

Thanks for the update. I patched my codebase with your change and everything worked including your unit test until I added a circular reference in my code. What I found is that if I specify a ContainerControlledLifetimeManager my tests fail. You can duplicate this by changing your unit test to use the following RegisterType calls:

IUnityContainer container = new UnityContainer()

 

.RegisterType<

IFooA, FooA>(new ContainerControlledLifetimeManager())

 

.RegisterType<

IFooB, FooB>(new ContainerControlledLifetimeManager());

--David Morris

 

Jul 23, 2008 at 6:35 PM
Edited Jul 23, 2008 at 6:38 PM
Hi David, what is the nature of the failure?  Can you provide a Unit Test that fails the scenario that meets your requirements?  I have to get back (I'm at lunch) but I did the following - looks like I'm going to have to beef up the test cases...  I should note that I don't get a stack overflow or failures - but then again I don't have any assertions yet ;)

The current unit test yields the following with debug output statements in the constructors:

UNITY TEST: PARAMETER CONSTRUCTOR: B
UNITY TEST: PARAMETER CONSTRUCTOR: A
UNITY TEST: PARAMETERLESS CONSTRUCTOR: B
UNITY TEST: PARAMETERLESS CONSTRUCTOR: B
UNITY TEST: PARAMETER CONSTRUCTOR: B
UNITY TEST: PARAMETER CONSTRUCTOR: A
UNITY TEST: PARAMETERLESS CONSTRUCTOR: B
UNITY TEST: PARAMETERLESS CONSTRUCTOR: B
UNITY TEST: PARAMETER CONSTRUCTOR: B
UNITY TEST: PARAMETER CONSTRUCTOR: A
UNITY TEST: PARAMETERLESS CONSTRUCTOR: B
UNITY TEST: PARAMETERLESS CONSTRUCTOR: B

New unit test with the applicable output results:

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

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

        }

UNITY TEST: PARAMETER CONSTRUCTOR: B
UNITY TEST: PARAMETER CONSTRUCTOR: A
UNITY TEST: PARAMETERLESS CONSTRUCTOR: B
UNITY TEST: PARAMETERLESS CONSTRUCTOR: B
UNITY TEST: PARAMETER CONSTRUCTOR: B
UNITY TEST: PARAMETER CONSTRUCTOR: B

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

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

        }

UNITY TEST: PARAMETER CONSTRUCTOR: B
UNITY TEST: PARAMETER CONSTRUCTOR: A
UNITY TEST: PARAMETERLESS CONSTRUCTOR: B
UNITY TEST: PARAMETERLESS CONSTRUCTOR: B

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

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

        }

UNITY TEST: PARAMETER CONSTRUCTOR: B
UNITY TEST: PARAMETER CONSTRUCTOR: A
UNITY TEST: PARAMETERLESS CONSTRUCTOR: B
UNITY TEST: PARAMETERLESS CONSTRUCTOR: B

 

 

 

Jul 24, 2008 at 2:33 AM

Hi David,  I completed what I believe is a complete set of test cases.   You'll find that results are not only biased by the class that has the ContainerLifetimeManager() but also the order in which it is resolved (as is shown below).  

My goals are to study, document and diagram Unity (my approach to learning new applications); I'm using this issue/requirement as one of my objectives to get there; that is of course Microsoft doesn't release a new technology that makes Unity obsolete before it matures - because then I'll have to move on to it  ;)

I also have WPF on my plate (I'm on lesson 8 of Microsoft's 18 part WebCast) so I'd just assume focus my time on something that will help you while I work towards my own personal objectives.  If you'll pick a test case that will meet your requirements, or provide a new test case/unit test, I'll be more than glad to use it as I move forward. 

=======================================================================
====[ NO ContainerLifetimeManager() ]==================================
=======================================================================

UNITY TEST: CanResolveCircularReference
UNITY TEST: ===>Resolving IFooA
UNITY TEST: FooB:INJECTION CONSTRUCTOR: AInstance=<NULL>
UNITY TEST: FooA:INJECTION CONSTRUCTOR: BInstance=<INSTANCE>
UNITY TEST: FooB:PARAMETERLESS CONSTRUCTOR: B
UNITY TEST: FooB:PARAMETERLESS CONSTRUCTOR: B
UNITY TEST: FooB:INJECTION CONSTRUCTOR: AInstance=<NULL>
UNITY TEST: ===>Resolving IFooB
UNITY TEST: FooA:INJECTION CONSTRUCTOR: BInstance=<NULL>
UNITY TEST: FooB:PARAMETERLESS CONSTRUCTOR: B
UNITY TEST: FooB:PARAMETERLESS CONSTRUCTOR: B
UNITY TEST: FooB:INJECTION CONSTRUCTOR: AInstance=<INSTANCE>
UNITY TEST: FooA:INJECTION CONSTRUCTOR: BInstance=<NULL>
UNITY TEST: FooB:PARAMETERLESS CONSTRUCTOR: B
UNITY TEST: FooB:PARAMETERLESS CONSTRUCTOR: B

=======================================================================
====[ FooA uses ContainerLifetimeManager() ]===========================
=======================================================================

[FooA / FooB]   <--- FooA is resolved first then FooB
UNITY TEST: CanResolveCircularReferenceWith_AB_A_LifetimeManager
UNITY TEST: ===>Resolving IFooA
UNITY TEST: FooB:INJECTION CONSTRUCTOR: AInstance=<NULL>
UNITY TEST: FooA:INJECTION CONSTRUCTOR: BInstance=<INSTANCE>
UNITY TEST: FooB:PARAMETERLESS CONSTRUCTOR: B
UNITY TEST: FooB:PARAMETERLESS CONSTRUCTOR: B
UNITY TEST: FooB:INJECTION CONSTRUCTOR: AInstance=<NULL>
UNITY TEST: ===>Resolving IFooB
UNITY TEST: FooB:INJECTION CONSTRUCTOR: AInstance=<INSTANCE>
-----------
[FooB / FooA]
UNITY TEST: CanResolveCircularReferenceWith_BA_A_LifetimeManager
UNITY TEST: ===>Resolving IFooB
UNITY TEST: FooA:INJECTION CONSTRUCTOR: BInstance=<NULL>
UNITY TEST: FooB:PARAMETERLESS CONSTRUCTOR: B
UNITY TEST: FooB:PARAMETERLESS CONSTRUCTOR: B
UNITY TEST: FooB:INJECTION CONSTRUCTOR: AInstance=<INSTANCE>
UNITY TEST: ===>Resolving IFooA

=======================================================================
====[ FooB uses ContainerLifetimeManager() ]===========================
=======================================================================

[FooA / FooB]
UNITY TEST: CanResolveCircularReferenceWith_AB_B_LifetimeManager
UNITY TEST: ===>Resolving IFooA
UNITY TEST: FooB:INJECTION CONSTRUCTOR: AInstance=<NULL>
UNITY TEST: FooA:INJECTION CONSTRUCTOR: BInstance=<INSTANCE>
UNITY TEST: FooB:PARAMETERLESS CONSTRUCTOR: B
UNITY TEST: FooB:PARAMETERLESS CONSTRUCTOR: B
UNITY TEST: ===>Resolving IFooB
-----------
[FooB / FooA]
UNITY TEST: CanResolveCircularReferenceWith_BA_B_LifetimeManager
UNITY TEST: ===>Resolving IFooB
UNITY TEST: FooA:INJECTION CONSTRUCTOR: BInstance=<NULL>
UNITY TEST: FooB:PARAMETERLESS CONSTRUCTOR: B
UNITY TEST: FooB:PARAMETERLESS CONSTRUCTOR: B
UNITY TEST: FooB:INJECTION CONSTRUCTOR: AInstance=<INSTANCE>
UNITY TEST: FooA:INJECTION CONSTRUCTOR: BInstance=<NULL>
UNITY TEST: FooB:PARAMETERLESS CONSTRUCTOR: B
UNITY TEST: FooB:PARAMETERLESS CONSTRUCTOR: B
UNITY TEST: ===>Resolving IFooA
UNITY TEST: FooA:INJECTION CONSTRUCTOR: BInstance=<INSTANCE>
UNITY TEST: FooB:PARAMETERLESS CONSTRUCTOR: B
UNITY TEST: FooB:PARAMETERLESS CONSTRUCTOR: B

=======================================================================
====[ BOTH use ContainerLifetimeManager() ]============================
=======================================================================

[FooA / FooB]
UNITY TEST: CanResolveCircularReferenceWith_AB_BOTH_LifetimeManager
UNITY TEST: ===>Resolving IFooA
UNITY TEST: FooB:INJECTION CONSTRUCTOR: AInstance=<NULL>
UNITY TEST: FooA:INJECTION CONSTRUCTOR: BInstance=<INSTANCE>
UNITY TEST: FooB:PARAMETERLESS CONSTRUCTOR: B
UNITY TEST: FooB:PARAMETERLESS CONSTRUCTOR: B
UNITY TEST: ===>Resolving IFooB
-----------
[FooB / FooA]
UNITY TEST: CanResolveCircularReferenceWith_BA_BOTH_LifetimeManager
UNITY TEST: ===>Resolving IFooB
UNITY TEST: FooA:INJECTION CONSTRUCTOR: BInstance=<NULL>
UNITY TEST: FooB:PARAMETERLESS CONSTRUCTOR: B
UNITY TEST: FooB:PARAMETERLESS CONSTRUCTOR: B
UNITY TEST: FooB:INJECTION CONSTRUCTOR: AInstance=<INSTANCE>
UNITY TEST: ===>Resolving IFooA

==================================================================

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

            Debug.WriteLine("");
            Debug.WriteLine("CanResolveCircularReference", "UNITY TEST");
            Debug.WriteLine("===>Resolving IFooA", "UNITY TEST");
            IFooA fooA = container.Resolve<IFooA>();
            Debug.WriteLine("===>Resolving IFooB", "UNITY TEST");
            IFooB fooB = container.Resolve<IFooB>();
            Debug.WriteLine("");

            int aFooCount = fooA.GetFooCount();
            int aFooBCount = fooB.A.GetFooCount();

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

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

            Assert.AreEqual(2, aFooCount);
            Assert.AreEqual(2, aFooBCount);
        }

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

            Debug.WriteLine("");
            Debug.WriteLine("CanResolveCircularReferenceWith_AB_A_LifetimeManager", "UNITY TEST");
            Debug.WriteLine("===>Resolving IFooA", "UNITY TEST");
            IFooA fooA = container.Resolve<IFooA>();
            Debug.WriteLine("===>Resolving IFooB", "UNITY TEST");
            IFooB fooB = container.Resolve<IFooB>();
            Debug.WriteLine("");

        }

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

            Debug.WriteLine("");
            Debug.WriteLine("CanResolveCircularReferenceWith_BA_A_LifetimeManager", "UNITY TEST");
            Debug.WriteLine("===>Resolving IFooB", "UNITY TEST");
            IFooB fooB = container.Resolve<IFooB>();
            Debug.WriteLine("===>Resolving IFooA", "UNITY TEST");
            IFooA fooA = container.Resolve<IFooA>();
            Debug.WriteLine("");

        }

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

            Debug.WriteLine("");
            Debug.WriteLine("CanResolveCircularReferenceWith_AB_B_LifetimeManager", "UNITY TEST");
            Debug.WriteLine("===>Resolving IFooA", "UNITY TEST");
            IFooA fooA = container.Resolve<IFooA>();
            Debug.WriteLine("===>Resolving IFooB", "UNITY TEST");
            IFooB fooB = container.Resolve<IFooB>();
            Debug.WriteLine("");
        }

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

            Debug.WriteLine("");
            Debug.WriteLine("CanResolveCircularReferenceWith_BA_B_LifetimeManager", "UNITY TEST");
            Debug.WriteLine("===>Resolving IFooB", "UNITY TEST");
            IFooB fooB = container.Resolve<IFooB>();
            Debug.WriteLine("===>Resolving IFooA", "UNITY TEST");
            IFooA fooA = container.Resolve<IFooA>();
        }

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

            Debug.WriteLine("");
            Debug.WriteLine("CanResolveCircularReferenceWith_AB_BOTH_LifetimeManager", "UNITY TEST");
            Debug.WriteLine("===>Resolving IFooA", "UNITY TEST");
            IFooA fooA = container.Resolve<IFooA>();
            Debug.WriteLine("===>Resolving IFooB", "UNITY TEST");
            IFooB fooB = container.Resolve<IFooB>();
            Debug.WriteLine("");
        }

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

            Debug.WriteLine("");
            Debug.WriteLine("CanResolveCircularReferenceWith_BA_BOTH_LifetimeManager", "UNITY TEST");
            Debug.WriteLine("===>Resolving IFooB", "UNITY TEST");
            IFooB fooB = container.Resolve<IFooB>();
            Debug.WriteLine("===>Resolving IFooA", "UNITY TEST");
            IFooA fooA = container.Resolve<IFooA>();
            Debug.WriteLine("");
        }
    }

Oct 15, 2009 at 8:01 PM

Is there any hope that they'll add this ability to the code base?   Post Buildup was pushed off in an earlier release, but I notice that other IOC containers properly handle it.  Spring seems to have support for it.

Feb 2, 2010 at 1:59 AM

Out of scope for v2.0. Will add to our backlog for post-v2.

Regards,

Grigori