Prism 7 WPF应用:多实现接口的VM注册与导航问题
我来帮你理清这两个问题,顺便给你一个能适配未来新增实现的最优方案:
问题1:为啥总是注入最后注册的IActivityService?
这是因为你注册的是命名实例,但ActivityManagementViewModel的构造函数里没有指定要解析哪个命名版本。Prism底层用的容器(比如Unity、DryIoc)在遇到同一个接口有多个注册项时,默认会返回最后注册的那个实例作为"默认实现"。你代码里先注册了TestActivtyService,之后又注册了ExerciseActivityService,所以容器自然每次都把后者注入到视图模型里了。
问题2:怎么在运行时选指定的实现导航到视图?
核心思路是在导航时把要使用的服务实例名称传递过去,让视图模型知道该用哪个。这里有两种实用方案:
方案1:导航传参+手动解析服务
在导航时把服务名称塞进导航参数,然后在视图模型的导航回调里手动从容器拉取对应实例:
先改导航代码:
private void NavigateToTesting() { var navParams = new NavigationParameters(); navParams.Add("TargetService", InstanceNames.TestActivtyService); RegionManager.RequestNavigate(RegionNames.MainRegion, ViewNames.ActivityManagement.GetUri(), navParams); } private void NavigateToExercise() { var navParams = new NavigationParameters(); navParams.Add("TargetService", InstanceNames.ExerciseActivityService); RegionManager.RequestNavigate(RegionNames.MainRegion, ViewNames.ActivityManagement.GetUri(), navParams); }
然后修改ActivityManagementViewModel,需要注入IContainerProvider来手动解析,并重写OnNavigatedTo方法:
private readonly IContainerProvider _containerProvider; private IActivityService _activityService; public ActivityManagementViewModel(IRegionManager regionManager, IContainerProvider containerProvider) : base(regionManager) { _containerProvider = containerProvider; } public override void OnNavigatedTo(NavigationContext navigationContext) { base.OnNavigatedTo(navigationContext); if (navigationContext.Parameters.TryGetValue<string>("TargetService", out var serviceName)) { _activityService = _containerProvider.Resolve<IActivityService>(serviceName); } }
方案2:Unity容器专属的命名参数注入(可选)
如果你的项目用的是Unity容器,可以在构造函数里通过[Dependency("实例名")]标记,但这种方式不够灵活,因为你没法动态切换,所以还是方案1更通用。
最优方案:用工厂模式适配未来新增实现
既然你的不同IActivityService只是数据仓库不同,视图和逻辑完全一致,那用工厂模式可以彻底解决重复配置的问题,以后新增实现只需要加一行注册代码就行:
步骤1:定义服务工厂接口
public interface IActivityServiceFactory { IActivityService GetService(string serviceName); }
步骤2:实现工厂类
public class ActivityServiceFactory : IActivityServiceFactory { private readonly IContainerProvider _containerProvider; public ActivityServiceFactory(IContainerProvider containerProvider) { _containerProvider = containerProvider; } public IActivityService GetService(string serviceName) { return _containerProvider.Resolve<IActivityService>(serviceName); } }
步骤3:注册工厂和服务
protected override void RegisterTypes(IContainerRegistry containerRegistry) { // 注册两个服务实例 containerRegistry.Register<IActivityService, TestActivtyService>(InstanceNames.TestActivtyService); containerRegistry.Register<IActivityService, ExerciseActivityService>(InstanceNames.ExerciseActivityService); // 注册工厂 containerRegistry.Register<IActivityServiceFactory, ActivityServiceFactory>(); // 导航注册不变 containerRegistry.RegisterForNavigation<Shell, ShellViewModel>(ViewNames.Shell); containerRegistry.RegisterForNavigation<Home, HomeViewModel>(ViewNames.Home); containerRegistry.RegisterForNavigation<ActivityManagement, ActivityManagementViewModel>(ViewNames.TestManagement); }
步骤4:视图模型注入工厂,动态获取服务
private readonly IActivityServiceFactory _serviceFactory; private IActivityService _activityService; public ActivityManagementViewModel(IRegionManager regionManager, IActivityServiceFactory serviceFactory) : base(regionManager) { _serviceFactory = serviceFactory; } public override void OnNavigatedTo(NavigationContext navigationContext) { base.OnNavigatedTo(navigationContext); if (navigationContext.Parameters.TryGetValue<string>("TargetService", out var serviceName)) { _activityService = _serviceFactory.GetService(serviceName); } }
以后新增IActivityService的实现时,只需要加一行containerRegistry.Register<IActivityService, NewService>(InstanceNames.NewService);,完全不用改视图模型或导航逻辑,完美符合开闭原则。
内容的提问来源于stack exchange,提问作者Michael Roy

