dotnet / maui

.NET MAUI is the .NET Multi-platform App UI, a framework for building native device applications spanning mobile, tablet, and desktop.
https://dot.net/maui
MIT License
21.98k stars 1.71k forks source link

Can't use App.xaml resources after Dependency Injection .NET MAUI #11485

Open luis95gr opened 1 year ago

luis95gr commented 1 year ago

Description

I have an application that uses mvvm pattern with Community Toolkit features. I am trying to use Dependency Injection to use an ApiService with its interface in my viewmodels, but after following the steps described here, I can't access to App.xaml Resources (specifically colors), the intellisense works when I am writing code in XAML, but it doesn´t work after running. It is important to notice that I was using colors from resources correctly before trying to use Dependency Injection and changing the ViewModel - View linking method to the one described here, but I was unable to use ApiService. Here is my code.

Steps to Reproduce

App.xaml.cs (Login page is my first page):

public App(LoginPage page)
{
    InitializeComponent();
    MainPage = page;
}

LoginPage.xaml.cs

public partial class LoginPage : ContentPage
{
  public LoginPage(LoginPageViewModel loginPageViewModel)
  {
    BindingContext = loginPageViewModel;
    InitializeComponent();
  }
}

LoginPageViewModel

public LoginPageViewModel(IApiService apiService)
    {
        _apiService = apiService;
        Opacity = 1f;
        IsEnabled = true;
        IsRunning = false;
    }

LoginPage.xaml

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
         xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
         x:Class="NewScholarApp.Views.LoginPage"
         Title="LoginPage"
         xmlns:vm="clr-namespace:NewScholarApp.ViewModels"
         x:DataType="vm:LoginPageViewModel"
         BackgroundColor="{StaticResource GreenSchool}">

RegisterViews and ViewModels

public static MauiAppBuilder ConfigureViews(this MauiAppBuilder mauiAppBuilder)
    {
        mauiAppBuilder.Services.AddTransient<LoginPage>();

        return mauiAppBuilder;
    }

public static class RegisterViewModels
{

    public static MauiAppBuilder ConfigureViewModels(this MauiAppBuilder mauiAppBuilder)

    {
        mauiAppBuilder.Services.AddSingleton<LoginPageViewModel>();

        return mauiAppBuilder;
    }
}

Link to public reproduction project repository

https://github.com/luis95gr/SampleProject

Version with bug

7.0 (current)

Last version that worked well

Unknown/Other

Affected platforms

Android, I was not able test on other platforms

Affected platform versions

Android 12.1

Did you find any workaround?

Don't use any StaticResource

Relevant log output

Microsoft.Maui.Controls.Xaml.XamlParseException: 'Position 8:14. StaticResource not found for key GreenSchool'
TWTIC commented 1 year ago

Try to change the order like this:

public partial class LoginPage : ContentPage
{
  public LoginPage(LoginPageViewModel loginPageViewModel)
  { 
    InitializeComponent();
    BindingContext = loginPageViewModel;
  }
}
luis95gr commented 1 year ago

Try to change the order like this:

public partial class LoginPage : ContentPage { public LoginPage(LoginPageViewModel loginPageViewModel) { InitializeComponent(); BindingContext = loginPageViewModel; } }

Thank you for the response, but the error still happens even with that change.

ghost commented 1 year ago

We've moved this issue to the Backlog milestone. This means that it is not going to be worked on for the coming release. We will reassess the backlog following the current release and consider this item at that time. To learn more about our issue management process and to have better expectation regarding different types of issues you can read our Triage Process.

symbiogenesis commented 1 year ago

I encountered this same issue when upgrading to MAUI and switching to the new DI style. This is a bug.

The options for workarounds I found are the following:

  1. Best: Inject IServiceProvider and use that to resolve the page only at the moment it is needed.
  2. Worse: For the App's MainPage, inject only the ViewModel, not the page, and new-up the page. (Lose DI for the page)
  3. Even Worse: Inject the main page using System.Lazy as described here or here
  4. Worst of all: Convert all StaticResources in App.xaml to DynamicResources and incur the cost of doing that.
Eilon commented 1 year ago

@luis95gr - we don't seem to have access to https://github.com/luis95gr/NewScholarApp. Can you ensure the repo is public?

ghost commented 1 year ago

