Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unity Container: Using PerResolveLifetimeManager and custom interception

I'm trying to implement my own interception while still using the Unity container. I want to do this in such a way where it respects the lifetime manager used. i.e. if it is a PerResolveLifetimeManager then I want to have wrap the instance once and I want that wrapped instance to be used throughout the resolve.

So far I implemented a BuilderStrategy that I add to my container using a custom UnityContainerExtension class (I pass PostInitialization to the AddNew method; I'm not sure what the most appropriate value is but this seemed to work). In a PostBuildUp override in my BuilderStrategy, I replace context.Existing with my wrapped value.

When I use this with the PerResolve lifetime, one wrap occurs but only the first use of the dependency gets the wrapped instance and the rest get a non wrapped instance. i.e. if my ctor takes in IFoo foo1 and IFoo foo2 then only foo1 is my wrapped instance whileas foo2 is the unwrapped instance.

Here is a sample repro (for simplicity, I am using an instance of another class, Foo2, instead of wrapping):

public class MyInterception : UnityContainerExtension
{
    protected override void Initialize()
    {
        Context.Strategies.AddNew<MyInterceptionStrategy>(UnityBuildStage.PostInitialization);
    }
}

public class MyInterceptionStrategy : BuilderStrategy
{
    public override void PostBuildUp(IBuilderContext context)
    {
        if (!context.OriginalBuildKey.Type.IsInterface)
        {
            return;
        }

        context.Existing = new Foo2();
    }
}

public class Bar
{
    public Bar(IFoo f1, IFoo f2, IFoo f3)
    {
    }
}

public interface IFoo
{
}

public class Foo1 : IFoo
{
}

public class Foo2 : IFoo
{
}

public void Main()
{
    UnityContainer container = new UnityContainer();
    container.AddNewExtension<MyInterception>();
    container.RegisterType(typeof (IFoo), typeof (Foo1), new PerResolveLifetimeManager());

    container.Resolve<Bar>();
}

If you put a breakpoint in PostBuildUp and in Bar's contructor and debug Main up through the resolve of Bar, you can see that PostBuildUp is only called once. However, in Bar's constructor, f1 is an instance of Foo2 (a "wrapped" instance) whileas f2 and f3 are instances of Foo1 (unwrapped).

I was wondering if what I want is possible and if I am taking the right approach? Are my problems related to lifetimes and/or the UnityBuildStage that I added the BuilderStrategy for?

Thanks

like image 828
lotrij Avatar asked Jun 17 '10 13:06

lotrij


1 Answers

You're doing the interception strategy too late in the build strategy. Unfortunately, at this time I don't have a more detailed reason for why the code is behaving this way inside the ObjectBuilder assembly.

Hopefully, I will return when I have more time to analyze the Unity ObjectBuilder, but to resolve your issue in the interim, just change your UnityBuildStage enumeration value from PostInitialization to Setup or TypeMapping

Context.Strategies.AddNew<MyInterceptionStrategy>(UnityBuildStage.Setup);

My full code:

using System;
using Microsoft.Practices.ObjectBuilder2;
using Microsoft.Practices.Unity;
using Microsoft.Practices.Unity.ObjectBuilder;

namespace UnityInterceptors
{
    class Program
    {
        public class MyInterception : UnityContainerExtension
        {
            protected override void Initialize()
            {
                Context.Strategies.AddNew<MyInterceptionStrategy>(UnityBuildStage.Setup);
            }
        }
        public class MyInterceptionStrategy : BuilderStrategy
        {
            public override void PostBuildUp(IBuilderContext context)
            {
                if (!context.OriginalBuildKey.Type.IsInterface)
                {
                    return;
                }

                context.Existing = new Foo2();
            }
        }

        public class Bar
        {
            public Bar(IFoo f1, IFoo f2, IFoo f3)
            {
                Console.WriteLine(f1.GetType().Name);
                Console.WriteLine(f2.GetType().Name);
                Console.WriteLine(f3.GetType().Name);
            }
        }

        public interface IFoo
        {
        }

        public class Foo1 : IFoo
        {
        }

        public class Foo2 : IFoo
        {
        }

        public static void Main()
        {
            UnityContainer container = new UnityContainer();
            container.AddNewExtension<MyInterception>();
            container.RegisterType(typeof(IFoo), typeof(Foo1), new PerResolveLifetimeManager());

            container.Resolve<Bar>();

            Console.ReadLine();
        }
    }
}
  • Also note that I'm using the Unity 2.0 library. Could possibly be some type of bug that has been resolved.
like image 155
Bryan Ray Avatar answered Nov 15 '22 10:11

Bryan Ray