Search code examples
c#xamarinprismdryioc

Prism + DryIoc crashes when trying to perform constructor injection?


I'm having trouble using DryIoc for constructor injection into a ViewModel using Prism with Xamarin. I am using the Nuget package Prism.DryIoc.Forms.

In my project I get the following error in AuthenticatePage.xaml.g.cs

Unable to resolve Object {RequiredServiceType=Project.ViewModels.AuthenticatePageViewModel} with 1 arg(s) 
in wrapper Func<Xamarin.Forms.Page, Object> {RequiredServiceType=Project.ViewModels.AuthenticatePageViewModel} with 1 arg(s) 
from container
with normal and dynamic registrations:
MainPage, {ID=44, ImplType=Project.Views.MainPage}}
NavigationPage, {ID=43, ImplType=Xamarin.Forms.NavigationPage}}
AuthenticatePage, {ID=45, ImplType=Project.Views.AuthenticatePage}}

Specifically, it points to the line

private void InitializeComponent() {
        global::Xamarin.Forms.Xaml.Extensions.LoadFromXaml(this, typeof(AuthenticatePage));
    }

Of note is that if I call the following in App.OnInitialized, the object resolves fine:

c.Register<INegotiator, Negotiator>(Reuse.Singleton);
var n = c.Resolve<INegotiator>();
n.ResumeSessionAsync(); // This works fine, no problems.
await NavigationService.NavigateAsync("NavigationPage/AuthenticatePage"); // Error thrown here

If I remove the constructor injection from my ViewModel it works fine (Aside from keeping the default navigationService injection, which works fine). Even trying to inject a basic class like ILogger (no dependencies) fails.

public AuthenticatePageViewModel(INavigationService navigationService, ILogger logger) : base (navigationService)
{
    Title = "Authentication Page...";
}

I'm going to keep investigating, but is it obvious to someone here if I'm fundamentally doing something wrong? If I had to guess I would say it's to do with a conflict with Prisms built in Ioc container and DryIoc?

Edit:

I'm using the latest version of Prism.DryIoc.Forms available on NuGet (7.0.0.396) which says it includes DryIoc 2.12.26. I have so far simply followed the template available for Visual Studio which lists setting up navigation as follows:

protected override async void OnInitialized()
{
    InitializeComponent();

    var c = new Container();
    c.Register<ILogger, LoggerConsole>(Reuse.Singleton);

    c.RegisterMany(new[] { Assembly.Load("Project.UWP") },
                   serviceTypeCondition: type => type == typeof (ILocalFileHandler));

    c.Register<INegotiator, Negotiator>(Reuse.Singleton);
    // var n = c.Resolve<INegotiator>(); 
    // n.ResumeSessionAsync(); // <- This will run fine. Negotiator class has ILogger and ILocalFileHandler injected into it.

    await NavigationService.NavigateAsync("NavigationPage/AuthenticatePage");

}

protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
    containerRegistry.RegisterForNavigation<NavigationPage>();
    containerRegistry.RegisterForNavigation<MainPage>();
    containerRegistry.RegisterForNavigation<AuthenticatePage>();
}

I can't find any info online on if/how I should be using Prism.DryIoc.DryIocContainerExtensions to set up navigation? Even modifying the sample app to include basic construction injection results in the error "Value Cannot Be Null" in the same xaml.g.cs file?


Solution

  • Following @Dan S.'s diagnoses suggestion as well as reading this article (http://brianlagunas.com/whats-new-in-prism-for-xamarin-forms-7-0/) I realized that I should have been using the Prism.Ioc.ContainerRegistry abstraction layer to interface with DryIoc. Prior to this I had been working directly with DryIoc's classes.

    Once I modified my registration code to use Prism.Ioc.IContainerRegistry it worked perfectly.

    protected override void RegisterTypes(IContainerRegistry cr)
    {
        cr.Register<ILogger, LoggerConsole>();
    
        cr.GetContainer().RegisterMany(new[] { Assembly.Load("Project.UWP") },
                       serviceTypeCondition: type => type == typeof(ILocalFileHandler));
    
        cr.Register<INegotiator, Negotiator>();
    
    
        cr.RegisterForNavigation<NavigationPage>();
        cr.RegisterForNavigation<MainPage>();
        cr.RegisterForNavigation<AuthenticatePage>();
    }