Dipping toes into linker friendliness (#24458)

- Annotated UseMiddleware and UseStartup to preserve constructors and public methods.
- Annotated UseHub and MapHub preserve constructors and public methods.
- Added a test to verify UseStartup and UseMiddleware works
- The linker.xml preserves constructors all of the types that are registered in DI. This should be removed once we get the linker friendly DI changes.
This commit is contained in:
David Fowler 2020-08-03 06:54:03 -07:00 committed by GitHub
parent dd25b5f91a
commit 404d817677
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 405 additions and 27 deletions

View File

@ -4,11 +4,13 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Reflection;
using System.Runtime.ExceptionServices;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting.Builder;
using Microsoft.AspNetCore.Hosting.Internal;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
@ -196,7 +198,7 @@ namespace Microsoft.AspNetCore.Hosting
return this;
}
public IWebHostBuilder UseStartup(Type startupType)
public IWebHostBuilder UseStartup([DynamicallyAccessedMembers(StartupLinkerOptions.Accessibility)] Type startupType)
{
// UseStartup can be called multiple times. Only run the last one.
_startupObject = startupType;
@ -213,7 +215,7 @@ namespace Microsoft.AspNetCore.Hosting
return this;
}
public IWebHostBuilder UseStartup(Func<WebHostBuilderContext, object> startupFactory)
public IWebHostBuilder UseStartup<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)]TStartup>(Func<WebHostBuilderContext, TStartup> startupFactory)
{
// Clear the startup type
_startupObject = startupFactory;
@ -232,7 +234,7 @@ namespace Microsoft.AspNetCore.Hosting
return this;
}
private void UseStartup(Type startupType, HostBuilderContext context, IServiceCollection services, object instance = null)
private void UseStartup([DynamicallyAccessedMembers(StartupLinkerOptions.Accessibility)] Type startupType, HostBuilderContext context, IServiceCollection services, object instance = null)
{
var webHostBuilderContext = GetWebHostBuilderContext(context);
var webHostOptions = (WebHostOptions)context.Properties[typeof(WebHostOptions)];

View File

@ -2,7 +2,9 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Diagnostics.CodeAnalysis;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting.Internal;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
@ -71,12 +73,12 @@ namespace Microsoft.AspNetCore.Hosting
return _builder.Configure(configure);
}
public IWebHostBuilder UseStartup(Type startupType)
public IWebHostBuilder UseStartup([DynamicallyAccessedMembers(StartupLinkerOptions.Accessibility)] Type startupType)
{
return _builder.UseStartup(startupType);
}
public IWebHostBuilder UseStartup(Func<WebHostBuilderContext, object> startupFactory)
public IWebHostBuilder UseStartup<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)]TStartup>(Func<WebHostBuilderContext, TStartup> startupFactory)
{
return _builder.UseStartup(startupFactory);
}

View File

@ -2,14 +2,16 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Diagnostics.CodeAnalysis;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting.Internal;
namespace Microsoft.AspNetCore.Hosting
{
internal interface ISupportsStartup
{
IWebHostBuilder Configure(Action<WebHostBuilderContext, IApplicationBuilder> configure);
IWebHostBuilder UseStartup(Type startupType);
IWebHostBuilder UseStartup(Func<WebHostBuilderContext, object> startupFactory);
IWebHostBuilder UseStartup([DynamicallyAccessedMembers(StartupLinkerOptions.Accessibility)] Type startupType);
IWebHostBuilder UseStartup<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)]TStartup>(Func<WebHostBuilderContext, TStartup> startupFactory);
}
}

View File

@ -0,0 +1,13 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Diagnostics.CodeAnalysis;
namespace Microsoft.AspNetCore.Hosting.Internal
{
internal static class StartupLinkerOptions
{
// We're going to keep all public constructors and public methods on Startup classes
public const DynamicallyAccessedMemberTypes Accessibility = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicMethods;
}
}

View File

@ -3,9 +3,11 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Linq;
using System.Reflection;
using Microsoft.AspNetCore.Hosting.Internal;
using Microsoft.Extensions.DependencyInjection;
namespace Microsoft.AspNetCore.Hosting
@ -37,7 +39,7 @@ namespace Microsoft.AspNetCore.Hosting
//
// If the Startup class ConfigureServices returns an <see cref="IServiceProvider"/> and there is at least an <see cref="IStartupConfigureServicesFilter"/> registered we
// throw as the filters can't be applied.
public static StartupMethods LoadMethods(IServiceProvider hostingServiceProvider, Type startupType, string environmentName, object instance = null)
public static StartupMethods LoadMethods(IServiceProvider hostingServiceProvider, [DynamicallyAccessedMembers(StartupLinkerOptions.Accessibility)] Type startupType, string environmentName, object instance = null)
{
var configureMethod = FindConfigureDelegate(startupType, environmentName);
@ -272,31 +274,31 @@ namespace Microsoft.AspNetCore.Hosting
return type;
}
internal static ConfigureBuilder FindConfigureDelegate(Type startupType, string environmentName)
internal static ConfigureBuilder FindConfigureDelegate([DynamicallyAccessedMembers(StartupLinkerOptions.Accessibility)] Type startupType, string environmentName)
{
var configureMethod = FindMethod(startupType, "Configure{0}", environmentName, typeof(void), required: true);
return new ConfigureBuilder(configureMethod);
}
internal static ConfigureContainerBuilder FindConfigureContainerDelegate(Type startupType, string environmentName)
internal static ConfigureContainerBuilder FindConfigureContainerDelegate([DynamicallyAccessedMembers(StartupLinkerOptions.Accessibility)] Type startupType, string environmentName)
{
var configureMethod = FindMethod(startupType, "Configure{0}Container", environmentName, typeof(void), required: false);
return new ConfigureContainerBuilder(configureMethod);
}
internal static bool HasConfigureServicesIServiceProviderDelegate(Type startupType, string environmentName)
internal static bool HasConfigureServicesIServiceProviderDelegate([DynamicallyAccessedMembers(StartupLinkerOptions.Accessibility)] Type startupType, string environmentName)
{
return null != FindMethod(startupType, "Configure{0}Services", environmentName, typeof(IServiceProvider), required: false);
}
internal static ConfigureServicesBuilder FindConfigureServicesDelegate(Type startupType, string environmentName)
internal static ConfigureServicesBuilder FindConfigureServicesDelegate([DynamicallyAccessedMembers(StartupLinkerOptions.Accessibility)] Type startupType, string environmentName)
{
var servicesMethod = FindMethod(startupType, "Configure{0}Services", environmentName, typeof(IServiceProvider), required: false)
?? FindMethod(startupType, "Configure{0}Services", environmentName, typeof(void), required: false);
return new ConfigureServicesBuilder(servicesMethod);
}
private static MethodInfo FindMethod(Type startupType, string methodName, string environmentName, Type returnType = null, bool required = true)
private static MethodInfo FindMethod([DynamicallyAccessedMembers(StartupLinkerOptions.Accessibility)] Type startupType, string methodName, string environmentName, Type returnType = null, bool required = true)
{
var methodNameWithEnv = string.Format(CultureInfo.InvariantCulture, methodName, environmentName);
var methodNameWithNoEnv = string.Format(CultureInfo.InvariantCulture, methodName, "");

View File

@ -2,8 +2,10 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting.Internal;
using Microsoft.AspNetCore.Hosting.StaticWebAssets;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
@ -67,7 +69,8 @@ namespace Microsoft.AspNetCore.Hosting
/// <param name="hostBuilder">The <see cref="IWebHostBuilder"/> to configure.</param>
/// <param name="startupFactory">A delegate that specifies a factory for the startup class.</param>
/// <returns>The <see cref="IWebHostBuilder"/>.</returns>
public static IWebHostBuilder UseStartup(this IWebHostBuilder hostBuilder, Func<WebHostBuilderContext, object> startupFactory)
/// <remarks>When using the il linker, all public methods of <typeparamref name="TStartup"/> are preserved. This should match the Startup type directly (and not a base type).</remarks>
public static IWebHostBuilder UseStartup<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)]TStartup>(this IWebHostBuilder hostBuilder, Func<WebHostBuilderContext, TStartup> startupFactory) where TStartup : class
{
if (startupFactory == null)
{
@ -110,7 +113,7 @@ namespace Microsoft.AspNetCore.Hosting
/// <param name="hostBuilder">The <see cref="IWebHostBuilder"/> to configure.</param>
/// <param name="startupType">The <see cref="Type"/> to be used.</param>
/// <returns>The <see cref="IWebHostBuilder"/>.</returns>
public static IWebHostBuilder UseStartup(this IWebHostBuilder hostBuilder, Type startupType)
public static IWebHostBuilder UseStartup(this IWebHostBuilder hostBuilder, [DynamicallyAccessedMembers(StartupLinkerOptions.Accessibility)] Type startupType)
{
if (startupType == null)
{
@ -151,7 +154,7 @@ namespace Microsoft.AspNetCore.Hosting
/// <param name="hostBuilder">The <see cref="IWebHostBuilder"/> to configure.</param>
/// <typeparam name ="TStartup">The type containing the startup methods for the application.</typeparam>
/// <returns>The <see cref="IWebHostBuilder"/>.</returns>
public static IWebHostBuilder UseStartup<TStartup>(this IWebHostBuilder hostBuilder) where TStartup : class
public static IWebHostBuilder UseStartup<[DynamicallyAccessedMembers(StartupLinkerOptions.Accessibility)]TStartup>(this IWebHostBuilder hostBuilder) where TStartup : class
{
return hostBuilder.UseStartup(typeof(TStartup));
}

View File

@ -74,7 +74,7 @@ namespace Microsoft.AspNetCore.Hosting.Tests.Fakes
return this;
}
public IWebHostBuilder UseStartup(Func<WebHostBuilderContext, object> startupFactory)
public IWebHostBuilder UseStartup<TStartup>(Func<WebHostBuilderContext, TStartup> startupFactory)
{
_builder.UseStartup(startupFactory);
return this;

View File

@ -85,7 +85,7 @@ namespace Microsoft.AspNetCore.Hosting
public void UseStartupThrowsWhenFactoryReturnsNull(IWebHostBuilder builder)
{
var server = new TestServer();
var ex = Assert.Throws<InvalidOperationException>(() => builder.UseServer(server).UseStartup(context => null).Build());
var ex = Assert.Throws<InvalidOperationException>(() => builder.UseServer(server).UseStartup<object>(context => null).Build());
Assert.Equal("The specified factory returned null startup instance.", ex.Message);
}
@ -96,7 +96,7 @@ namespace Microsoft.AspNetCore.Hosting
var server = new TestServer();
var host = builder.UseServer(server)
.UseStartup<StartupCtorThrows>()
.UseStartup(context => throw new InvalidOperationException("This doesn't run"))
.UseStartup<object>(context => throw new InvalidOperationException("This doesn't run"))
.Configure(app =>
{
throw new InvalidOperationException("This doesn't run");

View File

@ -38,7 +38,7 @@ namespace Microsoft.AspNetCore.Server.IntegrationTesting
// avoids triggering builds of dependencies of the test app which could cause issues like https://github.com/dotnet/arcade/issues/2941
+ $" --no-dependencies"
+ $" /p:TargetArchitecture={deploymentParameters.RuntimeArchitecture}"
+ " --no-restore";
+ (deploymentParameters.RestoreDependencies ? "" : " --no-restore");
if (deploymentParameters.ApplicationType == ApplicationType.Standalone)
{

View File

@ -113,6 +113,8 @@ namespace Microsoft.AspNetCore.Server.IntegrationTesting
/// </summary>
public string ApplicationBaseUriHint { get; set; }
public bool RestoreDependencies { get; set; }
/// <summary>
/// Scheme used by the deployed application if <see cref="ApplicationBaseUriHint"/> is empty.
/// </summary>

View File

@ -0,0 +1,57 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Server.IntegrationTesting;
using Microsoft.AspNetCore.Testing;
using Microsoft.Extensions.Logging;
using Xunit;
namespace Microsoft.AspNetCore.Hosting.FunctionalTests
{
public class LinkedApplicationTests : LoggedTest
{
[Fact]
public async Task LinkedApplicationWorks()
{
using (StartLog(out var loggerFactory))
{
var logger = loggerFactory.CreateLogger("LinkedApplicationWorks");
// https://github.com/dotnet/aspnetcore/issues/8247
#pragma warning disable 0618
var applicationPath = Path.Combine(TestPathUtilities.GetSolutionRootDirectory("Hosting"), "test", "testassets",
"BasicLinkedApp");
#pragma warning restore 0618
var deploymentParameters = new DeploymentParameters(
applicationPath,
ServerType.Kestrel,
RuntimeFlavor.CoreClr,
RuntimeArchitecture.x64)
{
TargetFramework = Tfm.Net50,
RuntimeArchitecture = RuntimeArchitecture.x64,
ApplicationType = ApplicationType.Standalone,
PublishApplicationBeforeDeployment = true,
RestoreDependencies = true,
StatusMessagesEnabled = false
};
using var deployer = new SelfHostDeployer(deploymentParameters, loggerFactory);
var result = await deployer.DeployAsync();
// The app should have started up
Assert.False(deployer.HostProcess.HasExited);
var response = await RetryHelper.RetryRequest(() => result.HttpClient.GetAsync("/"), logger, retryCount: 10);
var body = await response.Content.ReadAsStringAsync();
Assert.Equal("Hello World", body);
}
}
}
}

View File

@ -0,0 +1,31 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>$(DefaultNetCoreTargetFramework)</TargetFramework>
<OutputType>Exe</OutputType>
<PublishTrimmed>true</PublishTrimmed>
<TrimMode>link</TrimMode>
</PropertyGroup>
<ItemGroup>
<Reference Include="Microsoft.AspNetCore.Hosting" />
<Reference Include="Microsoft.Extensions.Hosting" />
<Reference Include="Microsoft.AspNetCore.Server.Kestrel" />
</ItemGroup>
<ItemGroup>
<TrimmerRootDescriptor Include="Linker.xml" />
</ItemGroup>
<!-- Link all assemblies -->
<Target Name="EnsureAllAssembliesAreLinked" BeforeTargets="PrepareForILLink">
<ItemGroup>
<ManagedAssemblyToLink>
<TrimMode>link</TrimMode>
</ManagedAssemblyToLink>
<!-- Root this project assembly -->
<TrimmerRootAssembly Include="$(TargetName)" />
</ItemGroup>
</Target>
</Project>

View File

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- See: https://github.com/mono/linker/blob/master/src/linker/README.md#syntax-of-xml-descriptor -->
<linker>
<assembly fullname="Microsoft.Extensions.Hosting">
<type fullname="Microsoft.Extensions.Hosting.Internal.ApplicationLifetime" />
<type fullname="Microsoft.Extensions.Hosting.Internal.ConsoleLifetime" />
<type fullname="Microsoft.Extensions.Hosting.ConsoleLifetimeOptions" />
<type fullname="Microsoft.Extensions.Hosting.Internal.Host" />
<type fullname="Microsoft.Extensions.Hosting.HostOptions" />
</assembly>
<assembly fullname="Microsoft.Extensions.Logging">
<type fullname="Microsoft.Extensions.Logging.LoggerFactory" />
</assembly>
<assembly fullname="Microsoft.Extensions.Logging.Configuration">
<type fullname="Microsoft.Extensions.Logging.Configuration.LoggerProviderConfigurationFactory" />
<type fullname="Microsoft.Extensions.Logging.Configuration.LoggerProviderConfigureOptions`2" />
<type fullname="Microsoft.Extensions.Logging.Configuration.LoggerProviderOptionsChangeTokenSource`2" />
<type fullname="Microsoft.Extensions.Logging.Configuration.LoggerProviderConfiguration`1" />
</assembly>
<assembly fullname="Microsoft.Extensions.Logging.Console" preserve="all" />
<assembly fullname="Microsoft.AspNetCore.Hosting">
<type fullname="Microsoft.AspNetCore.Hosting.GenericWebHostApplicationLifetime" />
<type fullname="Microsoft.AspNetCore.Http.DefaultHttpContextFactory" />
<type fullname="Microsoft.AspNetCore.Hosting.Builder.ApplicationBuilderFactory" />
<type fullname="Microsoft.AspNetCore.Hosting.GenericWebHostService" />
<type fullname="Microsoft.AspNetCore.Hosting.GenericWebHostServiceOptions" />
</assembly>
<assembly fullname="Microsoft.AspNetCore.Http">
<type fullname="Microsoft.AspNetCore.Http.MiddlewareFactory" />
</assembly>
<assembly fullname="Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets">
<type fullname="Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.SocketTransportFactory" />
<type fullname="Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.SocketTransportOptions" />
</assembly>
<assembly fullname="Microsoft.AspNetCore.Server.Kestrel.Core">
<type fullname="Microsoft.AspNetCore.Server.Kestrel.Core.Internal.KestrelServerOptionsSetup" />
<type fullname="Microsoft.AspNetCore.Server.Kestrel.Core.KestrelServer" />
</assembly>
<assembly fullname="Microsoft.Extensions.Options">
<type fullname="Microsoft.Extensions.Options.OptionsCache`1" />
<type fullname="Microsoft.Extensions.Options.OptionsFactory`1" />
<type fullname="Microsoft.Extensions.Options.OptionsMonitor`1" />
<type fullname="Microsoft.Extensions.Options.OptionsManager`1" />
</assembly>
</linker>

View File

@ -0,0 +1,38 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Configuration;
namespace BasicLinkedApp
{
public class Program
{
public static void Main(string[] args)
{
CreateWebHostBuilder(args).Build().Run();
}
// Do not change the signature of this method. It's used for tests.
private static IHostBuilder CreateWebHostBuilder(string[] args)
{
return new HostBuilder()
.ConfigureHostConfiguration(config =>
{
config.AddCommandLine(args);
})
.ConfigureLogging(logging =>
{
logging.AddConsole();
})
.ConfigureWebHost(webHostBuilder =>
{
webHostBuilder.UseKestrel().UseStartup<Startup>();
});
}
}
}

View File

@ -0,0 +1,29 @@

using System;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using System.Threading.Tasks;
namespace BasicLinkedApp
{
public class Startup
{
public void Configure(IApplicationBuilder app)
{
app.UseMiddleware<HelloWorldMiddleware>();
}
}
public class HelloWorldMiddleware
{
public HelloWorldMiddleware(RequestDelegate next)
{
}
public Task InvokeAsync(HttpContext context)
{
return context.Response.WriteAsync("Hello World");
}
}
}

View File

@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
@ -22,6 +23,9 @@ namespace Microsoft.AspNetCore.Builder
private static readonly MethodInfo GetServiceInfo = typeof(UseMiddlewareExtensions).GetMethod(nameof(GetService), BindingFlags.NonPublic | BindingFlags.Static)!;
// We're going to keep all public constructors and public methods on middleware
private const DynamicallyAccessedMemberTypes MiddlewareAccessibility = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicMethods;
/// <summary>
/// Adds a middleware type to the application's request pipeline.
/// </summary>
@ -29,7 +33,7 @@ namespace Microsoft.AspNetCore.Builder
/// <param name="app">The <see cref="IApplicationBuilder"/> instance.</param>
/// <param name="args">The arguments to pass to the middleware type instance's constructor.</param>
/// <returns>The <see cref="IApplicationBuilder"/> instance.</returns>
public static IApplicationBuilder UseMiddleware<TMiddleware>(this IApplicationBuilder app, params object[] args)
public static IApplicationBuilder UseMiddleware<[DynamicallyAccessedMembers(MiddlewareAccessibility)]TMiddleware>(this IApplicationBuilder app, params object[] args)
{
return app.UseMiddleware(typeof(TMiddleware), args);
}
@ -41,7 +45,7 @@ namespace Microsoft.AspNetCore.Builder
/// <param name="middleware">The middleware type.</param>
/// <param name="args">The arguments to pass to the middleware type instance's constructor.</param>
/// <returns>The <see cref="IApplicationBuilder"/> instance.</returns>
public static IApplicationBuilder UseMiddleware(this IApplicationBuilder app, Type middleware, params object[] args)
public static IApplicationBuilder UseMiddleware(this IApplicationBuilder app, [DynamicallyAccessedMembers(MiddlewareAccessibility)] Type middleware, params object[] args)
{
if (typeof(IMiddleware).GetTypeInfo().IsAssignableFrom(middleware.GetTypeInfo()))
{
@ -110,7 +114,7 @@ namespace Microsoft.AspNetCore.Builder
});
}
private static IApplicationBuilder UseMiddlewareInterface(IApplicationBuilder app, Type middlewareType)
private static IApplicationBuilder UseMiddlewareInterface(IApplicationBuilder app, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type middlewareType)
{
return app.Use(next =>
{

View File

@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Diagnostics.CodeAnalysis;
using System.Threading.Tasks;
using Microsoft.Extensions.Internal;
@ -9,7 +10,7 @@ namespace Microsoft.AspNetCore.Connections
{
public static class ConnectionBuilderExtensions
{
public static IConnectionBuilder UseConnectionHandler<TConnectionHandler>(this IConnectionBuilder connectionBuilder) where TConnectionHandler : ConnectionHandler
public static IConnectionBuilder UseConnectionHandler<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)]TConnectionHandler>(this IConnectionBuilder connectionBuilder) where TConnectionHandler : ConnectionHandler
{
var handler = ActivatorUtilities.GetServiceOrCreateInstance<TConnectionHandler>(connectionBuilder.ApplicationServices);

View File

@ -17,6 +17,7 @@
<Compile Include="$(SharedSourceRoot)ActivatorUtilities\*.cs" />
<Compile Include="$(SharedSourceRoot)ParameterDefaultValue\*.cs" />
<Compile Include="$(SharedSourceRoot)CodeAnalysis\*.cs" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">

View File

@ -0,0 +1,92 @@
#if !NET5_0
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
namespace System.Diagnostics.CodeAnalysis
{
/// <summary>
/// Specifies the types of members that are dynamically accessed.
///
/// This enumeration has a <see cref="FlagsAttribute"/> attribute that allows a
/// bitwise combination of its member values.
/// </summary>
[Flags]
internal enum DynamicallyAccessedMemberTypes
{
/// <summary>
/// Specifies no members.
/// </summary>
None = 0,
/// <summary>
/// Specifies the default, parameterless public constructor.
/// </summary>
PublicParameterlessConstructor = 0x0001,
/// <summary>
/// Specifies all public constructors.
/// </summary>
PublicConstructors = 0x0002 | PublicParameterlessConstructor,
/// <summary>
/// Specifies all non-public constructors.
/// </summary>
NonPublicConstructors = 0x0004,
/// <summary>
/// Specifies all public methods.
/// </summary>
PublicMethods = 0x0008,
/// <summary>
/// Specifies all non-public methods.
/// </summary>
NonPublicMethods = 0x0010,
/// <summary>
/// Specifies all public fields.
/// </summary>
PublicFields = 0x0020,
/// <summary>
/// Specifies all non-public fields.
/// </summary>
NonPublicFields = 0x0040,
/// <summary>
/// Specifies all public nested types.
/// </summary>
PublicNestedTypes = 0x0080,
/// <summary>
/// Specifies all non-public nested types.
/// </summary>
NonPublicNestedTypes = 0x0100,
/// <summary>
/// Specifies all public properties.
/// </summary>
PublicProperties = 0x0200,
/// <summary>
/// Specifies all non-public properties.
/// </summary>
NonPublicProperties = 0x0400,
/// <summary>
/// Specifies all public events.
/// </summary>
PublicEvents = 0x0800,
/// <summary>
/// Specifies all non-public events.
/// </summary>
NonPublicEvents = 0x1000,
/// <summary>
/// Specifies all members.
/// </summary>
All = ~None
}
}
#endif

View File

@ -0,0 +1,48 @@
#if !NET5_0
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
namespace System.Diagnostics.CodeAnalysis
{
/// <summary>
/// Indicates that certain members on a specified <see cref="Type"/> are accessed dynamically,
/// for example through <see cref="System.Reflection"/>.
/// </summary>
/// <remarks>
/// This allows tools to understand which members are being accessed during the execution
/// of a program.
///
/// This attribute is valid on members whose type is <see cref="Type"/> or <see cref="string"/>.
///
/// When this attribute is applied to a location of type <see cref="string"/>, the assumption is
/// that the string represents a fully qualified type name.
///
/// If the attribute is applied to a method it's treated as a special case and it implies
/// the attribute should be applied to the "this" parameter of the method. As such the attribute
/// should only be used on instance methods of types assignable to System.Type (or string, but no methods
/// will use it there).
/// </remarks>
[AttributeUsage(
AttributeTargets.Field | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter |
AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.Method,
Inherited = false)]
internal sealed class DynamicallyAccessedMembersAttribute : Attribute
{
/// <summary>
/// Initializes a new instance of the <see cref="DynamicallyAccessedMembersAttribute"/> class
/// with the specified member types.
/// </summary>
/// <param name="memberTypes">The types of members dynamically accessed.</param>
public DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes memberTypes)
{
MemberTypes = memberTypes;
}
/// <summary>
/// Gets the <see cref="DynamicallyAccessedMemberTypes"/> which specifies the type
/// of members dynamically accessed.
/// </summary>
public DynamicallyAccessedMemberTypes MemberTypes { get; }
}
}
#endif

View File

@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Diagnostics.CodeAnalysis;
using Microsoft.AspNetCore.Connections;
using Microsoft.AspNetCore.SignalR.Internal;
using Microsoft.Extensions.DependencyInjection;
@ -13,13 +14,15 @@ namespace Microsoft.AspNetCore.SignalR
/// </summary>
public static class SignalRConnectionBuilderExtensions
{
private const DynamicallyAccessedMemberTypes HubAccessibility = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicMethods;
/// <summary>
/// Configure the connection to host the specified <see cref="Hub"/> type.
/// </summary>
/// <typeparam name="THub">The <see cref="Hub"/> type to host on the connection.</typeparam>
/// <param name="connectionBuilder">The connection to configure.</param>
/// <returns>The same instance of the <see cref="IConnectionBuilder"/> for chaining.</returns>
public static IConnectionBuilder UseHub<THub>(this IConnectionBuilder connectionBuilder) where THub : Hub
public static IConnectionBuilder UseHub<[DynamicallyAccessedMembers(HubAccessibility)]THub>(this IConnectionBuilder connectionBuilder) where THub : Hub
{
var marker = connectionBuilder.ApplicationServices.GetService(typeof(SignalRCoreMarkerService));
if (marker == null)

View File

@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Diagnostics.CodeAnalysis;
using Microsoft.AspNetCore.Http.Connections;
using Microsoft.AspNetCore.Routing;
using Microsoft.AspNetCore.SignalR;
@ -11,6 +12,8 @@ namespace Microsoft.AspNetCore.Builder
{
public static class HubEndpointRouteBuilderExtensions
{
private const DynamicallyAccessedMemberTypes HubAccessibility = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicMethods;
/// <summary>
/// Maps incoming requests with the specified path to the specified <see cref="Hub"/> type.
/// </summary>
@ -18,7 +21,7 @@ namespace Microsoft.AspNetCore.Builder
/// <param name="endpoints">The <see cref="IEndpointRouteBuilder"/> to add the route to.</param>
/// <param name="pattern">The route pattern.</param>
/// <returns>An <see cref="HubEndpointConventionBuilder"/> for endpoints associated with the connections.</returns>
public static HubEndpointConventionBuilder MapHub<THub>(this IEndpointRouteBuilder endpoints, string pattern) where THub : Hub
public static HubEndpointConventionBuilder MapHub<[DynamicallyAccessedMembers(HubAccessibility)]THub>(this IEndpointRouteBuilder endpoints, string pattern) where THub : Hub
{
return endpoints.MapHub<THub>(pattern, configureOptions: null);
}
@ -31,7 +34,7 @@ namespace Microsoft.AspNetCore.Builder
/// <param name="pattern">The route pattern.</param>
/// <param name="configureOptions">A callback to configure dispatcher options.</param>
/// <returns>An <see cref="HubEndpointConventionBuilder"/> for endpoints associated with the connections.</returns>
public static HubEndpointConventionBuilder MapHub<THub>(this IEndpointRouteBuilder endpoints, string pattern, Action<HttpConnectionDispatcherOptions> configureOptions) where THub : Hub
public static HubEndpointConventionBuilder MapHub<[DynamicallyAccessedMembers(HubAccessibility)]THub>(this IEndpointRouteBuilder endpoints, string pattern, Action<HttpConnectionDispatcherOptions> configureOptions) where THub : Hub
{
var marker = endpoints.ServiceProvider.GetService<SignalRMarkerService>();