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

[Enhancement] Revisit using Ms.Ext.Di as our default container #880

Closed
PureWeen opened this issue Apr 28, 2021 · 47 comments
Closed

[Enhancement] Revisit using Ms.Ext.Di as our default container #880

PureWeen opened this issue Apr 28, 2021 · 47 comments
Assignees
Labels
area/hosting 🧩 Extensions / Hosting / AppBuilder / Startup proposal/open t/enhancement ☀️ New feature or request

Comments

@PureWeen
Copy link
Member

PureWeen commented Apr 28, 2021

Summary

Early on we decided to implement a Vanilla version of the DI abstractions for startup performance reasons but as time is going one we're going further and further down the rabbit whole of DI Container complexities so at this point we need to remeasure performance differences between our container and the MS.Ext.Di and see if there's a way to just use the MS.Ext.Di without sacrificing startup performance

-- Can the Ms.Ext.Di be mutable?

-- Insert numbers here

-- insert sample apps here

@Eilon
Copy link
Member

Eilon commented Apr 28, 2021

I think it's also important that Blazor Desktop works with whatever our default choice is for MAUI dependency resolution. Right now the default MAUI service container is lacking some features, such as support for scoped services, so it can't be used. This is fundamental to how some core Blazor services work and isn't something that can be controlled from Blazor Desktop specifically (Blazor Desktop uses core Blazor services for most of its functionality).

So with MAUI's default service collection you get this exception:

No service for type 'Microsoft.Extensions.DependencyInjection.IServiceScopeFactory' has been registered.
   at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider, Type serviceType) in Microsoft.Extensions.DependencyInjection.Abstractions.dll:token 0x600008e+0x4f
   at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService[T](IServiceProvider provider) in Microsoft.Extensions.DependencyInjection.Abstractions.dll:token 0x600008f+0xe
   at Microsoft.AspNetCore.Components.WebView.WebViewManager.AttachToPageAsync(String baseUrl, String startUrl) in Microsoft.AspNetCore.Components.WebView.dll:token 0x600008b+0x22
   at Microsoft.AspNetCore.Components.WebView.IpcReceiver.OnMessageReceivedAsync(PageContext pageContext, String message) in Microsoft.AspNetCore.Components.WebView.dll:token 0x6000026+0xb3
   at Microsoft.AspNetCore.Components.WebView.WebViewManager.<>c__DisplayClass17_0.<<MessageReceived>b__0>d.MoveNext() in Microsoft.AspNetCore.Components.WebView.dll:token 0x600012f+0x80

If I switch the app builder to use MS.Extensions.DI then this starts to work, but apparently there are some non-trivial startup perf costs to this choice, making it functional, but not desirable.

@charlesroddie
Copy link
Contributor

From linked thread:

quite some time ago I profiled Xamarin.Forms and the bulk of startup time was spent looking for types in assemblies that need to be registered.

This is appaling and searching for types at runtime needs to be banned. Whatever DI gets included - and the best approach is not to include anything - it's very important that is has zero startup time implication for anyone that doesn't explicitly decide to use it.

@danroth27
Copy link
Member

danroth27 commented May 10, 2021

@davidfowl @eerhardt

@davidfowl
Copy link
Member

-- Can the Ms.Ext.Di be mutable?

No it was intentional to have 2 phase approach is fundamental to the container design. Constructor selection is based on what registrations are in the container, making it mutable fundamentally changes that. It also means we would need to handle container mutation at arbitrary points in time which has performance implications.

This is appaling and searching for types at runtime needs to be banned. Whatever DI gets included - and the best approach is not to include anything - it's very important that is has zero startup time implication for anyone that doesn't explicitly decide to use it.

Yes, that should be avoided, but I don't know what that has to do with the default DI container.

@davidfowl
Copy link
Member

