simpleinjector / SimpleInjector

An easy, flexible, and fast Dependency Injection library that promotes best practice to steer developers towards the pit of success.
https://simpleinjector.org
MIT License
1.22k stars 152 forks source link

How to use Simple Injector in MAUI application? #963

Closed tboy93 closed 1 year ago

tboy93 commented 2 years ago

Hello!

I'd like to use Simple Injector in a .NET Core MAUI application. I've tried to use the SimpleInjector.Integration.ServiceCollection nuget package, but I can't get it to work.

ServiceProviderBuilder CreateBuilder throws an Exception:

"The DefaultScopedLifestyle property cannot be changed after the first registration has been made to the container."

I've minimized the code for clarity. Maybe I'm initializing Simple Injector wrong?

using CommunityToolkit.Maui;

using SimpleInjector;

namespace SwhMauiTestApp;

public static class MauiProgram
{
    public static MauiApp CreateMauiApp()
    {
        var builder = MauiApp.CreateBuilder();

        builder.UseMauiApp<App>()
            .UseMauiCommunityToolkit()
            .ConfigureFonts(fonts =>
            {
                fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
                fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
            });

        Container container = new Container();

        // Register some sample service
        container.RegisterServices();

        container.Verify();

        var serviceProviderBuilder = new ServiceProviderBuilder(container);

        builder.ConfigureContainer(serviceProviderBuilder);

        return builder.Build();
    }

    public class ServiceProviderBuilder : IServiceProviderFactory<ServiceProvider>
    {
        private readonly Container container;

        public ServiceProviderBuilder(Container container)
        {
            this.container = container;
        }

        public ServiceProvider CreateBuilder(IServiceCollection services)
        {
            var serviceProvider = services
                .AddSimpleInjector(container, options => 
                {
                    options.AutoCrossWireFrameworkComponents = false;
                })
                .BuildServiceProvider(validateScopes: true);

            return serviceProvider;
        }

        public IServiceProvider CreateServiceProvider(ServiceProvider containerBuilder)
        {
            var provider = containerBuilder.UseSimpleInjector(container);

            return provider;
        }
    }

    public static Container RegisterServices(this Container container)
    {
        //Sample service
        container.RegisterInstance<IConnectivity>(Connectivity.Current);

        return container;
    }
}
dotnetjunkie commented 2 years ago

I don't think it is necessary to use an IServiceProviderFactory<T> implemention; that would only complicate things. Instead, try following the guidance for SimpleInjector.Integration.ServiceCollection as described here.

That said, the problem occurs because you are calling container.Verify() too early. A call to Verify() locks the container, disallowing any other modifications to be made to the container. Make sure you call Verify() as very last step.

I have no experience with MAUI, so you'll have to try this out yourself, but if you have any new questions after following the documentation, please let me know.

tboy93 commented 2 years ago

I don't think it is necessary to use an IServiceProviderFactory<T> implemention; that would only complicate things. Instead, try following the guidance for SimpleInjector.Integration.ServiceCollection as described here.

That said, the problem occurs because you are calling container.Verify() too early. A call to Verify() locks the container, disallowing any other modifications to be made to the container. Make sure you call Verify() as very last step.

I have no experience with MAUI, so you'll have to try this out yourself, but if you have any new questions after following the documentation, please let me know.

Thanks for the reply. I've tried to verify later, but unfortunately it doesn't work. Only the exception changed, currently I got stuck with this error:

Cannot resolve scoped service 'Microsoft.Maui.Dispatching.IDispatcher' from root provider.'

I suspect maybe the cross wiring not works. (Of course I added both of the AddSimpleInjector and UseSimpleInjector methods from the documentation)

dotnetjunkie commented 2 years ago

Please post a repro including the full stack trace.

tboy93 commented 2 years ago

a repro including the full stack trace.

Thanks for the response!

I have attached a github repository where my problem can be tested.

The repo: https://github.com/tboy93/MauiSimpleInjectorTest

(There are no stracktrace, the whole project collapses)

dotnetjunkie commented 2 years ago

I tried to get the repo to build and run by trying to installing the required workloads, but unfortunately, I can't get it working. So I can't test what's going on.

(There are no stracktrace, the whole project collapses)

Can you elaborate on this? What does it mean for the project to "collapse"?

Have you tried the following the following things to extract more details on the possible error?:

Crotalus commented 1 year ago

I managed to get it to build/run by having MAUI workload installed and getting Android SDK v12 Tools -> Android SDK Manager -> Android 12.0 S -> Android SDK Platform 31

The error is

System.MissingMethodException
  HResult=0x80131513
  Message=No parameterless constructor defined.
  Source=<Cannot evaluate the exception source>
  StackTrace:
<Cannot evaluate the exception stack trace>

Seems like it tries to find a parameter-less constructor of MainPage.cs class, by using the locator pattern it's possible to bypass


// Doesn't work
public MainPage(ISampleService service)
{
    InitializeComponent();

    this.service = service;
}

// Works
public MainPage()
{
    InitializeComponent();

    this.service = ServiceLocator.Container.GetInstance<ISampleService>();
}

Callstack:

    [Managed to Native Transition]  
