Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prism.Autofac RegisterTypeForNavigation issue #1120

Closed
galmond opened this issue Jul 18, 2017 · 13 comments
Closed

Prism.Autofac RegisterTypeForNavigation issue #1120

galmond opened this issue Jul 18, 2017 · 13 comments

Comments

@galmond
Copy link

galmond commented Jul 18, 2017

Navigation doesn't appear to be working correctly if I specify a 'name' when using ContainerBuilder.RegisterTypeForNavigation< TView >(name)

I can correctly navigate to the view the first time but subsequent navigation attempts do not call INavigationAware.IsNavigationTarget()

The issue is reproducable by modifying the NavigationParticipation sample solution to use Autofac with named views, e.g.:

builder.RegisterTypeForNavigation< ViewA >("ThisIsViewA");

Calling regionManager.RequestNavigate("ContentRegion", "ThisIsViewA") for the second time does not call INavigationAware.IsNavigationTarget() on the ViewA viewmodel. I tried this with Unity and it works as expected.

@brianlagunas
Copy link
Member

This is very strange. Just so you know builder.RegisterTypeForNavigation<ViewA>("MyViewA"); is the exact same as builder.RegisterType<ViewA>().Named<object>("MyViewA");. RegisterTypeForNavigation is just an extension method. I don't use Autofac and have no understanding of how it works, but this is very weird. It works with Unity, so this tells me this has something to do with the container itself.

This will work: builder.RegisterType<ViewA>().Named<object>("ViewA");
This won't work: builder.RegisterType<ViewA>().Named<object>("MyViewA");

Maybe someone more familiar with Autofac can help you out. For now, just use the type name, because Autofac seems to prefer that.

@brianlagunas
Copy link
Member

Actually, I dug a little deeper and found that the other containers do not properly implement the necessary classes to function properly. What Autofac is missing is the AutofacRegionNavigationContentLoader.

You can see Unity's here: https://github.com/PrismLibrary/Prism/blob/master/Source/Wpf/Prism.Unity.Wpf/Regions/UnityRegionNavigationContentLoader.cs

If you create this class and implement the GetCandidatesFromRegion method, and register it with the container in the botstrapper, it will work just fine. You can see how Unity does it here:
https://github.com/PrismLibrary/Prism/blob/master/Source/Wpf/Prism.Unity.Wpf/UnityBootstrapper.cs#L157

@brianlagunas
Copy link
Member

It would be great if someone from the Autofac community could implement this class using the Unity class as a guide and submit a PR.

@galmond
Copy link
Author

galmond commented Jul 19, 2017

Thanks for investigating Brian, much appreciated. Unfortunately the view names are already defined and I can't change them so I'll follow your fix suggestion above. I'll also raise the issue with the autofac devs. Thanks again.

@brianlagunas
Copy link
Member

brianlagunas commented Jul 19, 2017

I'm not familiar with Autofac but I think this should work

	public class AutofacRegionNavigationContentLoader : RegionNavigationContentLoader
	{
		private IContainer container;

		public AutofacRegionNavigationContentLoader(IServiceLocator serviceLocator, IContainer container)
			: base(serviceLocator)
		{
			this.container = container;
		}

		/// <summary>
		/// Returns the set of candidates that may satisfiy this navigation request.
		/// </summary>
		/// <param name="region">The region containing items that may satisfy the navigation request.</param>
		/// <param name="candidateNavigationContract">The candidate navigation target.</param>
		/// <returns>An enumerable of candidate objects from the <see cref="IRegion"/></returns>
		protected override IEnumerable<object> GetCandidatesFromRegion(IRegion region, string candidateNavigationContract)
		{
			if (candidateNavigationContract == null || candidateNavigationContract.Equals(string.Empty))
				throw new ArgumentNullException(nameof(candidateNavigationContract));

			IEnumerable<object> contractCandidates = base.GetCandidatesFromRegion(region, candidateNavigationContract);

			if (!contractCandidates.Any())
			{
				//First try friendly name registration.
				var matchingRegistration = container.ComponentRegistry.Registrations.Where(r => r.Services.OfType<KeyedService>().Any(s => s.ServiceKey.Equals(candidateNavigationContract))).FirstOrDefault();

				//If not found, try type registration
				if (matchingRegistration == null)
					matchingRegistration = container.ComponentRegistry.Registrations.Where(r => candidateNavigationContract.Equals(r.Activator.LimitType.Name, StringComparison.Ordinal)).FirstOrDefault();

				if (matchingRegistration == null)
					return new object[0];

				string typeCandidateName = matchingRegistration.Activator.LimitType.FullName;

				contractCandidates = base.GetCandidatesFromRegion(region, typeCandidateName);
			}

			return contractCandidates;
		}
	}

Then register it in the bootstrapper:

	protected override void ConfigureContainerBuilder(ContainerBuilder builder)
	{
		base.ConfigureContainerBuilder(builder);
		RegisterTypeIfMissing<IRegionNavigationContentLoader, AutofacRegionNavigationContentLoader>(builder, true);
	}

You'll want to have someone that actually knows Autofac to double check that.

@galmond
Copy link
Author

galmond commented Jul 19, 2017

Thanks Brian, I attempted it myself and it's exactly the same as yours. I raised the issue with the Autofac devs but haven't had a response yet, although I suspect the reason the Autofac content loader doesn't exist is due to this stackoverflow response...

https://stackoverflow.com/a/9120836

@gitlsl
Copy link

gitlsl commented Aug 2, 2017

public void Initialize() { _container.RegisterTypeForNavigation<ViewA>(); _container.RegisterTypeForNavigation<ViewB>(); }

this work in the demo NavigationParticipation which use IUnityContainer

if you switch to Autofac you should do like next

                   var containerBuilder  = new ContainerBuilder();
                        containerBuilder.RegisterTypeForNavigation<ViewA>("ViewA");
                        containerBuilder.RegisterTypeForNavigation<ViewB>("ViewB");
                        
                        containerBuilder.Update(container);
`

@brianlagunas
Copy link
Member

brianlagunas commented Aug 2, 2017

Unfortunately, it doesn't work unless you add the custom content loader that I provided in this thread. Also, Autofac is switching to an immutable container. Which means you will no longer be able to update the container using Update()

@brianlagunas
Copy link
Member

fixed

@wowfood
Copy link
Contributor

wowfood commented Sep 11, 2017

While this has been marked as fixed / closed. I appear to be having the same issue in Prism.Ninject.

Would I be correct in assuming it's down to the same root cause? It seems navigation called OnNavigatedTo and OnNavigatedFrom fine, but never calls any of my IsNavigationTarget.

@brianlagunas
Copy link
Member

It is probably related to the same issue. Whoever implemented the Ninject support, didn't provide the necessary content loader. Just follow the same pattern discussed in this thread and see if it works for you.

@brianlagunas brianlagunas added this to the Prism 7.0 milestone Sep 14, 2017
@brianlagunas brianlagunas modified the milestones: Prism 7.0 for XF, Prism 7.0 for WPF Jan 2, 2018
@topfunet
Copy link

topfunet commented Jul 6, 2018

not just autofac, i'm using DryIoc, this issue still exist.

@lock
Copy link

lock bot commented Jan 29, 2020

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

@lock lock bot locked as resolved and limited conversation to collaborators Jan 29, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

5 participants