Does anyone have any performance measurements/profiles/traces for startup time with the default DI container in maui apps? (I'm coming in blind here, I haven't looked at what's been done so far).

@PureWeen
Copy link
Member Author

#822

@PureWeen
Copy link
Member Author

-- Can the Ms.Ext.Di be mutable?

No it was intentional to have 2 phase approach is fundamental to the container design. Constructor selection is based on what registrations are in the container, making it mutable fundamentally changes that. It also means we would need to handle container mutation at arbitrary points in time which has performance implications.

Perfect! "The mutation at arbitrary points" has been my main response. If someone wants control over the returned type at runtime my general recommendation has been to use a factory and then from the factory you can control what's returned.

@davidfowl
Copy link
Member

#822

That thread doesn't have any measurements but I see lots of speculation. Also, there is no type scanning in DI so I'm guessing those comments are referring to something else and not the Microsoft.Extensions.DependencyInjection (maybe other DI containers, are what others tend to do generally).

To make progress here, we need to get more precise on what the problems are so we can narrow in and fix them (if they are any...).

@rmarinho
Copy link
Member

Hey @davidfowl i did some of the initial measurements when we started working the Host/builder pattern for MAUI and i prepared a small repo that you can see a simple use case with a script to help getting a average of the startup time on Android.
To test we use a blank hello world Xamarin Android application, right now we are not even going to think in the net6.

The repo is here you can clone it and run from the command line :

profile-android.ps1 -project HelloAndroid/HelloAndroid/HelloAndroid.csproj -package com.microsoft.helloandroid -configuration Release

this will start the normal app 10 times and get the average for the startup time.

Then we can try the same with a Host/builder, the code for the host is something simple like this:

var builder = Host.CreateDefaultBuilder();
builder.ConfigureServices(collection =>
{
	collection.AddSingleton<ITextService, TextService>();
	collection.AddSingleton<IHostLifetime, CustomHostLifetime>();
});
var host = builder.Build();
host.Start();
var textService = host.Services.GetRequiredService<ITextService>();

profile-android.ps1 -project HelloAndroid/HelloAndroid/HelloAndroid.csproj -package com.microsoft.helloandroid -configuration Release -extra /p:USE_BUILDER=true

You can see there's a huge difference, the startup time is around 300ms (on a Pixel 2 device) for a simple app, and when we introduce the Host its around 600ms.

A couple of runs i did:

xamarin android normal

Average(ms): 296.2
Std Err(ms): 4.80462739912727
Std Dev(ms): 15.1935658896931

Average(ms): 300
Std Err(ms): 4.89671091425436
Std Dev(ms): 15.4847595324492

Average(ms): 303.6
Std Err(ms): 7.20986977845108
Std Dev(ms): 22.799610133119

xamarin android with builder

Average(ms): 571.2
Std Err(ms): 9.117504531882
Std Dev(ms): 28.8320808976544

Average(ms): 574.7
Std Err(ms): 7.98616859882691
Std Dev(ms): 25.2544825504085

Average(ms): 576.7
Std Err(ms): 9.10073257124575
Std Dev(ms): 28.7790433012172

Average(ms): 588.9
Std Err(ms): 15.4850824272194
Std Dev(ms): 48.9681302254617

@mattleibow
Copy link
Member

I tested on an older Razer Phone with Android 9 and got an average of 300-340ms on the basic part, and then about 600-680ms with the host builder.

@davidfowl
Copy link
Member

davidfowl commented May 12, 2021

Lets try a few more things in your environments:

  1. The host builder without defaults:
var builder = new HostBuilder();
builder.ConfigureServices(collection =>
{
	collection.AddSingleton<ITextService, TextService>();
	collection.AddSingleton<IHostLifetime, CustomHostLifetime>();
});
var host = builder.Build();
host.Start();
var textService = host.Services.GetRequiredService<ITextService>();
  1. The DI container directly:
var collection = new ServiceCollection();
collection.AddSingleton<ITextService, TextService>();
var serviceProvider = collection.BuildServiceProvider();
var textService = serviceProvider.GetRequiredService<ITextService>();

That should help us narrow down some of where the cost is coming from.

Another question, do you have a profiler you can run here? That would help us further break down where time is being spent.

@AmrAlSayed0
Copy link
Contributor

Xamarin.Forms had a Performance class that was sometimes used to measure different methods. You could pass the [CallerFilePath] and the [CallerMemberName] directly to be able to measure the performance across methods instead of in just 1 method.

@rmarinho
Copy link
Member

rmarinho commented May 13, 2021

Hey @davidfowl ok i tried but we have a couple of issues if we try to have the configuration you recommend first is we get

System.ArgumentException: 'The path must be absolute. Parameter name: root'

That's because it needs the ContentRoot, so we can fix that by adding :
builder.UseContentRoot(System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal));

the other issue we get is that by default the IHostLifetime is ConsoleHostLifetime and that throws the following running on Android

System.PlatformNotSupportedException: 'Operation is not supported on this platform.'

so to fix that we need:
builder.ConfigureServices(collection => collection.AddSingleton<IHostLifetime, CustomHostLifetime>());

the final code is something like

void UseBuilder()
{
	var builder = new HostBuilder();
	builder.UseContentRoot(System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal));
	builder.ConfigureServices(collection => collection.AddSingleton<IHostLifetime, CustomHostLifetime>());
	var host = builder.Build();
	host.Start();
	var collection = new ServiceCollection();
	collection.AddSingleton<ITextService, TextService>();
	var serviceProvider = collection.BuildServiceProvider();
	var textService = serviceProvider.GetRequiredService<ITextService>();
	SetText($"From Builder {textService.GetText()}");
}

But yeah the changes takes us to around 450ms so it saves almost 150ms

Average(ms): 448.3
Std Err(ms): 7.36214038925577
Std Dev(ms): 23.2811320839669

Average(ms): 448.7
Std Err(ms): 7.61000803036516
Std Dev(ms): 24.0649583881257

Average(ms): 449.7
Std Err(ms): 8.14732403239793
Std Dev(ms): 25.7641007778049

Average(ms): 483.5
Std Err(ms): 10.2775375347297
Std Dev(ms): 32.5004273476177

yeah we can get the mono profiler working on xamarin.android app

@davidfowl
Copy link
Member

That code was meant to be run separately. The host without defaults and then, just the DI container alone

@rmarinho
Copy link
Member

oh sorry, sure.. here are the numbers:

Just using the host without the defaults: around 445ms

Average(ms): 445.3
Std Err(ms): 5.56387155375343
Std Dev(ms): 17.5945067184808

Average(ms): 446.5
Std Err(ms): 7.41057802513857
Std Dev(ms): 23.4343053378304

Average(ms): 483.5
Std Err(ms): 12.7010498253753
Std Dev(ms): 40.1642461234699

Just using the ServiceCollection and provider: around 334 ms

Average(ms): 332.9
Std Err(ms): 4.80844165100411
Std Dev(ms): 15.2056276131934

Average(ms): 334.2
Std Err(ms): 8.14425496217357
Std Dev(ms): 25.754395525597

Average(ms): 334.6
Std Err(ms): 4.97594212184989
Std Dev(ms): 15.7353106102168

@davidfowl
Copy link
Member

Great! That helps. Just for my edification, I have a few questions:

  • What's the baseline we're looking at for startup time? Without hosting or DI, what's the cost? (That'll tell us how much overhead there is today)
  • What's acceptable overhead? I know we want things as fast as possible but today I'm sure we do more at startup to make execution faster. I want to get a sense of what sort of overhead is reasonable for these scenarios (I'm not very familiar with them)

@rmarinho
Copy link
Member

  • Baseline is 300m , so i will say that looking at the numbers above Hosting is adding around 145ms, and DI is adding 34ms ...
  • I will loop the rest of the team on this but i will say that the overhead of only 50ms would be awesome, 100ms for DI+Host we can live with,

Also take note this is the very basic, one of the issues with DI is as you add more services to the ServiceCollection it gets slower , also on the hosting we want to enable for example the logging and that also increases our startup time.

At this time we have our own Host implementation as well as a ServiceCollection based on Dictionary it does break a little bit the provider concepts since it doesn't allow multiple service registration, doesn't handle scopes etc, but thats how we got the things to be a little faster. We also have a ServiceCollection just for host/configuration and other for the user services. But we would love to get feedback on the best approach going forward and in a perfercent hold we could just use the Host/ServiceCollection provided by Microsoft.Extensions.Hosting and Microsoft.Extensions.DI.

One other thing is like @PureWeen said we are looking to see if is possible to have also dynamic container, this is because in Xamarin.Forms (now MAUI) users could register services at any time, we had just a simple singleton DependencyService. We also have a use case where 3rd parties want to register a particular service only if a particular type is use.. so only if you instantiate that type the static ctor would register the service on the container. But we are still lookg at ways we could allow this by maybe using the linker to link out the services if the type isn't used or source generators.

@davidfowl
Copy link
Member

Also take note this is the very basic, one of the issues with DI is as you add more services to the ServiceCollection it gets slower , also on the hosting we want to enable for example the logging and that also increases our startup time.

At a bare minimum, building the service collection has to enumerate all of the registrations. When you resolve a service, it also ends up building a call graph of the objects to make future resolution fast. It's possible some of this code could be optimized.

Another low hanging fruit optimization on the table is fixing the O(N^2) algorithms in TryAdd and TryAddEnumerable. I don't know how much that helps here.

At this time we have our own Host implementation as well as a ServiceCollection based on Dictionary it does break a little bit the provider concepts since it doesn't allow multiple service registration, doesn't handle scopes etc, but thats how we got the things to be a little faster. We also have a ServiceCollection just for host/configuration and other for the user services. But we would love to get feedback on the best approach going forward and in a perfercent hold we could just use the Host/ServiceCollection provided by Microsoft.Extensions.Hosting and Microsoft.Extensions.DI.

If you go this route I wouldn't implement the interface at all since it'll break people expectation. If we can't optimize the implementation for Maui then we should use a stripped down version of the real thing and clearly describe the gaps.

One other thing is like @PureWeen said we are looking to see if is possible to have also dynamic container, this is because in Xamarin.Forms (now MAUI) users could register services at any time, we had just a simple singleton DependencyService. We also have a use case where 3rd parties want to register a particular service only if a particular type is use.. so only if you instantiate that type the static ctor would register the service on the container.

I think that's a non-starter for the reasons mentioned above so it's possible you'll need to build another service provider.

@Eilon
Copy link
Member

Eilon commented May 13, 2021

I think support for ServiceScopes in whatever the default MAUI provider is critical because BlazorWebView needs it. I expect BlazorWebView usage to be quite common and we wouldn't want people to go down a less-desirable routes to use this feature.

@PureWeen
Copy link
Member Author

PureWeen commented Jun 21, 2021

@davidfowl I've attached our first tests using a profiler to see where time is being spent inside Ms.Ext.Di
The ability to profile our apps is fairly new so still playing around with it a little bit.

One folder contains the traces using our own and the other contains the traces using the Ms.Ext.Di implementation

Traces.zip

@eerhardt
Copy link
Member

ExpressionResolverBuilder.cs

FYI - I removed the dependency on ExpressionResolverBuilder in dotnet/runtime#55246. Would it be possible to try it again with the latest build? I believe the ExpressionResolverBuilder.cctor() in the above screenshot should go away now.

Use of System.Reflection and emitting IL?

The IL emit only happens on the 2nd time a service is realized, and it happens on a background thread. The reasoning is that if the app gets a service twice, it will probably need it again. So generate IL to make it faster the next time.
But since it happens on a background thread, it shouldn't affect the current thread, does it? Maybe we could schedule the background thread with a low priority?

@jonathanpeppers
Copy link
Member

@eerhardt we can profile and see, but is there a way to not use threads at all? One issue on Android, is when the device is in a battery saving state -- the OS can put all cores asleep besides 1.

We should try replacing the DI container with a hardcoded switch statement -- and compare the difference? @mattleibow @PureWeen is that possible? We could potentially hardcode core Maui services as well?

@eerhardt
Copy link
Member

but is there a way to not use threads at all?

We have a check for RuntimeFeature.IsDynamicCodeCompiled before deciding to use the ILEmit strategy:

https://github.com/dotnet/runtime/blob/dfd618dc648ba9b11dd0f8034f78113d69f223cd/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceProvider.cs#L187-L195

I'm guessing on Android this is returning true - which means the IL can be JIT'd on Android? When RuntimeFeature.IsDynamicCodeCompiled returns false, we use pure Reflection to create the services. No IL Emit and no Expressions.

One option could be to add a new switch to ServiceProviderOptions, that when set would cause us to use the pure Reflection path. Android apps could set this by default. Another (probably worse) idea would be to check for mobile targets with OperatingSystem.IsAndroid|IsIOS|etc.

@jonathanpeppers
Copy link
Member

@eerhardt yeah is there a way to make an AppContext.GetData() flag (or something else) where we could toggle this?

https://github.com/dotnet/runtime/blob/dfd618dc648ba9b11dd0f8034f78113d69f223cd/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceProvider.cs#L187

Android has a JIT, so I think it's going to opt into DynamicServiceProviderEngine no matter what.

@davidfowl
Copy link
Member

I'm not sure we should make the mode public but we should definitely find out what the best strategy is for Android. If your want to measure reflection only mode then I wonder if we can use private reflection to set the option and re-test the scenario

@eerhardt
Copy link
Member

is there a way to make an AppContext.GetData() flag (or something else) where we could toggle this?

I don't really like using AppContext switches for things that should/can be normal public APIs. They feel like "undocumented" APIs, that we don't have a solid strategy for support. And we are confused at whether we can remove them in the future.

I could be persuaded here to go the AppContext route, if we verify that this adds value, and we really don't want to add a "real" public API. But I guess I'd rather opt for a public API.

@jonathanpeppers - can run the benchmarks against a private build where it is just hard-coded to use the Reflection only path? That way we can get evidence whether this would be valuable?

@jonathanpeppers
Copy link
Member

I gave a shot at trying to set this with System.Reflection:

https://github.com/dotnet/runtime/blob/57bfe474518ab5b7cfe6bf7424a79ce3af9d6657/src/mono/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeFeature.Mono.cs#L9-L13

Is this some magic type where it's not possible?

For example:

var type = typeof(System.Runtime.CompilerServices.RuntimeFeature);
foreach (var field in type.GetFields(BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public))
{
	Console.WriteLine($"Field: {field.Name}");
}
foreach (var p in type.GetProperties(BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public))
{
	Console.WriteLine($"Property: {p.Name}");
}
var property = type.GetProperty("IsDynamicCodeSupported");
if (property == null)
	throw new Exception("Did not find IsDynamicCodeSupported!");
if (!property.CanWrite)
	throw new Exception("No way to write to IsDynamicCodeSupported!");
property.SetValue(null, false);

The property has no setter (CanWrite is false), and the output is:

07-13 10:57:56.516 22369 22369 I DOTNET  : Field: PortablePdb
07-13 10:57:56.516 22369 22369 I DOTNET  : Field: DefaultImplementationsOfInterfaces
07-13 10:57:56.516 22369 22369 I DOTNET  : Field: UnmanagedSignatureCallingConvention
07-13 10:57:56.516 22369 22369 I DOTNET  : Field: CovariantReturnsOfClasses
07-13 10:57:56.516 22369 22369 I DOTNET  : Field: VirtualStaticsInInterfaces
07-13 10:57:56.521 22369 22369 I DOTNET  : Property: IsDynamicCodeSupported
07-13 10:57:56.521 22369 22369 I DOTNET  : Property: IsDynamicCodeCompiled

@eerhardt
Copy link
Member

Is this some magic type where it's not possible?

Yes, RuntimeFeature is special to the runtime. You can't change it with reflection.

That's why I suggested to use a private build of DependencyInjection where this code just becomes:

 engine = RuntimeServiceProviderEngine.Instance;

@davidfowl
Copy link
Member

@jonathantims What do the services look like in the DI container? Are they mostly singletons? Are they scoped/transient? The DI container is optimized for throughout over startup today.

@jonathanpeppers
Copy link
Member

Maybe @PureWeen or @mattleibow can answer more specifically what dotnet/maui is doing.

I think they are mostly using singletons:

https://github.com/dotnet/maui/search?q=addsingleton

@mattleibow
Copy link
Member

We are using a combination of both. Things like the font manager are singletons, but things for the ui element handlers are all transient. We are not using scoped right now, but we may. However, if scoped adds too much complexity then we can potentially avoid it.

But, right now, we use singletons and transient.

@davidfowl
Copy link
Member

Good to know. The transient services are the only ones that would incur this background thread compilation, singletons don't. As @eerhardt said, background compilation kicks in once a service type is resolved at least 2 times. I don't know what the typical application flow looks like in an android app at startup.

ui element handlers are all transient

Does that mean ui event handlers are transient? Or something else (forgive my inexperience with maui 😅 )

@mattleibow
Copy link
Member

Ah, we use handlers to mean other things these days 'cause.

Basically a "handler" is something that represents the "controller" for a UI element. For example, this will take a XAML <Button> and then ask the system for a "handler" such as ButtonHandler which then is now responsible for instantiating the native control and then making sure property changes from <Button> reach the UIButton

We are bad at names. This totally should have been called a controller... I guess that is for UI framework 100.0 for .NET 73 to be released in 2050?

@davidfowl
Copy link
Member

So handlers are transient, are they created often? What services are injected into them?

@PureWeen
Copy link
Member Author

PureWeen commented Jul 20, 2021

So handlers are transient, are they created often?

Handlers are created for every single control that's on the screen (button, label, entry, etc...)

The majority (usually all) of the handlers for a screen will be created before anything shows up for the user. Each time you navigate to a new screen a new set of handlers will be created. I would estimate that each screen has between 10 to 50 handlers.

All of the handlers are created on the UI Thread because they have to be. Each handler instantiates a native control and those controls have to be created on the UI Thread. We currently have little to no use for multi threading when resolving dependencies because most (maybe all) of our dependencies can only be created on the UI Thread. This is why the container that we wrote doesn't account for race conditions across multiple threads.

What services are injected into them?

Technically there are no services that are injected via the ctor but the handlers themselves might still use services that are reached via the serviceprovider.

var fontManager = handler.GetRequiredService<IFontManager>();

I realize this might not be best practice, but this is so we can lazy load and save on startup performance. Some of these services will be accessed after the page renderers to the user so we're just trying to reduce the amount of stuff being instantiated on application startup. Though, this might end up being a premature optimization and just injecting via the constructor will prove to be the same.

@davidfowl
Copy link
Member

Technically there are no services that are injected via the ctor but the handlers themselves might still use services that are reached via the serviceprovider.

Why isn't it a singleton then?

@PureWeen
Copy link
Member Author

PureWeen commented Jul 21, 2021

.

@PureWeen
Copy link
Member Author

PureWeen commented Jul 21, 2021

@davidfowl FYI I removed my previous response

The handlers themselves are "transient" but they aren't created through the Ms.Ext.DI.

We have a static factory that's retrieved from the ServiceProvider and then we use that to create handlers ourselves. So, I think the conversation around handlers themselves isn't really relevant to the discussion here.

I need to 100 percent verify this but I'm fairly sure right now we aren't creating 2 of any transient types on the non-blazor side of things. That will start happening once we add multi window support.

Though, If there is a performance hit from transient I do worry if that's going to survive users developing application who are most likely going to register transient things. That being said I don't think we currently have any data to back up the performance hit of creating 2 transient things.

@mattleibow
Copy link
Member

mattleibow commented Jul 21, 2021

I got a new trace file using the MAUI service provider implementation:

maui-app_20210722_005925.zip

And the one that uses the MS.Ext.DI:

maui-app_20210722_012347.zip

This is not to compare the two to see which one is better because we basically do nothing, so theoretically it should always be faster since it does way less. But it is more a reference between them.

Another note, in these traces I disabled the logging and blazor stuff because I wanted an equal set of features that were nothing special.

The full trace of the sample profiling app that does very little is below. It is using registering the blazor stuff and uses the logging and other generic injection things.

maui-app_20210722_013745.zip

@PureWeen
Copy link
Member Author

@mattleibow also verified that we aren't currently resolving any transient services more than once and anything we are resolving more than once is a singleton

@eerhardt
Copy link
Member

@mattleibow - I looked at your traces above

And the one that uses the MS.Ext.DI:

maui-app_20210722_012347.zip

In that trace, I'm still seeing

microsoft.maui!MauiServiceProvider.GetService

Is that expected?

@mattleibow
Copy link
Member

Yes, we use that internally as a factory for getting other things.

@Eilon
Copy link
Member

Eilon commented Sep 3, 2021

The default is now Microsoft.Extensions.DependencyInjection. You can plug in other compatible DI frameworks such as Autofac, as I mentioned in the MauiAppBuilder PR that was merged.

Should we close this issue now?

@PureWeen
Copy link
Member Author

PureWeen commented Sep 3, 2021

@Eilon do we have the performance issues logged anywhere currently?

@Eilon Eilon added the area/hosting 🧩 Extensions / Hosting / AppBuilder / Startup label Sep 27, 2021
@jsuarezruiz jsuarezruiz added this to Under consideration in Enhancements Oct 26, 2021
@Eilon
Copy link
Member

Eilon commented Nov 12, 2021

@PureWeen - FYI I updated #2270 to include any remaining perf investigations so this bug is good to be closed I think.

@dotnet dotnet locked as resolved and limited conversation to collaborators Feb 19, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area/hosting 🧩 Extensions / Hosting / AppBuilder / Startup proposal/open t/enhancement ☀️ New feature or request
Projects
Enhancements
Under consideration
Preview 5
Awaiting triage
Development

No branches or pull requests