3

I am converting a Prim 6 using MEF for IOC to Prism 7 Unity since MEF is not longer supported. The biggest difference I have to contend with is the presumption in MEF that everything is singleton by default while Unity tends to assume the opposite.

I have most of it converted but one thing I ran into was having a constructor reference a singleton via collections. For example, I have these interfaces and classes:

public interface IAppService { }

public interface IDebugService : IAppService { }
public class DebugService : IDebugService { }

public interface IOtherService : IAppService { }
public class OtherService : IOtherService { }

with DebugService and OtherService registers as singletons and mapped to both of their respective interfaces:

public void RegisterTypes(IContainerRegistry containerRegistry)
{
    containerRegistry.RegisterSingleton<IModuleMessageBus, ModuleMessageBus>();

    containerRegistry.RegisterSingleton<DebugService>();
    containerRegistry.RegisterSingleton<IDebugService, DebugService>();
    containerRegistry.Register<IAppService, DebugService>(typeof(DebugService).FullName);

    containerRegistry.RegisterSingleton<OtherService>();
    containerRegistry.RegisterSingleton<IOtherService, OtherService>();
    containerRegistry.Register<IAppService, OtherService>(typeof(OtherService).FullName);
}

The intent is DebugService/IDebugService and OtherService/IOtherService are registered/mapped as singletons which would return the same instance but IAppService as named instances of each so there can be more one IAppService registered. Any call to get a collection would include the singleton of DebugService and OtherService.

The second part is what is giving me a problem. Getting a collection of IAppService in a constructor always generates new instances. So, if I have this:

public SomeViewModel(
    IModuleMessageBus messageBus
    , IDebugService debugService
    , IEnumerable<IAppService> services
)   {   }

public AnotherViewModel(
    IModuleMessageBus messageBus
    , IDebugService debugService
)   {   }

What will happen is the constructor for DebugService will be hit twice. Once for the IDebugService injection for BOTH constructors and again specifically for the IEnumerable. If I comment out , IEnumerable<IAppService> services then it is only hit once. How can I have one and only one instance of the services but still register them with other interfaces to obtain as collections.


UPDATE

Thanks to Haukinger's response, I took a look at Prism's github since that overload is not exposed by the module. Turns out they are adding it in 7.2. THe latest preview (5 days old as of the time of this writing) has it merged in:

https://github.com/PrismLibrary/Prism/pull/1668


UPDATE 2

Unfortunately, it still seems to be a problem. I changed the registration to this and it still is hit twice:

containerRegistry.RegisterSingleton<DebugService>();
containerRegistry.RegisterSingleton<IDebugService, DebugService>();
containerRegistry.GetContainer().RegisterSingleton(
    typeof(IAppService)
    , typeof(DebugService)
    , typeof(DebugService).FullName
);

containerRegistry.RegisterSingleton<OtherService>();
containerRegistry.RegisterSingleton<IOtherService, OtherService>();
containerRegistry.GetContainer().RegisterSingleton(
    typeof(IAppService)
    , typeof(OtherService)
    , typeof(OtherService).FullName
);

Upgrading PRISM to the preview will not make a difference since it still references the same unity versions. But updating the unity packages will break Prism since they contain braking changes (looks like in Unity.Abstraction).


ANSWER THANKS TO HAUKINGER

Haukinger put me on the right track so give him a bump if this helps!!

Since prism 7 (currently) is using Unit Container 5.8 / Unity Abstraction 3, we have to use the InjectionFactory which is marked as obsolete and will soon be removed. To get the above to work, I had to do this (soooo gross):

containerRegistry.RegisterSingleton<DebugService>();
containerRegistry.RegisterSingleton<IDebugService, DebugService>();
//Manually map the registration resolve
containerRegistry.GetContainer().RegisterType<IAppService>(
    typeof(DebugService).FullName
    , new InjectionFactory(c => c.Resolve<DebugService>())
);

containerRegistry.RegisterSingleton<OtherService>();
containerRegistry.RegisterSingleton<IOtherService, OtherService>();
//Manually map the registration resolve
containerRegistry.GetContainer().RegisterType<IAppService>(
    typeof(OtherService).FullName
    , new InjectionFactory(c => c.Resolve<OtherService>())
);
4

1 回答 1

3

If you register transiently like this

containerRegistry.Register<IAppService, DebugService>(typeof(DebugService).FullName);

then resolving a new DebugService every time is expected behavior.

If you want only one DebugService instance to exist, register it as singleton:

containerRegistry.RegisterSingleton<IAppService, DebugService>(typeof(DebugService).FullName);

For a detailed description of what Unity 5 does here and why it does so (unlike older versions), have a look at this wiki page on github.

EDIT: it's a bit more complicated, and the solution looks quite a bit workaround-ish...

container.RegisterSingleton<Service>();
container.RegisterFactory<ISingleInterface>( x => x.Resolve<Service>() );
container.RegisterFactory<IEnumeratedInterface>( "service", x => x.Resolve<Service>() );

This is all for IUnityContainer, you have to get it from the IContainerRegistryby use of the GetContainer() extension.

Further reading at unity's github. Depending on your unity version, you can/have to use InjectionFactory instead of RegisterFactory.

于 2019-05-20T21:22:11.970 回答