如何从 Prism 应用程序的各个模块中的 Unity 容器中获取相同的对象实例?

How to get the same instance of object from Unity container in various modules of Prism application?

我有一个 Prism 应用程序,其中包含三个模块:

SharedServiceModule 只有接口及其实现 CommonService:

public interface ICommonService
{
    string SomeStorage { get; set; }        
}

public class CommonService : ICommonService
{
    string fooStorage;
    public string FooStorage
    {
        get
        {
           return fooStorage;
        }
        set
        {
            fooStorage = value;
            OnPropertyChanged("FooStorage");                                
        }
    }
}

我想要的是使用 Shared Services 在模块之间创建通信。所以我在 ModuleAViewModelA 分配了 «ModuleAValue»,然后我想在 ViewModelB 中读取这个值模块 B。来看看详情吧。

我在 ModuleAViewModelA 中创建了一个 ICommonService 的实例,并将值 "ModuleAValue" 分配给FooStorage:

ViewModelA的方法:

unityContainer = new UnityContainer();
unityContainer.RegisterType<ICommonService, CommonService>(new ContainerControlledLifetimeManager());
IMyService someFoo = unityContainer.Resolve<ICommonService>();
someFoo.FooStorage = "ModuleAValue";//value is "ModuleAValue" in FooStorage

然后我想在ModuleBviewModelB中读取这个数据。但是 FooStorage 的值不是 'Module A',而是空值:

ViewModelB的方法:

IUnityContainer unityContainer=new UnityContainer//creation of UnityContainer in ModuleB
ICommonService someFoo = unityContainer.Resolve<CommonService>();
string str=someFoo.FooStorage;//value is empty in 
FooStorage, but it should be "ModuleAValue"    

我的引导程序是:

public class Bootstrapper:UnityBootstrapper
{
    protected override DependencyObject CreateShell()
    {
        return Container.Resolve<Shell>();
    }

    protected override void InitializeShell()
    {
        base.InitializeShell();
        App.Current.MainWindow = (Window)Shell;
        App.Current.MainWindow.Show();
    }

    protected override void ConfigureContainer()
    {
        base.ConfigureContainer();
        Container.RegisterType<IShellViewModel, ShellViewModel>();
        RegisterTypeIfMissing(typeof(IMyService), typeof(MyService), true);
    }

    protected override RegionAdapterMappings ConfigureRegionAdapterMappings()
    {
        RegionAdapterMappings mappings = base.ConfigureRegionAdapterMappings();
        mappings.RegisterMapping(typeof(StackPanel), Container.Resolve<StackPanelRegionAdapter>());
        return mappings;
    }

    protected override IModuleCatalog CreateModuleCatalog()
    {
        ModuleCatalog catalog = new ModuleCatalog();
        catalog.AddModule(typeof(ModuleAModule));
        catalog.AddModule(typeof(ModuleBModule));
        return catalog;
    }
}

我做错了什么?在我看来,Unity 总是创建 CommonService 的新实例。从 Unity 容器获取同一个实例时我做错了什么?

任何帮助将不胜感激!

您不应该创建新的容器实例。通常您的应用程序中应该只有一个容器实例。 如果您使用的是棱镜,您的视图模型也应该从容器中创建(如果棱镜负责视图模型的创建而不是从容器中创建的)。在这种情况下,只需在视图模型中使用 ICommonService 类型的参数创建构造函数,如下所示:

public ViewModelA(ICommonService service) { ... }

然后在创建 ViewModel 期间,相同的服务实例将被注入到该 ViewModel。

并且通常在应用程序启动时在Shell中注册常用服务。但是如果你愿意,你也可以在模块中注册一个服务,只需使用在应用程序启动期间创建的相同的统一容器。在视图模型中使用带有 IUnityContainer 类型参数的构造函数。

您应用的引导程序会为您创建一个 UnityContainer,请参阅 UnityBootstrapper.cs:

protected virtual IUnityContainer CreateContainer()
{
    return new UnityContainer();
}

引导程序还应将容器注册为视图模型工厂:

protected override void ConfigureContainer()
{
    base.ConfigureContainer();

    ViewModelLocationProvider.SetDefaultViewModelFactory( type => Container.Resolve( type ) );
}

在您的模块定义 class 中,您可以将此 'global' 容器作为依赖项注入:

public class ClientModule : IModule
{
    private readonly IUnityContainer _unityContainer;

    public ClientModule( IUnityContainer unityContainer )
    {
        _unityContainer = unityContainer;
    }
}

在您的模块初始化中,您向此容器注册类型或实例:

public void Initialize()
{
    // Register services
    _unityContainer.RegisterType<IGameClock, LocalGameClock>( new ContainerControlledLifetimeManager() );
    _unityContainer.RegisterType<IWorldStateService, LocalWorldStateService>( new ContainerControlledLifetimeManager() );
}

在您的视图中(在 xaml 中),您现在可以使用 ViewModelLocator.AutoWireViewModel="True" 自动为您的视图创建视图模型。 ViewModelLocationProvider 将使用 'global' 容器来解析视图模型(如上定义),因此所有视图模型都将收到我们的相同实例,例如 IGameClock.

辅助建议:您不想自己直接调用 Resolve。以这种方式使用它首先会破坏使用统一性的全部目的。最好将依赖项作为构造函数参数注入,并且只在最高级别使用容器,即在模块初始化中。而且您永远不需要创建多个容器,以免您确切地知道自己在做什么。