简体   繁体   中英

Sharing instance between assemblies in Unity

Currently I'm using a singleton class and it works ok in every place I use it. However I'd like to make use of Unity IoC container for that. Registering it as below doesn't work the way I would like it to:

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

Meaning that if I do this:

container.Resolve<SomeNiceClass>();

In a class in assembly other than that it was first registered, I will get a new instance.

As you noticed yourself, you need to use the same instance of the container.

You can do this via Unity's IModule interface.

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

In your application, you register the module:

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

edit: As an additional note, your modules should not instantiate their own containers. The container only has to be instantiated in the main application . The modules only do registrations, nothing more!

If you need an instance of the container in your modules (outside of the IModule implementation), you just have to declare it in constructor.

public class MyModuleResolver 
{
    private readonly IUnityContainer container;

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

However, please note that it's not a good practice to directly reference your container inside of your domain/business layer. There are only two reasons you should ever have to use the IUnityContainer yourself:

  1. Inside your bootstrapper
  2. Inside your application types, ie certain factory classes that need to resolve a certain type manually depending on a runtime parameter

Everything else should be done automatically from your container via injection.

The assembly boundary shouldn't matter as long as you're passing a reference to the same container.

You could try naming it:

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

Its also worth noting that the documentation says that container lifetime is the default for RegisterInstance so you could just register like so:

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

Read the MSDN comments on this here: https://msdn.microsoft.com/en-ca/library/ff647854.aspx

If you still have an issue, you need to take another look at how you deal with the container.

I had exactly the same problem (not sure why everyone is commenting that it's so difficult or impossible to reproduce - from what I can see it's quite simple.

FYI here are steps to reproduce:

  1. Create a solution with 2 or more assemblies.
  2. Put one or more modules in each assembly.
  3. Rather than having the bootstrapper hardcode loading of each assembly, use some approach that dynamically discovers and loads them. I used a simplified version of this: http://brianlagunas.com/prism-dynamically-discover-and-load-modules-at-runtime/
  4. Try to register some singleton (ContainerControlledLifetime) type or instance in one module.
  5. Also try to reference this singleton in the other modules.
  6. In my case, I also pre-loaded the first "main" module out of order, within the ConfigureContainer part of the bootstrapper - maybe this is part of what caused my problem.

Once all of this is in place, I reproduced the same problem - modules in assembly1 (the same as the main module) somehow got instance 1 of my "singleton", whereas modules in assembly2 (which is only loaded later, dynamically) got instance 2 of my singleton. I verified that they are using the same container instance.

I fixed the problem in my case, by moving registration of these singletons out of the modules entirely, and into my bootstrapper (again in ConfigureContainer) before any of the modules ever get loaded.

Also note - when I explicitly call ModuleCatalog.AddModule with the module in assembly2 (without changing any of the rest of the code) the multiple instances problem went away. But when I removed that call to explicitly load it (in ConfigureModuleCatalog) and instead just allowed my dynamic module catalog to load it dynamically, the problem occurs. I guess this is all about just getting things in the right order of events within the bootstrapper - but it certainly doesn't work as you would expect, in this scenario.

Sorry if that's not a very succinct solution - it took quite a lot of trial and error to work all of this out. But I hope that helps somebody.

** update **

After all of that writeup, and thinking through things a bit more... i finally realized what had been causing this. Essentially, because I am pre-loading the main module which also happens to register some of the singletons, and then later on Unity is re-loading this same module along with all other modules during the bootstrapper module init phase, the same module is effectively re-registering those singleton classes, which I guess results in a duplicate definition and ultimately another instance being created. I had incorrectly assumed Unity was somehow smart enough to know it had already loaded the module, and not to reload it.

I've added some functionality to my modules (with a base module) to only allow each module to be loaded once, and ignore any subsequent calls to Initialize on that same module type (statically). I've then moved all of my singleton definitions back into the module. Things seem to be working fine now. So - that's what it was in my particular case.

So back to the original person who posted this question - and to anyone else looking for answers - if you are seeing your singletons instantiated more than once in Unity, perhaps double check that you aren't loading the modules or registering the types more than once.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM