镜像站点 · 本页由第三方 GitHub 只读镜像提供,非 GitHub 官方站点,不接受任何登录或凭据输入。前往 github.com
Skip to content

Developers can safely trim their apps which use Dependency Injection to reduce the size of their apps #44432

Closed

Description

@samsp-msft

Source Generators are a new technology that was added to Roslyn in the .NET 5 timeframe, but we have not yet utilized them by creating generators to assist with runtime and framework features. We will work to define customer scenarios in more detail here with customer development.

Code that uses runtime reflection and reflection.emit causes issues for Trimming and Native AOT. Source generators can be used to generate alternative code at build time that is static so can be used in conjunction with Trimming and AOT. Work on trimming ASP.NET apps has revealed where there are current limitations that can be solved by source generators.

This item tracks creating a Source Generator for Dependency Injection (DI).

Activity

  1. added
    User StoryA single user-facing feature. Can be grouped under an epic.
    on Nov 9, 2020
  2. Dotnet-GitSync-Bot commented on Nov 9, 2020

    @Dotnet-GitSync-Bot
    Collaborator

    I couldn't figure out the best area label to add to this issue. If you have write-permissions please help me learn by adding exactly one area label.

  3. samsp-msft commented on Nov 9, 2020

    @samsp-msft
    Author

    Refactored from #43545

  4. danmoseley commented on Nov 9, 2020

    @danmoseley
    Contributor

    @danroth27 how important is DI to Blazor scenarios? I suspect this story would be expensive, so I'd like to understand whether it's particularly valuable.
    @glennc My understanding is that this is mainly about startup of ASP.NET apps, so it would not be P0 for this release.

    cc @marek-safar

  5. danroth27 commented on Nov 9, 2020

    @danroth27
    Member

    @danmosemsft DI is used heavily in both Blazor and ASP.NET Core.

  6. eerhardt commented on Nov 10, 2020

    @eerhardt
    Member

    DI is used heavily in both Blazor and ASP.NET Core.

    @danroth27 - But how important is it to have a Source Generator for DI for Blazor? We are able to use DI in a Blazor WASM app just fine in 5.0 without a Source Generator. Is one critical for 6.0 scenarios?

    Note: For Blazor WASM, we currently use a "Reflection" provider for DI since RuntimeFeature.IsDynamicCodeCompiled is false on Blazor WASM:

    if (RuntimeFeature.IsDynamicCodeCompiled)
    {
    engine = new DynamicServiceProviderEngine(services);
    }
    else
    {
    // Don't try to compile Expressions/IL if they are going to get interpreted
    engine = new RuntimeServiceProviderEngine(services);
    }

  7. ericstj commented on Nov 10, 2020

    @ericstj
    Member

    @davidfowl was mentioning that this was important to investigate in 6.0 but not "do".

  8. danroth27 commented on Nov 10, 2020

    @danroth27
    Member

    For Blazor WebAssembly, our goal is to get significantly better runtime performance with AoT compilation to WebAssembly, but while also hitting our download size targets. I don't know for sure if this work is needed to hit those requirements, but I think it makes sense to assume it's not until we have data that shows otherwise.

  9. danmoseley commented on Nov 10, 2020

    @danmoseley
    Contributor

    That sounds like Priority:2 (moderately important) until we have data otherwise?

  10. 28 remaining items

  11. danmoseley commented on Mar 28, 2022

    @danmoseley
    Contributor

    leaving open for future

  12. eerhardt commented on Jan 30, 2023

    @eerhardt
    Member

    Note that the title of this issue is no longer a problem. With #55102 and #79425, Microsoft.Extensions.DependencyInjection can be used safely with trimmed and NativeAOT applications.

    Do we want to leave this issue open to explicitly track creating a source generator for DI? If so, I think we should update the title.

  13. eerhardt commented on Jan 30, 2023

    @eerhardt
    Member

    Jotting down my thoughts on why making a source generator for DI is hard.

    1. There are patterns used today that inject certain services based on information only available at runtime. For example:

    if (WindowsServiceHelpers.IsWindowsService())
    {
    AddWindowsServiceLifetime(services, configure);
    }
    return services;
    }
    private static void AddWindowsServiceLifetime(IServiceCollection services, Action<WindowsServiceLifetimeOptions> configure)
    {
    Debug.Assert(RuntimeInformation.IsOSPlatform(OSPlatform.Windows));
    services.AddLogging(logging =>
    {
    Debug.Assert(RuntimeInformation.IsOSPlatform(OSPlatform.Windows));
    logging.AddEventLog();
    });
    services.AddSingleton<IHostLifetime, WindowsServiceLifetime>();

    In this code, the WindowsServiceLifetime service is only injected if the current process is a Windows Service process. This cannot be checked at compile time. Or if it was, you would need to compile for both Windows Services and non-Windows Services and then decide which assemblies to publish. If there are more than a couple of these, the combination of compilation targets gets unwieldly.

    1. The order the services are added to the service collection is important. For non-enumerable services, the last one added is the one that gets resolved. Ordering is hard to express at compile-time.

    2. Given (2) above, in order for the source generator to know which service to use, it needs to know the entire closure of the services registered. This can only be done at the "app level" - the only place where the whole closure can be known. However, there are many service implementations that are not public types. For example:

    services.TryAdd(ServiceDescriptor.Singleton(typeof(IOptions<>), typeof(UnnamedOptionsManager<>)));

    internal sealed class UnnamedOptionsManager<[DynamicallyAccessedMembers(Options.DynamicallyAccessedMembers)] TOptions> :

    It isn't possible for a source generator in the application to reference internal types in libraries it references.

    1. Some services are runtime object instances that have information built up imperatively in code.

    _hostBuilderContext = new HostBuilderContext(new Dictionary<object, object>())
    {
    HostingEnvironment = hostingEnvironment,
    Configuration = Configuration,
    };
    Environment = hostingEnvironment;
    HostBuilder.PopulateServiceCollection(
    Services,
    _hostBuilderContext,
    hostingEnvironment,
    physicalFileProvider,
    Configuration,
    () => _appServices!);

    internal static void PopulateServiceCollection(
    IServiceCollection services,
    HostBuilderContext hostBuilderContext,
    HostingEnvironment hostingEnvironment,
    PhysicalFileProvider defaultFileProvider,
    IConfiguration appConfiguration,
    Func<IServiceProvider> serviceProviderGetter)
    {
    #pragma warning disable CS0618 // Type or member is obsolete
    services.AddSingleton<IHostingEnvironment>(hostingEnvironment);
    #pragma warning restore CS0618 // Type or member is obsolete
    services.AddSingleton<IHostEnvironment>(hostingEnvironment);
    services.AddSingleton(hostBuilderContext);

    Here you can see hostBuilderContext is created at runtime with some settings, and then passed to services.AddSingleton(hostBuilderContext);. Again, this is not possible to accomplish at compile-time.

  14. mairaw commented on May 26, 2023

    @mairaw
    Contributor

    I'm cleaning up the .NET 6 project. This shows as completed on the project dashboard but still open here. Not sure if this needs any updates.

  15. eerhardt commented on Aug 30, 2024

    @eerhardt
    Member

    Closing this issue. I don't believe we will add support for a source generated DI system given the issues outlined in #44432 (comment). https://github.com/pakrym/jab is available for anyone who wants to use a source generated DI system in their application.

    As of .NET 8, developers can use Dependency Injection in both trimmed and native AOT'd apps safely. The only "gotcha" is using value types in open generic services described in #79286. This was addressed by #79425.

  16. julealgon commented on Aug 30, 2024

    @julealgon

    https://github.com/pakrym/jab is available for anyone who wants to use a source generated DI system in their application.

    @eerhardt how does that work with built-in DI registration extensions though.... entire mechanisms such as IOptions and HttpClient registration extensions would not be compatible with that. Nor would any library that uses the MEDI registration syntax... say stuff like AddAzureClients from Microsoft.Extensions.Azure, or all the OTEL methods from OpenTelemetry.Extensions.Hosting. You suggest that library as an alternative but to me it really is not an alternative if it just cannot work at all with existing well-defined patterns.

    I'm sad to hear Microsoft won't attempt anything native in this regard, especially considering how the team has been prioritizing performance so much in the past years. Optimizing the standard DI mechanism would go a long way considering how pervasive it is.

  17. eerhardt commented on Aug 30, 2024

    @eerhardt
    Member

    @julealgon - See the issues outlined in #44432 (comment). Your questions are pointing to exactly why this isn't possible with a source generator (at least how they exist today), and thus why I closed this issue.

  18. julealgon commented on Aug 30, 2024

    @julealgon

    why this isn't possible with a source generator (at least how they exist today)

    @eerhardt You say this, but there was a lot of custom work to allow minimal API to be source generated behind the scenes to support AOT while keeping the exact same runtime method calls in place/same signatures. Surely some of that work could be applied to DI and keep the same DI registration interface intact?

    Did that use the experimental interceptors capability?

    My point was that Microsoft would be better equipped to "do something about it" vs a vendor library that cannot enhance the underlying framework to support new things. My hope was that, if you kept this open, there would be at least a chance that Microsoft would look into alternative ways, be them use of interceptors, or improvements to source generators, to make it work, like they did for minimal API.

  19. davidfowl commented on Aug 30, 2024

    @davidfowl
    Member

    See #82679 (comment). The current model is incompatible with source generators. Minimal APIs is much easier in comparison and we were able to make it work. We would need to design a new system that was statically analyzable to make this work.

  20. julealgon commented on Aug 30, 2024

    @julealgon

    @davidfowl

    See #82679 (comment). The current model is incompatible with source generators. Minimal APIs is much easier in comparison and we were able to make it work. We would need to design a new system that was statically analyzable to make this work.

    Fair enough. Thanks for sharing that interesting comment David. I know you were directly involved in the Minimal API AOT work so I'll take your word for it that this is much harder to achieve.

  21. locked and limited conversation to collaborators on Oct 2, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions