Request for configurable default lifetimemanager

Apr 13, 2012 at 8:22 AM

Hello!

Currently, Unity has the TransientLifetimeManager as the default LifetimeManager. I would like to configure Unity to use a different default LifetimeManager as required.

My main reason to do this is to support deterministic disposal. I would like to use a custom ScopeBasedLifetimeManager as the default.

Does this make sense to you? Or should I write some kind of strategy or extension to use a default LifetimeManager?

Thanks for any help!

Editor
Apr 13, 2012 at 9:08 PM

You could log your suggestion at the EntLib Uservoice site.

Could you create your specific lifetime manager and then supply it when registering types & instances?  You could create extension methods to automatically add your custom lifetime manager.  E.g. container.RegisterTypeWithScope<T>(...) or something similar.  

--
Randy Levy
Enterprise Library support engineer
entlib.support@live.com 

Apr 14, 2012 at 5:14 AM
Edited Apr 14, 2012 at 5:15 AM

No, I don't want to have to use it during explicit registrations, because Unity supports implicit registrations.

Whenever a concrete dependency is used during object build up, Unity does not require you to have an explicit registration for that. Unity just knows it is not a interface or an abstract class and just tries to resolve the dependency for you.

Imagine a class which has a SqlConnection in the constructor (not the way I would write it, but imagine for this case). Unity is able to resolve the SqlConnection without explicit registration, but I do want Unity to use the custom (default) lifetimemanager for it.

I will put it on the Uservoice site. Thanks!

Editor
Apr 14, 2012 at 8:02 AM

Yes, I understand the scenario.

I'm thinking you can use a UnityContainerExtension to do what you want.

In this container extension the target's lifetime is set to be a singelton:

    public class LifetimeContainerExtension : UnityContainerExtension
    {
        protected override void Initialize()
        {
            var strategy = new MyBuilderStrategy(Container);

            Context.Strategies.Add(strategy, UnityBuildStage.Creation);
        }

        class MyBuilderStrategy : BuilderStrategy
        {
            private readonly IUnityContainer container;

            public MyBuilderStrategy(IUnityContainer container)
            {
                this.container = container;
            }

            public override void PreBuildUp(IBuilderContext context)
            {
                context.PersistentPolicies.Set<ILifetimePolicy>(new ContainerControlledLifetimeManager(), 
                    context.BuildKey);
            }
        }
    }

 

    var container = new UnityContainer()
        .AddNewExtension<LifetimeContainerExtension>();
    //container.AddNewExtension<InterfacePropertyContainerExtension>();
            
    var myClass1 = container.Resolve<MyClass>();
    var myClass2 = container.Resolve<MyClass>();

    System.Diagnostics.Debug.Assert(ReferenceEquals(myClass1, myClass2));

 

 

Hopefully that helps.

--
Randy Levy
Enterprise Library support engineer
entlib.support@live.com 

Apr 16, 2012 at 11:40 AM

Hi Randy!

I tried you approach and modified it to this extension:

    public class DefaultLifetimeManagerExtension<TLifetimeManager> : UnityContainerExtension where TLifetimeManager : ILifetimePolicy, new()
    {
        protected override void Initialize()
        {
            Context.Strategies.Add(new DefaultLifetimeManagerStrategy(), UnityBuildStage.Creation);
        }

        #region Nested type: DefaultLifetimeManagerStrategy

        private class DefaultLifetimeManagerStrategy : BuilderStrategy
        {
            public override void PreBuildUp(IBuilderContext context)
            {
                Debug.WriteLine(string.Format("Setting ILifetimePolicy to {0} for build key {1}.",
                                              typeof (TLifetimeManager).Name, context.BuildKey), "DefaultLifetimeManagerExtension");

                // This overrides the current LifetimeManager.
                // We should only apply a new policy if we don't have a registered LifetimeManager.
                // No way to know here whether the current LifetimeManager is the custom registered one,
                // or the TransientLifetimeManager added by Unity by default.
                context.PersistentPolicies.Set<ILifetimePolicy>(new TLifetimeManager(), context.BuildKey);
            }
        }

        #endregion
    }

As you can see in the comments, this does not respect custom LifetimeManager registrations. With this piece of code, all objects are created using the default LifetimeManager...

