在Unity中的程序集之间共享实例

时间:2015-11-27 20:41:33

标签: c# unity-container

目前我正在使用单例类,它在我使用它的每个地方都可以正常工作。但是我想为此使用Unity IoC容器。如下注册它不会按照我希望的方式工作:

container.RegisterInstance(new SomeNiceClass(), new ContainerControlledLifetimeManager());

意思是如果我这样做:

container.Resolve<SomeNiceClass>();

在除了首次注册之外的程序集中的类中,我将获得一个新实例。

3 个答案:

答案 0 :(得分:3)

正如您自己注意到的那样,您需要使用相同的容器实例。

您可以通过Unity的IModule界面执行此操作。

[Module(ModuleName="MyExternalModule", OnDemand=false)]
public class MyExternalModule : IModule
{
    private readonly IUnityContainer container;

    public MyExternalModule(IUnityContainer container)
    {
        if (container == null)
            throw new ArgumentNullException("container");

        this.container = container;
    }

    public void Initialize()
    {
        container.RegisterInstance(new MyService());
    }
}

在您的应用程序中,您注册了模块:

public class ApplicationBootstrapper : UnityBootstrapper
{
    protected override IModuleCatalog CreateModuleCatalog()
    {
        var moduleCatalog = new ModuleCatalog();
        moduleCatalog.AddModule(typeof(YourCompany.MyModule.MyExternalModule), InitializationMode.WhenAvailable);
        return moduleCatalog;
    }
    ...
}

修改 另外请注意,您的模块不应实例化自己的容器。只需在主应用程序中实例化容器。这些模块只进行注册,仅此而已!

如果您需要模块中的容器实例(IModule实现之外),则只需在构造函数中声明它。

public class MyModuleResolver 
{
    private readonly IUnityContainer container;

    public MyModuleResolver(IUnityContainer container)
    {
        if(container == null)
        {
            throw new ArgumentNullException("container");
        }
        this.container = container;
    }
}

但请注意,在您的域/业务层内直接引用您的容器并不是一个好习惯。你应该自己使用IUnityContainer只有两个原因:

  1. 在引导程序内
  2. 在您的应用程序类型中,即需要根据运行时参数手动解析某种类型的某些工厂类
  3. 其他所有东西都应该通过注射从容器中自动完成。

答案 1 :(得分:1)

只要您将引用传递给同一个容器,程序集边界就不重要了。

您可以尝试命名它:

container.RegisterInstance<SomeNiceClass>("MySingleton", new SomeNiceClass(), new ContainerControlledLifetimeManager());
container.Resolve<SomeNiceClass>("MySingleton");

还值得注意的是,文档说容器生命周期是RegisterInstance的默认值,所以你可以像这样注册:

container.RegisterInstance<SomeNiceClass>(new SomeNiceClass());

在此处阅读MSDN评论:https://msdn.microsoft.com/en-ca/library/ff647854.aspx

如果您仍有问题,则需要再次了解如何处理容器。

答案 2 :(得分:1)

我遇到了完全相同的问题(不确定为什么每个人都在评论复制它是如此困难或不可能 - 从我看来它非常简单。

仅供参考,以下是重现的步骤:

  1. 创建包含2个或更多程序集的解决方案。
  2. 在每个组件中放置一个或多个模块。
  3. 使用一些动态发现并加载它们的方法,而不是让每个程序集的引导程序硬编码加载。我使用了这个的简化版本: http://brianlagunas.com/prism-dynamically-discover-and-load-modules-at-runtime/
  4. 尝试在一个模块中注册一些singleton(ContainerControlledLifetime)类型或实例。
  5. 还尝试在其他模块中引用此单例。
  6. 在我的情况下,我还在引导程序的ConfigureContainer部分中预先加载了第一个“main”模块 - 也许这是导致我的问题的一部分。
  7. 一旦所有这一切都到位,我再现了同样的问题 - assembly1中的模块(与主模块相同)以某种方式获得了我的“singleton”的实例1,而assembly2中的模块(仅稍后加载,动态得到了我的单身实例2。我确认他们正在使用相同的容器实例。

    我在我的情况下修复了问题,将这些单例的注册完全移出模块,并在任何模块加载之前进入我的引导程序(再次在ConfigureContainer中)。

    另请注意 - 当我使用assembly2中的模块显式调用ModuleCatalog.AddModule时(不更改任何其余代码),多实例问题就消失了。但是当我删除该调用以显式加载它(在ConfigureModuleCatalog中)而只是允许我的动态模块目录动态加载它时,就会出现问题。我想这就是在引导程序中以正确的事件顺序获取事物 - 但在这种情况下它肯定不会像你期望的那样工作。

    很抱歉,如果这不是一个非常简洁的解决方案 - 需要进行大量的反复试验才能完成所有这些工作。但我希望能帮到别人。

    **更新**

    在所有这些写作之后,再多思考一下......我终于意识到导致这种情况的原因。本质上,因为我正在预加载主模块,这也恰好注册了一些单例,然后Unity在引导程序模块初始化阶段重新加载同一模块和所有其他模块,同样的模块是有效的重新注册那些单例类,我猜这会产生一个重复的定义,最终会创建另一个实例。我错误地认为Unity非常聪明,知道它已经加载了模块,而不是重新加载它。

    我已经为我的模块添加了一些功能(使用基本模块),只允许每个模块加载一次,并忽略对相同模块类型(静态)的任何后续调用Initialize。然后我将所有单例定义移回模块中。现在似乎工作正常。所以 - 这就是我的具体情况。

    回到发布此问题的原始人 - 以及寻找答案的其他人 - 如果您在Unity中看到您的单身人士不止一次实例化,也许请仔细检查您是否未加载模块或注册类型不止一次。