>   System.Private.CoreLib.dll!System.RuntimeTypeHandle.GetActivationInfo(System.RuntimeType rt, out delegate*<void*, object> pfnAllocator, out void* vAllocatorFirstArg, out delegate*<object, void> pfnCtor, out bool ctorIsPublic) Line 289  C#
    System.Private.CoreLib.dll!System.RuntimeType.ActivatorCache.ActivatorCache(System.RuntimeType rt) Line 47  C#
    System.Private.CoreLib.dll!System.Activator.CreateInstance(System.Type type, bool nonPublic, bool wrapExceptions) Line 100  C#
    Microsoft.Maui.Controls.dll!Microsoft.Maui.Controls.ElementTemplate.CreateContent() Unknown
    Microsoft.Maui.Controls.dll!Microsoft.Maui.Controls.ShellContent.Microsoft.Maui.Controls.IShellContentController.GetOrCreateContent()   Unknown
    Microsoft.Maui.Controls.dll!Microsoft.Maui.Controls.Handlers.ShellSectionHandler.SyncNavigationStack(bool animated) Unknown
    Microsoft.Maui.dll!Microsoft.Maui.PropertyMapper.UpdateProperties(Microsoft.Maui.IElementHandler viewHandler, Microsoft.Maui.IElement virtualView)  Unknown
    Microsoft.Maui.Controls.dll!Microsoft.Maui.Controls.Handlers.ShellSectionHandler.SetVirtualView(Microsoft.Maui.IElement view)   Unknown
    Microsoft.Maui.Controls.dll!Microsoft.Maui.Controls.Element.SetHandler(Microsoft.Maui.IElementHandler newHandler)   Unknown
    Microsoft.Maui.dll!Microsoft.Maui.Platform.ElementExtensions.ToHandler(Microsoft.Maui.IElement view, Microsoft.Maui.IMauiContext context)   Unknown
    Microsoft.Maui.Controls.dll!Microsoft.Maui.Controls.Handlers.ShellItemHandler.UpdateCurrentItem()   Unknown
    Microsoft.Maui.dll!Microsoft.Maui.PropertyMapper.UpdateProperties(Microsoft.Maui.IElementHandler viewHandler, Microsoft.Maui.IElement virtualView)  Unknown
    Microsoft.Maui.Controls.dll!Microsoft.Maui.Controls.Handlers.ShellItemHandler.SetVirtualView(Microsoft.Maui.IElement view)  Unknown
    Microsoft.Maui.Controls.dll!Microsoft.Maui.Controls.Element.SetHandler(Microsoft.Maui.IElementHandler newHandler)   Unknown
    Microsoft.Maui.dll!Microsoft.Maui.Platform.ElementExtensions.ToHandler(Microsoft.Maui.IElement view, Microsoft.Maui.IMauiContext context)   Unknown
    Microsoft.Maui.Controls.dll!Microsoft.Maui.Controls.Platform.ShellView.CreateShellItemView()    Unknown
    Microsoft.Maui.Controls.dll!Microsoft.Maui.Controls.Platform.ShellView.SwitchShellItem(Microsoft.Maui.Controls.ShellItem newItem, bool animate) Unknown
    Microsoft.Maui.dll!Microsoft.Maui.PropertyMapper.UpdateProperties(Microsoft.Maui.IElementHandler viewHandler, Microsoft.Maui.IElement virtualView)  Unknown
    Microsoft.Maui.Controls.dll!Microsoft.Maui.Controls.Handlers.ShellHandler.SetVirtualView(Microsoft.Maui.IView view) Unknown
    Microsoft.Maui.Controls.dll!Microsoft.Maui.Controls.Element.SetHandler(Microsoft.Maui.IElementHandler newHandler)   Unknown
    Microsoft.Maui.dll!Microsoft.Maui.Platform.ElementExtensions.ToHandler(Microsoft.Maui.IElement view, Microsoft.Maui.IMauiContext context)   Unknown
    Microsoft.Maui.dll!Microsoft.Maui.Platform.ElementExtensions.ToPlatform(Microsoft.Maui.IElement view, Microsoft.Maui.IMauiContext context)  Unknown
    Microsoft.Maui.dll!Microsoft.Maui.Handlers.WindowHandler.MapContent(Microsoft.Maui.Handlers.IWindowHandler handler, Microsoft.Maui.IWindow window)  Unknown
    Microsoft.Maui.dll!Microsoft.Maui.PropertyMapper.UpdateProperties(Microsoft.Maui.IElementHandler viewHandler, Microsoft.Maui.IElement virtualView)  Unknown
    Microsoft.Maui.Controls.dll!Microsoft.Maui.Controls.Element.SetHandler(Microsoft.Maui.IElementHandler newHandler)   Unknown
    Microsoft.Maui.dll!Microsoft.Maui.Platform.ElementExtensions.SetHandler(WinRT.IWinRTObject nativeElement, Microsoft.Maui.IElement element, Microsoft.Maui.IMauiContext context) Unknown
    Microsoft.Maui.dll!Microsoft.Maui.Platform.ApplicationExtensions.CreatePlatformWindow(Microsoft.UI.Xaml.Application platformApplication, Microsoft.Maui.IApplication application, Microsoft.Maui.Handlers.OpenWindowRequest args)   Unknown
    Microsoft.Maui.dll!Microsoft.Maui.MauiWinUIApplication.OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs args)  Unknown
    Microsoft.WinUI.dll!ABI.Microsoft.UI.Xaml.IApplicationOverrides.Do_Abi_OnLaunched_0(System.IntPtr thisPtr, System.IntPtr args)  Unknown
    [Native to Managed Transition]  
    [Managed to Native Transition]  
    Microsoft.WinUI.dll!ABI.Microsoft.UI.Xaml.IApplicationStaticsMethods.Start(WinRT.IObjectReference _obj, Microsoft.UI.Xaml.ApplicationInitializationCallback callback)   Unknown
    MauiSimpleInjectorTest.dll!MauiSimpleInjectorTest.WinUI.Program.Main(string[] args) Line 31 C#