Hi @luis95gr. We have added the "s/needs-repro" label to this issue, which indicates that we require steps and sample code to reproduce the issue before we can take further action. Please try to create a minimal sample project/solution or code samples which reproduce the issue, ideally as a GitHub repo that we can clone. See more details about creating repros here: https://github.com/dotnet/maui/blob/main/.github/repro.md

This issue will be closed automatically in 7 days if we do not hear back from you by then - please feel free to re-open it if you come back to this issue after that time.

ghost commented 1 year ago

This issue has been automatically marked as stale because it has been marked as requiring author feedback to reproduce the issue but has not had any activity for 4 days. It will be closed if no further activity occurs within 3 days of this comment. If it is closed, feel free to comment when you are able to provide the additional information and we will re-investigate.

luis95gr commented 1 year ago

@luis95gr - we don't seem to have access to https://github.com/luis95gr/NewScholarApp. Can you ensure the repo is public?

I just update the original post with a public repo. This is the link: https://github.com/luis95gr/SampleProject

satya-prismhr commented 1 year ago

Hi @luis95gr I have a workaround for it, you can create service helper class with this code

public static class ServiceHelper
{
    public static TService GetService<TService>()
        => Current.GetService<TService>();

    public static IServiceProvider Current =>
#if WINDOWS10_0_17763_0_OR_GREATER
            MauiWinUIApplication.Current.Services;
#elif ANDROID
            MauiApplication.Current.Services;
#elif IOS || MACCATALYST
            MauiUIApplicationDelegate.Current.Services;
#else
            null;
#endif
}

and you can use it where ever it is required(lazy loading)

        MyViewModel myViewModel = ServiceHelper.Current.GetService<MyViewModel ();

Do not register pages in builder.Services, just register services and Viewmodels

symbiogenesis commented 1 year ago

If you use DI for everything, you can just use constructor injection to resolve IServiceProvider from anywhere in your application, and you shouldn't need a static IServiceProvider exposed.

Except maybe for integration testing purposes, and then you can use an ifdef for that case.

satya-prismhr commented 1 year ago

Hi @symbiogenesis

Thanks for the input,

MauiApp1.zip I have attached my implementation in basic project. Please modify it with your implementation or share if you have any working sample project with IServiceProvider implementation in MAUI. It will help many .NET folks

Thanks in advance

symbiogenesis commented 1 year ago

Modified

https://github.com/symbiogenesis/ServiceProviderInjection

MartyIX commented 1 year ago

Could anybody explain the likely cause of this issue in a nutshell? We have hit it too.

symbiogenesis commented 1 year ago

What is happening is that if you use DI to register ContentPage elements and maybe other UI elements, you will not be able to access static resources declared in your App.xaml file. I'm sure the technical reasons involve the app lifecycle assumptions deep within MAUI. A UI element resolved via constructor injection is initialized very early in the app or page lifecycle, and it can only access those resources later on. I believe I saw other bugs also logged about this, and maybe it will get fixed.

But essentially you can get around this by injecting IServiceProvider into the App class, and then using that to resolve the MainPage inside the App class, thereby forcing the resolution of the page element to occur later than the constructor injection would've resolved it. I demonstrated this approach in the comment above yours.

Another benefit of that approach is if you were injecting multiple modals/elements/popups into the same class, you would be able to ensure that those modals/elements/popups were only instantiated if needed, and only when they are needed. So you could paint the screen quicker, use less memory, etc.

janseris commented 1 year ago

@symbiogenesis
Thanks a lot for pointing the root cause out. I did not know that actually the instance is lazily created when you call GetService()/GetRequiredService(), I assumed it is already created anyways and is just picked from the pile of the services living in the IServiceProvider. Injecting IServiceProvider really helped solving the issue. My use case was:

Injecting AppShell into App because AppShell needs injected AppShellViewModel to be able to control menu items visibility via bound properties.

Trying to achieve:

<Shell.FlyoutHeader>
        <VerticalStackLayout Padding="20, 14">
            <Label Text="User name:" TextColor="{StaticResource Primary}" FontAttributes="Bold"/>
            <Label Text="{Binding UserName}"/>
        </VerticalStackLayout>
    </Shell.FlyoutHeader>

With this code, resolving the resource failed with the error shown below:

public AppShell(AppShellViewModel viewModel)
    {
        InitializeComponent();
        this.BindingContext = viewModel;
    }

image

With this code, resolving the resource works fine:

public App(IServiceProvider services)
    {
        InitializeComponent();
        var appShell = services.GetRequiredService<AppShell>();
        MainPage = appShell;
    }