The problem is that the LifetimeStrategy already injects the default policy before the code above is called:

        private static ILifetimePolicy GetLifetimePolicy(IBuilderContext context)
        {
            ILifetimePolicy policy = context.Policies.GetNoDefault<ILifetimePolicy>(context.BuildKey, false);
            if(policy == null && context.BuildKey.Type.IsGenericType)
            {
                policy = GetLifetimePolicyForGenericType(context);
            }

            if(policy == null)
            {
                policy = new TransientLifetimeManager();
                context.PersistentPolicies.Set<ILifetimePolicy>(policy, context.BuildKey);
            }
            return policy;
        }

Due to this behavior, I suspect it is impossible to know whether the assigned ILifetimePolicy has been assigned via a custom type registration, or set as the default TransientLifetimeManager by the LifetimeStrategy.

Editor
Apr 17, 2012 at 2:20 AM

Yes, I see what you are saying.  The container extension above sets the LifetimeStrategy for all objects.

The above approach works but does not respect custom lifetime registrations.  You might be able to create (yet) another container extension to handle custom registrations (Registering event) and record when a type is registered with a non-default LifetimeManager and then utilize the explicit registration values if appropriate.  I think that would work but it's getting a bit messy.  

Another option might be to create a new extension that removes the applicable strategies (LifetimeStrategy) and policies (ILifetimePolicy) and replaces them with an appropriate implementation.  The downside of that is you may have to recreate some existing Unity functionality (copy/paste from source) into the extension.

As a last resort there is always modifying the source code to do exactly what you want.

--
Randy Levy
Enterprise Library support engineer
entlib.support@live.com 

Apr 17, 2012 at 9:13 AM

I think the only viable solution is the one you call 'the last resort'. Having a configurable default lifetime manager is probably a good thing for Unity.

So, I created the feature request on UserVoice.

Jul 30, 2013 at 12:40 PM
Edited Jul 30, 2013 at 12:47 PM
I think i have a solution to this issue, I have not had a chance to check and make sure it works, but the logic seems sound.

using Microsoft.Practices.Unity;
using Microsoft.Practices.Unity.ObjectBuilder;

 public class DefaultLifetimeManagerExtension<TLifetimeManager> : UnityContainerExtension
        where TLifetimeManager : LifetimeManager, new()
    {
        protected override void Initialize()
        {
            LifetimeManagerFactory factory = new LifetimeManagerFactory(this.Context, typeof(TLifetimeManager));
            Context.Strategies.Add(new DefaultLifetimeManagerStrategy(factory), UnityBuildStage.TypeMapping);
        }
    }
using System;
using Microsoft.Practices.ObjectBuilder2;
using Microsoft.Practices.Unity;

 public class DefaultLifetimeManagerStrategy : BuilderStrategy
    {
        private LifetimeManagerFactory factory;

        public DefaultLifetimeManagerStrategy(LifetimeManagerFactory factory)
            : base()
        {
            this.factory = factory;
        }

        public override void PreBuildUp(IBuilderContext context)
        {
            if (context.Existing == null)
            {
                ILifetimePolicy lifetime = context.Policies.GetNoDefault<ILifetimePolicy>(context.BuildKey, false);

                if (lifetime == null && context.BuildKey.Type.IsGenericType)
                {
                    lifetime = GetLifetimePolicyForGenericType(context);
                }

                if (lifetime == null)
                {
                    // calling CreateLifetimePolicy does the following 
                    // 1) Creates a new instance of the life time using containerContext.Container.Resolve.
                    // 2) If the lifetime is an IDisposable calls containerContext.Lifetime.Add passing the lifetime.
                    // 3) Sets lifetime.InUse to true. 
                    // 4 returns the lifetime.
                    lifetime = factory.CreateLifetimePolicy();

                    context.PersistentPolicies.Set<ILifetimePolicy>(lifetime, context.BuildKey);
                }
            }
        }

       //TODO: method was taken from unity source. need to document it as such and give credit where credit is due.
        private ILifetimePolicy GetLifetimePolicyForGenericType(IBuilderContext context)
        {
            Type typeToBuild = context.BuildKey.Type;

            object openGenericBuildKey = new NamedTypeBuildKey(
                typeToBuild.GetGenericTypeDefinition(),
                context.BuildKey.Name);

            IPolicyList factorySource;

            ILifetimeFactoryPolicy factoryPolicy =
                context.Policies.Get<ILifetimeFactoryPolicy>(openGenericBuildKey, out factorySource);

            if (factoryPolicy != null)
            {
                ILifetimePolicy lifetime = factoryPolicy.CreateLifetimePolicy();

                factorySource.Set<ILifetimePolicy>(lifetime, context.BuildKey);

                return lifetime;
            }
            return null;
        }
    }