1
votes

I am facing problem with disposing unnecessary objects from DI container. I use Prism for Xamarin.Forms with Unity container.

Application gets configuration from some database, creates some services using this configuration and registers this services in container using ContainerControlledLifetimeManager. This services are used while resolving views and viewmodels. When configuration changes application retrieves again changed configuration and now problem comes: how can I remove previous registrations and register new services? If I simply re-register service then previous service is not GC-ed until disposing container.

I cannot dispose container, because it is created and managed by Prism (can I?).

I cannot use child container because Prism will not resolve views and viewmodels using child container (can I?)

Should I use different DI? Does Autofac or other DI support such approach?

EDIT:

I just have tested disposing of re-registered objects in Unity. It came out that re-registering using:

Container.RegisterType<IFoo, Foo>(new ContainerControlledLifetimeManager())

really releases previously registered objects. But I have also registrations using just type:

Container.RegisterType<Foo>(new ContainerControlledLifetimeManager())

or using instance:

Container.RegisterInstance(new Foo())

and these objects are not released when re-registering.

So now the only solution is to reconstruct the Unity container? Or give a try to other ioc container?

2
With Unity, re-registering overwrites the prior registration. There is no concept of an unregister with Unity, so that is an option. Could be interesting to you; Other SO refR. Richards
@R.Richards does re-registering cause disposing previously registered object? Will it be GC-ed?raV720
As long as any of your code doesn't maintain a reference to the previously registered object, it should be GC-ed. Are you using any lifetime management with the registrations you currently have?R. Richards
As I wrote I use ContainerControlledLifetimeManager - as I know this manager will keep objects until container is disposed.raV720
Even that one could be overwritten, if need be. I think that re-registering is safe. Although, I can't think of a good way to validate/test that to be 100% certain.R. Richards

2 Answers

2
votes

Without knowing all of the specifics of what you are looking to accomplish it's impossible to give you a solid roadmap, so I'll touch on some things to consider.

Reregistering Services

If you have some service IFoo, and two implementations FooA and FooB and you initially registered FooA as the implementation for IFoo (with a container controlled lifetime, registering FooB with the container should dispose of the FooA instance and FooB should be generated going forward.

Reconstructing the container

If you have to reconstruct the Container, it should possible. I haven't ever run into a use case where I have had to try something like what you are looking to do. For starters you probably want to take a look at the Initialize method from PrismApplicationBase. This is where the container gets constructed and setup. To handle the reconstruction, you will want to create an event that you subscribe to in your App class.

public partial class App
{
    protected override void OnInitialized()
    {
        var ea = Container.Resolve<IEventAggregator>();
        ea.GetEvent<SettingsChangedEvent>().Subscribe(OnSettingsChangedEvent);
        // navigate
    }

    private void OnSettingsChangedEvent()
    {
        var ea = Container.Resolve<IEventAggregator>();
        // prevent a memory leak
        ea.GetEvent<SettingsChangedEvent>().Unsubscribe(OnSettingsChangedEvent);

        // If you need platform specific types be sure to register either the 
        // IPlatformInitializer or some similar helper
        var platformInitializer = Container.Resolve<IPlatformInitializer>();

        ModuleCatalog = CreateModuleCatalog();
        ConfigureModuleCatalog();
        Container = CreateContainer();
        ConfigureContainer();

        // This would be your original RegisterTypes, so this assumes you 
        // look at your settings when initially registering types.
        RegisterTypes();

        // See notes above
        platformInitializer.RegisterTypes(Container);

        NavigationService = CreateNavigationService();
        InitializeModules();

        // Your container is now reset. 
        var ea = Container.Resolve<IEventAggregator>();
        ea.GetEvent<SettingsChangedEvent>().Subscribe(OnSettingsChangedEvent>()
    }
}

Containers

As for choosing a container. There is nothing wrong with Unity. Just know that when you're working with Unity, you're going to be stuck with the way it is since it apparently it is a dead project now. Ninject for Prism Forms uses a PCL variant that doesn't seem to be maintained anymore, but when the switch to NetStandard is made Prism will be able to target the current version of Ninject. As for Autofac, there you are dealing with an immutable container so the moment you resolve something you cannot update any new registrations. Autofac for Prism Forms is also a version behind for the same reason as Ninject. DryIoc for Prism forms is a great container and actually the one I am using on all of my current projects. It is also being actively maintained so you can expect use cases you run into to at least be heard.

0
votes

Thanks for help to Dan S. and R. Richards.

Recreating Prism container caused problems in navigation. Maybe it is possible to fix it but I do not know how.

Using different IOC container would require too much time to learn it.

I ended up with custom lifetime manager (the solution provided in R. Richards link):

class CustomLifetimeManager : LifetimeManager
{
    private object _Value;

    public override object GetValue()
    {
        return _Value;
    }

    public override void RemoveValue()
    {
        _Value = null;
    }

    public override void SetValue(object newValue)
    {
        _Value = newValue;
    }
}

Above lifetime manager allows to remove registrations:

public static class UnityContainerExtension
{
    /// <summary>
    /// Removes registrations that were registred using <see cref="CustomLifetimeManager"/>
    /// </summary>
    /// <param name="container"></param>
    public static void RemoveCustomLifetimeRegistrations(this IUnityContainer container)
    {
        var registrations = container.Registrations.Where(r => r.LifetimeManagerType == typeof(CustomLifetimeManager));

        foreach(var r in registrations)
        {
            r.LifetimeManager.RemoveValue();
        }
    }
}