ToolmakerSteve commented 1 year ago

This StackOverflow question is another example of the problem.

As I explain in first comment there, the problem is a direct consequence of how DI works.

The assumption in XamarinForms/Maui has always been that App gets initialized, BEFORE any page is constructed. Using DI to inject a page into App constructor breaks that design assumption.

MSicc commented 1 year ago

Having the same problem on iOS

Magic73 commented 1 year ago

I hit this issue also. I fixed in this way:

public partial class App : Application
    {
        public App(IServiceProvider serviceProvider)
        {
            InitializeComponent();

            MainPage = serviceProvider.GetRequiredService<MainPage>();
        }
    }
dennis-garavsky commented 1 year ago

I experienced the same behavior ("StaticResource not found for key ShellMenuItemTemplate") when injecting a custom service into my MainPage (derived from Shell) as part of my pet project powered by DevExpress .NET MAUI project templates and controls. Your solution certainly worked (MainPage = serviceProvider.GetRequiredService<MainPage>();) and it is fine with me - thank you for documenting it here for others.

Please just consider the following documentation enhancements for the .NET MAUI developer community (not urgent):

  1. This DI specificity with StaticResource is not mentioned anywhere in the .NET MAUI docs ( https://learn.microsoft.com/en-us/dotnet/architecture/maui/dependency-injection), where the problematic approach in the App.xaml.cs is shown everywhere (MainPage = mainPage;) - probably your tech writers can add a note there. CC @Eilon
  2. I also feel that just like myself, many people searched the internet for prebuilt code samples using .NET MAUI + DI, "maui dependency injection", and other relevant keywords). As a result, the following highly trustworthy resources from the recognized influencers demonstrated the same approach without highlighting this potential specificity - I am mentioning you directly just in case you may want to consider updates in the future. CC @jamesmontemagno (https://montemagno.com/tag/dependency-injection/ and https://youtu.be/ugem4UbAtC0) @jfversluis (https://youtu.be/wkgbvMlrMhU)

Thank you, Everyone.

Zhanglirong-Winnie commented 10 months ago

Verified this issue with Visual Studio Enterprise 17.8.0 Preview 3.0. Can repro on android platform with sample project. SampleProject-master.zip image

thomasgalliker commented 10 months ago

We cannot inject the "MainPage" into App ctor if MainPage uses static resources from App.xaml (or further references xamls, e.g. Styles.xaml or Colors.xaml). The instance of MainPage is created by DI just before it is injected into App.xaml.cs' constructor. We need to run App.xaml.cs' InitializeComponent() method before we can run MainPage's InitializeComponent() method.

public partial class App : Application
{
    public App(IServiceProvider serviceProvider)
    {
        this.InitializeComponent();

        var mainPage = serviceProvider.GetRequiredService<MainPage>();
        this.MainPage = new NavigationPage(mainPage);
    }
}

I would expect .NET MAUI to provide better support for Page/ViewModel handling with DI.

awaescher commented 9 months ago

This issue was reported a whole year ago and noone from the team reacted so far. StaticResources are a fundamental thing for ANY real world app that is not just another Maui Sample. Seriously, you can't be ignoring this issue every single enterprise customer is dependent on.

Eilon commented 9 months ago

Hi, I'm from the team 😁

Here's what I've understood from this thread:

Using .NET MAUI + XAML + StaticResources + DI for pages causes an exception. To avoid the exception, the code can be rewritten to take in the IServiceProvider in the constructor (instead of taking in the page), and then the service provider can be asked to retrieve the page after InitializeComponent() is called. This workaround has been posted earlier in this thread, and can be summarized like this:

Causes exception: (Microsoft.Maui.Controls.Xaml.XamlParseException: 'Position row:column. StaticResource not found for key MyStaticResource')

    public App(LoginPage page)
    {
        InitializeComponent();
        MainPage = page;
    }

Works:

    public App(IServiceProvider sp)
    {
        InitializeComponent();
        MainPage = sp.GetService<LoginPage>();
    }

The cause of this is that XAML creates object trees outside-in, meaning the outer-most container is created first (say, the app container), then the next deeper one (say, a page or layout), etc., until finally the inner-most content is created (say, a button). However, Microsoft.Extensions.DependencyInjection creates object trees inside-out, meaning the inner-most item is created first, then whatever contains it is created next, until eventually the thing you 'asked' for is finally created.

This conflict is problematic because of the two different patterns: outside-in vs. inside-out.

The reason the workaround works is that it waits to do the DI part until after the XAML part is sufficiently done.

Given this fairly fundamental conflict, I'd like to hear on suggestions for how we can make this better.

  1. Actually fix the problem. I'm not sure if that's even possible without some huge changes. If someone here thinks it is possible, please share your thoughts on how we could do that. Cost: I'm not sure how expensive this is because I'm not sure if it's even possible.
  2. Add docs. This was suggested earlier and I logged https://github.com/dotnet/docs-maui/issues/1919 with some ideas. Please add any additional thoughts on docs to that. Cost: This is relatively inexpensive.
  3. Add a Code Analyzer to detect the pattern and at least warn about the problem, and possibly suggest a code fix for the user. Cost: This could be expensive, but I'm not sure, because I've never written a proper analyzer.

If anyone has some additional thoughts on how we can improve this, please let us know.

symbiogenesis commented 9 months ago

I think using a fully MVVM and DI-centric approach in the MAUI project templates would ensure people don't run into this.

IServiceProvider would be used by default to grab the initial MainPage and the ViewModel for the AppShell.

awaescher commented 9 months ago

I could not get the workaround to work for me. Therefore, I have no choice but to remove all static resources in order to continue my migration efforts. Might be another issue in my migrated codebase that is causing this in combination but as for almost every error I encounter, the error messages and callstacks are completely useless.

This migration from a medium sized enterprise Xamarin.Forms app was the worst experience I've ever had as a developer and that truly means a lot. I used to be a Xamarin and especially Xamarin.Forms advocate. I have been in from the very beginning and gladly paying $999 yearly for Xamarin.iOS.

These days I'm downright frustrated with how tedious the porting and how immature the runtime is. It's a disastrous state and the way I see it, nothing has really changed with .NET 8.

Eilon commented 9 months ago

I could not get the workaround to work for me. Therefore, I have no choice but to remove all static resources in order to continue my migration efforts. Might be another issue in my migrated codebase that is causing this in combination but as for almost every error I encounter, the error messages and callstacks are completely useless.

This migration from a medium sized enterprise Xamarin.Forms app was the worst experience I've ever had as a developer and that truly means a lot. I used to be a Xamarin and especially Xamarin.Forms advocate. I have been in from the very beginning and gladly paying $999 yearly for Xamarin.iOS.

These days I'm downright frustrated with how tedious the porting and how immature the runtime is. It's a disastrous state and the way I see it, nothing has really changed with .NET 8.

Hi @awaescher if you can provide a simplified repro that shows the issue you're having I can try to investigate. My familiarity is mostly with the DI side of things and much less so on the XAML/resources side. I wonder in your case are there multiple levels of XAML pages using static resources? If so, the workaround I mentioned in my comment would need to be applied at each level. Or, it could be something else entirely.

There could also be other completely different approaches, such as querying each service "on demand" once the page loads, instead of using constructor injection at all. While this would most likely work, because it allows all the XAML stuff to work "normally," in some apps it could require significant changes to allow some functionality to happen after the page loads.

janseris commented 9 months ago

I could not get the workaround to work for me. Therefore, I have no choice but to remove all static resources in order to continue my migration efforts. Might be another issue in my migrated codebase that is causing this in combination but as for almost every error I encounter, the error messages and callstacks are completely useless. This migration from a medium sized enterprise Xamarin.Forms app was the worst experience I've ever had as a developer and that truly means a lot. I used to be a Xamarin and especially Xamarin.Forms advocate. I have been in from the very beginning and gladly paying $999 yearly for Xamarin.iOS. These days I'm downright frustrated with how tedious the porting and how immature the runtime is. It's a disastrous state and the way I see it, nothing has really changed with .NET 8.

Hi @awaescher if you can provide a simplified repro that shows the issue you're having I can try to investigate. My familiarity is mostly with the DI side of things and much less so on the XAML/resources side. I wonder in your case are there multiple levels of XAML pages using static resources? If so, the workaround I mentioned in my comment would need to be applied at each level. Or, it could be something else entirely.

There could also be other completely different approaches, such as querying each service "on demand" once the page loads, instead of using constructor injection at all. While this would most likely work, because it allows all the XAML stuff to work "normally," in some apps it could require significant changes to allow some functionality to happen after the page loads.

So we should use service locator antipattern because MAUI team cannot implement DI properly? No thanks

Rabosa616 commented 9 months ago

I have a complex app with more than 30 pages and a bunch of services and the only think I´ve did was on the App.xaml.cs at the constructor apply @Eilon workarround, then I´ve added all my static resources at App.xaml at:

 <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>

Then on each page I use propertly the DI. Here by an example:

MauiProgram.cs:

public static class MauiProgram
{
    public static MauiApp CreateMauiApp()
    {
        var builder = MauiApp.CreateBuilder();
        builder
            .UseMauiApp<App>()
            .ConfigureFonts(fonts =>
            {
                fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
                fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
            });
                        # register services
                        builder.Services.AddSingleton<IConnectivity>((e) => Connectivity.Current);

                        # register pages
                        builder.Services.AddSingleton<MainPage>();

                        # register view models
                        builder.Services.AddSingleton<MainPageViewModel>();

        return builder.Build();
    }
}

App.xaml

<?xml version = "1.0" encoding = "UTF-8" ?>
<Application xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:MauiApp1"
             x:Class="MauiApp1.App">
    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="Resources/Styles/Colors.xaml" />
                <ResourceDictionary Source="Resources/Styles/Styles.xaml" />
                 <!-- Add here your Resource Dictionaries -->
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Application.Resources>
</Application>

App.xaml.cs

public partial class App : Application
    {
        public App(IServiceProvider sp)
        {
            InitializeComponent();

            MainPage = MainPage = sp.GetService<MainPage>();
        }
    }

MainPage.xaml.cs

public partial class MainPage : ContentPage
{
    int count = 0;

    public MainPage(MainPageViewModel viewModel)
    {
        InitializeComponent();
        BindingContext = viewModel;
    }
   ...

MainPageViewModel.cs

 public class MainPageViewModel
 {
     private readonly IConnectivity connectivityService
     public MainPageViewModel(IConnectivity connectivity)
     {
         connectivityService = connectivity ?? throw new ArgumentNullException(nameof(connectivity));
     }
 }
awaescher commented 9 months ago

can provide a simplified repro that shows the issue you're having I can try to investigate

Hard to provide as this is a larger app I am currently "upgrading" (not sure about that) from Xamarin.Forms to Maui. Because all the efforts with the upgrade-assistant went directly to hell, I started a new Maui app and manually moved class for class from my older project to the newer one. So it's not that I threw in loads of old tech, manifests and plists. Still, I just cannot get this thing to work, but without knowing why, it's hard to build a repro.

My code however looks very much like the one @Rabosa616 just posted. However, the troublemakers in my case are static resources in the XAML itself:

<?xml version="1.0" encoding="UTF-8"?>
<ContentPage
    x:Name="anyPage"
    x:Class="MyApp.Pages.AnyPage"
    Title="{StaticResource AppResources.MyAppName}">
    <ContentPage.Content>
        ...

I just found this closed issue #3878 which is about the same thing, there's a repro repository.

awaescher commented 9 months ago

Okay wait, I am very confused to report that this works:

Title="{Static AppResources.MyAppName}">

While this does not:

Title="{StaticResource AppResources.MyAppName}">
Exception has occurred: Microsoft.Maui.Controls.Xaml.XamlParseException
Position 15:2. StaticResource not found for key AppResources.MyAppName
    at at Microsoft.Maui.Controls.Xaml.ApplyPropertiesVisitor.ProvideValue(Object& value, ElementNode node, Object source, XmlName propertyName)
    at at Microsoft.Maui.Controls.Xaml.ApplyPropertiesVisitor.Visit(ElementNode node, INode parentNode)
    at at Microsoft.Maui.Controls.Xaml.ElementNode.Accept(IXamlNodeVisitor visitor, INode parentNode)
    at at Microsoft.Maui.Controls.Xaml.RootNode.Accept(IXamlNodeVisitor visitor, INode parentNode)
    at at Microsoft.Maui.Controls.Xaml.XamlLoader.Visit(RootNode rootnode, HydrationContext visitorContext, Boolean useDesignProperties)
    at at Microsoft.Maui.Controls.Xaml.XamlLoader.Load(Object view, String xaml, Assembly rootAssembly, Boolean useDesignProperties)
    at at Microsoft.Maui.Controls.Xaml.XamlLoader.Load(Object view, String xaml, Boolean useDesignProperties)
    at at Microsoft.Maui.Controls.Xaml.XamlLoader.Load(Object view, Type callingType)
    at at Microsoft.Maui.Controls.Xaml.Extensions.LoadFromXaml[LandingPage](LandingPage view, Type callingType)
    at MyApp.Pages.AnyPage.InitializeComponent() in 

I am not sure if this is the way it should be when working when using resx files for localization, I used StaticResource with Xamarin.Forms over the last years.

AndreasReitberger commented 9 months ago

Just for reference, for styles and colors, which are defined in a ResourceDictionary, I use {StaticResource ...}, for classes and static resources from code behind, I'm using {x:Static ...}. Haven't had any problems yet, even with DI.

 <Label 
        Margin="2,16"
        Text="This is a headline"
        VerticalTextAlignment="Center" 
        HorizontalTextAlignment="Center"
        Style="{StaticResource PrimaryHeadlineLabelStyle}"
        />

    <Label 
        Margin="2,16"
        Text="{x:Static icons:MaterialIcons.HeartOutline}"
        VerticalTextAlignment="Center" 
        HorizontalTextAlignment="Center"
        Style="{StaticResource PrimaryMaterialFontFamilyIconLabelStyle}"
        />

The MaterialFontIcons class looks like that.

https://github.com/AndreasReitberger/SharedMauiXamlStyles/blob/main/src/SharedMauiXamlStylesLibrary/Models/FontIcons/MaterialIcons.cs

Maybe this helps!

ThysBrits commented 4 months ago

I was faced with this issue the last week and struggled to find any solution that worked. None of the above possible solutions worked for me. Then eventually I found that I had added a Style property on some of my controls that was invalid. The style was in the styles.xaml file, but I was using it incorrectly. And the error was clear enough that the style was not found. Once I removed those Style properties, the app worked using the DI, etc. The only bug though is the fact that the app would run when attached to the debugger, but it would crash when not. The way I managed to solve it better was to run the Windows app version, which does in fact give the error when attached to the debugger. Hope this helps someone, and also helps in resolving the actual problem.

janseris commented 4 months ago

I was faced with this issue the last week and struggled to find any solution that worked. None of the above possible solutions worked for me. Then eventually I found that I had added a Style property on some of my controls that was invalid. The style was in the styles.xaml file, but I was using it incorrectly. And the error was clear enough that the style was not found. Once I removed those Style properties, the app worked using the DI, etc. The only bug though is the fact that the app would run when attached to the debugger, but it would crash when not. The way I managed to solve it better was to run the Windows app version, which does in fact give the error when attached to the debugger. Hope this helps someone, and also helps in resolving the actual problem.

the cause debug × release might be interpreted vs compiled XAML what I read in this repository sometimes.

tele-bird commented 1 month ago

I think using a fully MVVM and DI-centric approach in the MAUI project templates would ensure people don't run into this.

IServiceProvider would be used by default to grab the initial MainPage and the ViewModel for the AppShell.

@Eilon - my feedback:

I agree with this recommendation by @symbiogenesis to add the IServiceProvider parameter to the App constructor in the default MAUI project template. This workaround is simple, DI-centric, and the issue should only appear when injecting the initial MainPage into the App constructor. No need to make this a big deal.

tele-bird commented 1 month ago

I note that @jfversluis 's DI injection sample project injects a MainPage into the App constructor, and it runs without this issue.... even in my pull request that upgrades it to .NET8.

janseris commented 1 month ago

btw proper DI (everything in the app resolved from DI) allows developers to validate DI setup on application start. If anything is resolved later during runtime using service locator antipattern, that cannot be done.

The code for doing that is (console and desktop)

private static void ValidateServiceDependenciesOnStartup(IHostBuilder builder)
{
    builder.UseDefaultServiceProvider(options =>
    {
        options.ValidateOnBuild = true; //validate on builder.Build()
    });
}

For example in console or desktop apps, the following code can be used to achieve that being called:

IHostBuilder builder = Host.CreateDefaultBuilder();


For ASP.NET Core (builder is WebApplicationBuilder present in the default application template code):

builder.Host.UseDefaultServiceProvider((context, options) =>
{
    options.ValidateOnBuild = true; //validate on builder.Build()
});

WebApplicationBuilder builder = WebApplication.CreateBuilder(args);

here hostBuilder is accessed from the property WebApplicationBuilder.Host which is of type ConfigureHostBuilder : IHostBuilder

This works in ASP.NET Core apps, console apps and desktop apps.

This is not possible in MAUI afaik