【问题标题】:Prism DI container - dispose unnecessary objectsPrism DI 容器 - 处理不必要的对象
【发布时间】:2017-10-11 17:29:45
【问题描述】:

我在处理 DI 容器中不必要的对象时遇到问题。我将 Prism 用于带有 Unity 容器的 Xamarin.Forms。

应用程序从某个数据库获取配置,使用此配置创建一些服务,并使用 ContainerControlledLifetimeManager 在容器中注册这些服务。在解析视图和视图模型时使用此服务。 当配置更改应用程序再次检索更改的配置时,现在问题来了:如何删除以前的注册并注册新服务?如果我只是重新注册服务,那么在处置容器之前,之前的服务不会被 GC 处理。

我无法处置容器,因为它是由 Prism 创建和管理的(我可以吗?)。

我不能使用子容器,因为 Prism 不会使用子容器解析视图和视图模型(可以吗?)

我应该使用不同的 DI 吗? Autofac 或其他 DI 是否支持这种方法?

编辑:

我刚刚测试了在 Unity 中处理重新注册的对象。结果是重新注册使用:

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

真正释放以前注册的对象。但我也有注册,只需输入:

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

或使用实例:

Container.RegisterInstance(new Foo())

并且这些对象在重新注册时不会被释放。

所以现在唯一的解决办法是重建 Unity 容器?或者试试其他ioc容器?

【问题讨论】:

  • 使用 Unity,重新注册会覆盖之前的注册。 Unity 没有取消注册的概念,因此这是一个选项。 Could be interesting to you; Other SO ref
  • @R.Richards 重新注册是否会导致处置以前注册的对象?会被 GC 编辑吗?
  • 只要您的任何代码不维护对先前注册对象的引用,就应该进行 GC-ed。您是否对当前的注册使用任何生命周期管理?
  • 正如我所写,我使用 ContainerControlledLifetimeManager - 我知道这个管理器会保留对象直到容器被处置。
  • 如果需要的话,即使是那个也可以被覆盖。我认为重新注册是安全的。虽然,我想不出一个好的方法来验证/测试它是 100% 确定的。

标签: c# unity-container prism autofac


【解决方案1】:

如果不了解您要完成的所有具体细节,就不可能为您提供可靠的路线图,因此我将谈谈一些需要考虑的事项。

重新注册服务

如果您有一些服务IFoo,以及两个实现FooAFooB,并且您最初将FooA 注册为IFoo 的实现(具有容器控制的生命周期,在容器中注册FooB应该处理 FooA 实例,并在以后生成 FooB

重构容器

如果必须重构Container,应该可以。我从来没有遇到过我不得不尝试像你想要做的事情的用例。对于初学者,您可能想看看PrismApplicationBase 中的Initialize 方法。这是构建和设置容器的地方。要处理重建,您需要创建一个您在 App 类中订阅的事件。

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>()
    }
}

容器

至于选择容器。 Unity 没有任何问题。只要知道,当你使用 Unity 时,你会被它的方式所困,因为它现在显然是一个死项目。 Prism Forms 的 Ninject 使用似乎不再维护的 PCL 变体,但是当切换到 NetStandard 时,Prism 将能够针对当前版本的 Ninject。至于 Autofac,您正在处理一个不可变的容器,因此当您解决某些问题时,您无法更新任何新的注册。 Autofac for Prism Forms 也是一个落后的版本,原因与 Ninject 相同。 DryIoc for Prism forms 是一个很棒的容器,实际上是我在所有当前项目中使用的容器。它也正在积极维护中,因此您可以期望您遇到的用例至少会被听到。

【讨论】:

  • 您对我编辑的问题有任何想法吗? Dryioc 是否支持重新注册并释放以前的对象?
  • DryIoc 实际上有一个Unregister 方法。所以真的,它可能是最适合你的容器。
【解决方案2】:

感谢 Dan S. 和 R. Richards 的帮助。

重新创建 Prism 容器导致导航出现问题。也许可以修复它,但我不知道如何。

使用不同的 IOC 容器需要太多时间来学习它。

我最终得到了自定义生命周期管理器(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;
    }
}

生命周期以上管理器允许删除注册:

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();
        }
    }
}

【讨论】:

    猜你喜欢
    • 2019-03-31
    • 2010-12-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-10-25
    • 1970-01-01
    • 2014-04-10
    • 2014-10-10
    相关资源
    最近更新 更多