Custom Factory Strategy with ContainerLifetimeManager not working?

Mar 9, 2013 at 3:01 PM
We have recently started using the unity container in our code base as a means to reduce our dependency management complexity and things like that. Since our code base is kind of big, and very convoluted (with some very bad practices in some places), I have not checked in the code yet, and have been experimenting with ways to approach the problem as to not interfere with the rest of the code (this will need to be incremental, so I'm not adding DI to everything just yet, just the basic structure and wiring).

After trying some things, I found an excellent post on stackoverflow, by Mark Heath, about a custom factory extension that would enable one to pass in a factory to create proxy remoting objects instead of concrete classes.

http://stackoverflow.com/questions/1380375/custom-object-factory-extension-for-unity

This was very fit to our reality as we have a bunch of separate web applications communicating over remoting, so I replicated the classes here and added the extension to the container. The problem I'm having with this though is that my factory is being called every time Resolve is called on the container. I would expect it to be called only the first time, and that after that, the stored singleton object would be returned.

My implementation is a bit simpler than his, because I'm not worried about child containers and stuff like that, so there is no need for the custom marker policy class there and all the extra handling on the strategy to find the marker.

So, I basically substituted this:
// Find the container to add this to
IPolicyList parentPolicies;
var parentMarker = context.Policies.Get<ParentMarkerPolicy>(new NamedTypeBuildKey<ParentMarkerPolicy>(), out parentPolicies);

// TODO: add error check - if policy is missing, extension is misconfigured

// Add lifetime manager to container
parentPolicies.Set<ILifetimePolicy>(ltm, new NamedTypeBuildKey(key.Type));
// And add to LifetimeContainer so it gets disposed
parentMarker.AddToLifetime(ltm);
by this:
context.Policies.Set(ltm, new NamedTypebuildKey(key.Type));
I'm kind of bummed by the fact that, when I compare two instances returned by consecutive Resolves (in the immediate window for instance), they are actually the same instance. It's as if the strategy is recreating the instance through the factory, but this newly created instance is not being returned, but the cached one. I think that if I just called Activator.GetObject twice they would be different instances right?

As a matter of fact, I'm not entirely sure what the whole code is doing here, since I've basically copied and adapted it. It would be nice if someone was able to point me to documentation describing the correct steps in implementing a custom extension and how everything works together. I managed to create a completely custom application block in the Entlib by following the tutorial, but in this case here it is just too hard to get something working as expected without knowing how the underlying mechanics actually use your classes. If I at least knew what happened in the life cycle of the container, or the steps involved when one requests an object or a BuildUp, and things like that maybe I could work out a solution on my own.

Even so, why is this strategy being called on every resolve for the same object? How are the strategies called anyways? Does it call every strategy in order on the strategy chain in the container, and stops on the first one that sets the BuildComplete flag? Maybe I'm wiring this the wrong way somehow, and my strategy is running earlier than it should? Am I missing something else here?

Regards,
Juliano Goncalves
Mar 10, 2013 at 4:29 PM
Edited May 1, 2013 at 9:13 PM
Unfortunately, there is no formal container extension documentation. In the next version of Enterprise Library there are plans to create more documentation for the internals.

I think the missing piece is that you are not adding an ILifetimePolicy for the build key:

So instead of the one line
context.Policies.Set(ltm, new NamedTypebuildKey(key.Type));
Use:
// Add lifetime manager to container
context.PersistentPolicies.Set<ILifetimePolicy>(ltm, new NamedTypeBuildKey(key.Type));
                
// And add to LifetimeContainer so it gets disposed
context.Lifetime.Add(ltm);
context.Policies.Set(ltm, new NamedTypeBuildKey(key.Type));
Now the ContainerControlledLifetimeManager will be found for that build key and the current instance returned without calling the container extension again.

~~
Randy Levy
entlib.support@live.com
Enterprise Library support engineer
Support How-to
Mar 11, 2013 at 3:16 PM
Thanks for the info Randy, but it still does not work. The strategy is still being called for the same interface type everytime I call resolve.

I was home when I wrote the question, but now I have access to the actual source code, so here is the current implementation:
namespace <OurCompany>.Framework.Unity
{
    using Microsoft.Practices.ObjectBuilder2;
    using Microsoft.Practices.Unity;

    internal class CustomFactoryBuildStrategy : BuilderStrategy
    {
        private readonly IFactory m_factory;

        public CustomFactoryBuildStrategy(IFactory _factory)
        {
            m_factory = _factory;
        }

        public override void PreBuildUp(IBuilderContext _context)
        {
            var key = _context.OriginalBuildKey;
            if (_context.Existing != null || !m_factory.CanCreate(key.Type)) return;

            _context.Existing = m_factory.Create(key.Type);
            var ltm = new ContainerControlledLifetimeManager();
            ltm.SetValue(_context.Existing);

            _context.PersistentPolicies.Set(ltm, new NamedTypeBuildKey(key.Type));
            _context.Lifetime.Add(ltm);
            _context.Policies.Set(ltm, new NamedTypeBuildKey(key.Type));
            _context.BuildComplete = true;
        }
    }
}
I still don't understand the actual differences on the "Policies" properties. It seems heavily redundant to have to add the key to both of them for some reason, and I'm also not sure why should I be creating a new instance of NamedTypeBuildKey instead of just passing the one I had around.

Could you perhaps elaborate a bit on these points too?

Thanks a lot for the prompt help,
Juliano
Mar 11, 2013 at 3:42 PM
Another way that completely bypasses the policies would be to register the instance created by your factory directly with the container.
public class CustomFactoryStrategy : BuilderStrategy
{
  private readonly IFactory _factory;

  public CustomFactoryStrategy(IFactory factory)
  {
    _factory = factory;
  }

  public override void PreBuildUp(IBuilderContext context)
  {
    NamedTypeBuildKey key = context.OriginalBuildKey;

    if (context.Existing != null || !_factory.CanCreate(key.Type))
    {
      return;
    }
      
    context.Existing = _factory.Create(key.Type);

    var container = context.NewBuildUp<IUnityContainer>();

    container.RegisterInstance(key.Type, key.Name, context.Existing);

    context.BuildComplete = true;
  }
}
RegisterInstance uses the ContainerControlledLifetimeManager by default. You just need to specify Type and name from the original BuildKey
Mar 11, 2013 at 4:05 PM
Edited May 1, 2013 at 9:13 PM
Quick answer:

Instead of setting the policy like this:
_context.PersistentPolicies.Set(ltm, new NamedTypeBuildKey(key.Type));
Set it like this:
_context.PersistentPolicies.Set<ILifetimePolicy>(ltm, new NamedTypeBuildKey(key.Type));
In the first code snippet the policy will be inferred as ContainerControlledLifeTime since that is the type of ltm but we really want to explicitly set the ILifetimePolicy.

~~
Randy Levy
entlib.support@live.com
Enterprise Library support engineer
Support How-to
Mar 11, 2013 at 4:57 PM
Wow! How unexpected!

I thought the type specification there was useless and knowingly removed it, because the ContainerLifetime class already implemented the interface. It works as expected now, awesome stuff.

I would really like to know the why's here though (the ones I asked previously and now this one too). I see you stated "Quick answer" there. If you have some free time later on and could either explain them a bit or point me somewhere that does I would be very glad.

Weberse, I would try your method if Randy's correction did not work. Thanks a lot for the input too. I would actually like to hear what the differences would be if I went with your solution though, since it seems less complicated than the other solution. Would there be any drawbacks, or would it be just a different way to do the same thing?

Thanks a lot to both of you.
Juliano
Mar 12, 2013 at 11:09 AM
RegisterInstance triggers some logic inside the UnityDefaultBehaviorExtension that handles build key and lifetime management for you. You don't have to figure out the details of working with the underlying policies yourself.

Btw.: If you inject the container via constructor parameter you can remove the call to NewBuildUp as well.