Merge remote-tracking branch 'origin/release/2.1' into dev
This commit is contained in:
commit
3141179344
|
|
@ -4,83 +4,84 @@
|
|||
</PropertyGroup>
|
||||
<PropertyGroup Label="Package Versions">
|
||||
<BenchmarkDotNetPackageVersion>0.10.13</BenchmarkDotNetPackageVersion>
|
||||
<InternalAspNetCoreSdkPackageVersion>2.1.0-preview2-15739</InternalAspNetCoreSdkPackageVersion>
|
||||
<MicrosoftAspNetCoreAntiforgeryPackageVersion>2.1.0-preview2-30328</MicrosoftAspNetCoreAntiforgeryPackageVersion>
|
||||
<MicrosoftAspNetCoreAuthenticationCookiesPackageVersion>2.1.0-preview2-30328</MicrosoftAspNetCoreAuthenticationCookiesPackageVersion>
|
||||
<MicrosoftAspNetCoreAuthenticationCorePackageVersion>2.1.0-preview2-30328</MicrosoftAspNetCoreAuthenticationCorePackageVersion>
|
||||
<MicrosoftAspNetCoreAuthenticationPackageVersion>2.1.0-preview2-30328</MicrosoftAspNetCoreAuthenticationPackageVersion>
|
||||
<MicrosoftAspNetCoreAuthorizationPolicyPackageVersion>2.1.0-preview2-30328</MicrosoftAspNetCoreAuthorizationPolicyPackageVersion>
|
||||
<MicrosoftAspNetCoreBenchmarkRunnerSourcesPackageVersion>2.1.0-preview2-30328</MicrosoftAspNetCoreBenchmarkRunnerSourcesPackageVersion>
|
||||
<MicrosoftAspNetCoreChunkingCookieManagerSourcesPackageVersion>2.1.0-preview2-30328</MicrosoftAspNetCoreChunkingCookieManagerSourcesPackageVersion>
|
||||
<MicrosoftAspNetCoreCookiePolicyPackageVersion>2.1.0-preview2-30328</MicrosoftAspNetCoreCookiePolicyPackageVersion>
|
||||
<MicrosoftAspNetCoreCorsPackageVersion>2.1.0-preview2-30328</MicrosoftAspNetCoreCorsPackageVersion>
|
||||
<MicrosoftAspNetCoreDiagnosticsAbstractionsPackageVersion>2.1.0-preview2-30328</MicrosoftAspNetCoreDiagnosticsAbstractionsPackageVersion>
|
||||
<MicrosoftAspNetCoreDiagnosticsPackageVersion>2.1.0-preview2-30328</MicrosoftAspNetCoreDiagnosticsPackageVersion>
|
||||
<MicrosoftAspNetCoreHostingAbstractionsPackageVersion>2.1.0-preview2-30328</MicrosoftAspNetCoreHostingAbstractionsPackageVersion>
|
||||
<MicrosoftAspNetCoreHostingPackageVersion>2.1.0-preview2-30328</MicrosoftAspNetCoreHostingPackageVersion>
|
||||
<MicrosoftAspNetCoreHtmlAbstractionsPackageVersion>2.1.0-preview2-30328</MicrosoftAspNetCoreHtmlAbstractionsPackageVersion>
|
||||
<MicrosoftAspNetCoreHttpExtensionsPackageVersion>2.1.0-preview2-30328</MicrosoftAspNetCoreHttpExtensionsPackageVersion>
|
||||
<MicrosoftAspNetCoreHttpPackageVersion>2.1.0-preview2-30328</MicrosoftAspNetCoreHttpPackageVersion>
|
||||
<MicrosoftAspNetCoreJsonPatchPackageVersion>2.1.0-preview2-30328</MicrosoftAspNetCoreJsonPatchPackageVersion>
|
||||
<MicrosoftAspNetCoreLocalizationPackageVersion>2.1.0-preview2-30328</MicrosoftAspNetCoreLocalizationPackageVersion>
|
||||
<MicrosoftAspNetCoreLocalizationRoutingPackageVersion>2.1.0-preview2-30328</MicrosoftAspNetCoreLocalizationRoutingPackageVersion>
|
||||
<MicrosoftAspNetCoreMvcRazorExtensionsPackageVersion>2.1.0-preview2-30328</MicrosoftAspNetCoreMvcRazorExtensionsPackageVersion>
|
||||
<MicrosoftAspNetCoreRangeHelperSourcesPackageVersion>2.1.0-preview2-30328</MicrosoftAspNetCoreRangeHelperSourcesPackageVersion>
|
||||
<MicrosoftAspNetCoreRazorDesignPackageVersion>2.1.0-preview2-30328</MicrosoftAspNetCoreRazorDesignPackageVersion>
|
||||
<MicrosoftAspNetCoreRazorRuntimePackageVersion>2.1.0-preview2-30328</MicrosoftAspNetCoreRazorRuntimePackageVersion>
|
||||
<MicrosoftAspNetCoreRazorTagHelpersTestingSourcesPackageVersion>2.1.0-preview2-30328</MicrosoftAspNetCoreRazorTagHelpersTestingSourcesPackageVersion>
|
||||
<MicrosoftAspNetCoreResponseCachingAbstractionsPackageVersion>2.1.0-preview2-30328</MicrosoftAspNetCoreResponseCachingAbstractionsPackageVersion>
|
||||
<MicrosoftAspNetCoreResponseCachingPackageVersion>2.1.0-preview2-30328</MicrosoftAspNetCoreResponseCachingPackageVersion>
|
||||
<MicrosoftAspNetCoreRoutingAbstractionsPackageVersion>2.1.0-preview2-30328</MicrosoftAspNetCoreRoutingAbstractionsPackageVersion>
|
||||
<MicrosoftAspNetCoreRoutingPackageVersion>2.1.0-preview2-30328</MicrosoftAspNetCoreRoutingPackageVersion>
|
||||
<MicrosoftAspNetCoreServerIISIntegrationPackageVersion>2.1.0-preview2-30328</MicrosoftAspNetCoreServerIISIntegrationPackageVersion>
|
||||
<MicrosoftAspNetCoreServerKestrelPackageVersion>2.1.0-preview2-30328</MicrosoftAspNetCoreServerKestrelPackageVersion>
|
||||
<MicrosoftAspNetCoreSessionPackageVersion>2.1.0-preview2-30328</MicrosoftAspNetCoreSessionPackageVersion>
|
||||
<MicrosoftAspNetCoreStaticFilesPackageVersion>2.1.0-preview2-30328</MicrosoftAspNetCoreStaticFilesPackageVersion>
|
||||
<MicrosoftAspNetCoreTestHostPackageVersion>2.1.0-preview2-30328</MicrosoftAspNetCoreTestHostPackageVersion>
|
||||
<MicrosoftAspNetCoreTestingPackageVersion>2.1.0-preview2-30328</MicrosoftAspNetCoreTestingPackageVersion>
|
||||
<MicrosoftAspNetCoreWebUtilitiesPackageVersion>2.1.0-preview2-30328</MicrosoftAspNetCoreWebUtilitiesPackageVersion>
|
||||
<InternalAspNetCoreSdkPackageVersion>2.1.0-preview3-15746</InternalAspNetCoreSdkPackageVersion>
|
||||
<MicrosoftAspNetCoreAntiforgeryPackageVersion>2.1.0-preview3-30392</MicrosoftAspNetCoreAntiforgeryPackageVersion>
|
||||
<MicrosoftAspNetCoreAuthenticationCookiesPackageVersion>2.1.0-preview3-30392</MicrosoftAspNetCoreAuthenticationCookiesPackageVersion>
|
||||
<MicrosoftAspNetCoreAuthenticationCorePackageVersion>2.1.0-preview3-30392</MicrosoftAspNetCoreAuthenticationCorePackageVersion>
|
||||
<MicrosoftAspNetCoreAuthenticationPackageVersion>2.1.0-preview3-30392</MicrosoftAspNetCoreAuthenticationPackageVersion>
|
||||
<MicrosoftAspNetCoreAuthorizationPolicyPackageVersion>2.1.0-preview3-30392</MicrosoftAspNetCoreAuthorizationPolicyPackageVersion>
|
||||
<MicrosoftAspNetCoreBenchmarkRunnerSourcesPackageVersion>2.1.0-preview3-30392</MicrosoftAspNetCoreBenchmarkRunnerSourcesPackageVersion>
|
||||
<MicrosoftAspNetCoreChunkingCookieManagerSourcesPackageVersion>2.1.0-preview3-30392</MicrosoftAspNetCoreChunkingCookieManagerSourcesPackageVersion>
|
||||
<MicrosoftAspNetCoreCookiePolicyPackageVersion>2.1.0-preview3-30392</MicrosoftAspNetCoreCookiePolicyPackageVersion>
|
||||
<MicrosoftAspNetCoreCorsPackageVersion>2.1.0-preview3-30392</MicrosoftAspNetCoreCorsPackageVersion>
|
||||
<MicrosoftAspNetCoreDiagnosticsAbstractionsPackageVersion>2.1.0-preview3-30392</MicrosoftAspNetCoreDiagnosticsAbstractionsPackageVersion>
|
||||
<MicrosoftAspNetCoreDiagnosticsPackageVersion>2.1.0-preview3-30392</MicrosoftAspNetCoreDiagnosticsPackageVersion>
|
||||
<MicrosoftAspNetCoreHostingAbstractionsPackageVersion>2.1.0-preview3-30392</MicrosoftAspNetCoreHostingAbstractionsPackageVersion>
|
||||
<MicrosoftAspNetCoreHostingPackageVersion>2.1.0-preview3-30392</MicrosoftAspNetCoreHostingPackageVersion>
|
||||
<MicrosoftAspNetCoreHtmlAbstractionsPackageVersion>2.1.0-preview3-30392</MicrosoftAspNetCoreHtmlAbstractionsPackageVersion>
|
||||
<MicrosoftAspNetCoreHttpExtensionsPackageVersion>2.1.0-preview3-30392</MicrosoftAspNetCoreHttpExtensionsPackageVersion>
|
||||
<MicrosoftAspNetCoreHttpPackageVersion>2.1.0-preview3-30392</MicrosoftAspNetCoreHttpPackageVersion>
|
||||
<MicrosoftAspNetCoreJsonPatchPackageVersion>2.1.0-preview3-30392</MicrosoftAspNetCoreJsonPatchPackageVersion>
|
||||
<MicrosoftAspNetCoreLocalizationPackageVersion>2.1.0-preview3-30392</MicrosoftAspNetCoreLocalizationPackageVersion>
|
||||
<MicrosoftAspNetCoreLocalizationRoutingPackageVersion>2.1.0-preview3-30392</MicrosoftAspNetCoreLocalizationRoutingPackageVersion>
|
||||
<MicrosoftAspNetCoreMvcRazorExtensionsPackageVersion>2.1.0-preview3-30392</MicrosoftAspNetCoreMvcRazorExtensionsPackageVersion>
|
||||
<MicrosoftAspNetCoreRangeHelperSourcesPackageVersion>2.1.0-preview3-30392</MicrosoftAspNetCoreRangeHelperSourcesPackageVersion>
|
||||
<MicrosoftAspNetCoreRazorDesignPackageVersion>2.1.0-preview3-30392</MicrosoftAspNetCoreRazorDesignPackageVersion>
|
||||
<MicrosoftAspNetCoreRazorRuntimePackageVersion>2.1.0-preview3-30392</MicrosoftAspNetCoreRazorRuntimePackageVersion>
|
||||
<MicrosoftAspNetCoreRazorTagHelpersTestingSourcesPackageVersion>2.1.0-preview3-30392</MicrosoftAspNetCoreRazorTagHelpersTestingSourcesPackageVersion>
|
||||
<MicrosoftAspNetCoreResponseCachingAbstractionsPackageVersion>2.1.0-preview3-30392</MicrosoftAspNetCoreResponseCachingAbstractionsPackageVersion>
|
||||
<MicrosoftAspNetCoreResponseCachingPackageVersion>2.1.0-preview3-30392</MicrosoftAspNetCoreResponseCachingPackageVersion>
|
||||
<MicrosoftAspNetCoreRoutingAbstractionsPackageVersion>2.1.0-preview3-30392</MicrosoftAspNetCoreRoutingAbstractionsPackageVersion>
|
||||
<MicrosoftAspNetCoreRoutingPackageVersion>2.1.0-preview3-30392</MicrosoftAspNetCoreRoutingPackageVersion>
|
||||
<MicrosoftAspNetCoreServerIISIntegrationPackageVersion>2.1.0-preview3-30392</MicrosoftAspNetCoreServerIISIntegrationPackageVersion>
|
||||
<MicrosoftAspNetCoreServerKestrelPackageVersion>2.1.0-preview3-30392</MicrosoftAspNetCoreServerKestrelPackageVersion>
|
||||
<MicrosoftAspNetCoreSessionPackageVersion>2.1.0-preview3-30392</MicrosoftAspNetCoreSessionPackageVersion>
|
||||
<MicrosoftAspNetCoreStaticFilesPackageVersion>2.1.0-preview3-30392</MicrosoftAspNetCoreStaticFilesPackageVersion>
|
||||
<MicrosoftAspNetCoreTestHostPackageVersion>2.1.0-preview3-30392</MicrosoftAspNetCoreTestHostPackageVersion>
|
||||
<MicrosoftAspNetCoreTestingPackageVersion>2.1.0-preview3-30392</MicrosoftAspNetCoreTestingPackageVersion>
|
||||
<MicrosoftAspNetCoreWebUtilitiesPackageVersion>2.1.0-preview3-30392</MicrosoftAspNetCoreWebUtilitiesPackageVersion>
|
||||
<MicrosoftAspNetWebApiClientPackageVersion>5.2.4</MicrosoftAspNetWebApiClientPackageVersion>
|
||||
<MicrosoftCodeAnalysisCSharpPackageVersion>2.6.1</MicrosoftCodeAnalysisCSharpPackageVersion>
|
||||
<MicrosoftCodeAnalysisCSharpWorkspacesPackageVersion>2.6.1</MicrosoftCodeAnalysisCSharpWorkspacesPackageVersion>
|
||||
<MicrosoftCodeAnalysisRazorPackageVersion>2.1.0-preview2-30328</MicrosoftCodeAnalysisRazorPackageVersion>
|
||||
<MicrosoftExtensionsCachingMemoryPackageVersion>2.1.0-preview2-30328</MicrosoftExtensionsCachingMemoryPackageVersion>
|
||||
<MicrosoftExtensionsClosedGenericMatcherSourcesPackageVersion>2.1.0-preview2-30328</MicrosoftExtensionsClosedGenericMatcherSourcesPackageVersion>
|
||||
<MicrosoftExtensionsConfigurationJsonPackageVersion>2.1.0-preview2-30328</MicrosoftExtensionsConfigurationJsonPackageVersion>
|
||||
<MicrosoftExtensionsConfigurationPackageVersion>2.1.0-preview2-30328</MicrosoftExtensionsConfigurationPackageVersion>
|
||||
<MicrosoftExtensionsCopyOnWriteDictionarySourcesPackageVersion>2.1.0-preview2-30328</MicrosoftExtensionsCopyOnWriteDictionarySourcesPackageVersion>
|
||||
<MicrosoftExtensionsDependencyInjectionPackageVersion>2.1.0-preview2-30328</MicrosoftExtensionsDependencyInjectionPackageVersion>
|
||||
<MicrosoftExtensionsDependencyModelPackageVersion>2.1.0-preview2-26308-02</MicrosoftExtensionsDependencyModelPackageVersion>
|
||||
<MicrosoftExtensionsDiagnosticAdapterPackageVersion>2.1.0-preview2-30328</MicrosoftExtensionsDiagnosticAdapterPackageVersion>
|
||||
<MicrosoftExtensionsFileProvidersAbstractionsPackageVersion>2.1.0-preview2-30328</MicrosoftExtensionsFileProvidersAbstractionsPackageVersion>
|
||||
<MicrosoftExtensionsFileProvidersCompositePackageVersion>2.1.0-preview2-30328</MicrosoftExtensionsFileProvidersCompositePackageVersion>
|
||||
<MicrosoftExtensionsFileProvidersEmbeddedPackageVersion>2.1.0-preview2-30328</MicrosoftExtensionsFileProvidersEmbeddedPackageVersion>
|
||||
<MicrosoftExtensionsFileSystemGlobbingPackageVersion>2.1.0-preview2-30328</MicrosoftExtensionsFileSystemGlobbingPackageVersion>
|
||||
<MicrosoftExtensionsHashCodeCombinerSourcesPackageVersion>2.1.0-preview2-30328</MicrosoftExtensionsHashCodeCombinerSourcesPackageVersion>
|
||||
<MicrosoftExtensionsLocalizationPackageVersion>2.1.0-preview2-30328</MicrosoftExtensionsLocalizationPackageVersion>
|
||||
<MicrosoftExtensionsLoggingAbstractionsPackageVersion>2.1.0-preview2-30328</MicrosoftExtensionsLoggingAbstractionsPackageVersion>
|
||||
<MicrosoftExtensionsLoggingConsolePackageVersion>2.1.0-preview2-30328</MicrosoftExtensionsLoggingConsolePackageVersion>
|
||||
<MicrosoftExtensionsLoggingDebugPackageVersion>2.1.0-preview2-30328</MicrosoftExtensionsLoggingDebugPackageVersion>
|
||||
<MicrosoftExtensionsLoggingPackageVersion>2.1.0-preview2-30328</MicrosoftExtensionsLoggingPackageVersion>
|
||||
<MicrosoftExtensionsLoggingTestingPackageVersion>2.1.0-preview2-30328</MicrosoftExtensionsLoggingTestingPackageVersion>
|
||||
<MicrosoftExtensionsObjectMethodExecutorSourcesPackageVersion>2.1.0-preview2-30328</MicrosoftExtensionsObjectMethodExecutorSourcesPackageVersion>
|
||||
<MicrosoftExtensionsOptionsPackageVersion>2.1.0-preview2-30328</MicrosoftExtensionsOptionsPackageVersion>
|
||||
<MicrosoftExtensionsPrimitivesPackageVersion>2.1.0-preview2-30328</MicrosoftExtensionsPrimitivesPackageVersion>
|
||||
<MicrosoftExtensionsPropertyActivatorSourcesPackageVersion>2.1.0-preview2-30328</MicrosoftExtensionsPropertyActivatorSourcesPackageVersion>
|
||||
<MicrosoftExtensionsPropertyHelperSourcesPackageVersion>2.1.0-preview2-30328</MicrosoftExtensionsPropertyHelperSourcesPackageVersion>
|
||||
<MicrosoftExtensionsSecurityHelperSourcesPackageVersion>2.1.0-preview2-30328</MicrosoftExtensionsSecurityHelperSourcesPackageVersion>
|
||||
<MicrosoftExtensionsTypeNameHelperSourcesPackageVersion>2.1.0-preview2-30328</MicrosoftExtensionsTypeNameHelperSourcesPackageVersion>
|
||||
<MicrosoftExtensionsValueStopwatchSourcesPackageVersion>2.1.0-preview2-30328</MicrosoftExtensionsValueStopwatchSourcesPackageVersion>
|
||||
<MicrosoftExtensionsWebEncodersPackageVersion>2.1.0-preview2-30328</MicrosoftExtensionsWebEncodersPackageVersion>
|
||||
<MicrosoftCodeAnalysisRazorPackageVersion>2.1.0-preview3-30392</MicrosoftCodeAnalysisRazorPackageVersion>
|
||||
<MicrosoftExtensionsCachingMemoryPackageVersion>2.1.0-preview3-30392</MicrosoftExtensionsCachingMemoryPackageVersion>
|
||||
<MicrosoftExtensionsClosedGenericMatcherSourcesPackageVersion>2.1.0-preview3-30392</MicrosoftExtensionsClosedGenericMatcherSourcesPackageVersion>
|
||||
<MicrosoftExtensionsConfigurationJsonPackageVersion>2.1.0-preview3-30392</MicrosoftExtensionsConfigurationJsonPackageVersion>
|
||||
<MicrosoftExtensionsConfigurationPackageVersion>2.1.0-preview3-30392</MicrosoftExtensionsConfigurationPackageVersion>
|
||||
<MicrosoftExtensionsCopyOnWriteDictionarySourcesPackageVersion>2.1.0-preview3-30392</MicrosoftExtensionsCopyOnWriteDictionarySourcesPackageVersion>
|
||||
<MicrosoftExtensionsDependencyInjectionPackageVersion>2.1.0-preview3-30392</MicrosoftExtensionsDependencyInjectionPackageVersion>
|
||||
<MicrosoftExtensionsDependencyModelPackageVersion>2.1.0-preview2-26313-01</MicrosoftExtensionsDependencyModelPackageVersion>
|
||||
<MicrosoftExtensionsDiagnosticAdapterPackageVersion>2.1.0-preview3-30392</MicrosoftExtensionsDiagnosticAdapterPackageVersion>
|
||||
<MicrosoftExtensionsFileProvidersAbstractionsPackageVersion>2.1.0-preview3-30392</MicrosoftExtensionsFileProvidersAbstractionsPackageVersion>
|
||||
<MicrosoftExtensionsFileProvidersCompositePackageVersion>2.1.0-preview3-30392</MicrosoftExtensionsFileProvidersCompositePackageVersion>
|
||||
<MicrosoftExtensionsFileProvidersEmbeddedPackageVersion>2.1.0-preview3-30392</MicrosoftExtensionsFileProvidersEmbeddedPackageVersion>
|
||||
<MicrosoftExtensionsFileSystemGlobbingPackageVersion>2.1.0-preview3-30392</MicrosoftExtensionsFileSystemGlobbingPackageVersion>
|
||||
<MicrosoftExtensionsHashCodeCombinerSourcesPackageVersion>2.1.0-preview3-30392</MicrosoftExtensionsHashCodeCombinerSourcesPackageVersion>
|
||||
<MicrosoftExtensionsLocalizationPackageVersion>2.1.0-preview3-30392</MicrosoftExtensionsLocalizationPackageVersion>
|
||||
<MicrosoftExtensionsLoggingAbstractionsPackageVersion>2.1.0-preview3-30392</MicrosoftExtensionsLoggingAbstractionsPackageVersion>
|
||||
<MicrosoftExtensionsLoggingConsolePackageVersion>2.1.0-preview3-30392</MicrosoftExtensionsLoggingConsolePackageVersion>
|
||||
<MicrosoftExtensionsLoggingDebugPackageVersion>2.1.0-preview3-30392</MicrosoftExtensionsLoggingDebugPackageVersion>
|
||||
<MicrosoftExtensionsLoggingPackageVersion>2.1.0-preview3-30392</MicrosoftExtensionsLoggingPackageVersion>
|
||||
<MicrosoftExtensionsLoggingTestingPackageVersion>2.1.0-preview3-30392</MicrosoftExtensionsLoggingTestingPackageVersion>
|
||||
<MicrosoftExtensionsObjectMethodExecutorSourcesPackageVersion>2.1.0-preview3-30392</MicrosoftExtensionsObjectMethodExecutorSourcesPackageVersion>
|
||||
<MicrosoftExtensionsOptionsPackageVersion>2.1.0-preview3-30392</MicrosoftExtensionsOptionsPackageVersion>
|
||||
<MicrosoftExtensionsPrimitivesPackageVersion>2.1.0-preview3-30392</MicrosoftExtensionsPrimitivesPackageVersion>
|
||||
<MicrosoftExtensionsPropertyActivatorSourcesPackageVersion>2.1.0-preview3-30392</MicrosoftExtensionsPropertyActivatorSourcesPackageVersion>
|
||||
<MicrosoftExtensionsPropertyHelperSourcesPackageVersion>2.1.0-preview3-30392</MicrosoftExtensionsPropertyHelperSourcesPackageVersion>
|
||||
<MicrosoftExtensionsSecurityHelperSourcesPackageVersion>2.1.0-preview3-30392</MicrosoftExtensionsSecurityHelperSourcesPackageVersion>
|
||||
<MicrosoftExtensionsTypeNameHelperSourcesPackageVersion>2.1.0-preview3-30392</MicrosoftExtensionsTypeNameHelperSourcesPackageVersion>
|
||||
<MicrosoftExtensionsValueStopwatchSourcesPackageVersion>2.1.0-preview3-30392</MicrosoftExtensionsValueStopwatchSourcesPackageVersion>
|
||||
<MicrosoftExtensionsWebEncodersPackageVersion>2.1.0-preview3-30392</MicrosoftExtensionsWebEncodersPackageVersion>
|
||||
<MicrosoftNETCoreApp20PackageVersion>2.0.0</MicrosoftNETCoreApp20PackageVersion>
|
||||
<MicrosoftNETCoreApp21PackageVersion>2.1.0-preview2-26308-01</MicrosoftNETCoreApp21PackageVersion>
|
||||
<MicrosoftNetHttpHeadersPackageVersion>2.1.0-preview2-30328</MicrosoftNetHttpHeadersPackageVersion>
|
||||
<MicrosoftNETTestSdkPackageVersion>15.6.0</MicrosoftNETTestSdkPackageVersion>
|
||||
<MicrosoftNETCoreApp21PackageVersion>2.1.0-preview2-26314-02</MicrosoftNETCoreApp21PackageVersion>
|
||||
<MicrosoftNetHttpHeadersPackageVersion>2.1.0-preview3-30392</MicrosoftNetHttpHeadersPackageVersion>
|
||||
<MicrosoftNETSdkRazorPackageVersion>2.1.0-preview3-30392</MicrosoftNETSdkRazorPackageVersion>
|
||||
<MicrosoftNETTestSdkPackageVersion>15.6.1</MicrosoftNETTestSdkPackageVersion>
|
||||
<MoqPackageVersion>4.7.49</MoqPackageVersion>
|
||||
<NewtonsoftJsonBsonPackageVersion>1.0.1</NewtonsoftJsonBsonPackageVersion>
|
||||
<SystemComponentModelAnnotationsPackageVersion>4.5.0-preview2-26308-02</SystemComponentModelAnnotationsPackageVersion>
|
||||
<SystemDiagnosticsDiagnosticSourcePackageVersion>4.5.0-preview2-26308-02</SystemDiagnosticsDiagnosticSourcePackageVersion>
|
||||
<SystemThreadingTasksExtensionsPackageVersion>4.5.0-preview2-26308-02</SystemThreadingTasksExtensionsPackageVersion>
|
||||
<SystemComponentModelAnnotationsPackageVersion>4.5.0-preview2-26313-01</SystemComponentModelAnnotationsPackageVersion>
|
||||
<SystemDiagnosticsDiagnosticSourcePackageVersion>4.5.0-preview2-26313-01</SystemDiagnosticsDiagnosticSourcePackageVersion>
|
||||
<SystemThreadingTasksExtensionsPackageVersion>4.5.0-preview2-26313-01</SystemThreadingTasksExtensionsPackageVersion>
|
||||
<XunitAnalyzersPackageVersion>0.8.0</XunitAnalyzersPackageVersion>
|
||||
<XunitPackageVersion>2.3.1</XunitPackageVersion>
|
||||
<XunitRunnerVisualStudioPackageVersion>2.4.0-beta.1.build3945</XunitRunnerVisualStudioPackageVersion>
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
version:2.1.0-preview2-15741
|
||||
commithash:d944172721e77b6ca05a512303a0859634786746
|
||||
version:2.1.0-preview3-15746
|
||||
commithash:bd7208b1b1533dad0fac5440948312ff269f0fb9
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="$(MicrosoftExtensionsConfigurationJsonPackageVersion)" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="$(MicrosoftExtensionsLoggingConsolePackageVersion)" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="$(MicrosoftExtensionsLoggingDebugPackageVersion)" />
|
||||
<PackageReference Include="Microsoft.NET.Sdk.Razor" Version="$(MicrosoftNETSdkRazorPackageVersion)" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
|||
|
|
@ -1,25 +0,0 @@
|
|||
// 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 System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.ApplicationParts
|
||||
{
|
||||
/// <summary>
|
||||
/// An <see cref="AssemblyPart"/> that was added by an assembly that referenced it through the use
|
||||
/// of an assembly metadata attribute.
|
||||
/// </summary>
|
||||
public class AdditionalAssemblyPart : AssemblyPart, ICompilationReferencesProvider, IApplicationPartTypeProvider
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public AdditionalAssemblyPart(Assembly assembly) : base(assembly)
|
||||
{
|
||||
}
|
||||
|
||||
IEnumerable<string> ICompilationReferencesProvider.GetReferencePaths() => Array.Empty<string>();
|
||||
|
||||
IEnumerable<TypeInfo> IApplicationPartTypeProvider.Types => Array.Empty<TypeInfo>();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,283 @@
|
|||
// 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 System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Microsoft.AspNetCore.Mvc.Core;
|
||||
using Microsoft.Extensions.DependencyModel;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.ApplicationParts
|
||||
{
|
||||
internal class ApplicationAssembliesProvider
|
||||
{
|
||||
internal static HashSet<string> ReferenceAssemblies { get; } = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
"Microsoft.AspNetCore.Mvc",
|
||||
"Microsoft.AspNetCore.Mvc.Abstractions",
|
||||
"Microsoft.AspNetCore.Mvc.ApiExplorer",
|
||||
"Microsoft.AspNetCore.Mvc.Core",
|
||||
"Microsoft.AspNetCore.Mvc.Cors",
|
||||
"Microsoft.AspNetCore.Mvc.DataAnnotations",
|
||||
"Microsoft.AspNetCore.Mvc.Formatters.Json",
|
||||
"Microsoft.AspNetCore.Mvc.Formatters.Xml",
|
||||
"Microsoft.AspNetCore.Mvc.Localization",
|
||||
"Microsoft.AspNetCore.Mvc.Razor",
|
||||
"Microsoft.AspNetCore.Mvc.Razor.Extensions",
|
||||
"Microsoft.AspNetCore.Mvc.RazorPages",
|
||||
"Microsoft.AspNetCore.Mvc.TagHelpers",
|
||||
"Microsoft.AspNetCore.Mvc.ViewFeatures",
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Returns an ordered list of application assemblies.
|
||||
/// <para>
|
||||
/// The order is as follows:
|
||||
/// * Entry assembly
|
||||
/// * Assemblies specified in the application's deps file ordered by name.
|
||||
/// <para>
|
||||
/// Each assembly is immediately followed by assemblies specified by annotated <see cref="RelatedAssemblyAttribute"/> ordered by name.
|
||||
/// </para>
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public IEnumerable<Assembly> ResolveAssemblies(Assembly entryAssembly)
|
||||
{
|
||||
var dependencyContext = LoadDependencyContext(entryAssembly);
|
||||
|
||||
IEnumerable<AssemblyItem> assemblyItems;
|
||||
|
||||
if (dependencyContext == null)
|
||||
{
|
||||
assemblyItems = new[] { GetAssemblyItem(entryAssembly) };
|
||||
}
|
||||
else
|
||||
{
|
||||
assemblyItems = ResolveFromDependencyContext(dependencyContext);
|
||||
}
|
||||
|
||||
assemblyItems = assemblyItems
|
||||
.OrderBy(item => item.Assembly == entryAssembly ? 0 : 1)
|
||||
.ThenBy(item => item.Assembly.FullName, StringComparer.Ordinal);
|
||||
|
||||
foreach (var item in assemblyItems)
|
||||
{
|
||||
yield return item.Assembly;
|
||||
|
||||
foreach (var associatedAssembly in item.RelatedAssemblies.Distinct().OrderBy(assembly => assembly.FullName, StringComparer.Ordinal))
|
||||
{
|
||||
yield return associatedAssembly;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual DependencyContext LoadDependencyContext(Assembly assembly) => DependencyContext.Load(assembly);
|
||||
|
||||
private List<AssemblyItem> ResolveFromDependencyContext(DependencyContext dependencyContext)
|
||||
{
|
||||
var assemblyItems = new List<AssemblyItem>();
|
||||
var relatedAssemblies = new Dictionary<Assembly, AssemblyItem>();
|
||||
|
||||
var candidateAssemblies = GetCandidateLibraries(dependencyContext)
|
||||
.SelectMany(library => GetLibraryAssemblies(dependencyContext, library));
|
||||
|
||||
foreach (var assembly in candidateAssemblies)
|
||||
{
|
||||
var assemblyItem = GetAssemblyItem(assembly);
|
||||
assemblyItems.Add(assemblyItem);
|
||||
|
||||
foreach (var relatedAssembly in assemblyItem.RelatedAssemblies)
|
||||
{
|
||||
if (relatedAssemblies.TryGetValue(relatedAssembly, out var otherEntry))
|
||||
{
|
||||
var message = string.Join(
|
||||
Environment.NewLine,
|
||||
Resources.FormatApplicationAssembliesProvider_DuplicateRelatedAssembly(relatedAssembly.FullName),
|
||||
otherEntry.Assembly.FullName,
|
||||
assembly.FullName);
|
||||
|
||||
throw new InvalidOperationException(message);
|
||||
}
|
||||
|
||||
relatedAssemblies.Add(relatedAssembly, assemblyItem);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove any top level assemblies that appear as an associated assembly.
|
||||
assemblyItems.RemoveAll(item => relatedAssemblies.ContainsKey(item.Assembly));
|
||||
|
||||
return assemblyItems;
|
||||
}
|
||||
|
||||
protected virtual IEnumerable<Assembly> GetLibraryAssemblies(DependencyContext dependencyContext, RuntimeLibrary runtimeLibrary)
|
||||
{
|
||||
foreach (var assemblyName in runtimeLibrary.GetDefaultAssemblyNames(dependencyContext))
|
||||
{
|
||||
var assembly = Assembly.Load(assemblyName);
|
||||
yield return assembly;
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual IReadOnlyList<Assembly> GetRelatedAssemblies(Assembly assembly)
|
||||
{
|
||||
// Do not require related assemblies to be available in the default code path.
|
||||
return RelatedAssemblyAttribute.GetRelatedAssemblies(assembly, throwOnError: false);
|
||||
}
|
||||
|
||||
private AssemblyItem GetAssemblyItem(Assembly assembly)
|
||||
{
|
||||
var relatedAssemblies = GetRelatedAssemblies(assembly);
|
||||
|
||||
// Ensure we don't have any cycles. A cycle could be formed if a related assembly points to the primary assembly.
|
||||
foreach (var relatedAssembly in relatedAssemblies)
|
||||
{
|
||||
if (relatedAssembly.IsDefined(typeof(RelatedAssemblyAttribute)))
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
Resources.FormatApplicationAssembliesProvider_RelatedAssemblyCannotDefineAdditional(relatedAssembly.FullName, assembly.FullName));
|
||||
}
|
||||
}
|
||||
|
||||
return new AssemblyItem(assembly, relatedAssemblies);
|
||||
}
|
||||
|
||||
// Returns a list of libraries that references the assemblies in <see cref="ReferenceAssemblies"/>.
|
||||
// By default it returns all assemblies that reference any of the primary MVC assemblies
|
||||
// while ignoring MVC assemblies.
|
||||
// Internal for unit testing
|
||||
internal static IEnumerable<RuntimeLibrary> GetCandidateLibraries(DependencyContext dependencyContext)
|
||||
{
|
||||
var candidatesResolver = new CandidateResolver(dependencyContext.RuntimeLibraries, ReferenceAssemblies);
|
||||
return candidatesResolver.GetCandidates();
|
||||
}
|
||||
|
||||
private class CandidateResolver
|
||||
{
|
||||
private readonly IDictionary<string, Dependency> _runtimeDependencies;
|
||||
|
||||
public CandidateResolver(IReadOnlyList<RuntimeLibrary> runtimeDependencies, ISet<string> referenceAssemblies)
|
||||
{
|
||||
var dependenciesWithNoDuplicates = new Dictionary<string, Dependency>(StringComparer.OrdinalIgnoreCase);
|
||||
foreach (var dependency in runtimeDependencies)
|
||||
{
|
||||
if (dependenciesWithNoDuplicates.ContainsKey(dependency.Name))
|
||||
{
|
||||
throw new InvalidOperationException(Resources.FormatCandidateResolver_DifferentCasedReference(dependency.Name));
|
||||
}
|
||||
dependenciesWithNoDuplicates.Add(dependency.Name, CreateDependency(dependency, referenceAssemblies));
|
||||
}
|
||||
|
||||
_runtimeDependencies = dependenciesWithNoDuplicates;
|
||||
}
|
||||
|
||||
private Dependency CreateDependency(RuntimeLibrary library, ISet<string> referenceAssemblies)
|
||||
{
|
||||
var classification = DependencyClassification.Unknown;
|
||||
if (referenceAssemblies.Contains(library.Name))
|
||||
{
|
||||
classification = DependencyClassification.MvcReference;
|
||||
}
|
||||
|
||||
return new Dependency(library, classification);
|
||||
}
|
||||
|
||||
private DependencyClassification ComputeClassification(string dependency)
|
||||
{
|
||||
if (!_runtimeDependencies.ContainsKey(dependency))
|
||||
{
|
||||
// Library does not have runtime dependency. Since we can't infer
|
||||
// anything about it's references, we'll assume it does not have a reference to Mvc.
|
||||
return DependencyClassification.DoesNotReferenceMvc;
|
||||
}
|
||||
|
||||
var candidateEntry = _runtimeDependencies[dependency];
|
||||
if (candidateEntry.Classification != DependencyClassification.Unknown)
|
||||
{
|
||||
return candidateEntry.Classification;
|
||||
}
|
||||
else
|
||||
{
|
||||
var classification = DependencyClassification.DoesNotReferenceMvc;
|
||||
foreach (var candidateDependency in candidateEntry.Library.Dependencies)
|
||||
{
|
||||
var dependencyClassification = ComputeClassification(candidateDependency.Name);
|
||||
if (dependencyClassification == DependencyClassification.ReferencesMvc ||
|
||||
dependencyClassification == DependencyClassification.MvcReference)
|
||||
{
|
||||
classification = DependencyClassification.ReferencesMvc;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
candidateEntry.Classification = classification;
|
||||
|
||||
return classification;
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<RuntimeLibrary> GetCandidates()
|
||||
{
|
||||
foreach (var dependency in _runtimeDependencies)
|
||||
{
|
||||
if (ComputeClassification(dependency.Key) == DependencyClassification.ReferencesMvc)
|
||||
{
|
||||
yield return dependency.Value.Library;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class Dependency
|
||||
{
|
||||
public Dependency(RuntimeLibrary library, DependencyClassification classification)
|
||||
{
|
||||
Library = library;
|
||||
Classification = classification;
|
||||
}
|
||||
|
||||
public RuntimeLibrary Library { get; }
|
||||
|
||||
public DependencyClassification Classification { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"Library: {Library.Name}, Classification: {Classification}";
|
||||
}
|
||||
}
|
||||
|
||||
private enum DependencyClassification
|
||||
{
|
||||
Unknown = 0,
|
||||
|
||||
/// <summary>
|
||||
/// References (directly or transitively) one of the Mvc packages listed in
|
||||
/// <see cref="ReferenceAssemblies"/>.
|
||||
/// </summary>
|
||||
ReferencesMvc = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Does not reference (directly or transitively) one of the Mvc packages listed by
|
||||
/// <see cref="ReferenceAssemblies"/>.
|
||||
/// </summary>
|
||||
DoesNotReferenceMvc = 2,
|
||||
|
||||
/// <summary>
|
||||
/// One of the references listed in <see cref="ReferenceAssemblies"/>.
|
||||
/// </summary>
|
||||
MvcReference = 3,
|
||||
}
|
||||
}
|
||||
|
||||
private readonly struct AssemblyItem
|
||||
{
|
||||
public AssemblyItem(Assembly assembly, IReadOnlyList<Assembly> associatedAssemblies)
|
||||
{
|
||||
Assembly = assembly;
|
||||
RelatedAssemblies = associatedAssemblies;
|
||||
}
|
||||
|
||||
public Assembly Assembly { get; }
|
||||
|
||||
public IReadOnlyList<Assembly> RelatedAssemblies { get; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +1,10 @@
|
|||
// 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 System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using Microsoft.AspNetCore.Mvc.Core;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.ApplicationParts
|
||||
{
|
||||
|
|
@ -19,6 +21,11 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationParts
|
|||
{
|
||||
public static readonly string DefaultContextName = "Default";
|
||||
|
||||
/// <summary>
|
||||
/// Default implementation for <see cref="ApplicationPartFactory"/>.
|
||||
/// </summary>
|
||||
public static ApplicationPartFactory Default { get; } = new DefaultApplicationPartFactory();
|
||||
|
||||
/// <summary>
|
||||
/// Gets one or more <see cref="ApplicationPart"/> instances for the specified <paramref name="assembly"/>.
|
||||
/// </summary>
|
||||
|
|
@ -27,5 +34,52 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationParts
|
|||
/// The context name. By default, value of this parameter is <see cref="DefaultContextName"/>.
|
||||
/// </param>
|
||||
public abstract IEnumerable<ApplicationPart> GetApplicationParts(Assembly assembly, string context);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="ApplicationPartFactory"/> for the specified assembly.
|
||||
/// <para>
|
||||
/// An assembly may specify an <see cref="ApplicationPartFactory"/> using <see cref="ProvideApplicationPartFactoryAttribute"/>.
|
||||
/// Otherwise, <see cref="ApplicationPartFactory.Default"/> is used.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
/// <param name="assembly">The <see cref="Assembly"/>.</param>
|
||||
/// <returns>An instance of <see cref="ApplicationPartFactory"/>.</returns>
|
||||
public static ApplicationPartFactory GetApplicationPartFactory(Assembly assembly)
|
||||
{
|
||||
if (assembly == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(assembly));
|
||||
}
|
||||
|
||||
var provideAttribute = assembly.GetCustomAttribute<ProvideApplicationPartFactoryAttribute>();
|
||||
if (provideAttribute == null)
|
||||
{
|
||||
return ApplicationPartFactory.Default;
|
||||
}
|
||||
|
||||
var type = provideAttribute.GetFactoryType();
|
||||
if (!typeof(ApplicationPartFactory).IsAssignableFrom(type))
|
||||
{
|
||||
throw new InvalidOperationException(Resources.FormatApplicationPartFactory_InvalidFactoryType(
|
||||
type,
|
||||
nameof(ProvideApplicationPartFactoryAttribute),
|
||||
typeof(ApplicationPartFactory)));
|
||||
}
|
||||
|
||||
return (ApplicationPartFactory)Activator.CreateInstance(type);
|
||||
}
|
||||
|
||||
private class DefaultApplicationPartFactory : ApplicationPartFactory
|
||||
{
|
||||
public override IEnumerable<ApplicationPart> GetApplicationParts(Assembly assembly, string context)
|
||||
{
|
||||
if (assembly == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(assembly));
|
||||
}
|
||||
|
||||
yield return new AssemblyPart(assembly);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.ApplicationParts
|
||||
{
|
||||
|
|
@ -48,5 +49,21 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationParts
|
|||
provider.PopulateFeature(ApplicationParts, feature);
|
||||
}
|
||||
}
|
||||
|
||||
internal void PopulateDefaultParts(string entryAssemblyName)
|
||||
{
|
||||
var entryAssembly = Assembly.Load(new AssemblyName(entryAssemblyName));
|
||||
var assembliesProvider = new ApplicationAssembliesProvider();
|
||||
var applicationAssemblies = assembliesProvider.ResolveAssemblies(entryAssembly);
|
||||
|
||||
foreach (var assembly in applicationAssemblies)
|
||||
{
|
||||
var partFactory = ApplicationPartFactory.GetApplicationPartFactory(assembly);
|
||||
foreach (var part in partFactory.GetApplicationParts(assembly, context: ApplicationPartFactory.DefaultContextName))
|
||||
{
|
||||
ApplicationParts.Add(part);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,24 @@
|
|||
// 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.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.ApplicationParts
|
||||
{
|
||||
/// <summary>
|
||||
/// An <see cref="ApplicationPartFactory"/> that produces no parts.
|
||||
/// <para>
|
||||
/// This factory may be used to to preempt Mvc's default part discovery allowing for custom configuration at a later stage.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public class NullApplicationPartFactory : ApplicationPartFactory
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override IEnumerable<ApplicationPart> GetApplicationParts(Assembly assembly, string context)
|
||||
{
|
||||
return Enumerable.Empty<ApplicationPart>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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 Microsoft.AspNetCore.Mvc.Core;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.ApplicationParts
|
||||
{
|
||||
|
|
@ -11,15 +12,40 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationParts
|
|||
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = false)]
|
||||
public sealed class ProvideApplicationPartFactoryAttribute : Attribute
|
||||
{
|
||||
private readonly Type _applicationPartFactoryType;
|
||||
private readonly string _applicationPartFactoryTypeName;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of <see cref="ProvideApplicationPartFactoryAttribute"/> with the specified type.
|
||||
/// </summary>
|
||||
/// <param name="factoryType">The factory type.</param>
|
||||
public ProvideApplicationPartFactoryAttribute(Type factoryType)
|
||||
{
|
||||
ApplicationPartFactoryType = factoryType ?? throw new ArgumentNullException(nameof(factoryType));
|
||||
_applicationPartFactoryType = factoryType ?? throw new ArgumentNullException(nameof(factoryType));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of <see cref="ProvideApplicationPartFactoryAttribute"/> with the specified type name.
|
||||
/// </summary>
|
||||
/// <param name="factoryTypeName">The assembly qualified type name.</param>
|
||||
public ProvideApplicationPartFactoryAttribute(string factoryTypeName)
|
||||
{
|
||||
if (string.IsNullOrEmpty(factoryTypeName))
|
||||
{
|
||||
throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, nameof(factoryTypeName));
|
||||
}
|
||||
|
||||
_applicationPartFactoryTypeName = factoryTypeName;
|
||||
}
|
||||
|
||||
public Type ApplicationPartFactoryType { get; }
|
||||
/// <summary>
|
||||
/// Gets the factory type.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public Type GetFactoryType()
|
||||
{
|
||||
return _applicationPartFactoryType ??
|
||||
Type.GetType(_applicationPartFactoryTypeName, throwOnError: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,23 +2,110 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Microsoft.AspNetCore.Mvc.Core;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.ApplicationParts
|
||||
{
|
||||
/// <summary>
|
||||
/// Specifies a assembly to load as part of MVC's assembly discovery mechanism.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
|
||||
public sealed class RelatedAssemblyAttribute : Attribute
|
||||
{
|
||||
public RelatedAssemblyAttribute(string name)
|
||||
private static readonly Func<string, Assembly> AssemblyLoadFileDelegate = Assembly.LoadFile;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of <see cref="RelatedAssemblyAttribute"/>.
|
||||
/// </summary>
|
||||
/// <param name="assemblyFileName">The file name, without extension, of the related assembly.</param>
|
||||
public RelatedAssemblyAttribute(string assemblyFileName)
|
||||
{
|
||||
if (string.IsNullOrEmpty(name))
|
||||
if (string.IsNullOrEmpty(assemblyFileName))
|
||||
{
|
||||
// Temporary workaround until we have a new build of RazorSdk.
|
||||
// TODO: Uncomment the below line.
|
||||
// throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, nameof(name));
|
||||
throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, nameof(assemblyFileName));
|
||||
}
|
||||
|
||||
AssemblyFileName = assemblyFileName;
|
||||
}
|
||||
|
||||
public string Name { get; }
|
||||
/// <summary>
|
||||
/// Gets the assembly file name without extension.
|
||||
/// </summary>
|
||||
public string AssemblyFileName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets <see cref="Assembly"/> instances specified by <see cref="RelatedAssemblyAttribute"/>.
|
||||
/// </summary>
|
||||
/// <param name="assembly">The assembly containing <see cref="RelatedAssemblyAttribute"/> instances.</param>
|
||||
/// <param name="throwOnError">Determines if the method throws if a related assembly could not be located.</param>
|
||||
/// <returns>Related <see cref="Assembly"/> instances.</returns>
|
||||
public static IReadOnlyList<Assembly> GetRelatedAssemblies(Assembly assembly, bool throwOnError)
|
||||
{
|
||||
if (assembly == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(assembly));
|
||||
}
|
||||
|
||||
return GetRelatedAssemblies(assembly, throwOnError, AssemblyLoadFileDelegate);
|
||||
}
|
||||
|
||||
internal static IReadOnlyList<Assembly> GetRelatedAssemblies(Assembly assembly, bool throwOnError, Func<string, Assembly> loadFile)
|
||||
{
|
||||
if (assembly == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(assembly));
|
||||
}
|
||||
|
||||
// MVC will specifically look for related parts in the same physical directory as the assembly.
|
||||
// No-op if the assembly does not have a location.
|
||||
if (assembly.IsDynamic || string.IsNullOrEmpty(assembly.CodeBase))
|
||||
{
|
||||
return Array.Empty<Assembly>();
|
||||
}
|
||||
|
||||
var attributes = assembly.GetCustomAttributes<RelatedAssemblyAttribute>().ToArray();
|
||||
if (attributes.Length == 0)
|
||||
{
|
||||
return Array.Empty<Assembly>();
|
||||
}
|
||||
|
||||
var assemblyName = assembly.GetName().Name;
|
||||
var assemblyDirectory = Path.GetDirectoryName(assembly.CodeBase);
|
||||
|
||||
var relatedAssemblies = new List<Assembly>();
|
||||
for (var i = 0; i < attributes.Length; i++)
|
||||
{
|
||||
var attribute = attributes[i];
|
||||
if (string.Equals(assemblyName, attribute.AssemblyFileName, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
Resources.FormatRelatedAssemblyAttribute_AssemblyCannotReferenceSelf(nameof(RelatedAssemblyAttribute), assemblyName));
|
||||
}
|
||||
|
||||
var relatedAssemblyLocation = Path.Combine(assemblyDirectory, attribute.AssemblyFileName + ".dll");
|
||||
if (!File.Exists(relatedAssemblyLocation))
|
||||
{
|
||||
if (throwOnError)
|
||||
{
|
||||
throw new FileNotFoundException(
|
||||
Resources.FormatRelatedAssemblyAttribute_CouldNotBeFound(attribute.AssemblyFileName, assemblyName, assemblyDirectory),
|
||||
relatedAssemblyLocation);
|
||||
}
|
||||
else
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
var relatedAssembly = loadFile(relatedAssemblyLocation);
|
||||
relatedAssemblies.Add(relatedAssembly);
|
||||
}
|
||||
|
||||
return relatedAssemblies;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -87,11 +87,7 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
return manager;
|
||||
}
|
||||
|
||||
var parts = DefaultAssemblyPartDiscoveryProvider.DiscoverAssemblyParts(entryAssemblyName);
|
||||
foreach (var part in parts)
|
||||
{
|
||||
manager.ApplicationParts.Add(part);
|
||||
}
|
||||
manager.PopulateDefaultParts(entryAssemblyName);
|
||||
}
|
||||
|
||||
return manager;
|
||||
|
|
|
|||
|
|
@ -1,406 +0,0 @@
|
|||
// 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 System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Microsoft.AspNetCore.Mvc.ApplicationParts;
|
||||
using Microsoft.AspNetCore.Mvc.Core;
|
||||
using Microsoft.Extensions.DependencyModel;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.Internal
|
||||
{
|
||||
// Discovers assemblies that are part of the MVC application using the DependencyContext.
|
||||
public static class DefaultAssemblyPartDiscoveryProvider
|
||||
{
|
||||
private static readonly string PrecompiledViewsAssemblySuffix = ".PrecompiledViews";
|
||||
private static readonly IReadOnlyList<string> ViewAssemblySuffixes = new string[]
|
||||
{
|
||||
PrecompiledViewsAssemblySuffix,
|
||||
".Views",
|
||||
};
|
||||
|
||||
private const string AdditionalReferenceKey = "Microsoft.AspNetCore.Mvc.AdditionalReference";
|
||||
private static readonly char[] MetadataSeparators = new[] { ',' };
|
||||
|
||||
internal static HashSet<string> ReferenceAssemblies { get; } = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
"Microsoft.AspNetCore.All",
|
||||
"Microsoft.AspNetCore.Mvc",
|
||||
"Microsoft.AspNetCore.Mvc.Abstractions",
|
||||
"Microsoft.AspNetCore.Mvc.ApiExplorer",
|
||||
"Microsoft.AspNetCore.Mvc.Core",
|
||||
"Microsoft.AspNetCore.Mvc.Cors",
|
||||
"Microsoft.AspNetCore.Mvc.DataAnnotations",
|
||||
"Microsoft.AspNetCore.Mvc.Formatters.Json",
|
||||
"Microsoft.AspNetCore.Mvc.Formatters.Xml",
|
||||
"Microsoft.AspNetCore.Mvc.Localization",
|
||||
"Microsoft.AspNetCore.Mvc.Razor",
|
||||
"Microsoft.AspNetCore.Mvc.Razor.Extensions",
|
||||
"Microsoft.AspNetCore.Mvc.RazorPages",
|
||||
"Microsoft.AspNetCore.Mvc.TagHelpers",
|
||||
"Microsoft.AspNetCore.Mvc.ViewFeatures"
|
||||
};
|
||||
|
||||
// For testing purposes only.
|
||||
internal static Func<string, Assembly> AssemblyLoader { get; set; } = Assembly.LoadFile;
|
||||
internal static Func<string, bool> AssemblyResolver { get; set; } = File.Exists;
|
||||
|
||||
public static IEnumerable<ApplicationPart> DiscoverAssemblyParts(string entryPointAssemblyName)
|
||||
{
|
||||
// We need to produce a stable order of the parts that is given by:
|
||||
// 1) Parts that are not additional parts go before parts that are additional parts.
|
||||
// 2) The entry point part goes before any other part in the system.
|
||||
// 3) The entry point additional parts go before any other additional parts.
|
||||
// 4) Parts are finally ordered by their full name to produce a stable ordering.
|
||||
var entryAssembly = Assembly.Load(new AssemblyName(entryPointAssemblyName));
|
||||
var context = DependencyContext.Load(entryAssembly);
|
||||
|
||||
var candidateAssemblies = new SortedSet<Assembly>(
|
||||
GetCandidateAssemblies(entryAssembly, context),
|
||||
FullNameAssemblyComparer.Instance);
|
||||
|
||||
var (additionalReferences, entryAssemblyAdditionalReferences) = ResolveAdditionalReferences(
|
||||
entryAssembly,
|
||||
candidateAssemblies);
|
||||
|
||||
candidateAssemblies.Remove(entryAssembly);
|
||||
candidateAssemblies.ExceptWith(additionalReferences);
|
||||
candidateAssemblies.ExceptWith(entryAssemblyAdditionalReferences);
|
||||
|
||||
// Create the list of assembly parts.
|
||||
return CreateParts();
|
||||
|
||||
IEnumerable<AssemblyPart> CreateParts()
|
||||
{
|
||||
yield return new AssemblyPart(entryAssembly);
|
||||
foreach (var candidateAssembly in candidateAssemblies)
|
||||
{
|
||||
yield return new AssemblyPart(candidateAssembly);
|
||||
}
|
||||
foreach (var entryAdditionalAssembly in entryAssemblyAdditionalReferences)
|
||||
{
|
||||
yield return new AdditionalAssemblyPart(entryAdditionalAssembly);
|
||||
}
|
||||
foreach (var additionalAssembly in additionalReferences)
|
||||
{
|
||||
yield return new AdditionalAssemblyPart(additionalAssembly);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal static AdditionalReferencesPair ResolveAdditionalReferences(
|
||||
Assembly entryAssembly,
|
||||
SortedSet<Assembly> candidateAssemblies)
|
||||
{
|
||||
var additionalAssemblyReferences = candidateAssemblies
|
||||
.Select(ca =>
|
||||
(assembly: ca,
|
||||
metadata: ca.GetCustomAttributes<AssemblyMetadataAttribute>()
|
||||
.Where(ama => ama.Key.Equals(AdditionalReferenceKey, StringComparison.Ordinal)).ToArray()));
|
||||
|
||||
// Find out all the additional references defined by the assembly.
|
||||
// [assembly: AssemblyMetadataAttribute("Microsoft.AspNetCore.Mvc.AdditionalReference", "Library.PrecompiledViews.dll,true|false")]
|
||||
var additionalReferences = new SortedSet<Assembly>(FullNameAssemblyComparer.Instance);
|
||||
var entryAssemblyAdditionalReferences = new SortedSet<Assembly>(FullNameAssemblyComparer.Instance);
|
||||
foreach (var (assembly, metadata) in additionalAssemblyReferences)
|
||||
{
|
||||
if (metadata.Length > 0)
|
||||
{
|
||||
foreach (var metadataAttribute in metadata)
|
||||
{
|
||||
AddAdditionalReference(
|
||||
LoadFromMetadata(assembly, metadataAttribute),
|
||||
entryAssembly,
|
||||
assembly,
|
||||
additionalReferences,
|
||||
entryAssemblyAdditionalReferences);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Fallback to loading the views like in previous versions if the additional reference metadata
|
||||
// attribute is not present.
|
||||
AddAdditionalReference(
|
||||
LoadFromConvention(assembly),
|
||||
entryAssembly,
|
||||
assembly,
|
||||
additionalReferences,
|
||||
entryAssemblyAdditionalReferences);
|
||||
}
|
||||
}
|
||||
|
||||
return new AdditionalReferencesPair
|
||||
{
|
||||
AdditionalAssemblies = additionalReferences,
|
||||
EntryAssemblyAdditionalAssemblies = entryAssemblyAdditionalReferences
|
||||
};
|
||||
}
|
||||
|
||||
private static Assembly LoadFromMetadata(Assembly assembly, AssemblyMetadataAttribute metadataAttribute)
|
||||
{
|
||||
var (metadataPath, metadataIncludeByDefault) = ParseMetadataAttribute(metadataAttribute);
|
||||
if (metadataPath == null ||
|
||||
metadataIncludeByDefault == null ||
|
||||
!string.Equals(metadataIncludeByDefault, "true", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var fileName = Path.GetFileName(metadataPath);
|
||||
var filePath = Path.Combine(Path.GetDirectoryName(assembly.Location), fileName);
|
||||
var additionalAssembly = LoadAssembly(filePath);
|
||||
|
||||
if (additionalAssembly == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return additionalAssembly;
|
||||
}
|
||||
|
||||
private static (string metadataPath, string metadataIncludeByDefault) ParseMetadataAttribute(
|
||||
AssemblyMetadataAttribute metadataAttribute)
|
||||
{
|
||||
var data = metadataAttribute.Value.Split(MetadataSeparators);
|
||||
if (data.Length != 2 || string.IsNullOrWhiteSpace(data[0]) || string.IsNullOrWhiteSpace(data[1]))
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
return (data[0], data[1]);
|
||||
}
|
||||
|
||||
private static Assembly LoadAssembly(string filePath)
|
||||
{
|
||||
Assembly viewsAssembly = null;
|
||||
if (AssemblyResolver(filePath))
|
||||
{
|
||||
try
|
||||
{
|
||||
viewsAssembly = AssemblyLoader(filePath);
|
||||
}
|
||||
catch (FileLoadException)
|
||||
{
|
||||
// Don't throw if assembly cannot be loaded. This can happen if the file is not a managed assembly.
|
||||
}
|
||||
}
|
||||
|
||||
return viewsAssembly;
|
||||
}
|
||||
|
||||
private static Assembly LoadFromConvention(Assembly assembly)
|
||||
{
|
||||
if (assembly.IsDynamic || string.IsNullOrEmpty(assembly.Location))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
for (var i = 0; i < ViewAssemblySuffixes.Count; i++)
|
||||
{
|
||||
var fileName = assembly.GetName().Name + ViewAssemblySuffixes[i] + ".dll";
|
||||
var filePath = Path.Combine(Path.GetDirectoryName(assembly.Location), fileName);
|
||||
|
||||
var viewsAssembly = LoadAssembly(filePath);
|
||||
if (viewsAssembly != null)
|
||||
{
|
||||
return viewsAssembly;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void AddAdditionalReference(
|
||||
Assembly additionalReference,
|
||||
Assembly entryAssembly,
|
||||
Assembly assembly,
|
||||
SortedSet<Assembly> additionalReferences,
|
||||
SortedSet<Assembly> entryAssemblyAdditionalReferences)
|
||||
{
|
||||
if (additionalReference == null ||
|
||||
additionalReferences.Contains(additionalReference) ||
|
||||
entryAssemblyAdditionalReferences.Contains(additionalReference))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (assembly.Equals(entryAssembly))
|
||||
{
|
||||
entryAssemblyAdditionalReferences.Add(additionalReference);
|
||||
}
|
||||
else
|
||||
{
|
||||
additionalReferences.Add(additionalReference);
|
||||
}
|
||||
}
|
||||
|
||||
internal class AdditionalReferencesPair
|
||||
{
|
||||
public SortedSet<Assembly> AdditionalAssemblies { get; set; }
|
||||
public SortedSet<Assembly> EntryAssemblyAdditionalAssemblies { get; set; }
|
||||
|
||||
public void Deconstruct(
|
||||
out SortedSet<Assembly> additionalAssemblies,
|
||||
out SortedSet<Assembly> entryAssemblyAdditionalAssemblies)
|
||||
{
|
||||
additionalAssemblies = AdditionalAssemblies;
|
||||
entryAssemblyAdditionalAssemblies = EntryAssemblyAdditionalAssemblies;
|
||||
}
|
||||
}
|
||||
|
||||
internal class FullNameAssemblyComparer : IComparer<Assembly>
|
||||
{
|
||||
public static IComparer<Assembly> Instance { get; } = new FullNameAssemblyComparer();
|
||||
|
||||
public int Compare(Assembly x, Assembly y) =>
|
||||
string.Compare(x?.FullName, y?.FullName, StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
internal static IEnumerable<Assembly> GetCandidateAssemblies(Assembly entryAssembly, DependencyContext dependencyContext)
|
||||
{
|
||||
if (dependencyContext == null)
|
||||
{
|
||||
// Use the entry assembly as the sole candidate.
|
||||
return new[] { entryAssembly };
|
||||
}
|
||||
|
||||
return GetCandidateLibraries(dependencyContext)
|
||||
.SelectMany(library => library.GetDefaultAssemblyNames(dependencyContext))
|
||||
.Select(Assembly.Load);
|
||||
}
|
||||
|
||||
// Returns a list of libraries that references the assemblies in <see cref="ReferenceAssemblies"/>.
|
||||
// By default it returns all assemblies that reference any of the primary MVC assemblies
|
||||
// while ignoring MVC assemblies.
|
||||
// Internal for unit testing
|
||||
internal static IEnumerable<RuntimeLibrary> GetCandidateLibraries(DependencyContext dependencyContext)
|
||||
{
|
||||
if (ReferenceAssemblies == null)
|
||||
{
|
||||
return Enumerable.Empty<RuntimeLibrary>();
|
||||
}
|
||||
|
||||
var candidatesResolver = new CandidateResolver(dependencyContext.RuntimeLibraries, ReferenceAssemblies);
|
||||
return candidatesResolver.GetCandidates();
|
||||
}
|
||||
|
||||
private class CandidateResolver
|
||||
{
|
||||
private readonly IDictionary<string, Dependency> _runtimeDependencies;
|
||||
|
||||
public CandidateResolver(IReadOnlyList<RuntimeLibrary> runtimeDependencies, ISet<string> referenceAssemblies)
|
||||
{
|
||||
var dependenciesWithNoDuplicates = new Dictionary<string, Dependency>(StringComparer.OrdinalIgnoreCase);
|
||||
foreach (var dependency in runtimeDependencies)
|
||||
{
|
||||
if (dependenciesWithNoDuplicates.ContainsKey(dependency.Name))
|
||||
{
|
||||
throw new InvalidOperationException(Resources.FormatCandidateResolver_DifferentCasedReference(dependency.Name));
|
||||
}
|
||||
dependenciesWithNoDuplicates.Add(dependency.Name, CreateDependency(dependency, referenceAssemblies));
|
||||
}
|
||||
|
||||
_runtimeDependencies = dependenciesWithNoDuplicates;
|
||||
}
|
||||
|
||||
private Dependency CreateDependency(RuntimeLibrary library, ISet<string> referenceAssemblies)
|
||||
{
|
||||
var classification = DependencyClassification.Unknown;
|
||||
if (referenceAssemblies.Contains(library.Name))
|
||||
{
|
||||
classification = DependencyClassification.MvcReference;
|
||||
}
|
||||
|
||||
return new Dependency(library, classification);
|
||||
}
|
||||
|
||||
private DependencyClassification ComputeClassification(string dependency)
|
||||
{
|
||||
if (!_runtimeDependencies.ContainsKey(dependency))
|
||||
{
|
||||
// Library does not have runtime dependency. Since we can't infer
|
||||
// anything about it's references, we'll assume it does not have a reference to Mvc.
|
||||
return DependencyClassification.DoesNotReferenceMvc;
|
||||
}
|
||||
|
||||
var candidateEntry = _runtimeDependencies[dependency];
|
||||
if (candidateEntry.Classification != DependencyClassification.Unknown)
|
||||
{
|
||||
return candidateEntry.Classification;
|
||||
}
|
||||
else
|
||||
{
|
||||
var classification = DependencyClassification.DoesNotReferenceMvc;
|
||||
foreach (var candidateDependency in candidateEntry.Library.Dependencies)
|
||||
{
|
||||
var dependencyClassification = ComputeClassification(candidateDependency.Name);
|
||||
if (dependencyClassification == DependencyClassification.ReferencesMvc ||
|
||||
dependencyClassification == DependencyClassification.MvcReference)
|
||||
{
|
||||
classification = DependencyClassification.ReferencesMvc;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
candidateEntry.Classification = classification;
|
||||
|
||||
return classification;
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<RuntimeLibrary> GetCandidates()
|
||||
{
|
||||
foreach (var dependency in _runtimeDependencies)
|
||||
{
|
||||
if (ComputeClassification(dependency.Key) == DependencyClassification.ReferencesMvc)
|
||||
{
|
||||
yield return dependency.Value.Library;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class Dependency
|
||||
{
|
||||
public Dependency(RuntimeLibrary library, DependencyClassification classification)
|
||||
{
|
||||
Library = library;
|
||||
Classification = classification;
|
||||
}
|
||||
|
||||
public RuntimeLibrary Library { get; }
|
||||
|
||||
public DependencyClassification Classification { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"Library: {Library.Name}, Classification: {Classification}";
|
||||
}
|
||||
}
|
||||
|
||||
private enum DependencyClassification
|
||||
{
|
||||
Unknown = 0,
|
||||
|
||||
/// <summary>
|
||||
/// References (directly or transitively) one of the Mvc packages listed in
|
||||
/// <see cref="ReferenceAssemblies"/>.
|
||||
/// </summary>
|
||||
ReferencesMvc = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Does not reference (directly or transitively) one of the Mvc packages listed by
|
||||
/// <see cref="ReferenceAssemblies"/>.
|
||||
/// </summary>
|
||||
DoesNotReferenceMvc = 2,
|
||||
|
||||
/// <summary>
|
||||
/// One of the references listed in <see cref="ReferenceAssemblies"/>.
|
||||
/// </summary>
|
||||
MvcReference = 3,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1368,6 +1368,76 @@ namespace Microsoft.AspNetCore.Mvc.Core
|
|||
internal static string FormatVirtualFileResultExecutor_NoFileProviderConfigured()
|
||||
=> GetString("VirtualFileResultExecutor_NoFileProviderConfigured");
|
||||
|
||||
/// <summary>
|
||||
/// Type {0} specified by {1} is invalid. Type specified by {1} must derive from {2}.
|
||||
/// </summary>
|
||||
internal static string ApplicationPartFactory_InvalidFactoryType
|
||||
{
|
||||
get => GetString("ApplicationPartFactory_InvalidFactoryType");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Type {0} specified by {1} is invalid. Type specified by {1} must derive from {2}.
|
||||
/// </summary>
|
||||
internal static string FormatApplicationPartFactory_InvalidFactoryType(object p0, object p1, object p2)
|
||||
=> string.Format(CultureInfo.CurrentCulture, GetString("ApplicationPartFactory_InvalidFactoryType"), p0, p1, p2);
|
||||
|
||||
/// <summary>
|
||||
/// {0} specified on {1} cannot be self referential.
|
||||
/// </summary>
|
||||
internal static string RelatedAssemblyAttribute_AssemblyCannotReferenceSelf
|
||||
{
|
||||
get => GetString("RelatedAssemblyAttribute_AssemblyCannotReferenceSelf");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// {0} specified on {1} cannot be self referential.
|
||||
/// </summary>
|
||||
internal static string FormatRelatedAssemblyAttribute_AssemblyCannotReferenceSelf(object p0, object p1)
|
||||
=> string.Format(CultureInfo.CurrentCulture, GetString("RelatedAssemblyAttribute_AssemblyCannotReferenceSelf"), p0, p1);
|
||||
|
||||
/// <summary>
|
||||
/// Related assembly '{0}' specified by assembly '{1}' could not be found in the directory {2}. Related assemblies must be co-located with the specifying assemblies.
|
||||
/// </summary>
|
||||
internal static string RelatedAssemblyAttribute_CouldNotBeFound
|
||||
{
|
||||
get => GetString("RelatedAssemblyAttribute_CouldNotBeFound");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Related assembly '{0}' specified by assembly '{1}' could not be found in the directory {2}. Related assemblies must be co-located with the specifying assemblies.
|
||||
/// </summary>
|
||||
internal static string FormatRelatedAssemblyAttribute_CouldNotBeFound(object p0, object p1, object p2)
|
||||
=> string.Format(CultureInfo.CurrentCulture, GetString("RelatedAssemblyAttribute_CouldNotBeFound"), p0, p1, p2);
|
||||
|
||||
/// <summary>
|
||||
/// Each related assembly must be declared by exactly one assembly. The assembly '{0}' was declared as related assembly by the following:
|
||||
/// </summary>
|
||||
internal static string ApplicationAssembliesProvider_DuplicateRelatedAssembly
|
||||
{
|
||||
get => GetString("ApplicationAssembliesProvider_DuplicateRelatedAssembly");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Each related assembly must be declared by exactly one assembly. The assembly '{0}' was declared as related assembly by the following:
|
||||
/// </summary>
|
||||
internal static string FormatApplicationAssembliesProvider_DuplicateRelatedAssembly(object p0)
|
||||
=> string.Format(CultureInfo.CurrentCulture, GetString("ApplicationAssembliesProvider_DuplicateRelatedAssembly"), p0);
|
||||
|
||||
/// <summary>
|
||||
/// Assembly '{0}' declared as a related assembly by assembly '{1}' cannot define additional related assemblies.
|
||||
/// </summary>
|
||||
internal static string ApplicationAssembliesProvider_RelatedAssemblyCannotDefineAdditional
|
||||
{
|
||||
get => GetString("ApplicationAssembliesProvider_RelatedAssemblyCannotDefineAdditional");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Assembly '{0}' declared as a related assembly by assembly '{1}' cannot define additional related assemblies.
|
||||
/// </summary>
|
||||
internal static string FormatApplicationAssembliesProvider_RelatedAssemblyCannotDefineAdditional(object p0, object p1)
|
||||
=> string.Format(CultureInfo.CurrentCulture, GetString("ApplicationAssembliesProvider_RelatedAssemblyCannotDefineAdditional"), p0, p1);
|
||||
|
||||
private static string GetString(string name, params string[] formatterNames)
|
||||
{
|
||||
var value = _resourceManager.GetString(name);
|
||||
|
|
|
|||
|
|
@ -421,4 +421,19 @@
|
|||
<data name="VirtualFileResultExecutor_NoFileProviderConfigured" xml:space="preserve">
|
||||
<value>No file provider has been configured to process the supplied file.</value>
|
||||
</data>
|
||||
</root>
|
||||
<data name="ApplicationPartFactory_InvalidFactoryType" xml:space="preserve">
|
||||
<value>Type {0} specified by {1} is invalid. Type specified by {1} must derive from {2}.</value>
|
||||
</data>
|
||||
<data name="RelatedAssemblyAttribute_AssemblyCannotReferenceSelf" xml:space="preserve">
|
||||
<value>{0} specified on {1} cannot be self referential.</value>
|
||||
</data>
|
||||
<data name="RelatedAssemblyAttribute_CouldNotBeFound" xml:space="preserve">
|
||||
<value>Related assembly '{0}' specified by assembly '{1}' could not be found in the directory {2}. Related assemblies must be co-located with the specifying assemblies.</value>
|
||||
</data>
|
||||
<data name="ApplicationAssembliesProvider_DuplicateRelatedAssembly" xml:space="preserve">
|
||||
<value>Each related assembly must be declared by exactly one assembly. The assembly '{0}' was declared as related assembly by the following:</value>
|
||||
</data>
|
||||
<data name="ApplicationAssembliesProvider_RelatedAssemblyCannotDefineAdditional" xml:space="preserve">
|
||||
<value>Assembly '{0}' declared as a related assembly by assembly '{1}' cannot define additional related assemblies.</value>
|
||||
</data>
|
||||
</root>
|
||||
|
|
@ -13,9 +13,7 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationParts
|
|||
public class CompiledRazorAssemblyApplicationPartFactory : ApplicationPartFactory
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override IEnumerable<ApplicationPart> GetApplicationParts(
|
||||
Assembly assembly,
|
||||
string configuration)
|
||||
public override IEnumerable<ApplicationPart> GetApplicationParts(Assembly assembly, string configuration)
|
||||
{
|
||||
if (assembly == null)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -2,19 +2,41 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using Microsoft.AspNetCore.Razor.Hosting;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.ApplicationParts
|
||||
{
|
||||
public class CompiledRazorAssemblyPart : ApplicationPart
|
||||
/// <summary>
|
||||
/// An <see cref="ApplicationPart"/> for compiled Razor assemblies.
|
||||
/// </summary>
|
||||
public class CompiledRazorAssemblyPart : ApplicationPart, IRazorCompiledItemProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of <see cref="CompiledRazorAssemblyPart"/>.
|
||||
/// </summary>
|
||||
/// <param name="assembly">The <see cref="System.Reflection.Assembly"/></param>
|
||||
public CompiledRazorAssemblyPart(Assembly assembly)
|
||||
{
|
||||
Assembly = assembly ?? throw new ArgumentNullException(nameof(assembly));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="System.Reflection.Assembly"/>.
|
||||
/// </summary>
|
||||
public Assembly Assembly { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Name => Assembly.GetName().Name;
|
||||
|
||||
IEnumerable<RazorCompiledItem> IRazorCompiledItemProvider.CompiledItems
|
||||
{
|
||||
get
|
||||
{
|
||||
var loader = new RazorCompiledItemLoader();
|
||||
return loader.LoadItems(Assembly);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,19 @@
|
|||
// 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.Collections.Generic;
|
||||
using Microsoft.AspNetCore.Razor.Hosting;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.ApplicationParts
|
||||
{
|
||||
/// <summary>
|
||||
/// Exposes one or more <see cref="RazorCompiledItem"/> instances from an <see cref="ApplicationPart"/>.
|
||||
/// </summary>
|
||||
public interface IRazorCompiledItemProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a sequence of <see cref="RazorCompiledItem"/> instances.
|
||||
/// </summary>
|
||||
IEnumerable<RazorCompiledItem> CompiledItems { get; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
// 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 System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Microsoft.AspNetCore.Mvc.Razor;
|
||||
using Microsoft.AspNetCore.Mvc.Razor.Compilation;
|
||||
using Microsoft.AspNetCore.Razor.Hosting;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.ApplicationParts
|
||||
{
|
||||
internal class RazorCompiledItemFeatureProvider : IApplicationFeatureProvider<ViewsFeature>
|
||||
{
|
||||
public void PopulateFeature(IEnumerable<ApplicationPart> parts, ViewsFeature feature)
|
||||
{
|
||||
foreach (var provider in parts.OfType<IRazorCompiledItemProvider>())
|
||||
{
|
||||
// Ensure parts do not specify views with differing cases. This is not supported
|
||||
// at runtime and we should flag at as such for precompiled views.
|
||||
var duplicates = provider.CompiledItems
|
||||
.GroupBy(i => i.Identifier, StringComparer.OrdinalIgnoreCase)
|
||||
.FirstOrDefault(g => g.Count() > 1);
|
||||
|
||||
if (duplicates != null)
|
||||
{
|
||||
var viewsDiffereningInCase = string.Join(Environment.NewLine, duplicates.Select(d => d.Identifier));
|
||||
|
||||
var message = string.Join(
|
||||
Environment.NewLine,
|
||||
Resources.RazorViewCompiler_ViewPathsDifferOnlyInCase,
|
||||
viewsDiffereningInCase);
|
||||
throw new InvalidOperationException(message);
|
||||
}
|
||||
|
||||
foreach (var item in provider.CompiledItems)
|
||||
{
|
||||
var descriptor = GetCompiledViewDescriptor(item);
|
||||
feature.ViewDescriptors.Add(descriptor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static CompiledViewDescriptor GetCompiledViewDescriptor(RazorCompiledItem item)
|
||||
{
|
||||
var itemAssembly = item.Type.Assembly;
|
||||
var razorViewAttribute = itemAssembly.GetCustomAttributes<RazorViewAttribute>()
|
||||
.FirstOrDefault(attribute => attribute.ViewType == item.Type);
|
||||
return new CompiledViewDescriptor(item, razorViewAttribute);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -63,6 +63,9 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Compilation
|
|||
/// <summary>
|
||||
/// Gets or sets the <see cref="RazorViewAttribute"/> decorating the view.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// May be <c>null</c>.
|
||||
/// </remarks>
|
||||
public RazorViewAttribute ViewAttribute { get; set; }
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -7,136 +7,71 @@ using System.IO;
|
|||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Microsoft.AspNetCore.Mvc.ApplicationParts;
|
||||
using Microsoft.AspNetCore.Razor.Hosting;
|
||||
using Microsoft.AspNetCore.Mvc.Razor.Internal;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.Razor.Compilation
|
||||
{
|
||||
/// <summary>
|
||||
/// An <see cref="IApplicationFeatureProvider{TFeature}"/> for <see cref="ViewsFeature"/>.
|
||||
/// </summary>
|
||||
[Obsolete("This type is obsolete and will be removed in a future version. See " + nameof(IRazorCompiledItemProvider) + " for alternatives.")]
|
||||
public class ViewsFeatureProvider : IApplicationFeatureProvider<ViewsFeature>
|
||||
{
|
||||
public static readonly string PrecompiledViewsAssemblySuffix = ".PrecompiledViews";
|
||||
|
||||
public static readonly IReadOnlyList<string> ViewAssemblySuffixes = new string[]
|
||||
{
|
||||
PrecompiledViewsAssemblySuffix,
|
||||
".Views",
|
||||
};
|
||||
|
||||
/// <inheritdoc />
|
||||
public void PopulateFeature(IEnumerable<ApplicationPart> parts, ViewsFeature feature)
|
||||
{
|
||||
var knownIdentifiers = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||
var descriptors = new List<CompiledViewDescriptor>();
|
||||
foreach (var assemblyPart in parts.OfType<AssemblyPart>())
|
||||
{
|
||||
var attributes = GetViewAttributes(assemblyPart);
|
||||
var items = LoadItems(assemblyPart);
|
||||
var viewAttributes = GetViewAttributes(assemblyPart)
|
||||
.Select(attribute => (Attribute: attribute, RelativePath: ViewPath.NormalizePath(attribute.Path)));
|
||||
|
||||
var merged = Merge(items, attributes);
|
||||
foreach (var item in merged)
|
||||
var duplicates = viewAttributes.GroupBy(a => a.RelativePath, StringComparer.OrdinalIgnoreCase)
|
||||
.FirstOrDefault(g => g.Count() > 1);
|
||||
|
||||
if (duplicates != null)
|
||||
{
|
||||
var descriptor = new CompiledViewDescriptor(item.item, item.attribute);
|
||||
// We iterate through ApplicationPart instances appear in precendence order.
|
||||
// If a view path appears in multiple views, we'll use the order to break ties.
|
||||
if (knownIdentifiers.Add(descriptor.RelativePath))
|
||||
// Ensure parts do not specify views with differing cases. This is not supported
|
||||
// at runtime and we should flag at as such for precompiled views.
|
||||
var viewsDiffereningInCase = string.Join(Environment.NewLine, duplicates.Select(d => d.RelativePath));
|
||||
|
||||
var message = string.Join(
|
||||
Environment.NewLine,
|
||||
Resources.RazorViewCompiler_ViewPathsDifferOnlyInCase,
|
||||
viewsDiffereningInCase);
|
||||
throw new InvalidOperationException(message);
|
||||
}
|
||||
|
||||
foreach (var (attribute, relativePath) in viewAttributes)
|
||||
{
|
||||
var viewDescriptor = new CompiledViewDescriptor
|
||||
{
|
||||
feature.ViewDescriptors.Add(descriptor);
|
||||
}
|
||||
ExpirationTokens = Array.Empty<IChangeToken>(),
|
||||
RelativePath = relativePath,
|
||||
ViewAttribute = attribute,
|
||||
IsPrecompiled = true,
|
||||
};
|
||||
|
||||
feature.ViewDescriptors.Add(viewDescriptor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private ICollection<(RazorCompiledItem item, RazorViewAttribute attribute)> Merge(
|
||||
IReadOnlyList<RazorCompiledItem> items,
|
||||
IEnumerable<RazorViewAttribute> attributes)
|
||||
{
|
||||
// This code is a intentionally defensive. We assume that it's possible to have duplicates
|
||||
// of attributes, and also items that have a single kind of metadata, but not the other.
|
||||
var dictionary = new Dictionary<string, (RazorCompiledItem item, RazorViewAttribute attribute)>(StringComparer.OrdinalIgnoreCase);
|
||||
for (var i = 0; i < items.Count; i++)
|
||||
{
|
||||
var item = items[i];
|
||||
if (!dictionary.TryGetValue(item.Identifier, out var entry))
|
||||
{
|
||||
dictionary.Add(item.Identifier, (item, null));
|
||||
|
||||
}
|
||||
else if (entry.item == null)
|
||||
{
|
||||
dictionary[item.Identifier] = (item, entry.attribute);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var attribute in attributes)
|
||||
{
|
||||
if (!dictionary.TryGetValue(attribute.Path, out var entry))
|
||||
{
|
||||
dictionary.Add(attribute.Path, (null, attribute));
|
||||
}
|
||||
else if (entry.attribute == null)
|
||||
{
|
||||
dictionary[attribute.Path] = (entry.item, attribute);
|
||||
}
|
||||
}
|
||||
|
||||
return dictionary.Values;
|
||||
}
|
||||
|
||||
internal virtual IReadOnlyList<RazorCompiledItem> LoadItems(AssemblyPart assemblyPart)
|
||||
{
|
||||
if (assemblyPart == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(assemblyPart));
|
||||
}
|
||||
|
||||
var viewAssembly = assemblyPart.Assembly;
|
||||
if (viewAssembly != null)
|
||||
{
|
||||
var loader = new RazorCompiledItemLoader();
|
||||
return loader.LoadItems(viewAssembly);
|
||||
}
|
||||
|
||||
return Array.Empty<RazorCompiledItem>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the sequence of <see cref="RazorViewAttribute"/> instances associated with the specified <paramref name="assemblyPart"/>.
|
||||
/// </summary>
|
||||
/// <param name="assemblyPart">The <see cref="AssemblyPart"/>.</param>
|
||||
/// <returns>The sequence of <see cref="RazorViewAttribute"/> instances.</returns>
|
||||
protected virtual IEnumerable<RazorViewAttribute> GetViewAttributes(AssemblyPart assemblyPart)
|
||||
{
|
||||
// We check if the method was overriden by a subclass and preserve the old behavior in that case.
|
||||
if (GetViewAttributesOverriden())
|
||||
{
|
||||
return GetViewAttributesLegacy(assemblyPart);
|
||||
}
|
||||
else
|
||||
{
|
||||
// It is safe to call this method for additional assembly parts even if there is a feature provider
|
||||
// present on the pipeline that overrides getviewattributes as dependent parts are later in the list
|
||||
// of application parts.
|
||||
return GetViewAttributesFromCurrentAssembly(assemblyPart);
|
||||
}
|
||||
|
||||
bool GetViewAttributesOverriden()
|
||||
{
|
||||
const BindingFlags bindingFlags = BindingFlags.NonPublic | BindingFlags.Instance;
|
||||
return GetType() != typeof(ViewsFeatureProvider) &&
|
||||
GetType().GetMethod(nameof(GetViewAttributes), bindingFlags).DeclaringType != typeof(ViewsFeatureProvider);
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerable<RazorViewAttribute> GetViewAttributesLegacy(AssemblyPart assemblyPart)
|
||||
{
|
||||
if (assemblyPart == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(assemblyPart));
|
||||
}
|
||||
|
||||
var featureAssembly = GetViewAssembly(assemblyPart);
|
||||
var featureAssembly = GetFeatureAssembly(assemblyPart);
|
||||
if (featureAssembly != null)
|
||||
{
|
||||
return featureAssembly.GetCustomAttributes<RazorViewAttribute>();
|
||||
|
|
@ -145,48 +80,34 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Compilation
|
|||
return Enumerable.Empty<RazorViewAttribute>();
|
||||
}
|
||||
|
||||
private Assembly GetViewAssembly(AssemblyPart assemblyPart)
|
||||
private static Assembly GetFeatureAssembly(AssemblyPart assemblyPart)
|
||||
{
|
||||
if (assemblyPart.Assembly.IsDynamic || string.IsNullOrEmpty(assemblyPart.Assembly.Location))
|
||||
if (assemblyPart.Assembly.IsDynamic || string.IsNullOrEmpty((string)assemblyPart.Assembly.Location))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
for (var i = 0; i < ViewAssemblySuffixes.Count; i++)
|
||||
{
|
||||
var fileName = assemblyPart.Assembly.GetName().Name + ViewAssemblySuffixes[i] + ".dll";
|
||||
var filePath = Path.Combine(Path.GetDirectoryName(assemblyPart.Assembly.Location), fileName);
|
||||
var precompiledAssemblyFileName = assemblyPart.Assembly.GetName().Name
|
||||
+ PrecompiledViewsAssemblySuffix
|
||||
+ ".dll";
|
||||
|
||||
if (File.Exists(filePath))
|
||||
var precompiledAssemblyFilePath = Path.Combine(
|
||||
Path.GetDirectoryName(assemblyPart.Assembly.Location),
|
||||
precompiledAssemblyFileName);
|
||||
|
||||
if (File.Exists(precompiledAssemblyFilePath))
|
||||
{
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
return Assembly.LoadFile(filePath);
|
||||
}
|
||||
catch (FileLoadException)
|
||||
{
|
||||
// Don't throw if assembly cannot be loaded. This can happen if the file is not a managed assembly.
|
||||
}
|
||||
return Assembly.LoadFile(precompiledAssemblyFilePath);
|
||||
}
|
||||
catch (FileLoadException)
|
||||
{
|
||||
// Don't throw if assembly cannot be loaded. This can happen if the file is not a managed assembly.
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static IEnumerable<RazorViewAttribute> GetViewAttributesFromCurrentAssembly(AssemblyPart assemblyPart)
|
||||
{
|
||||
if (assemblyPart == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(assemblyPart));
|
||||
}
|
||||
|
||||
var featureAssembly = assemblyPart.Assembly;
|
||||
if (featureAssembly != null)
|
||||
{
|
||||
return featureAssembly.GetCustomAttributes<RazorViewAttribute>();
|
||||
}
|
||||
|
||||
return Enumerable.Empty<RazorViewAttribute>();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -6,6 +6,7 @@ using System.Diagnostics;
|
|||
using System.Linq;
|
||||
using System.Text.Encodings.Web;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.ApplicationParts;
|
||||
using Microsoft.AspNetCore.Mvc.Razor;
|
||||
using Microsoft.AspNetCore.Mvc.Razor.Compilation;
|
||||
using Microsoft.AspNetCore.Mvc.Razor.Extensions;
|
||||
|
|
@ -74,10 +75,21 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
builder.PartManager.FeatureProviders.Add(new TagHelperFeatureProvider());
|
||||
}
|
||||
|
||||
// ViewFeature items have precedence semantics - when two views have the same path \ identifier,
|
||||
// the one that appears earlier in the list wins. Therefore the ordering of
|
||||
// RazorCompiledItemFeatureProvider and ViewsFeatureProvider is pertinent - any view compiled
|
||||
// using the Sdk will be prefered to views compiled using MvcPrecompilation.
|
||||
if (!builder.PartManager.FeatureProviders.OfType<RazorCompiledItemFeatureProvider>().Any())
|
||||
{
|
||||
builder.PartManager.FeatureProviders.Add(new RazorCompiledItemFeatureProvider());
|
||||
}
|
||||
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
if (!builder.PartManager.FeatureProviders.OfType<ViewsFeatureProvider>().Any())
|
||||
{
|
||||
builder.PartManager.FeatureProviders.Add(new ViewsFeatureProvider());
|
||||
}
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -100,17 +100,12 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
|
|||
{
|
||||
logger.ViewCompilerLocatedCompiledView(precompiledView.RelativePath);
|
||||
|
||||
if (_precompiledViews.TryGetValue(precompiledView.RelativePath, out var otherValue))
|
||||
if (!_precompiledViews.ContainsKey(precompiledView.RelativePath))
|
||||
{
|
||||
var message = string.Join(
|
||||
Environment.NewLine,
|
||||
Resources.RazorViewCompiler_ViewPathsDifferOnlyInCase,
|
||||
otherValue.RelativePath,
|
||||
precompiledView.RelativePath);
|
||||
throw new InvalidOperationException(message);
|
||||
// View ordering has precedence semantics, a view with a higher precedence was
|
||||
// already added to the list.
|
||||
_precompiledViews.Add(precompiledView.RelativePath, precompiledView);
|
||||
}
|
||||
|
||||
_precompiledViews.Add(precompiledView.RelativePath, precompiledView);
|
||||
}
|
||||
|
||||
if (_precompiledViews.Count == 0)
|
||||
|
|
|
|||
|
|
@ -3,4 +3,5 @@
|
|||
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Mvc.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Mvc.Razor.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||
|
|
|
|||
|
|
@ -45,9 +45,8 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
|
||||
var compileTask = Compiler.CompileAsync(actionDescriptor.RelativePath);
|
||||
var viewDescriptor = compileTask.GetAwaiter().GetResult();
|
||||
var pageAttribute = (RazorPageAttribute)viewDescriptor.ViewAttribute;
|
||||
|
||||
var context = new PageApplicationModelProviderContext(actionDescriptor, pageAttribute.ViewType.GetTypeInfo());
|
||||
var context = new PageApplicationModelProviderContext(actionDescriptor, viewDescriptor.Type.GetTypeInfo());
|
||||
for (var i = 0; i < _applicationModelProviders.Length; i++)
|
||||
{
|
||||
_applicationModelProviders[i].OnProvidersExecuting(context);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,436 @@
|
|||
// 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 System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.DependencyModel;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.ApplicationParts
|
||||
{
|
||||
public class ApplicationAssembliesProviderTest
|
||||
{
|
||||
private static readonly Assembly ThisAssembly = typeof(ApplicationAssembliesProviderTest).Assembly;
|
||||
|
||||
[Fact]
|
||||
public void ResolveAssemblies_ReturnsCurrentAssembly_IfNoDepsFileIsPresent()
|
||||
{
|
||||
// Arrange
|
||||
var provider = new TestApplicationAssembliesProvider();
|
||||
|
||||
// Act
|
||||
var result = provider.ResolveAssemblies(ThisAssembly);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(new[] { ThisAssembly }, result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ResolveAssemblies_ReturnsRelatedAssembliesOrderedByName()
|
||||
{
|
||||
// Arrange
|
||||
var assembly1 = typeof(ApplicationAssembliesProvider).Assembly;
|
||||
var assembly2 = typeof(IActionResult).Assembly;
|
||||
var assembly3 = typeof(FactAttribute).Assembly;
|
||||
|
||||
var relatedAssemblies = new[] { assembly1, assembly2, assembly3 };
|
||||
var provider = new TestApplicationAssembliesProvider
|
||||
{
|
||||
GetRelatedAssembliesDelegate = (assembly) => relatedAssemblies,
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = provider.ResolveAssemblies(ThisAssembly);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(new[] { ThisAssembly, assembly2, assembly1, assembly3 }, result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ResolveAssemblies_ReturnsLibrariesFromTheDepsFileThatReferenceMvc()
|
||||
{
|
||||
// Arrange
|
||||
var mvcAssembly = typeof(IActionResult).Assembly;
|
||||
var classLibrary = typeof(FactAttribute).Assembly;
|
||||
|
||||
var dependencyContext = GetDependencyContext(new[]
|
||||
{
|
||||
GetLibrary(ThisAssembly.GetName().Name, new[] { mvcAssembly.GetName().Name, classLibrary.GetName().Name }),
|
||||
GetLibrary(mvcAssembly.GetName().Name),
|
||||
GetLibrary(classLibrary.GetName().Name, new[] { mvcAssembly.GetName().Name }),
|
||||
});
|
||||
|
||||
var provider = new TestApplicationAssembliesProvider
|
||||
{
|
||||
DependencyContext = dependencyContext,
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = provider.ResolveAssemblies(ThisAssembly);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(new[] { ThisAssembly, classLibrary, }, result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ResolveAssemblies_ReturnsRelatedAssembliesForLibrariesFromDepsFile()
|
||||
{
|
||||
// Arrange
|
||||
var mvcAssembly = typeof(IActionResult).Assembly;
|
||||
var classLibrary = typeof(object).Assembly;
|
||||
var relatedPart = typeof(FactAttribute).Assembly;
|
||||
|
||||
var dependencyContext = GetDependencyContext(new[]
|
||||
{
|
||||
GetLibrary(ThisAssembly.GetName().Name, new[] { relatedPart.GetName().Name, classLibrary.GetName().Name }),
|
||||
GetLibrary(classLibrary.GetName().Name, new[] { mvcAssembly.GetName().Name }),
|
||||
GetLibrary(relatedPart.GetName().Name, new[] { mvcAssembly.GetName().Name }),
|
||||
GetLibrary(mvcAssembly.GetName().Name),
|
||||
});
|
||||
|
||||
var provider = new TestApplicationAssembliesProvider
|
||||
{
|
||||
DependencyContext = dependencyContext,
|
||||
GetRelatedAssembliesDelegate = (assembly) =>
|
||||
{
|
||||
if (assembly == classLibrary)
|
||||
{
|
||||
return new[] { relatedPart };
|
||||
}
|
||||
|
||||
return Array.Empty<Assembly>();
|
||||
},
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = provider.ResolveAssemblies(ThisAssembly);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(new[] { ThisAssembly, classLibrary, relatedPart, }, result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ResolveAssemblies_ThrowsIfRelatedAssemblyDefinesAdditionalRelatedAssemblies()
|
||||
{
|
||||
// Arrange
|
||||
var expected = $"Assembly 'TestRelatedAssembly' declared as a related assembly by assembly '{ThisAssembly}' cannot define additional related assemblies.";
|
||||
var assembly1 = typeof(ApplicationAssembliesProvider).Assembly;
|
||||
var assembly2 = new TestAssembly();
|
||||
|
||||
var relatedAssemblies = new[] { assembly1, assembly2 };
|
||||
var provider = new TestApplicationAssembliesProvider
|
||||
{
|
||||
GetRelatedAssembliesDelegate = (assembly) => relatedAssemblies,
|
||||
};
|
||||
|
||||
// Act & Assert
|
||||
var ex = Assert.Throws<InvalidOperationException>(() => provider.ResolveAssemblies(ThisAssembly).ToArray());
|
||||
Assert.Equal(expected, ex.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ResolveAssemblies_ThrowsIfMultipleAssembliesDeclareTheSameRelatedPart()
|
||||
{
|
||||
// Arrange
|
||||
var mvcAssembly = typeof(IActionResult).Assembly;
|
||||
var libraryAssembly1 = typeof(object).Assembly;
|
||||
var libraryAssembly2 = typeof(HttpContext).Assembly;
|
||||
var relatedPart = typeof(FactAttribute).Assembly;
|
||||
var expected = string.Join(
|
||||
Environment.NewLine,
|
||||
$"Each related assembly must be declared by exactly one assembly. The assembly '{relatedPart.FullName}' was declared as related assembly by the following:",
|
||||
libraryAssembly1.FullName,
|
||||
libraryAssembly2.FullName);
|
||||
|
||||
var dependencyContext = GetDependencyContext(new[]
|
||||
{
|
||||
GetLibrary(ThisAssembly.GetName().Name, new[] { relatedPart.GetName().Name, libraryAssembly1.GetName().Name }),
|
||||
GetLibrary(libraryAssembly1.GetName().Name, new[] { mvcAssembly.GetName().Name }),
|
||||
GetLibrary(libraryAssembly2.GetName().Name, new[] { mvcAssembly.GetName().Name }),
|
||||
GetLibrary(mvcAssembly.GetName().Name),
|
||||
});
|
||||
|
||||
var provider = new TestApplicationAssembliesProvider
|
||||
{
|
||||
DependencyContext = dependencyContext,
|
||||
GetRelatedAssembliesDelegate = (assembly) =>
|
||||
{
|
||||
if (assembly == libraryAssembly1 || assembly == libraryAssembly2)
|
||||
{
|
||||
return new[] { relatedPart };
|
||||
}
|
||||
|
||||
return Array.Empty<Assembly>();
|
||||
},
|
||||
};
|
||||
|
||||
// Act & Assert
|
||||
var ex = Assert.Throws<InvalidOperationException>(() => provider.ResolveAssemblies(ThisAssembly).ToArray());
|
||||
Assert.Equal(expected, ex.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CandidateResolver_ThrowsIfDependencyContextContainsDuplicateRuntimeLibraryNames()
|
||||
{
|
||||
// Arrange
|
||||
var upperCaseLibrary = "Microsoft.AspNetCore.Mvc";
|
||||
var mixedCaseLibrary = "microsoft.aspNetCore.mvc";
|
||||
|
||||
var dependencyContext = GetDependencyContext(new[]
|
||||
{
|
||||
GetLibrary(mixedCaseLibrary),
|
||||
GetLibrary(upperCaseLibrary),
|
||||
});
|
||||
|
||||
// Act
|
||||
var exception = Assert.Throws<InvalidOperationException>(() => ApplicationAssembliesProvider.GetCandidateLibraries(dependencyContext));
|
||||
|
||||
// Assert
|
||||
Assert.Equal($"A duplicate entry for library reference {upperCaseLibrary} was found. Please check that all package references in all projects use the same casing for the same package references.", exception.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetCandidateLibraries_IgnoresMvcAssemblies()
|
||||
{
|
||||
// Arrange
|
||||
var expected = GetLibrary("SomeRandomAssembly", "Microsoft.AspNetCore.Mvc.Abstractions");
|
||||
var dependencyContext = GetDependencyContext(new[]
|
||||
{
|
||||
GetLibrary("Microsoft.AspNetCore.Mvc.Core"),
|
||||
GetLibrary("Microsoft.AspNetCore.Mvc"),
|
||||
GetLibrary("Microsoft.AspNetCore.Mvc.Abstractions"),
|
||||
expected,
|
||||
});
|
||||
|
||||
// Act
|
||||
var candidates = ApplicationAssembliesProvider.GetCandidateLibraries(dependencyContext);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(new[] { expected }, candidates);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetCandidateLibraries_DoesNotThrow_IfLibraryDoesNotHaveRuntimeComponent()
|
||||
{
|
||||
// Arrange
|
||||
var expected = GetLibrary("MyApplication", "Microsoft.AspNetCore.Server.Kestrel", "Microsoft.AspNetCore.Mvc");
|
||||
var dependencyContext = GetDependencyContext(new[]
|
||||
{
|
||||
expected,
|
||||
GetLibrary("Microsoft.AspNetCore.Server.Kestrel", "Libuv"),
|
||||
GetLibrary("Microsoft.AspNetCore.Mvc"),
|
||||
});
|
||||
|
||||
// Act
|
||||
var candidates = ApplicationAssembliesProvider.GetCandidateLibraries(dependencyContext).ToList();
|
||||
|
||||
// Assert
|
||||
Assert.Equal(new[] { expected }, candidates);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetCandidateLibraries_ReturnsLibrariesReferencingAnyMvcAssembly()
|
||||
{
|
||||
// Arrange
|
||||
var dependencyContext = GetDependencyContext(new[]
|
||||
{
|
||||
GetLibrary("Foo", "Microsoft.AspNetCore.Mvc.Core"),
|
||||
GetLibrary("Bar", "Microsoft.AspNetCore.Mvc"),
|
||||
GetLibrary("Qux", "Not.Mvc.Assembly", "Unofficial.Microsoft.AspNetCore.Mvc"),
|
||||
GetLibrary("Baz", "Microsoft.AspNetCore.Mvc.Abstractions"),
|
||||
GetLibrary("Microsoft.AspNetCore.Mvc.Core"),
|
||||
GetLibrary("Microsoft.AspNetCore.Mvc"),
|
||||
GetLibrary("Not.Mvc.Assembly"),
|
||||
GetLibrary("Unofficial.Microsoft.AspNetCore.Mvc"),
|
||||
GetLibrary("Microsoft.AspNetCore.Mvc.Abstractions"),
|
||||
});
|
||||
|
||||
// Act
|
||||
var candidates = ApplicationAssembliesProvider.GetCandidateLibraries(dependencyContext);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(new[] { "Foo", "Bar", "Baz" }, candidates.Select(a => a.Name));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetCandidateLibraries_LibraryNameComparisonsAreCaseInsensitive()
|
||||
{
|
||||
// Arrange
|
||||
var dependencyContext = GetDependencyContext(new[]
|
||||
{
|
||||
GetLibrary("Foo", "MICROSOFT.ASPNETCORE.MVC.CORE"),
|
||||
GetLibrary("Bar", "microsoft.aspnetcore.mvc"),
|
||||
GetLibrary("Qux", "Not.Mvc.Assembly", "Unofficial.Microsoft.AspNetCore.Mvc"),
|
||||
GetLibrary("Baz", "mIcRoSoFt.AsPnEtCoRe.MvC.aBsTrAcTiOnS"),
|
||||
GetLibrary("Microsoft.AspNetCore.Mvc.Core"),
|
||||
GetLibrary("LibraryA", "LIBRARYB"),
|
||||
GetLibrary("LibraryB", "microsoft.aspnetcore.mvc"),
|
||||
GetLibrary("Microsoft.AspNetCore.Mvc"),
|
||||
GetLibrary("Not.Mvc.Assembly"),
|
||||
GetLibrary("Unofficial.Microsoft.AspNetCore.Mvc"),
|
||||
GetLibrary("Microsoft.AspNetCore.Mvc.Abstractions"),
|
||||
});
|
||||
|
||||
// Act
|
||||
var candidates = ApplicationAssembliesProvider.GetCandidateLibraries(dependencyContext);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(new[] { "Foo", "Bar", "Baz", "LibraryA", "LibraryB" }, candidates.Select(a => a.Name));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetCandidateLibraries_ReturnsLibrariesWithTransitiveReferencesToAnyMvcAssembly()
|
||||
{
|
||||
// Arrange
|
||||
var expectedLibraries = new[] { "Foo", "Bar", "Baz", "LibraryA", "LibraryB", "LibraryC", "LibraryE", "LibraryG", "LibraryH" };
|
||||
|
||||
var dependencyContext = GetDependencyContext(new[]
|
||||
{
|
||||
GetLibrary("Foo", "Bar"),
|
||||
GetLibrary("Bar", "Microsoft.AspNetCore.Mvc"),
|
||||
GetLibrary("Qux", "Not.Mvc.Assembly", "Unofficial.Microsoft.AspNetCore.Mvc"),
|
||||
GetLibrary("Baz", "Microsoft.AspNetCore.Mvc.Abstractions"),
|
||||
GetLibrary("Microsoft.AspNetCore.Mvc"),
|
||||
GetLibrary("Not.Mvc.Assembly"),
|
||||
GetLibrary("Microsoft.AspNetCore.Mvc.Abstractions"),
|
||||
GetLibrary("Unofficial.Microsoft.AspNetCore.Mvc"),
|
||||
GetLibrary("LibraryA", "LibraryB"),
|
||||
GetLibrary("LibraryB","LibraryC"),
|
||||
GetLibrary("LibraryC", "LibraryD", "Microsoft.AspNetCore.Mvc.Abstractions"),
|
||||
GetLibrary("LibraryD"),
|
||||
GetLibrary("LibraryE","LibraryF","LibraryG"),
|
||||
GetLibrary("LibraryF"),
|
||||
GetLibrary("LibraryG", "LibraryH"),
|
||||
GetLibrary("LibraryH", "LibraryI", "Microsoft.AspNetCore.Mvc"),
|
||||
GetLibrary("LibraryI"),
|
||||
});
|
||||
|
||||
// Act
|
||||
var candidates = ApplicationAssembliesProvider.GetCandidateLibraries(dependencyContext);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expectedLibraries, candidates.Select(a => a.Name));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetCandidateLibraries_SkipsMvcAssemblies()
|
||||
{
|
||||
// Arrange
|
||||
var dependencyContext = GetDependencyContext(new[]
|
||||
{
|
||||
GetLibrary("MvcSandbox", "Microsoft.AspNetCore.Mvc.Core", "Microsoft.AspNetCore.Mvc"),
|
||||
GetLibrary("Microsoft.AspNetCore.Mvc.Core", "Microsoft.AspNetCore.HttpAbstractions"),
|
||||
GetLibrary("Microsoft.AspNetCore.HttpAbstractions"),
|
||||
GetLibrary("Microsoft.AspNetCore.Mvc", "Microsoft.AspNetCore.Mvc.Abstractions", "Microsoft.AspNetCore.Mvc.Core"),
|
||||
GetLibrary("Microsoft.AspNetCore.Mvc.Abstractions"),
|
||||
GetLibrary("Microsoft.AspNetCore.Mvc.TagHelpers", "Microsoft.AspNetCore.Mvc.Razor"),
|
||||
GetLibrary("Microsoft.AspNetCore.Mvc.Razor"),
|
||||
GetLibrary("ControllersAssembly", "Microsoft.AspNetCore.Mvc"),
|
||||
});
|
||||
|
||||
// Act
|
||||
var candidates = ApplicationAssembliesProvider.GetCandidateLibraries(dependencyContext);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(new[] { "MvcSandbox", "ControllersAssembly" }, candidates.Select(a => a.Name));
|
||||
}
|
||||
|
||||
// This test verifies DefaultAssemblyPartDiscoveryProvider.ReferenceAssemblies reflects the actual loadable assemblies
|
||||
// of the libraries that Microsoft.AspNetCore.Mvc depends on.
|
||||
// If we add or remove dependencies, this test should be changed together.
|
||||
[Fact]
|
||||
public void ReferenceAssemblies_ReturnsLoadableReferenceAssemblies()
|
||||
{
|
||||
// Arrange
|
||||
var excludeAssemblies = new string[]
|
||||
{
|
||||
"Microsoft.AspNetCore.Mvc.Core.Test",
|
||||
"Microsoft.AspNetCore.Mvc.TestCommon",
|
||||
"Microsoft.AspNetCore.Mvc.TestDiagnosticListener",
|
||||
"Microsoft.AspNetCore.Mvc.WebApiCompatShim",
|
||||
};
|
||||
|
||||
var additionalAssemblies = new[]
|
||||
{
|
||||
// The following assemblies are not reachable from Microsoft.AspNetCore.Mvc
|
||||
"Microsoft.AspNetCore.Mvc.Formatters.Xml",
|
||||
};
|
||||
|
||||
var dependencyContextLibraries = DependencyContext.Load(ThisAssembly)
|
||||
.CompileLibraries
|
||||
.Where(r => r.Name.StartsWith("Microsoft.AspNetCore.Mvc", StringComparison.OrdinalIgnoreCase) &&
|
||||
!excludeAssemblies.Contains(r.Name, StringComparer.OrdinalIgnoreCase))
|
||||
.Select(r => r.Name);
|
||||
|
||||
var expected = dependencyContextLibraries
|
||||
.Concat(additionalAssemblies)
|
||||
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||
.OrderBy(p => p, StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
// Act
|
||||
var referenceAssemblies = ApplicationAssembliesProvider
|
||||
.ReferenceAssemblies
|
||||
.OrderBy(p => p, StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expected, referenceAssemblies, StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
private class TestApplicationAssembliesProvider : ApplicationAssembliesProvider
|
||||
{
|
||||
public DependencyContext DependencyContext { get; set; }
|
||||
|
||||
public Func<Assembly, IReadOnlyList<Assembly>> GetRelatedAssembliesDelegate { get; set; } = (assembly) => Array.Empty<Assembly>();
|
||||
|
||||
protected override DependencyContext LoadDependencyContext(Assembly assembly) => DependencyContext;
|
||||
|
||||
protected override IReadOnlyList<Assembly> GetRelatedAssemblies(Assembly assembly) => GetRelatedAssembliesDelegate(assembly);
|
||||
|
||||
protected override IEnumerable<Assembly> GetLibraryAssemblies(DependencyContext dependencyContext, RuntimeLibrary runtimeLibrary)
|
||||
{
|
||||
var assemblyName = new AssemblyName(runtimeLibrary.Name);
|
||||
yield return Assembly.Load(assemblyName);
|
||||
}
|
||||
}
|
||||
|
||||
private static DependencyContext GetDependencyContext(RuntimeLibrary[] libraries)
|
||||
{
|
||||
var dependencyContext = new DependencyContext(
|
||||
new TargetInfo("framework", "runtime", "signature", isPortable: true),
|
||||
CompilationOptions.Default,
|
||||
new CompilationLibrary[0],
|
||||
libraries,
|
||||
Enumerable.Empty<RuntimeFallbacks>());
|
||||
return dependencyContext;
|
||||
}
|
||||
|
||||
private static RuntimeLibrary GetLibrary(string name, params string[] dependencyNames)
|
||||
{
|
||||
var dependencies = dependencyNames?.Select(d => new Dependency(d, "42.0.0")) ?? new Dependency[0];
|
||||
|
||||
return new RuntimeLibrary(
|
||||
"package",
|
||||
name,
|
||||
"23.0.0",
|
||||
"hash",
|
||||
new RuntimeAssetGroup[0],
|
||||
new RuntimeAssetGroup[0],
|
||||
new ResourceAssembly[0],
|
||||
dependencies: dependencies.ToArray(),
|
||||
serviceable: true);
|
||||
}
|
||||
|
||||
private class TestAssembly : Assembly
|
||||
{
|
||||
public override string FullName => "TestRelatedAssembly";
|
||||
|
||||
public override bool IsDefined(Type attributeType, bool inherit)
|
||||
{
|
||||
return attributeType == typeof(RelatedAssemblyAttribute);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -24,7 +24,8 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationParts
|
|||
new ControllersFeatureProvider((f, v) => f.Values.Add($"ControllersFeatureProvider2{v}")));
|
||||
|
||||
var feature = new ControllersFeature();
|
||||
var expectedResults = new[] {
|
||||
var expectedResults = new[]
|
||||
{
|
||||
"ControllersFeatureProvider1ControllersPartA",
|
||||
"ControllersFeatureProvider1ControllersPartC",
|
||||
"ControllersFeatureProvider2ControllersPartA",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,104 @@
|
|||
// 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 System.IO;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.ApplicationParts
|
||||
{
|
||||
public class RelatedAssemblyPartTest
|
||||
{
|
||||
private static readonly string AssemblyDirectory = Path.GetTempPath().TrimEnd(Path.DirectorySeparatorChar);
|
||||
|
||||
[Fact]
|
||||
public void GetRelatedAssemblies_Noops_ForDynamicAssemblies()
|
||||
{
|
||||
// Arrange
|
||||
var name = new AssemblyName($"DynamicAssembly-{Guid.NewGuid()}");
|
||||
var assembly = AssemblyBuilder.DefineDynamicAssembly(name, AssemblyBuilderAccess.RunAndCollect);
|
||||
|
||||
// Act
|
||||
var result = RelatedAssemblyAttribute.GetRelatedAssemblies(assembly, throwOnError: true);
|
||||
|
||||
// Assert
|
||||
Assert.Empty(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetRelatedAssemblies_ThrowsIfRelatedAttributeReferencesSelf()
|
||||
{
|
||||
// Arrange
|
||||
var expected = "RelatedAssemblyAttribute specified on MyAssembly cannot be self referential.";
|
||||
var assembly = new TestAssembly { AttributeAssembly = "MyAssembly" };
|
||||
|
||||
// Act & Assert
|
||||
var ex = Assert.Throws<InvalidOperationException>(() => RelatedAssemblyAttribute.GetRelatedAssemblies(assembly, throwOnError: true));
|
||||
Assert.Equal(expected, ex.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetRelatedAssemblies_ThrowsIfAssemblyCannotBeFound()
|
||||
{
|
||||
// Arrange
|
||||
var expected = $"Related assembly 'DoesNotExist' specified by assembly 'MyAssembly' could not be found in the directory {AssemblyDirectory}. Related assemblies must be co-located with the specifying assemblies.";
|
||||
var assemblyPath = Path.Combine(AssemblyDirectory, "MyAssembly.dll");
|
||||
var assembly = new TestAssembly
|
||||
{
|
||||
AttributeAssembly = "DoesNotExist"
|
||||
};
|
||||
|
||||
// Act & Assert
|
||||
var ex = Assert.Throws<FileNotFoundException>(() => RelatedAssemblyAttribute.GetRelatedAssemblies(assembly, throwOnError: true));
|
||||
Assert.Equal(expected, ex.Message);
|
||||
Assert.Equal(Path.Combine(AssemblyDirectory, "DoesNotExist.dll"), ex.FileName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetRelatedAssemblies_LoadsRelatedAssembly()
|
||||
{
|
||||
// Arrange
|
||||
var destination = Path.Combine(AssemblyDirectory, "RelatedAssembly.dll");
|
||||
var assembly = new TestAssembly
|
||||
{
|
||||
AttributeAssembly = "RelatedAssembly",
|
||||
};
|
||||
var relatedAssembly = typeof(RelatedAssemblyPartTest).Assembly;
|
||||
|
||||
try
|
||||
{
|
||||
File.WriteAllBytes(destination, new byte[0]);
|
||||
var result = RelatedAssemblyAttribute.GetRelatedAssemblies(assembly, throwOnError: true, file =>
|
||||
{
|
||||
Assert.Equal(file, destination);
|
||||
return relatedAssembly;
|
||||
});
|
||||
Assert.Equal(new[] { relatedAssembly }, result);
|
||||
}
|
||||
finally
|
||||
{
|
||||
File.Delete(destination);
|
||||
}
|
||||
}
|
||||
|
||||
private class TestAssembly : Assembly
|
||||
{
|
||||
public override AssemblyName GetName()
|
||||
{
|
||||
return new AssemblyName("MyAssembly");
|
||||
}
|
||||
|
||||
public string AttributeAssembly { get; set; }
|
||||
|
||||
public override string CodeBase => Path.Combine(AssemblyDirectory, "MyAssembly.dll");
|
||||
|
||||
public override object[] GetCustomAttributes(Type attributeType, bool inherit)
|
||||
{
|
||||
var attribute = new RelatedAssemblyAttribute(AttributeAssembly);
|
||||
return new[] { attribute };
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,501 +0,0 @@
|
|||
// 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 System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using Microsoft.Extensions.DependencyModel;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.Internal
|
||||
{
|
||||
public class DefaultAssemblyPartDiscoveryProviderTests
|
||||
{
|
||||
private static readonly Assembly CurrentAssembly =
|
||||
typeof(DefaultAssemblyPartDiscoveryProviderTests).GetTypeInfo().Assembly;
|
||||
|
||||
[Fact]
|
||||
public void CandidateResolver_ThrowsIfDependencyContextContainsDuplicateRuntimeLibraryNames()
|
||||
{
|
||||
// Arrange
|
||||
var upperCaseLibrary = "Microsoft.AspNetCore.Mvc";
|
||||
var mixedCaseLibrary = "microsoft.aspNetCore.mvc";
|
||||
|
||||
var dependencyContext = new DependencyContext(
|
||||
new TargetInfo("framework", "runtime", "signature", isPortable: true),
|
||||
CompilationOptions.Default,
|
||||
new CompilationLibrary[0],
|
||||
new[]
|
||||
{
|
||||
GetLibrary(mixedCaseLibrary),
|
||||
GetLibrary(upperCaseLibrary),
|
||||
},
|
||||
Enumerable.Empty<RuntimeFallbacks>());
|
||||
|
||||
// Act
|
||||
var exception = Assert.Throws<InvalidOperationException>(() => DefaultAssemblyPartDiscoveryProvider.GetCandidateLibraries(dependencyContext));
|
||||
|
||||
// Assert
|
||||
Assert.Equal($"A duplicate entry for library reference {upperCaseLibrary} was found. Please check that all package references in all projects use the same casing for the same package references.", exception.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetCandidateLibraries_IgnoresMvcAssemblies()
|
||||
{
|
||||
// Arrange
|
||||
var expected = GetLibrary("SomeRandomAssembly", "Microsoft.AspNetCore.Mvc.Abstractions");
|
||||
var dependencyContext = new DependencyContext(
|
||||
new TargetInfo("framework", "runtime", "signature", isPortable: true),
|
||||
CompilationOptions.Default,
|
||||
new CompilationLibrary[0],
|
||||
new[]
|
||||
{
|
||||
GetLibrary("Microsoft.AspNetCore.Mvc.Core"),
|
||||
GetLibrary("Microsoft.AspNetCore.Mvc"),
|
||||
GetLibrary("Microsoft.AspNetCore.Mvc.Abstractions"),
|
||||
expected,
|
||||
},
|
||||
Enumerable.Empty<RuntimeFallbacks>());
|
||||
|
||||
// Act
|
||||
var candidates = DefaultAssemblyPartDiscoveryProvider.GetCandidateLibraries(dependencyContext);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(new[] { expected }, candidates);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(ResolveAdditionalReferencesData))]
|
||||
public void ResolveAdditionalReferences_DiscoversAdditionalReferences(ResolveAdditionalReferencesTestData testData)
|
||||
{
|
||||
// Arrange
|
||||
var resolver = testData.AssemblyResolver;
|
||||
DefaultAssemblyPartDiscoveryProvider.AssemblyResolver = path => resolver.ContainsKey(path);
|
||||
DefaultAssemblyPartDiscoveryProvider.AssemblyLoader = path => resolver.TryGetValue(path, out var result) ? result : null;
|
||||
|
||||
// Arrange & Act
|
||||
var (additionalReferences, entryAssemblyAdditionalReferences) =
|
||||
DefaultAssemblyPartDiscoveryProvider.ResolveAdditionalReferences(testData.EntryAssembly, testData.CandidateAssemblies);
|
||||
|
||||
var additionalRefs = additionalReferences.Select(a => a.FullName).OrderBy(id => id).ToArray();
|
||||
var entryAssemblyAdditionalRefs = entryAssemblyAdditionalReferences.Select(a => a.FullName).OrderBy(id => id).ToArray();
|
||||
|
||||
// Assert
|
||||
Assert.Equal(testData.ExpectedAdditionalReferences, additionalRefs);
|
||||
Assert.Equal(testData.ExpectedEntryAssemblyAdditionalReferences, entryAssemblyAdditionalRefs);
|
||||
}
|
||||
|
||||
public class ResolveAdditionalReferencesTestData
|
||||
{
|
||||
public Assembly EntryAssembly { get; set; }
|
||||
public SortedSet<Assembly> CandidateAssemblies { get; set; }
|
||||
public IDictionary<string, Assembly> AssemblyResolver { get; set; }
|
||||
public string[] ExpectedAdditionalReferences { get; set; }
|
||||
public string[] ExpectedEntryAssemblyAdditionalReferences { get; set; }
|
||||
}
|
||||
|
||||
public static TheoryData<ResolveAdditionalReferencesTestData> ResolveAdditionalReferencesData
|
||||
{
|
||||
get
|
||||
{
|
||||
var data = new TheoryData<ResolveAdditionalReferencesTestData>();
|
||||
var noCandidates = Array.Empty<Assembly>();
|
||||
var noResolvable = new Dictionary<string, Assembly>();
|
||||
var noAdditionalReferences = new string[] { };
|
||||
|
||||
// Single assembly app no precompilation
|
||||
var aAssembly = new DiscoveryTestAssembly("A");
|
||||
var singleAppNoPrecompilation = new ResolveAdditionalReferencesTestData
|
||||
{
|
||||
EntryAssembly = aAssembly,
|
||||
CandidateAssemblies = CreateCandidates(aAssembly),
|
||||
AssemblyResolver = noResolvable,
|
||||
ExpectedAdditionalReferences = Array.Empty<string>(),
|
||||
ExpectedEntryAssemblyAdditionalReferences = Array.Empty<string>()
|
||||
};
|
||||
data.Add(singleAppNoPrecompilation);
|
||||
|
||||
// Single assembly app with old precompilation not included in the graph
|
||||
var bAssembly = new DiscoveryTestAssembly("B");
|
||||
var (bPath, bPrecompiledViews) = CreateResolvablePrecompiledViewsAssembly("B");
|
||||
var singleAssemblyPrecompilationNotInGraph = new ResolveAdditionalReferencesTestData
|
||||
{
|
||||
EntryAssembly = bAssembly,
|
||||
CandidateAssemblies = CreateCandidates(bAssembly),
|
||||
AssemblyResolver = new Dictionary<string, Assembly> { [bPath] = bPrecompiledViews },
|
||||
ExpectedAdditionalReferences = noAdditionalReferences,
|
||||
ExpectedEntryAssemblyAdditionalReferences = new[] { bPrecompiledViews.FullName }
|
||||
};
|
||||
data.Add(singleAssemblyPrecompilationNotInGraph);
|
||||
|
||||
//// Single assembly app with new precompilation not included in the graph
|
||||
var cAssembly = new DiscoveryTestAssembly(
|
||||
"C",
|
||||
DiscoveryTestAssembly.DefaultLocationBase,
|
||||
new[] { ("C.PrecompiledViews.dll", true) });
|
||||
var (cPath, cPrecompiledViews) = CreateResolvablePrecompiledViewsAssembly("C");
|
||||
var singleAssemblyNewPrecompilationNotInGraph = new ResolveAdditionalReferencesTestData
|
||||
{
|
||||
EntryAssembly = cAssembly,
|
||||
CandidateAssemblies = CreateCandidates(cAssembly),
|
||||
AssemblyResolver = new Dictionary<string, Assembly> { [cPath] = cPrecompiledViews },
|
||||
ExpectedAdditionalReferences = noAdditionalReferences,
|
||||
ExpectedEntryAssemblyAdditionalReferences = new[] { cPrecompiledViews.FullName }
|
||||
};
|
||||
data.Add(singleAssemblyNewPrecompilationNotInGraph);
|
||||
|
||||
//// Single assembly app with new precompilation included in the graph
|
||||
var dAssembly = new DiscoveryTestAssembly(
|
||||
"D",
|
||||
DiscoveryTestAssembly.DefaultLocationBase,
|
||||
new[] { (Path.Combine(DiscoveryTestAssembly.DefaultLocationBase, "subfolder", "D.PrecompiledViews.dll"), true) });
|
||||
var (dPath, dPrecompiledViews) = CreateResolvablePrecompiledViewsAssembly("D");
|
||||
var singleAssemblyNewPrecompilationInGraph = new ResolveAdditionalReferencesTestData
|
||||
{
|
||||
EntryAssembly = dAssembly,
|
||||
CandidateAssemblies = CreateCandidates(dAssembly, dPrecompiledViews),
|
||||
AssemblyResolver = new Dictionary<string, Assembly> { [dPath] = dPrecompiledViews },
|
||||
ExpectedAdditionalReferences = noAdditionalReferences,
|
||||
ExpectedEntryAssemblyAdditionalReferences = new[] { dPrecompiledViews.FullName }
|
||||
};
|
||||
data.Add(singleAssemblyNewPrecompilationInGraph);
|
||||
|
||||
//// Single assembly app with new precompilation included in the graph optional part
|
||||
var hAssembly = new DiscoveryTestAssembly(
|
||||
"h",
|
||||
DiscoveryTestAssembly.DefaultLocationBase,
|
||||
new[] { ("H.PrecompiledViews.dll", false) });
|
||||
var (hPath, hPrecompiledViews) = CreateResolvablePrecompiledViewsAssembly("H");
|
||||
var singleAssemblyNewPrecompilationInGraphOptionalDependency = new ResolveAdditionalReferencesTestData
|
||||
{
|
||||
EntryAssembly = hAssembly,
|
||||
CandidateAssemblies = CreateCandidates(hAssembly, hPrecompiledViews),
|
||||
AssemblyResolver = new Dictionary<string, Assembly> { [hPath] = hPrecompiledViews },
|
||||
ExpectedAdditionalReferences = noAdditionalReferences,
|
||||
ExpectedEntryAssemblyAdditionalReferences = noAdditionalReferences
|
||||
};
|
||||
data.Add(singleAssemblyNewPrecompilationInGraphOptionalDependency);
|
||||
|
||||
//// Entry assembly with two dependencies app with new precompilation included in the graph
|
||||
var eAssembly = new DiscoveryTestAssembly(
|
||||
"E",
|
||||
DiscoveryTestAssembly.DefaultLocationBase,
|
||||
new[] { ("E.PrecompiledViews.dll", true) });
|
||||
var (ePath, ePrecompiledViews) = CreateResolvablePrecompiledViewsAssembly("E");
|
||||
|
||||
var fAssembly = new DiscoveryTestAssembly(
|
||||
"F",
|
||||
DiscoveryTestAssembly.DefaultLocationBase,
|
||||
new[] { ("F.PrecompiledViews.dll", true) });
|
||||
var (fPath, fPrecompiledViews) = CreateResolvablePrecompiledViewsAssembly("F");
|
||||
|
||||
var gAssembly = new DiscoveryTestAssembly(
|
||||
"G",
|
||||
DiscoveryTestAssembly.DefaultLocationBase,
|
||||
new[] { (Path.Combine(DiscoveryTestAssembly.DefaultLocationBase, "subfolder", "G.PrecompiledViews.dll"), true) });
|
||||
|
||||
var (gPath, gPrecompiledViews) = CreateResolvablePrecompiledViewsAssembly("G");
|
||||
var multipleAssembliesNewPrecompilationInGraph = new ResolveAdditionalReferencesTestData
|
||||
{
|
||||
EntryAssembly = gAssembly,
|
||||
CandidateAssemblies = CreateCandidates(
|
||||
fAssembly,
|
||||
fPrecompiledViews,
|
||||
gAssembly,
|
||||
gPrecompiledViews,
|
||||
eAssembly,
|
||||
ePrecompiledViews),
|
||||
AssemblyResolver = new Dictionary<string, Assembly> {
|
||||
[ePath] = ePrecompiledViews,
|
||||
[fPath] = fPrecompiledViews,
|
||||
[gPath] = gPrecompiledViews
|
||||
},
|
||||
ExpectedAdditionalReferences = new[] { ePrecompiledViews.FullName, fPrecompiledViews.FullName },
|
||||
ExpectedEntryAssemblyAdditionalReferences = new[] {
|
||||
gPrecompiledViews.FullName
|
||||
}
|
||||
};
|
||||
data.Add(multipleAssembliesNewPrecompilationInGraph);
|
||||
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
private static SortedSet<Assembly> CreateCandidates(params Assembly[] assemblies) =>
|
||||
new SortedSet<Assembly>(assemblies, DefaultAssemblyPartDiscoveryProvider.FullNameAssemblyComparer.Instance);
|
||||
|
||||
private static (string, Assembly) CreateResolvablePrecompiledViewsAssembly(string name, string path = null) =>
|
||||
(path ?? Path.Combine(DiscoveryTestAssembly.DefaultLocationBase, $"{name}.PrecompiledViews.dll"),
|
||||
new DiscoveryTestAssembly($"{name}.PrecompiledViews"));
|
||||
|
||||
[Fact]
|
||||
public void GetCandidateLibraries_DoesNotThrow_IfLibraryDoesNotHaveRuntimeComponent()
|
||||
{
|
||||
// Arrange
|
||||
var expected = GetLibrary("MyApplication", "Microsoft.AspNetCore.Server.Kestrel", "Microsoft.AspNetCore.Mvc");
|
||||
var deps = new DependencyContext(
|
||||
new TargetInfo("netcoreapp2.0", "rurntime", "signature", isPortable: true),
|
||||
CompilationOptions.Default,
|
||||
Enumerable.Empty<CompilationLibrary>(),
|
||||
new[]
|
||||
{
|
||||
expected,
|
||||
GetLibrary("Microsoft.AspNetCore.Server.Kestrel", "Libuv"),
|
||||
GetLibrary("Microsoft.AspNetCore.Mvc"),
|
||||
},
|
||||
Enumerable.Empty<RuntimeFallbacks>());
|
||||
|
||||
// Act
|
||||
var candidates = DefaultAssemblyPartDiscoveryProvider.GetCandidateLibraries(deps).ToList();
|
||||
|
||||
// Assert
|
||||
Assert.Equal(new[] { expected }, candidates);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CandidateAssemblies_ReturnsEntryAssemblyIfDependencyContextIsNull()
|
||||
{
|
||||
// Arrange & Act
|
||||
var candidates = DefaultAssemblyPartDiscoveryProvider.GetCandidateAssemblies(CurrentAssembly, dependencyContext: null);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(new[] { CurrentAssembly }, candidates);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetCandidateLibraries_ReturnsLibrariesReferencingAnyMvcAssembly()
|
||||
{
|
||||
// Arrange
|
||||
var dependencyContext = new DependencyContext(
|
||||
new TargetInfo("framework", "runtime", "signature", isPortable: true),
|
||||
CompilationOptions.Default,
|
||||
new CompilationLibrary[0],
|
||||
new[]
|
||||
{
|
||||
GetLibrary("Foo", "Microsoft.AspNetCore.Mvc.Core"),
|
||||
GetLibrary("Bar", "Microsoft.AspNetCore.Mvc"),
|
||||
GetLibrary("Qux", "Not.Mvc.Assembly", "Unofficial.Microsoft.AspNetCore.Mvc"),
|
||||
GetLibrary("Baz", "Microsoft.AspNetCore.Mvc.Abstractions"),
|
||||
GetLibrary("Microsoft.AspNetCore.Mvc.Core"),
|
||||
GetLibrary("Microsoft.AspNetCore.Mvc"),
|
||||
GetLibrary("Not.Mvc.Assembly"),
|
||||
GetLibrary("Unofficial.Microsoft.AspNetCore.Mvc"),
|
||||
GetLibrary("Microsoft.AspNetCore.Mvc.Abstractions"),
|
||||
|
||||
},
|
||||
Enumerable.Empty<RuntimeFallbacks>());
|
||||
|
||||
// Act
|
||||
var candidates = DefaultAssemblyPartDiscoveryProvider.GetCandidateLibraries(dependencyContext);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(new[] { "Foo", "Bar", "Baz" }, candidates.Select(a => a.Name));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetCandidateLibraries_LibraryNameComparisonsAreCaseInsensitive()
|
||||
{
|
||||
// Arrange
|
||||
var dependencyContext = new DependencyContext(
|
||||
new TargetInfo("framework", "runtime", "signature", isPortable: true),
|
||||
CompilationOptions.Default,
|
||||
new CompilationLibrary[0],
|
||||
new[]
|
||||
{
|
||||
GetLibrary("Foo", "MICROSOFT.ASPNETCORE.MVC.CORE"),
|
||||
GetLibrary("Bar", "microsoft.aspnetcore.mvc"),
|
||||
GetLibrary("Qux", "Not.Mvc.Assembly", "Unofficial.Microsoft.AspNetCore.Mvc"),
|
||||
GetLibrary("Baz", "mIcRoSoFt.AsPnEtCoRe.MvC.aBsTrAcTiOnS"),
|
||||
GetLibrary("Microsoft.AspNetCore.Mvc.Core"),
|
||||
GetLibrary("LibraryA", "LIBRARYB"),
|
||||
GetLibrary("LibraryB", "microsoft.aspnetcore.mvc"),
|
||||
GetLibrary("Microsoft.AspNetCore.Mvc"),
|
||||
GetLibrary("Not.Mvc.Assembly"),
|
||||
GetLibrary("Unofficial.Microsoft.AspNetCore.Mvc"),
|
||||
GetLibrary("Microsoft.AspNetCore.Mvc.Abstractions"),
|
||||
},
|
||||
Enumerable.Empty<RuntimeFallbacks>());
|
||||
|
||||
// Act
|
||||
var candidates = DefaultAssemblyPartDiscoveryProvider.GetCandidateLibraries(dependencyContext);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(new[] { "Foo", "Bar", "Baz", "LibraryA", "LibraryB" }, candidates.Select(a => a.Name));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetCandidateLibraries_ReturnsLibrariesWithTransitiveReferencesToAnyMvcAssembly()
|
||||
{
|
||||
// Arrange
|
||||
var expectedLibraries = new[] { "Foo", "Bar", "Baz", "LibraryA", "LibraryB", "LibraryC", "LibraryE", "LibraryG", "LibraryH" };
|
||||
|
||||
var dependencyContext = new DependencyContext(
|
||||
new TargetInfo("framework", "runtime", "signature", isPortable: true),
|
||||
CompilationOptions.Default,
|
||||
new CompilationLibrary[0],
|
||||
new[]
|
||||
{
|
||||
GetLibrary("Foo", "Bar"),
|
||||
GetLibrary("Bar", "Microsoft.AspNetCore.Mvc"),
|
||||
GetLibrary("Qux", "Not.Mvc.Assembly", "Unofficial.Microsoft.AspNetCore.Mvc"),
|
||||
GetLibrary("Baz", "Microsoft.AspNetCore.Mvc.Abstractions"),
|
||||
GetLibrary("Microsoft.AspNetCore.Mvc"),
|
||||
GetLibrary("Not.Mvc.Assembly"),
|
||||
GetLibrary("Microsoft.AspNetCore.Mvc.Abstractions"),
|
||||
GetLibrary("Unofficial.Microsoft.AspNetCore.Mvc"),
|
||||
GetLibrary("LibraryA", "LibraryB"),
|
||||
GetLibrary("LibraryB","LibraryC"),
|
||||
GetLibrary("LibraryC", "LibraryD", "Microsoft.AspNetCore.Mvc.Abstractions"),
|
||||
GetLibrary("LibraryD"),
|
||||
GetLibrary("LibraryE","LibraryF","LibraryG"),
|
||||
GetLibrary("LibraryF"),
|
||||
GetLibrary("LibraryG", "LibraryH"),
|
||||
GetLibrary("LibraryH", "LibraryI", "Microsoft.AspNetCore.Mvc"),
|
||||
GetLibrary("LibraryI")
|
||||
},
|
||||
Enumerable.Empty<RuntimeFallbacks>());
|
||||
|
||||
// Act
|
||||
var candidates = DefaultAssemblyPartDiscoveryProvider.GetCandidateLibraries(dependencyContext);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expectedLibraries, candidates.Select(a => a.Name));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetCandidateLibraries_SkipsMvcAssemblies()
|
||||
{
|
||||
// Arrange
|
||||
var dependencyContext = new DependencyContext(
|
||||
new TargetInfo("framework", "runtime", "signature", isPortable: true),
|
||||
CompilationOptions.Default,
|
||||
new CompilationLibrary[0],
|
||||
new[]
|
||||
{
|
||||
GetLibrary("MvcSandbox", "Microsoft.AspNetCore.Mvc.Core", "Microsoft.AspNetCore.Mvc"),
|
||||
GetLibrary("Microsoft.AspNetCore.Mvc.Core", "Microsoft.AspNetCore.HttpAbstractions"),
|
||||
GetLibrary("Microsoft.AspNetCore.HttpAbstractions"),
|
||||
GetLibrary("Microsoft.AspNetCore.Mvc", "Microsoft.AspNetCore.Mvc.Abstractions", "Microsoft.AspNetCore.Mvc.Core"),
|
||||
GetLibrary("Microsoft.AspNetCore.Mvc.Abstractions"),
|
||||
GetLibrary("Microsoft.AspNetCore.Mvc.TagHelpers", "Microsoft.AspNetCore.Mvc.Razor"),
|
||||
GetLibrary("Microsoft.AspNetCore.Mvc.Razor"),
|
||||
GetLibrary("ControllersAssembly", "Microsoft.AspNetCore.Mvc"),
|
||||
},
|
||||
Enumerable.Empty<RuntimeFallbacks>());
|
||||
|
||||
// Act
|
||||
var candidates = DefaultAssemblyPartDiscoveryProvider.GetCandidateLibraries(dependencyContext);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(new[] { "MvcSandbox", "ControllersAssembly" }, candidates.Select(a => a.Name));
|
||||
}
|
||||
|
||||
// This test verifies DefaultAssemblyPartDiscoveryProvider.ReferenceAssemblies reflects the actual loadable assemblies
|
||||
// of the libraries that Microsoft.AspNetCore.Mvc depends on.
|
||||
// If we add or remove dependencies, this test should be changed together.
|
||||
[Fact]
|
||||
public void ReferenceAssemblies_ReturnsLoadableReferenceAssemblies()
|
||||
{
|
||||
// Arrange
|
||||
var excludeAssemblies = new string[]
|
||||
{
|
||||
"Microsoft.AspNetCore.Mvc.Core.Test",
|
||||
"Microsoft.AspNetCore.Mvc.TestCommon",
|
||||
"Microsoft.AspNetCore.Mvc.TestDiagnosticListener",
|
||||
"Microsoft.AspNetCore.Mvc.WebApiCompatShim",
|
||||
};
|
||||
|
||||
var additionalAssemblies = new[]
|
||||
{
|
||||
// The following assemblies are not reachable from Microsoft.AspNetCore.Mvc
|
||||
"Microsoft.AspNetCore.Mvc.Formatters.Xml",
|
||||
"Microsoft.AspnetCore.All",
|
||||
};
|
||||
|
||||
var dependencyContextLibraries = DependencyContext.Load(CurrentAssembly)
|
||||
.RuntimeLibraries
|
||||
.Where(r => r.Name.StartsWith("Microsoft.AspNetCore.Mvc", StringComparison.OrdinalIgnoreCase) &&
|
||||
!excludeAssemblies.Contains(r.Name, StringComparer.OrdinalIgnoreCase))
|
||||
.Select(r => r.Name);
|
||||
|
||||
var expected = dependencyContextLibraries
|
||||
.Concat(additionalAssemblies)
|
||||
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||
.OrderBy(p => p, StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
// Act
|
||||
var referenceAssemblies = DefaultAssemblyPartDiscoveryProvider
|
||||
.ReferenceAssemblies
|
||||
.OrderBy(p => p, StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expected, referenceAssemblies, StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
private static RuntimeLibrary GetLibrary(string name, params string[] dependencyNames)
|
||||
{
|
||||
var dependencies = dependencyNames?.Select(d => new Dependency(d, "42.0.0")) ?? new Dependency[0];
|
||||
|
||||
return new RuntimeLibrary(
|
||||
"package",
|
||||
name,
|
||||
"23.0.0",
|
||||
"hash",
|
||||
new RuntimeAssetGroup[0],
|
||||
new RuntimeAssetGroup[0],
|
||||
new ResourceAssembly[0],
|
||||
dependencies: dependencies.ToArray(),
|
||||
serviceable: true);
|
||||
}
|
||||
|
||||
private class DiscoveryTestAssembly : Assembly
|
||||
{
|
||||
private readonly string _fullName;
|
||||
private readonly string _location;
|
||||
private readonly Attribute[] _additionalDependencies;
|
||||
public static readonly string DefaultLocationBase =
|
||||
RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? @"c:\app\" : "/app/";
|
||||
|
||||
public DiscoveryTestAssembly(string fullName, string location = null)
|
||||
: this(
|
||||
fullName,
|
||||
location ?? Path.Combine(DefaultLocationBase, new AssemblyName(fullName).Name + ".dll"),
|
||||
Array.Empty<(string, bool)>())
|
||||
{ }
|
||||
|
||||
public DiscoveryTestAssembly(string fullName, string location, IEnumerable<(string, bool)> additionalDependencies)
|
||||
{
|
||||
_fullName = fullName;
|
||||
_location = location;
|
||||
_additionalDependencies = additionalDependencies
|
||||
.Select(ad => new AssemblyMetadataAttribute(
|
||||
"Microsoft.AspNetCore.Mvc.AdditionalReference",
|
||||
$"{ad.Item1},{ad.Item2}")).ToArray();
|
||||
}
|
||||
|
||||
public override string FullName => _fullName;
|
||||
|
||||
public override string Location => _location;
|
||||
|
||||
public override object[] GetCustomAttributes(bool inherit) => _additionalDependencies;
|
||||
|
||||
public override object[] GetCustomAttributes(Type attributeType, bool inherit)
|
||||
{
|
||||
var attributes = _additionalDependencies
|
||||
.Where(t => t.GetType().IsAssignableFrom(attributeType))
|
||||
.ToArray();
|
||||
|
||||
var result = Array.CreateInstance(attributeType, attributes.Length);
|
||||
attributes.CopyTo(result, 0);
|
||||
return (object[])result;
|
||||
}
|
||||
|
||||
public override AssemblyName GetName(bool copiedName) => new AssemblyName(FullName);
|
||||
|
||||
public override AssemblyName GetName() => new AssemblyName(FullName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -64,5 +64,18 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
|
|||
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||
Assert.Equal("Hello from runtime-compiled rzc view!", responseBody.Trim());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RzcViewsArePreferredToPrecompiledViews()
|
||||
{
|
||||
// Verifies that when two views have the same paths, the one compiled using rzc is preferred to the one from Precompilation.
|
||||
// Act
|
||||
var response = await Client.GetAsync("http://localhost/Common/View");
|
||||
var responseBody = await response.Content.ReadAsStringAsync();
|
||||
|
||||
// Assert
|
||||
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||
Assert.Equal("Hello from buildtime-compiled rzc view!", responseBody.Trim());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,131 @@
|
|||
// 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 System.Linq;
|
||||
using System.Reflection;
|
||||
using Microsoft.AspNetCore.Mvc.Razor.Compilation;
|
||||
using Microsoft.AspNetCore.Razor.Hosting;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.ApplicationParts
|
||||
{
|
||||
public class RazorCompiledItemFeatureProviderTest
|
||||
{
|
||||
[Fact]
|
||||
public void PopulateFeature_AddsItemsFromProviderTypes()
|
||||
{
|
||||
// Arrange
|
||||
var item1 = Mock.Of<RazorCompiledItem>(i => i.Identifier == "Item1" && i.Type == typeof(TestView));
|
||||
var item2 = Mock.Of<RazorCompiledItem>(i => i.Identifier == "Item2" && i.Type == typeof(TestPage));
|
||||
var part1 = new AssemblyPart(typeof(RazorCompiledItemFeatureProviderTest).Assembly);
|
||||
var part2 = new Mock<ApplicationPart>();
|
||||
part2
|
||||
.As<IRazorCompiledItemProvider>()
|
||||
.Setup(p => p.CompiledItems).Returns(new[] { item1, item2, });
|
||||
var featureProvider = new RazorCompiledItemFeatureProvider();
|
||||
var feature = new ViewsFeature();
|
||||
|
||||
// Act
|
||||
featureProvider.PopulateFeature(new[] { part1, part2.Object }, feature);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(new[] { item1, item2 }, feature.ViewDescriptors.Select(d => d.Item));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PopulateFeature_PopulatesRazorViewAttributeFromTypeAssembly()
|
||||
{
|
||||
// Arrange
|
||||
var item1 = Mock.Of<RazorCompiledItem>(i => i.Identifier == "Item1" && i.Type == typeof(TestView));
|
||||
var item2 = Mock.Of<RazorCompiledItem>(i => i.Identifier == "Item2" && i.Type == typeof(TestPage));
|
||||
|
||||
var attribute1 = new RazorViewAttribute("Item1", typeof(TestView));
|
||||
var attribute2 = new RazorViewAttribute("Item2", typeof(TestPage));
|
||||
|
||||
var assembly = new TestAssembly(new[] { attribute1, attribute2 });
|
||||
|
||||
var part1 = new AssemblyPart(assembly);
|
||||
var part2 = new Mock<ApplicationPart>();
|
||||
part2
|
||||
.As<IRazorCompiledItemProvider>()
|
||||
.Setup(p => p.CompiledItems).Returns(new[] { item1, item2, });
|
||||
var featureProvider = new RazorCompiledItemFeatureProvider();
|
||||
var feature = new ViewsFeature();
|
||||
|
||||
// Act
|
||||
featureProvider.PopulateFeature(new[] { part1, part2.Object }, feature);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(new[] { item1, item2 }, feature.ViewDescriptors.Select(d => d.Item));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PopulateFeature_AllowsDuplicateItemsFromMultipleParts()
|
||||
{
|
||||
// Arrange
|
||||
var item1 = Mock.Of<RazorCompiledItem>(i => i.Identifier == "Item" && i.Type == typeof(TestView));
|
||||
var item2 = Mock.Of<RazorCompiledItem>(i => i.Identifier == "Item" && i.Type == typeof(TestPage));
|
||||
var part1 = new Mock<ApplicationPart>();
|
||||
part1
|
||||
.As<IRazorCompiledItemProvider>()
|
||||
.Setup(p => p.CompiledItems).Returns(new[] { item1, });
|
||||
var part2 = new Mock<ApplicationPart>();
|
||||
part2
|
||||
.As<IRazorCompiledItemProvider>()
|
||||
.Setup(p => p.CompiledItems).Returns(new[] { item2, });
|
||||
var featureProvider = new RazorCompiledItemFeatureProvider();
|
||||
var feature = new ViewsFeature();
|
||||
|
||||
// Act
|
||||
featureProvider.PopulateFeature(new[] { part1.Object, part2.Object }, feature);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(new[] { item1, item2 }, feature.ViewDescriptors.Select(d => d.Item));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PopulateFeature_ThrowsIfTwoItemsFromSamePart_OnlyDifferInCase()
|
||||
{
|
||||
// Arrange
|
||||
var item1 = Mock.Of<RazorCompiledItem>(i => i.Identifier == "Item");
|
||||
var item2 = Mock.Of<RazorCompiledItem>(i => i.Identifier == "item");
|
||||
var expected = string.Join(
|
||||
Environment.NewLine,
|
||||
"The following precompiled view paths differ only in case, which is not supported:",
|
||||
"Item",
|
||||
"item");
|
||||
var part1 = new AssemblyPart(typeof(RazorCompiledItemFeatureProviderTest).Assembly);
|
||||
var part2 = new Mock<ApplicationPart>();
|
||||
part2
|
||||
.As<IRazorCompiledItemProvider>()
|
||||
.Setup(p => p.CompiledItems).Returns(new[] { item1, item2, });
|
||||
var featureProvider = new RazorCompiledItemFeatureProvider();
|
||||
var feature = new ViewsFeature();
|
||||
|
||||
// Act & Assert
|
||||
var ex = Assert.Throws<InvalidOperationException>(() => featureProvider.PopulateFeature(new[] { part1, part2.Object }, feature));
|
||||
Assert.Equal(expected, ex.Message);
|
||||
}
|
||||
|
||||
private class TestAssembly : Assembly
|
||||
{
|
||||
private readonly object[] _attributes;
|
||||
|
||||
public TestAssembly(object[] attributes)
|
||||
{
|
||||
_attributes = attributes;
|
||||
}
|
||||
|
||||
public override object[] GetCustomAttributes(Type attributeType, bool inherit)
|
||||
{
|
||||
return _attributes;
|
||||
}
|
||||
}
|
||||
|
||||
private class TestView { }
|
||||
|
||||
private class TestPage { }
|
||||
}
|
||||
}
|
||||
|
|
@ -6,27 +6,26 @@ using System.Collections.Generic;
|
|||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
using System.Text;
|
||||
using Microsoft.AspNetCore.Mvc.ApplicationParts;
|
||||
using Microsoft.AspNetCore.Razor.Hosting;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.Razor.Compilation
|
||||
{
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
public class ViewsFeatureProviderTest
|
||||
{
|
||||
[Fact]
|
||||
public void PopulateFeature_ReturnsEmptySequenceIfNoAssemblyPartHasViewAssembly()
|
||||
{
|
||||
// Arrange
|
||||
var partManager = new ApplicationPartManager();
|
||||
partManager.ApplicationParts.Add(new AssemblyPart(typeof(ViewsFeatureProviderTest).Assembly));
|
||||
partManager.FeatureProviders.Add(new ViewsFeatureProvider());
|
||||
var applicationPartManager = new ApplicationPartManager();
|
||||
applicationPartManager.ApplicationParts.Add(
|
||||
new AssemblyPart(typeof(ViewsFeatureProviderTest).GetTypeInfo().Assembly));
|
||||
applicationPartManager.FeatureProviders.Add(new ViewsFeatureProvider());
|
||||
var feature = new ViewsFeature();
|
||||
|
||||
// Act
|
||||
partManager.PopulateFeature(feature);
|
||||
applicationPartManager.PopulateFeature(feature);
|
||||
|
||||
// Assert
|
||||
Assert.Empty(feature.ViewDescriptors);
|
||||
|
|
@ -36,31 +35,9 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Compilation
|
|||
public void PopulateFeature_ReturnsViewsFromAllAvailableApplicationParts()
|
||||
{
|
||||
// Arrange
|
||||
var part1 = new AssemblyPart(typeof(object).Assembly);
|
||||
var part2 = new AssemblyPart(GetType().Assembly);
|
||||
|
||||
var items = new Dictionary<AssemblyPart, IReadOnlyList<RazorCompiledItem>>
|
||||
{
|
||||
{
|
||||
part1,
|
||||
new[]
|
||||
{
|
||||
new TestRazorCompiledItem(typeof(object), "mvc.1.0.view", "/Views/test/Index.cshtml", new object[]{ }),
|
||||
|
||||
// This one doesn't have a RazorViewAttribute
|
||||
new TestRazorCompiledItem(typeof(StringBuilder), "mvc.1.0.view", "/Views/test/About.cshtml", new object[]{ }),
|
||||
}
|
||||
},
|
||||
{
|
||||
part2,
|
||||
new[]
|
||||
{
|
||||
new TestRazorCompiledItem(typeof(string), "mvc.1.0.view", "/Areas/Admin/Views/Index.cshtml", new object[]{ }),
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
var attributes = new Dictionary<AssemblyPart, IEnumerable<RazorViewAttribute>>
|
||||
var part1 = new AssemblyPart(typeof(object).GetTypeInfo().Assembly);
|
||||
var part2 = new AssemblyPart(GetType().GetTypeInfo().Assembly);
|
||||
var featureProvider = new TestableViewsFeatureProvider(new Dictionary<AssemblyPart, IEnumerable<RazorViewAttribute>>
|
||||
{
|
||||
{
|
||||
part1,
|
||||
|
|
@ -74,105 +51,71 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Compilation
|
|||
new[]
|
||||
{
|
||||
new RazorViewAttribute("/Areas/Admin/Views/Index.cshtml", typeof(string)),
|
||||
|
||||
// This one doesn't have a RazorCompiledItem
|
||||
new RazorViewAttribute("/Areas/Admin/Views/About.cshtml", typeof(int)),
|
||||
}
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
var featureProvider = new TestableViewsFeatureProvider(items, attributes);
|
||||
var partManager = new ApplicationPartManager();
|
||||
partManager.ApplicationParts.Add(part1);
|
||||
partManager.ApplicationParts.Add(part2);
|
||||
partManager.FeatureProviders.Add(featureProvider);
|
||||
var applicationPartManager = new ApplicationPartManager();
|
||||
applicationPartManager.ApplicationParts.Add(part1);
|
||||
applicationPartManager.ApplicationParts.Add(part2);
|
||||
applicationPartManager.FeatureProviders.Add(featureProvider);
|
||||
var feature = new ViewsFeature();
|
||||
|
||||
// Act
|
||||
partManager.PopulateFeature(feature);
|
||||
applicationPartManager.PopulateFeature(feature);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(feature.ViewDescriptors.OrderBy(f => f.RelativePath, StringComparer.Ordinal),
|
||||
view =>
|
||||
{
|
||||
Assert.Empty(view.ExpirationTokens);
|
||||
Assert.True(view.IsPrecompiled);
|
||||
Assert.Null(view.Item);
|
||||
Assert.Equal("/Areas/Admin/Views/About.cshtml", view.RelativePath);
|
||||
Assert.Equal(typeof(int), view.Type);
|
||||
Assert.Equal("/Areas/Admin/Views/About.cshtml", view.ViewAttribute.Path);
|
||||
Assert.Equal(typeof(int), view.ViewAttribute.ViewType);
|
||||
},
|
||||
view =>
|
||||
{
|
||||
// This one doesn't have a RazorCompiledItem
|
||||
Assert.Empty(view.ExpirationTokens);
|
||||
Assert.True(view.IsPrecompiled);
|
||||
Assert.Equal("/Areas/Admin/Views/Index.cshtml", view.Item.Identifier);
|
||||
Assert.Equal("mvc.1.0.view", view.Item.Kind);
|
||||
Assert.Equal(typeof(string), view.Item.Type);
|
||||
Assert.Equal("/Areas/Admin/Views/Index.cshtml", view.RelativePath);
|
||||
Assert.Equal(typeof(string), view.Type);
|
||||
Assert.Equal("/Areas/Admin/Views/Index.cshtml", view.ViewAttribute.Path);
|
||||
Assert.Equal(typeof(string), view.ViewAttribute.ViewType);
|
||||
},
|
||||
view =>
|
||||
{
|
||||
// This one doesn't have a RazorViewAttribute
|
||||
Assert.Empty(view.ExpirationTokens);
|
||||
Assert.True(view.IsPrecompiled);
|
||||
Assert.Equal("/Views/test/About.cshtml", view.Item.Identifier);
|
||||
Assert.Equal("mvc.1.0.view", view.Item.Kind);
|
||||
Assert.Equal(typeof(StringBuilder), view.Item.Type);
|
||||
Assert.Equal("/Views/test/About.cshtml", view.RelativePath);
|
||||
Assert.Equal(typeof(StringBuilder), view.Type);
|
||||
Assert.Null(view.ViewAttribute);
|
||||
},
|
||||
view =>
|
||||
{
|
||||
Assert.Empty(view.ExpirationTokens);
|
||||
Assert.True(view.IsPrecompiled);
|
||||
Assert.Equal("/Views/test/Index.cshtml", view.Item.Identifier);
|
||||
Assert.Equal("mvc.1.0.view", view.Item.Kind);
|
||||
Assert.Equal(typeof(object), view.Item.Type);
|
||||
Assert.Equal("/Views/test/Index.cshtml", view.RelativePath);
|
||||
Assert.Equal(typeof(object), view.Type);
|
||||
Assert.Equal("/Views/test/Index.cshtml", view.ViewAttribute.Path);
|
||||
Assert.Equal(typeof(object), view.ViewAttribute.ViewType);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PopulateFeature_PrefersViewsFromPartsWithHigherPrecedence()
|
||||
public void PopulateFeature_ThrowsIfSingleAssemblyContainsMultipleAttributesWithTheSamePath()
|
||||
{
|
||||
// Arrange
|
||||
var part1 = new AssemblyPart(typeof(ViewsFeatureProvider).Assembly);
|
||||
var item1 = new TestRazorCompiledItem(typeof(StringBuilder), "mvc.1.0.view", "/Areas/Admin/Views/Shared/_Layout.cshtml", new object[] { });
|
||||
|
||||
var part2 = new AssemblyPart(GetType().Assembly);
|
||||
var item2 = new TestRazorCompiledItem(typeof(string), "mvc.1.0.view", "/Areas/Admin/Views/Shared/_Layout.cshtml", new object[] { });
|
||||
var item3 = new TestRazorCompiledItem(typeof(string), "mvc.1.0.view", "/Areas/Admin/Views/Shared/_Partial.cshtml", new object[] { });
|
||||
|
||||
var items = new Dictionary<AssemblyPart, IReadOnlyList<RazorCompiledItem>>
|
||||
var path1 = "/Views/test/Index.cshtml";
|
||||
var path2 = "/views/test/index.cshtml";
|
||||
var expected = string.Join(
|
||||
Environment.NewLine,
|
||||
"The following precompiled view paths differ only in case, which is not supported:",
|
||||
path1,
|
||||
path2);
|
||||
var part = new AssemblyPart(typeof(object).GetTypeInfo().Assembly);
|
||||
var featureProvider = new TestableViewsFeatureProvider(new Dictionary<AssemblyPart, IEnumerable<RazorViewAttribute>>
|
||||
{
|
||||
{ part1, new[] { item1 } },
|
||||
{ part2, new[] { item2, item3, } },
|
||||
};
|
||||
{
|
||||
part,
|
||||
new[]
|
||||
{
|
||||
new RazorViewAttribute(path1, typeof(object)),
|
||||
new RazorViewAttribute(path2, typeof(object)),
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
var featureProvider = new TestableViewsFeatureProvider(items, attributes: new Dictionary<AssemblyPart, IEnumerable<RazorViewAttribute>>());
|
||||
var partManager = new ApplicationPartManager();
|
||||
partManager.ApplicationParts.Add(part1);
|
||||
partManager.ApplicationParts.Add(part2);
|
||||
partManager.FeatureProviders.Add(featureProvider);
|
||||
var applicationPartManager = new ApplicationPartManager();
|
||||
applicationPartManager.ApplicationParts.Add(part);
|
||||
applicationPartManager.FeatureProviders.Add(featureProvider);
|
||||
var feature = new ViewsFeature();
|
||||
|
||||
// Act
|
||||
partManager.PopulateFeature(feature);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(feature.ViewDescriptors.OrderBy(f => f.RelativePath, StringComparer.Ordinal),
|
||||
view => Assert.Same(item1, view.Item),
|
||||
view => Assert.Same(item3, view.Item));
|
||||
// Act & Assert
|
||||
var ex = Assert.Throws<InvalidOperationException>(() => applicationPartManager.PopulateFeature(feature));
|
||||
Assert.Equal(expected, ex.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -180,168 +123,58 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Compilation
|
|||
{
|
||||
// Arrange
|
||||
var name = new AssemblyName($"DynamicAssembly-{Guid.NewGuid()}");
|
||||
var assembly = AssemblyBuilder.DefineDynamicAssembly(name, AssemblyBuilderAccess.RunAndCollect);
|
||||
var assembly = AssemblyBuilder.DefineDynamicAssembly(name,
|
||||
AssemblyBuilderAccess.RunAndCollect);
|
||||
|
||||
var partManager = new ApplicationPartManager();
|
||||
partManager.ApplicationParts.Add(new AssemblyPart(assembly));
|
||||
partManager.FeatureProviders.Add(new ViewsFeatureProvider());
|
||||
var applicationPartManager = new ApplicationPartManager();
|
||||
applicationPartManager.ApplicationParts.Add(new AssemblyPart(assembly));
|
||||
applicationPartManager.FeatureProviders.Add(new ViewsFeatureProvider());
|
||||
var feature = new ViewsFeature();
|
||||
|
||||
// Act
|
||||
partManager.PopulateFeature(feature);
|
||||
applicationPartManager.PopulateFeature(feature);
|
||||
|
||||
// Assert
|
||||
Assert.Empty(feature.ViewDescriptors);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PopulateFeature_ReadsAttributesFromTheCurrentAssembly()
|
||||
{
|
||||
// Arrange
|
||||
var item1 = new RazorCompiledItemAttribute(typeof(string), "mvc.1.0.view", "view");
|
||||
var assembly = new AssemblyWithEmptyLocation(
|
||||
new RazorViewAttribute[] { new RazorViewAttribute("view", typeof(string)) },
|
||||
new RazorCompiledItemAttribute[] { item1 });
|
||||
|
||||
var partManager = new ApplicationPartManager();
|
||||
partManager.ApplicationParts.Add(new AssemblyPart(assembly));
|
||||
partManager.FeatureProviders.Add(new ViewsFeatureProvider());
|
||||
var feature = new ViewsFeature();
|
||||
|
||||
// Act
|
||||
partManager.PopulateFeature(feature);
|
||||
|
||||
// Assert
|
||||
var descriptor = Assert.Single(feature.ViewDescriptors);
|
||||
Assert.Equal(typeof(string), descriptor.Item.Type);
|
||||
Assert.Equal("mvc.1.0.view", descriptor.Item.Kind);
|
||||
Assert.Equal("view", descriptor.Item.Identifier);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PopulateFeature_LegacyBehaviorDoesNotFail_IfAssemblyHasEmptyLocation()
|
||||
public void PopulateFeature_DoesNotFail_IfAssemblyHasEmptyLocation()
|
||||
{
|
||||
// Arrange
|
||||
var assembly = new AssemblyWithEmptyLocation();
|
||||
var partManager = new ApplicationPartManager();
|
||||
partManager.ApplicationParts.Add(new AssemblyPart(assembly));
|
||||
partManager.FeatureProviders.Add(new OverrideViewsFeatureProvider());
|
||||
var applicationPartManager = new ApplicationPartManager();
|
||||
applicationPartManager.ApplicationParts.Add(new AssemblyPart(assembly));
|
||||
applicationPartManager.FeatureProviders.Add(new ViewsFeatureProvider());
|
||||
var feature = new ViewsFeature();
|
||||
|
||||
// Act
|
||||
partManager.PopulateFeature(feature);
|
||||
applicationPartManager.PopulateFeature(feature);
|
||||
|
||||
// Assert
|
||||
Assert.Empty(feature.ViewDescriptors);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PopulateFeature_PreservesOldBehavior_IfGetViewAttributesWasOverriden()
|
||||
{
|
||||
// Arrange
|
||||
var assembly = new AssemblyWithEmptyLocation(
|
||||
new RazorViewAttribute[] { new RazorViewAttribute("view", typeof(string)) },
|
||||
new RazorCompiledItemAttribute[] { });
|
||||
|
||||
var partManager = new ApplicationPartManager();
|
||||
partManager.ApplicationParts.Add(new AssemblyPart(assembly));
|
||||
partManager.FeatureProviders.Add(new OverrideViewsFeatureProvider());
|
||||
var feature = new ViewsFeature();
|
||||
|
||||
// Act
|
||||
partManager.PopulateFeature(feature);
|
||||
|
||||
// Assert
|
||||
Assert.Empty(feature.ViewDescriptors);
|
||||
}
|
||||
|
||||
internal class OverrideViewsFeatureProvider : ViewsFeatureProvider
|
||||
{
|
||||
protected override IEnumerable<RazorViewAttribute> GetViewAttributes(AssemblyPart assemblyPart)
|
||||
=> base.GetViewAttributes(assemblyPart);
|
||||
}
|
||||
|
||||
private class TestRazorCompiledItem : RazorCompiledItem
|
||||
{
|
||||
public TestRazorCompiledItem(Type type, string kind, string identifier, object[] metadata)
|
||||
{
|
||||
Type = type;
|
||||
Kind = kind;
|
||||
Identifier = identifier;
|
||||
Metadata = metadata;
|
||||
}
|
||||
|
||||
public override string Identifier { get; }
|
||||
|
||||
public override string Kind { get; }
|
||||
|
||||
public override IReadOnlyList<object> Metadata { get; }
|
||||
|
||||
public override Type Type { get; }
|
||||
}
|
||||
|
||||
private class TestableViewsFeatureProvider : ViewsFeatureProvider
|
||||
{
|
||||
private readonly Dictionary<AssemblyPart, IEnumerable<RazorViewAttribute>> _attributes;
|
||||
private readonly Dictionary<AssemblyPart, IReadOnlyList<RazorCompiledItem>> _items;
|
||||
private readonly Dictionary<AssemblyPart, IEnumerable<RazorViewAttribute>> _attributeLookup;
|
||||
|
||||
public TestableViewsFeatureProvider(
|
||||
Dictionary<AssemblyPart, IReadOnlyList<RazorCompiledItem>> items,
|
||||
Dictionary<AssemblyPart, IEnumerable<RazorViewAttribute>> attributes)
|
||||
public TestableViewsFeatureProvider(Dictionary<AssemblyPart, IEnumerable<RazorViewAttribute>> attributeLookup)
|
||||
{
|
||||
_items = items;
|
||||
_attributes = attributes;
|
||||
_attributeLookup = attributeLookup;
|
||||
}
|
||||
|
||||
protected override IEnumerable<RazorViewAttribute> GetViewAttributes(AssemblyPart assemblyPart)
|
||||
{
|
||||
if (_attributes.TryGetValue(assemblyPart, out var attributes))
|
||||
{
|
||||
return attributes;
|
||||
}
|
||||
|
||||
return Enumerable.Empty<RazorViewAttribute>();
|
||||
}
|
||||
|
||||
internal override IReadOnlyList<RazorCompiledItem> LoadItems(AssemblyPart assemblyPart)
|
||||
{
|
||||
return _items[assemblyPart];
|
||||
return _attributeLookup[assemblyPart];
|
||||
}
|
||||
}
|
||||
|
||||
private class AssemblyWithEmptyLocation : Assembly
|
||||
{
|
||||
private readonly RazorViewAttribute[] _razorViewAttributes;
|
||||
private readonly RazorCompiledItemAttribute[] _razorCompiledItemAttributes;
|
||||
|
||||
public AssemblyWithEmptyLocation()
|
||||
: this(Array.Empty<RazorViewAttribute>(), Array.Empty<RazorCompiledItemAttribute>())
|
||||
{
|
||||
}
|
||||
|
||||
public AssemblyWithEmptyLocation(
|
||||
RazorViewAttribute[] razorViewAttributes,
|
||||
RazorCompiledItemAttribute[] razorCompiledItemAttributes)
|
||||
{
|
||||
_razorViewAttributes = razorViewAttributes;
|
||||
_razorCompiledItemAttributes = razorCompiledItemAttributes;
|
||||
}
|
||||
|
||||
public override string Location => string.Empty;
|
||||
|
||||
public override string FullName => typeof(ViewsFeatureProviderTest).Assembly.FullName;
|
||||
|
||||
public override object[] GetCustomAttributes(Type attributeType, bool inherit)
|
||||
{
|
||||
if (attributeType == typeof(RazorViewAttribute))
|
||||
{
|
||||
return _razorViewAttributes;
|
||||
}
|
||||
else
|
||||
{
|
||||
return _razorCompiledItemAttributes;
|
||||
}
|
||||
}
|
||||
public override string FullName => typeof(ViewsFeatureProviderTest).GetTypeInfo().Assembly.FullName;
|
||||
|
||||
public override IEnumerable<TypeInfo> DefinedTypes
|
||||
{
|
||||
|
|
@ -360,4 +193,5 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Compilation
|
|||
}
|
||||
}
|
||||
}
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,27 +23,6 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
|
|||
{
|
||||
public class RazorViewCompilerTest
|
||||
{
|
||||
[Fact]
|
||||
public void Constructor_ThrowsIfMultiplePrecompiledViewsHavePathsDifferingOnlyInCase()
|
||||
{
|
||||
// Arrange
|
||||
var fileProvider = new TestFileProvider();
|
||||
var precompiledViews = new[]
|
||||
{
|
||||
new CompiledViewDescriptor { RelativePath = "/Views/Home/About.cshtml" },
|
||||
new CompiledViewDescriptor { RelativePath = "/Views/home/About.cshtml" },
|
||||
};
|
||||
var message = string.Join(
|
||||
Environment.NewLine,
|
||||
"The following precompiled view paths differ only in case, which is not supported:",
|
||||
precompiledViews[0].RelativePath,
|
||||
precompiledViews[1].RelativePath);
|
||||
|
||||
// Act & Assert
|
||||
var ex = Assert.Throws<InvalidOperationException>(() => GetViewCompiler(fileProvider, precompiledViews: precompiledViews));
|
||||
Assert.Equal(message, ex.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CompileAsync_ReturnsResultWithNullAttribute_IfFileIsNotFoundInFileSystem()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -216,7 +216,10 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
feature => Assert.IsType<ViewComponentFeatureProvider>(feature),
|
||||
feature => Assert.IsType<MetadataReferenceFeatureProvider>(feature),
|
||||
feature => Assert.IsType<TagHelperFeatureProvider>(feature),
|
||||
feature => Assert.IsType<RazorCompiledItemFeatureProvider>(feature),
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
feature => Assert.IsType<ViewsFeatureProvider>(feature));
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
|
|||
|
|
@ -0,0 +1,37 @@
|
|||
#pragma checksum "D:\k\Mvc\test\WebSites\RazorBuildWebSite\Views\Common\CommonView.cshtml" "{ff1816ec-aa5e-4d10-87f7-6f4963833460}" "a09a0106df2e63aecf6fc6ddf30df39b489d9783"
|
||||
// <auto-generated/>
|
||||
#pragma warning disable 1591
|
||||
[assembly: global::Microsoft.AspNetCore.Mvc.Razor.Compilation.RazorViewAttribute(@"/Views/Common/CommonView.cshtml", typeof(RazorBuildWebSite.Views.Precompilation._Views_Precompilation_CommonView))]
|
||||
namespace RazorBuildWebSite.Views.Precompilation
|
||||
{
|
||||
#line hidden
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
||||
public class _Views_Precompilation_CommonView : global::Microsoft.AspNetCore.Mvc.Razor.RazorPage<dynamic>
|
||||
{
|
||||
#pragma warning disable 1998
|
||||
public async override global::System.Threading.Tasks.Task ExecuteAsync()
|
||||
{
|
||||
BeginContext(0, 48, true);
|
||||
WriteLiteral("Hello from buildtime-compiled precompilation view!");
|
||||
EndContext();
|
||||
}
|
||||
#pragma warning restore 1998
|
||||
[global::Microsoft.AspNetCore.Mvc.Razor.Internal.RazorInjectAttribute]
|
||||
public global::Microsoft.AspNetCore.Mvc.ViewFeatures.IModelExpressionProvider ModelExpressionProvider { get; private set; }
|
||||
[global::Microsoft.AspNetCore.Mvc.Razor.Internal.RazorInjectAttribute]
|
||||
public global::Microsoft.AspNetCore.Mvc.IUrlHelper Url { get; private set; }
|
||||
[global::Microsoft.AspNetCore.Mvc.Razor.Internal.RazorInjectAttribute]
|
||||
public global::Microsoft.AspNetCore.Mvc.IViewComponentHelper Component { get; private set; }
|
||||
[global::Microsoft.AspNetCore.Mvc.Razor.Internal.RazorInjectAttribute]
|
||||
public global::Microsoft.AspNetCore.Mvc.Rendering.IJsonHelper Json { get; private set; }
|
||||
[global::Microsoft.AspNetCore.Mvc.Razor.Internal.RazorInjectAttribute]
|
||||
public global::Microsoft.AspNetCore.Mvc.Rendering.IHtmlHelper<dynamic> Html { get; private set; }
|
||||
}
|
||||
}
|
||||
#pragma warning restore 1591
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// Generated by the MSBuild WriteCodeFragment class.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
using System.Reflection;
|
||||
|
||||
[assembly: Microsoft.AspNetCore.Mvc.ApplicationParts.ProvideApplicationPartFactoryAttribute("Microsoft.AspNetCore.Mvc.ApplicationParts.CompiledRazorAssemblyApplicationPartFac" +
|
||||
"tory, Microsoft.AspNetCore.Mvc.Razor")]
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
#pragma checksum "D:\k\Mvc\test\WebSites\RazorBuildWebSite\Views\Common\CommonView.cshtml" "{ff1816ec-aa5e-4d10-87f7-6f4963833460}" "56260f24a80ae2b8854fd2c9a1005bae485882cc"
|
||||
// <auto-generated/>
|
||||
#pragma warning disable 1591
|
||||
[assembly: global::Microsoft.AspNetCore.Razor.Hosting.RazorCompiledItemAttribute(typeof(RazorBuildWebSite.Views.Rzc.Views_Rzc_View), @"mvc.1.0.view", @"/Views/Common/CommonView.cshtml")]
|
||||
[assembly:global::Microsoft.AspNetCore.Mvc.Razor.Compilation.RazorViewAttribute(@"/Views/CommonView.cshtml", typeof(RazorBuildWebSite.Views.Rzc.Views_Rzc_View))]
|
||||
namespace RazorBuildWebSite.Views.Rzc
|
||||
{
|
||||
#line hidden
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
||||
[global::Microsoft.AspNetCore.Razor.Hosting.RazorSourceChecksumAttribute(@"SHA1", @"56260f24a80ae2b8854fd2c9a1005bae485882cc", @"/Views/Common/CommonView.cshtml")]
|
||||
[global::Microsoft.AspNetCore.Razor.Hosting.RazorSourceChecksumAttribute(@"SHA1", @"f265f06036e4378eada2a78f5366ad0e13e1d8af", @"/_ViewImports.cshtml")]
|
||||
internal class Views_Rzc_CommonView : global::Microsoft.AspNetCore.Mvc.Razor.RazorPage<dynamic>
|
||||
{
|
||||
#pragma warning disable 1998
|
||||
public async override global::System.Threading.Tasks.Task ExecuteAsync()
|
||||
{
|
||||
BeginContext(0, 39, true);
|
||||
WriteLiteral("Hello from buildtime-compiled rzc view!");
|
||||
EndContext();
|
||||
}
|
||||
#pragma warning restore 1998
|
||||
[global::Microsoft.AspNetCore.Mvc.Razor.Internal.RazorInjectAttribute]
|
||||
public global::Microsoft.AspNetCore.Mvc.ViewFeatures.IModelExpressionProvider ModelExpressionProvider { get; private set; }
|
||||
[global::Microsoft.AspNetCore.Mvc.Razor.Internal.RazorInjectAttribute]
|
||||
public global::Microsoft.AspNetCore.Mvc.IUrlHelper Url { get; private set; }
|
||||
[global::Microsoft.AspNetCore.Mvc.Razor.Internal.RazorInjectAttribute]
|
||||
public global::Microsoft.AspNetCore.Mvc.IViewComponentHelper Component { get; private set; }
|
||||
[global::Microsoft.AspNetCore.Mvc.Razor.Internal.RazorInjectAttribute]
|
||||
public global::Microsoft.AspNetCore.Mvc.Rendering.IJsonHelper Json { get; private set; }
|
||||
[global::Microsoft.AspNetCore.Mvc.Razor.Internal.RazorInjectAttribute]
|
||||
public global::Microsoft.AspNetCore.Mvc.Rendering.IHtmlHelper<dynamic> Html { get; private set; }
|
||||
}
|
||||
}
|
||||
#pragma warning restore 1591
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace RazorBuildWebSite.Controllers
|
||||
{
|
||||
public class CommonController : Controller
|
||||
{
|
||||
public new ActionResult View()
|
||||
{
|
||||
return base.View("CommonView");
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue