Skip to content

Make DependencyInjection more linker trimmable #38678

Closed

Description

@eerhardt

In Microsoft.Extensions.DependencyInjection, there are a few different "engines" that decide how to instantiate services, and how to inject those services into objects using those services.

Options are:

  • Dynamic
  • Runtime
  • ILEmit
  • Expressions (System.Linq.Expressions)

The way it decides which to pick is based on an enum:

switch (options.Mode)
{
case ServiceProviderMode.Default:
#if !NETCOREAPP
_engine = new DynamicServiceProviderEngine(serviceDescriptors, callback);
#else
if (RuntimeFeature.IsSupported("IsDynamicCodeCompiled"))
{
_engine = new DynamicServiceProviderEngine(serviceDescriptors, callback);
}
else
{
// Don't try to compile Expressions/IL if they are going to get interpreted
_engine = new RuntimeServiceProviderEngine(serviceDescriptors, callback);
}
#endif
break;
case ServiceProviderMode.Dynamic:
_engine = new DynamicServiceProviderEngine(serviceDescriptors, callback);
break;
case ServiceProviderMode.Runtime:
_engine = new RuntimeServiceProviderEngine(serviceDescriptors, callback);
break;
#if IL_EMIT
case ServiceProviderMode.ILEmit:
_engine = new ILEmitServiceProviderEngine(serviceDescriptors, callback);
break;
#endif
case ServiceProviderMode.Expressions:
_engine = new ExpressionsServiceProviderEngine(serviceDescriptors, callback);
break;
default:
throw new NotSupportedException(nameof(options.Mode));

However, this enum is internal, and is only used in unit testing. An actual consumer will only ever get the ServiceProviderMode.Default option.

internal enum ServiceProviderMode
{
Default,
Dynamic,
Runtime,
Expressions,
ILEmit

internal ServiceProviderMode Mode { get; set; } = ServiceProviderMode.Default;

The issue is, the way the above code is written, none of unused options are being trimmed, even though they are never used. This means that all the code, and its dependencies (ILEmit, Linq.Expressions) are being preserved, even though they are not being used.

We should change DependencyInjection so that these unused implementations can be trimmed by the linker.

Also we should change:

if (RuntimeFeature.IsSupported("IsDynamicCodeCompiled"))

to be

if (RuntimeFeature.IsDynamicCodeCompiled)

Because on WASM the linker will see that property is hard-coded to false and the unused branch will be trimmed. See:

<type fullname="System.Runtime.CompilerServices.RuntimeFeature">
<method signature="System.Boolean get_IsDynamicCodeCompiled()" body="stub" value="false" />
</type>

Activity

  1. added
    linkable-frameworkIssues associated with delivering a linker friendly framework
    on Jul 1, 2020
  2. self-assigned this
    on Jul 1, 2020
  3. added this to the 5.0.0 milestone on Jul 2, 2020
  4. removed
    untriagedNew issue has not been triaged by the area owner
    on Jul 2, 2020
  5. added a commit that references this issue on Jul 2, 2020
    4ec9ecb
  6. added a commit that references this issue on Jul 3, 2020
    2014fb5
  7. ghost locked as resolved and limited conversation to collaborators on Dec 8, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions