问题
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?
回答1:
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.
回答2:
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();
}
}
}
来源:https://stackoverflow.com/questions/43936976/prism-di-container-dispose-unnecessary-objects