How to get the correct INavigationService instance on resolving a type manually in Prism application

Viewed 257

It seems that when I resolve a type manually and request the INavigationService in it a different instance is injected than the one being used everywhere else.

To clearify my use-case here are excerpts from the relevant files. As you can see when resolving the type SampleProcess the INavigationService will be injected but the instance is different to the one that I got in ProcessService. (Which btw is the correct instance, that can be used for navigation. The one injected in SampleProcess cannot be used for navigating.)

Any ideas why this is happening and more importantely how I can get the correct instance of INavigationService be injected into SampleProcess. Yes, I could provide it for example by passing it in with a method, but that's not so pretty.

App.xaml.cs

protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
    containerRegistry.RegisterSingleton<ProcessService>();
    containerRegistry.Register<Processes.SampleProcess>();
}

ProcessService.cs

public class ProcessService
{
    private readonly IContainer container;
    private readonly INavigationService navigationService;

    public ProcessService(IContainer container, INavigationService navigationService)
    {
        this.container = container;
        this.navigationService = navigationService;
    }

    public void ExecuteProcess(ProcessEnum processEnumValue)
    {
        Type processType = processEnumValue switch
        {
            ProcessEnum.SampleProcess => typeof(Processes.SampleProcess),
            _ => throw new NotImplementedException()
        };

        var process = App.Current.Container.Resolve(processType) as IProcess;

        bool test = process.CheckNavigationService(navigationService); // will return false
    }
}

SampleProcess.cs

public class SampleProcess : IProcess
{
    private readonly INavigationService navigationService;

    public SampleProcess(INavigationService navigationService)
    {
        this.navigationService = navigationService;
    }

    public bool CheckNavigationService(INavigationService navigationService)
    {
        return this.navigationService == navigationService;
    }
}
2 Answers

Navigation in Prism is based on which page you are Navigating from. For this reason the NavigationService is a transient service. It is constructed special for your ViewModel so that the instance provided for you is aware of which page it will need to Navigate from.

There are several potential solutions for your issue.

  1. Don't inject the INavigationService into another service as it isn't meant to be handled that way.

  2. Provide the INavigationService as a parameter that you pass to whatever service needs to use it

  3. Simply make your ProcessService a transient

  4. Update to Prism 8 and make your Process Service a Scoped Service as we've made a change in Prism 8 that creates a new scope each time a new page is created. This means that any Scoped or Transient services that need access to the NavigationService would be able to inject the same exact instance that is injected into the ViewModel

I fixed the issue by adding an INavigationService parameter to the ExecuteProcess method that gets called from within the viewmodel (that in fact has the correct INavigationService instance). Within the method I then resolve the process by doing the following:

var process = container.Resolve(processType, new[] { navigationService }) as IProcess;

This way the correct INavigationService instance is available within the IProcess instance.

As @Dan Siegel stated this could possibly be further improved by using Prism 8 and registering the ProcessService as a scoped service as this would eliminate the necessity to have a INavigationService parameter in ExecuteProcess.

Related