Removing type registrations from Unity

Apr 23, 2012 at 5:54 AM

Is there currently a way to remove registrations from the Unity container? From what I can tell, there's no simple way to do it.  The only potential solution looks to be creating a custom lifetime manager.

Specifically, I need a way to dispose of already-resolved singleton objects which were initially registered with the ContainerControlledLifetimeManager.  I could resolve the objects and call dispose on them - however, would this remove all of the strong references to it from Unity?

Apr 24, 2012 at 5:39 AM

I don't know of a way to remove registrations from the Unity container.  Depending on the circumstance a child container may help since when the container is disposed the registrations will also be removed (and the parent container will be unchanged).

If you resolved the objects and called Dispose on them Unity would still maintain a strong reference to the object and would return the singleton on the next resolve (but probably the internal state would be invalid since Dispose was already called).  If you need weak references then the ExternallyControlledLifetimeManager provides that capability.  I'm not sure if that's what you are after here, though.

If you call RemoveValue on the LifetimeManager then you can Dispose the object and have Unity release it's strong reference.  This does not remove the registration, though, so a further resolve will result in a new object being created:

    IUnityContainer container = new UnityContainer();
            
    // register ILogger as singleton
    container.RegisterType<ILogger, Logger>(new ContainerControlledLifetimeManager());

    // logger1 and logger2 are the same instance since singleton
    var logger1 = container.Resolve<ILogger>();
    var logger2 = container.Resolve<ILogger>();

    // Get registration for ILogger
    var registration = container.Registrations.First(r => r.RegisteredType == typeof(ILogger));
            
    // Dispose called and value set to null in container but logger1 and logger2 still point to singleton instance 
    // although state may be invalid since they are "Disposed"
    registration.LifetimeManager.RemoveValue();
            
    // Type registration still valid but new singleton instance created on next resolve
    var logger3 = container.Resolve<ILogger>();

If you register an existing instance you will get a slightly difference behavior:

    IUnityContainer container = new UnityContainer();

    // register ILogger as singleton
    ILogger logger = new Logger();

    // Register ILogger instance as singleton
    container.RegisterInstance<ILogger>(logger);

    // logger and logger1 are the same instance since singleton
    var logger1 = container.Resolve<ILogger>();
    
    // Get registration for ILogger
    var registration = container.Registrations.First(r => r.RegisteredType == typeof(ILogger));

    // Dispose called but logger and logger1 still point to singleton 
    // although state may be invalid since they are "Disposed"
    registration.LifetimeManager.RemoveValue();

    // Exception thrown since no concrete type matches ILogger
    var logger3 = container.Resolve<ILogger>();

I'm not sure if that is preferable in your scenario (you would probably know that Dispose was already called since an exception is thrown).

If the above still doesn't help then a custom lifetime manager should be able to handle your requirements.

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

Apr 24, 2012 at 6:31 AM

The intention behind removing registrations is to register new services to replace existing services, and remove existing services from memory.  As long as a Unity registration doesn't contain a strong reference to an existing instance, and only the lifetime manager contains a strong reference to an existing instance (or static type), then I should just be able to call .RemoveValue() on the lifetime manager.

But do I actually need a custom lifetime manager for this, or would TransientLifetimeManager and ContainerControlledLifetimeManager be sufficient for my needs? In other words, if I call RemoveValue() on either one of these lifetime managers, would that remove all string references to the existing instance / static service?

Apr 24, 2012 at 6:58 AM

Actually, after thinking on it some more, I don't believe I even need to worry about non-singleton registrations types, because Unity doesn't actually store any references to them, does it? 

The only scenario I need to be concerned about for properly disposing objects would be singleton objects - in which case, I could have a Dictionary store a registration Type (as the key) and its ContainerControlledLifetimeManager (as the value) for each registration.  Then, when a new service needs to be registered for the same registration Type, I could iterate on the Dictionary, and call RemoveValue on the previously registered and existing service. 

Apr 24, 2012 at 7:43 AM

Yes, you are right: TransientLifetimeManager "does nothing" so you don't have to worry about it.

The dictionary approach would work.  Or you could look up the registration in the list of registrations (both should work).  You could implement it as an extension method:

    public static class ContainerExtensions
    {
        private static object locker = new object();

        public static IUnityContainer ReRegisterType<TFROM, TTO>(this IUnityContainer container, LifetimeManager manager) 
        {
            lock (locker)
            {
                if (container.IsRegistered<TFROM>())
                {
                    var registration = container.Registrations.First(r => r.RegisteredType == typeof(TFROM));
                    registration.LifetimeManager.RemoveValue();

                    container.RegisterType(typeof(TFROM), typeof(TTO), manager);
                }
            }

            return container;
        }
    }

The above is a bit naive but the approach should work.

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

Apr 24, 2012 at 7:52 AM
Edited Apr 24, 2012 at 7:53 AM

Thanks, Randy - I already tested it and it did work.  Here's what I created:

        private static IDictionary<Tuple<Type, object>, LifetimeManager> _lifetimeManagers;

        /// <summary>
        /// Store the specified registration type, registration context, and lifetime manager.
        /// </summary>
        /// <param name="type">The registration type.</param>
        /// <param name="context">The registration context.</param>
        /// <param name="lifetimeManager">The lifetime manager.</param>
        /// <returns>The specified lifetime manager.</returns>
        public static LifetimeManager StoreRegistrationTypeAndLifetimeManager(Type type, object context, LifetimeManager lifetimeManager)
        {
            _lifetimeManagers.Add(Tuple.Create(type, (object)null), lifetimeManager);
            return lifetimeManager;
        }

        /// <summary>
        /// Remove the lifetime manager references from the specified type and registration context,
        /// and then remove the lifetime manager from the list of lifetime managers (typically used when
        /// registering new singleton services via the Plugin features).
        /// </summary>
        /// <param name="type">The specified type.</param>
        /// <param name="context">The specified context.</param>
        public static void RemoveLifetimeManagerReferencesFrom(Type type, object context)
        {
            LifetimeManager lifetimeManager;
            Tuple<Type, object> key = Tuple.Create(type, context);

            if (_lifetimeManagers.TryGetValue(key, out lifetimeManager))
            {
                _lifetimeManagers.Remove(key);
                lifetimeManager.RemoveValue();
            }
        }

New registrations can then be made this way:

                RemoveLifetimeManagerReferencesFrom(typeof(ILoginCore));
                unityContainer.RegisterType<ILoginCore, DemoLoginCore>(StoreRegistrationTypeAndLifetimeManager(typeof(ILoginCore), null, new ContainerControlledLifetimeManager()));

The first line above ensures that all previously registered singletons are removed from their lifetimes managers, and the second line registers a new implementation and stores the lifetime manager in the dictionary.