Merge pull request #19033 from dotnet/johluo/migrating-extensions

Migrating sources from dotnet/extensions
This commit is contained in:
John Luo 2020-02-21 14:34:32 -08:00 committed by GitHub
commit 5a0526dfd9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
412 changed files with 27085 additions and 194 deletions

View File

@ -109,6 +109,7 @@
$(RepoRoot)src\Components\Web.JS\Microsoft.AspNetCore.Components.Web.JS.npmproj; $(RepoRoot)src\Components\Web.JS\Microsoft.AspNetCore.Components.Web.JS.npmproj;
$(RepoRoot)src\SignalR\**\*.npmproj; $(RepoRoot)src\SignalR\**\*.npmproj;
$(RepoRoot)src\Middleware\**\*.npmproj; $(RepoRoot)src\Middleware\**\*.npmproj;
$(RepoRoot)src\JSInterop\**\*.npmproj;
" "
RestoreInParallel="false" RestoreInParallel="false"
Exclude="@(ProjectToExclude)" /> Exclude="@(ProjectToExclude)" />
@ -152,6 +153,14 @@
$(RepoRoot)src\SignalR\**\*.csproj; $(RepoRoot)src\SignalR\**\*.csproj;
$(RepoRoot)src\Components\**\*.csproj; $(RepoRoot)src\Components\**\*.csproj;
$(RepoRoot)src\Analyzers\**\*.csproj; $(RepoRoot)src\Analyzers\**\*.csproj;
$(RepoRoot)src\FileProviders\**\*.csproj;
$(RepoRoot)src\Configuration.KeyPerFile\**\*.csproj;
$(RepoRoot)src\Localization\**\*.csproj;
$(RepoRoot)src\ObjectPool\**\*.csproj;
$(RepoRoot)src\JSInterop\**\*.csproj;
$(RepoRoot)src\WebEncoders\**\*.csproj;
$(RepoRoot)src\HealthChecks\**\*.csproj;
$(RepoRoot)src\Testing\**\*.csproj;
$(RepoRoot)src\ProjectTemplates\*\*.csproj; $(RepoRoot)src\ProjectTemplates\*\*.csproj;
$(RepoRoot)src\ProjectTemplates\testassets\*\*.csproj; $(RepoRoot)src\ProjectTemplates\testassets\*\*.csproj;
" "
@ -181,6 +190,14 @@
$(RepoRoot)src\Azure\**\src\*.csproj; $(RepoRoot)src\Azure\**\src\*.csproj;
$(RepoRoot)src\SignalR\**\src\*.csproj; $(RepoRoot)src\SignalR\**\src\*.csproj;
$(RepoRoot)src\Components\**\src\*.csproj; $(RepoRoot)src\Components\**\src\*.csproj;
$(RepoRoot)src\FileProviders\**\src\*.csproj;
$(RepoRoot)src\Configuration.KeyPerFile\**\src\*.csproj;
$(RepoRoot)src\Localization\**\src\*.csproj;
$(RepoRoot)src\ObjectPool\**\src\*.csproj;
$(RepoRoot)src\JSInterop\**\src\*.csproj;
$(RepoRoot)src\WebEncoders\**\src\*.csproj;
$(RepoRoot)src\HealthChecks\**\src\*.csproj;
$(RepoRoot)src\Testing\**\src\*.csproj;
" "
Exclude=" Exclude="
@(ProjectToBuild); @(ProjectToBuild);

View File

@ -30,10 +30,8 @@ and are generated based on the last package release.
<LatestPackageReference Include="Microsoft.CSharp" Version="$(MicrosoftCSharpPackageVersion)" /> <LatestPackageReference Include="Microsoft.CSharp" Version="$(MicrosoftCSharpPackageVersion)" />
<LatestPackageReference Include="Microsoft.DotNet.GenAPI" Version="$(MicrosoftDotNetGenApiPackageVersion)" /> <LatestPackageReference Include="Microsoft.DotNet.GenAPI" Version="$(MicrosoftDotNetGenApiPackageVersion)" />
<LatestPackageReference Include="Microsoft.DotNet.PlatformAbstractions" Version="$(MicrosoftDotNetPlatformAbstractionsPackageVersion)" /> <LatestPackageReference Include="Microsoft.DotNet.PlatformAbstractions" Version="$(MicrosoftDotNetPlatformAbstractionsPackageVersion)" />
<LatestPackageReference Include="Microsoft.Extensions.ActivatorUtilities.Sources" Version="$(MicrosoftExtensionsActivatorUtilitiesSourcesPackageVersion)" />
<LatestPackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="$(MicrosoftExtensionsCachingAbstractionsPackageVersion)" /> <LatestPackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="$(MicrosoftExtensionsCachingAbstractionsPackageVersion)" />
<LatestPackageReference Include="Microsoft.Extensions.Caching.Memory" Version="$(MicrosoftExtensionsCachingMemoryPackageVersion)" /> <LatestPackageReference Include="Microsoft.Extensions.Caching.Memory" Version="$(MicrosoftExtensionsCachingMemoryPackageVersion)" />
<LatestPackageReference Include="Microsoft.Extensions.CommandLineUtils.Sources" Version="$(MicrosoftExtensionsCommandLineUtilsSourcesPackageVersion)" />
<LatestPackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="$(MicrosoftExtensionsConfigurationAbstractionsPackageVersion)" /> <LatestPackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="$(MicrosoftExtensionsConfigurationAbstractionsPackageVersion)" />
<LatestPackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="$(MicrosoftExtensionsConfigurationBinderPackageVersion)" /> <LatestPackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="$(MicrosoftExtensionsConfigurationBinderPackageVersion)" />
<LatestPackageReference Include="Microsoft.Extensions.Configuration.CommandLine" Version="$(MicrosoftExtensionsConfigurationCommandLinePackageVersion)" /> <LatestPackageReference Include="Microsoft.Extensions.Configuration.CommandLine" Version="$(MicrosoftExtensionsConfigurationCommandLinePackageVersion)" />
@ -41,7 +39,6 @@ and are generated based on the last package release.
<LatestPackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="$(MicrosoftExtensionsConfigurationFileExtensionsPackageVersion)" /> <LatestPackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="$(MicrosoftExtensionsConfigurationFileExtensionsPackageVersion)" />
<LatestPackageReference Include="Microsoft.Extensions.Configuration.Ini" Version="$(MicrosoftExtensionsConfigurationIniPackageVersion)" /> <LatestPackageReference Include="Microsoft.Extensions.Configuration.Ini" Version="$(MicrosoftExtensionsConfigurationIniPackageVersion)" />
<LatestPackageReference Include="Microsoft.Extensions.Configuration.Json" Version="$(MicrosoftExtensionsConfigurationJsonPackageVersion)" /> <LatestPackageReference Include="Microsoft.Extensions.Configuration.Json" Version="$(MicrosoftExtensionsConfigurationJsonPackageVersion)" />
<LatestPackageReference Include="Microsoft.Extensions.Configuration.KeyPerFile" Version="$(MicrosoftExtensionsConfigurationKeyPerFilePackageVersion)" />
<LatestPackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="$(MicrosoftExtensionsConfigurationUserSecretsPackageVersion)" /> <LatestPackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="$(MicrosoftExtensionsConfigurationUserSecretsPackageVersion)" />
<LatestPackageReference Include="Microsoft.Extensions.Configuration.Xml" Version="$(MicrosoftExtensionsConfigurationXmlPackageVersion)" /> <LatestPackageReference Include="Microsoft.Extensions.Configuration.Xml" Version="$(MicrosoftExtensionsConfigurationXmlPackageVersion)" />
<LatestPackageReference Include="Microsoft.Extensions.Configuration" Version="$(MicrosoftExtensionsConfigurationPackageVersion)" /> <LatestPackageReference Include="Microsoft.Extensions.Configuration" Version="$(MicrosoftExtensionsConfigurationPackageVersion)" />
@ -49,20 +46,13 @@ and are generated based on the last package release.
<LatestPackageReference Include="Microsoft.Extensions.DependencyInjection" Version="$(MicrosoftExtensionsDependencyInjectionPackageVersion)" /> <LatestPackageReference Include="Microsoft.Extensions.DependencyInjection" Version="$(MicrosoftExtensionsDependencyInjectionPackageVersion)" />
<LatestPackageReference Include="Microsoft.Extensions.DependencyModel" Version="$(MicrosoftExtensionsDependencyModelPackageVersion)" /> <LatestPackageReference Include="Microsoft.Extensions.DependencyModel" Version="$(MicrosoftExtensionsDependencyModelPackageVersion)" />
<LatestPackageReference Include="Microsoft.Extensions.DiagnosticAdapter" Version="$(MicrosoftExtensionsDiagnosticAdapterPackageVersion)" /> <LatestPackageReference Include="Microsoft.Extensions.DiagnosticAdapter" Version="$(MicrosoftExtensionsDiagnosticAdapterPackageVersion)" />
<LatestPackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions" Version="$(MicrosoftExtensionsDiagnosticsHealthChecksAbstractionsPackageVersion)" />
<LatestPackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks" Version="$(MicrosoftExtensionsDiagnosticsHealthChecksPackageVersion)" />
<LatestPackageReference Include="Microsoft.Extensions.FileProviders.Abstractions" Version="$(MicrosoftExtensionsFileProvidersAbstractionsPackageVersion)" /> <LatestPackageReference Include="Microsoft.Extensions.FileProviders.Abstractions" Version="$(MicrosoftExtensionsFileProvidersAbstractionsPackageVersion)" />
<LatestPackageReference Include="Microsoft.Extensions.FileProviders.Composite" Version="$(MicrosoftExtensionsFileProvidersCompositePackageVersion)" /> <LatestPackageReference Include="Microsoft.Extensions.FileProviders.Composite" Version="$(MicrosoftExtensionsFileProvidersCompositePackageVersion)" />
<LatestPackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="$(MicrosoftExtensionsFileProvidersEmbeddedPackageVersion)" />
<LatestPackageReference Include="Microsoft.Extensions.FileProviders.Physical" Version="$(MicrosoftExtensionsFileProvidersPhysicalPackageVersion)" /> <LatestPackageReference Include="Microsoft.Extensions.FileProviders.Physical" Version="$(MicrosoftExtensionsFileProvidersPhysicalPackageVersion)" />
<LatestPackageReference Include="Microsoft.Extensions.FileSystemGlobbing" Version="$(MicrosoftExtensionsFileSystemGlobbingPackageVersion)" /> <LatestPackageReference Include="Microsoft.Extensions.FileSystemGlobbing" Version="$(MicrosoftExtensionsFileSystemGlobbingPackageVersion)" />
<LatestPackageReference Include="Microsoft.Extensions.HashCodeCombiner.Sources" Version="$(MicrosoftExtensionsHashCodeCombinerSourcesPackageVersion)" />
<LatestPackageReference Include="Microsoft.Extensions.HostFactoryResolver.Sources" Version="$(MicrosoftExtensionsHostFactoryResolverSourcesPackageVersion)" />
<LatestPackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="$(MicrosoftExtensionsHostingAbstractionsPackageVersion)" /> <LatestPackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="$(MicrosoftExtensionsHostingAbstractionsPackageVersion)" />
<LatestPackageReference Include="Microsoft.Extensions.Hosting" Version="$(MicrosoftExtensionsHostingPackageVersion)" /> <LatestPackageReference Include="Microsoft.Extensions.Hosting" Version="$(MicrosoftExtensionsHostingPackageVersion)" />
<LatestPackageReference Include="Microsoft.Extensions.Http" Version="$(MicrosoftExtensionsHttpPackageVersion)" /> <LatestPackageReference Include="Microsoft.Extensions.Http" Version="$(MicrosoftExtensionsHttpPackageVersion)" />
<LatestPackageReference Include="Microsoft.Extensions.Localization.Abstractions" Version="$(MicrosoftExtensionsLocalizationAbstractionsPackageVersion)" />
<LatestPackageReference Include="Microsoft.Extensions.Localization" Version="$(MicrosoftExtensionsLocalizationPackageVersion)" />
<LatestPackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="$(MicrosoftExtensionsLoggingAbstractionsPackageVersion)" /> <LatestPackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="$(MicrosoftExtensionsLoggingAbstractionsPackageVersion)" />
<LatestPackageReference Include="Microsoft.Extensions.Logging.Configuration" Version="$(MicrosoftExtensionsLoggingConfigurationPackageVersion)" /> <LatestPackageReference Include="Microsoft.Extensions.Logging.Configuration" Version="$(MicrosoftExtensionsLoggingConfigurationPackageVersion)" />
<LatestPackageReference Include="Microsoft.Extensions.Logging.Console" Version="$(MicrosoftExtensionsLoggingConsolePackageVersion)" /> <LatestPackageReference Include="Microsoft.Extensions.Logging.Console" Version="$(MicrosoftExtensionsLoggingConsolePackageVersion)" />
@ -71,16 +61,10 @@ and are generated based on the last package release.
<LatestPackageReference Include="Microsoft.Extensions.Logging.EventLog" Version="$(MicrosoftExtensionsLoggingEventLogPackageVersion)" /> <LatestPackageReference Include="Microsoft.Extensions.Logging.EventLog" Version="$(MicrosoftExtensionsLoggingEventLogPackageVersion)" />
<LatestPackageReference Include="Microsoft.Extensions.Logging.TraceSource" Version="$(MicrosoftExtensionsLoggingTraceSourcePackageVersion)" /> <LatestPackageReference Include="Microsoft.Extensions.Logging.TraceSource" Version="$(MicrosoftExtensionsLoggingTraceSourcePackageVersion)" />
<LatestPackageReference Include="Microsoft.Extensions.Logging" Version="$(MicrosoftExtensionsLoggingPackageVersion)" /> <LatestPackageReference Include="Microsoft.Extensions.Logging" Version="$(MicrosoftExtensionsLoggingPackageVersion)" />
<LatestPackageReference Include="Microsoft.Extensions.ObjectPool" Version="$(MicrosoftExtensionsObjectPoolPackageVersion)" />
<LatestPackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="$(MicrosoftExtensionsOptionsConfigurationExtensionsPackageVersion)" /> <LatestPackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="$(MicrosoftExtensionsOptionsConfigurationExtensionsPackageVersion)" />
<LatestPackageReference Include="Microsoft.Extensions.Options.DataAnnotations" Version="$(MicrosoftExtensionsOptionsDataAnnotationsPackageVersion)" /> <LatestPackageReference Include="Microsoft.Extensions.Options.DataAnnotations" Version="$(MicrosoftExtensionsOptionsDataAnnotationsPackageVersion)" />
<LatestPackageReference Include="Microsoft.Extensions.Options" Version="$(MicrosoftExtensionsOptionsPackageVersion)" /> <LatestPackageReference Include="Microsoft.Extensions.Options" Version="$(MicrosoftExtensionsOptionsPackageVersion)" />
<LatestPackageReference Include="Microsoft.Extensions.ParameterDefaultValue.Sources" Version="$(MicrosoftExtensionsParameterDefaultValueSourcesPackageVersion)" />
<LatestPackageReference Include="Microsoft.Extensions.Primitives" Version="$(MicrosoftExtensionsPrimitivesPackageVersion)" /> <LatestPackageReference Include="Microsoft.Extensions.Primitives" Version="$(MicrosoftExtensionsPrimitivesPackageVersion)" />
<LatestPackageReference Include="Microsoft.Extensions.TypeNameHelper.Sources" Version="$(MicrosoftExtensionsTypeNameHelperSourcesPackageVersion)" />
<LatestPackageReference Include="Microsoft.Extensions.ValueStopWatch.Sources" Version="$(MicrosoftExtensionsValueStopwatchSourcesPackageVersion)" />
<LatestPackageReference Include="Microsoft.Extensions.WebEncoders" Version="$(MicrosoftExtensionsWebEncodersPackageVersion)" />
<LatestPackageReference Include="Microsoft.JSInterop" Version="$(MicrosoftJSInteropPackageVersion)" />
<LatestPackageReference Include="Microsoft.Win32.Registry" Version="$(MicrosoftWin32RegistryPackageVersion)" /> <LatestPackageReference Include="Microsoft.Win32.Registry" Version="$(MicrosoftWin32RegistryPackageVersion)" />
<LatestPackageReference Include="Microsoft.Win32.SystemEvents" Version="$(MicrosoftWin32SystemEventsPackageVersion)" /> <LatestPackageReference Include="Microsoft.Win32.SystemEvents" Version="$(MicrosoftWin32SystemEventsPackageVersion)" />
<LatestPackageReference Include="System.Buffers" Version="$(SystemBuffersPackageVersion)" /> <LatestPackageReference Include="System.Buffers" Version="$(SystemBuffersPackageVersion)" />
@ -94,14 +78,17 @@ and are generated based on the last package release.
<LatestPackageReference Include="System.Net.Http" Version="$(SystemNetHttpPackageVersion)" /> <LatestPackageReference Include="System.Net.Http" Version="$(SystemNetHttpPackageVersion)" />
<LatestPackageReference Include="System.Reflection.Metadata" Version="$(SystemReflectionMetadataPackageVersion)" /> <LatestPackageReference Include="System.Reflection.Metadata" Version="$(SystemReflectionMetadataPackageVersion)" />
<LatestPackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="$(SystemRuntimeCompilerServicesUnsafePackageVersion)" /> <LatestPackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="$(SystemRuntimeCompilerServicesUnsafePackageVersion)" />
<LatestPackageReference Include="System.Runtime.InteropServices.RuntimeInformation" Version="$(SystemRuntimeInteropServicesRuntimeInformationPackageVersion)" />
<LatestPackageReference Include="System.Security.Cryptography.Cng" Version="$(SystemSecurityCryptographyCngPackageVersion)" /> <LatestPackageReference Include="System.Security.Cryptography.Cng" Version="$(SystemSecurityCryptographyCngPackageVersion)" />
<LatestPackageReference Include="System.Security.Cryptography.Pkcs" Version="$(SystemSecurityCryptographyPkcsPackageVersion)" /> <LatestPackageReference Include="System.Security.Cryptography.Pkcs" Version="$(SystemSecurityCryptographyPkcsPackageVersion)" />
<LatestPackageReference Include="System.Security.Cryptography.Xml" Version="$(SystemSecurityCryptographyXmlPackageVersion)" /> <LatestPackageReference Include="System.Security.Cryptography.Xml" Version="$(SystemSecurityCryptographyXmlPackageVersion)" />
<LatestPackageReference Include="System.Security.Permissions" Version="$(SystemSecurityPermissionsPackageVersion)" /> <LatestPackageReference Include="System.Security.Permissions" Version="$(SystemSecurityPermissionsPackageVersion)" />
<LatestPackageReference Include="System.Security.Principal.Windows" Version="$(SystemSecurityPrincipalWindowsPackageVersion)" /> <LatestPackageReference Include="System.Security.Principal.Windows" Version="$(SystemSecurityPrincipalWindowsPackageVersion)" />
<LatestPackageReference Include="System.Text.Encodings.Web" Version="$(SystemTextEncodingsWebPackageVersion)" />
<LatestPackageReference Include="System.Text.Json" Version="$(SystemTextJsonPackageVersion)" /> <LatestPackageReference Include="System.Text.Json" Version="$(SystemTextJsonPackageVersion)" />
<LatestPackageReference Include="System.Threading.Channels" Version="$(SystemThreadingChannelsPackageVersion)" /> <LatestPackageReference Include="System.Threading.Channels" Version="$(SystemThreadingChannelsPackageVersion)" />
<LatestPackageReference Include="System.Windows.Extensions" Version="$(SystemWindowsExtensionsPackageVersion)" /> <LatestPackageReference Include="System.Windows.Extensions" Version="$(SystemWindowsExtensionsPackageVersion)" />
<LatestPackageReference Include="System.ValueTuple" Version="$(SystemValueTuplePackageVersion)" />
<!-- Runtime packages required for crossgen --> <!-- Runtime packages required for crossgen -->
<LatestPackageReference Include="microsoft.netcore.app.runtime.win-x64" Version="$(MicrosoftNETCoreAppRuntimeVersion)" /> <LatestPackageReference Include="microsoft.netcore.app.runtime.win-x64" Version="$(MicrosoftNETCoreAppRuntimeVersion)" />
@ -122,9 +109,7 @@ and are generated based on the last package release.
<LatestPackageReference Include="Microsoft.AspNetCore.AzureAppServices.SiteExtension.2.2" Version="$(MicrosoftAspNetCoreAzureAppServicesSiteExtension22PackageVersion)" /> <LatestPackageReference Include="Microsoft.AspNetCore.AzureAppServices.SiteExtension.2.2" Version="$(MicrosoftAspNetCoreAzureAppServicesSiteExtension22PackageVersion)" />
<LatestPackageReference Include="Microsoft.AspNetCore.AzureAppServices.SiteExtension.3.1.x64" Version="$(MicrosoftAspNetCoreAzureAppServicesSiteExtension31PackageVersion)" /> <LatestPackageReference Include="Microsoft.AspNetCore.AzureAppServices.SiteExtension.3.1.x64" Version="$(MicrosoftAspNetCoreAzureAppServicesSiteExtension31PackageVersion)" />
<LatestPackageReference Include="Microsoft.AspNetCore.AzureAppServices.SiteExtension.3.1.x86" Version="$(MicrosoftAspNetCoreAzureAppServicesSiteExtension31PackageVersion)" /> <LatestPackageReference Include="Microsoft.AspNetCore.AzureAppServices.SiteExtension.3.1.x86" Version="$(MicrosoftAspNetCoreAzureAppServicesSiteExtension31PackageVersion)" />
<LatestPackageReference Include="Microsoft.AspNetCore.BenchmarkRunner.Sources" Version="$(MicrosoftAspNetCoreBenchmarkRunnerSourcesPackageVersion)" />
<LatestPackageReference Include="Microsoft.AspNetCore.Blazor.Mono" Version="$(MicrosoftAspNetCoreBlazorMonoPackageVersion)" /> <LatestPackageReference Include="Microsoft.AspNetCore.Blazor.Mono" Version="$(MicrosoftAspNetCoreBlazorMonoPackageVersion)" />
<LatestPackageReference Include="Microsoft.AspNetCore.Testing" Version="$(MicrosoftAspNetCoreTestingPackageVersion)" />
<LatestPackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="$(MicrosoftBclAsyncInterfacesPackageVersion)" /> <LatestPackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="$(MicrosoftBclAsyncInterfacesPackageVersion)" />
<LatestPackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="$(MicrosoftEntityFrameworkCoreInMemoryPackageVersion)" /> <LatestPackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="$(MicrosoftEntityFrameworkCoreInMemoryPackageVersion)" />
<LatestPackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="$(MicrosoftEntityFrameworkCoreRelationalPackageVersion)" /> <LatestPackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="$(MicrosoftEntityFrameworkCoreRelationalPackageVersion)" />

View File

@ -63,6 +63,7 @@
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Blazor.Server" ProjectPath="$(RepoRoot)src\Components\Blazor\Server\src\Microsoft.AspNetCore.Blazor.Server.csproj" /> <ProjectReferenceProvider Include="Microsoft.AspNetCore.Blazor.Server" ProjectPath="$(RepoRoot)src\Components\Blazor\Server\src\Microsoft.AspNetCore.Blazor.Server.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Blazor.DataAnnotations.Validation" ProjectPath="$(RepoRoot)src\Components\Blazor\Validation\src\Microsoft.AspNetCore.Blazor.DataAnnotations.Validation.csproj" /> <ProjectReferenceProvider Include="Microsoft.AspNetCore.Blazor.DataAnnotations.Validation" ProjectPath="$(RepoRoot)src\Components\Blazor\Validation\src\Microsoft.AspNetCore.Blazor.DataAnnotations.Validation.csproj" />
<ProjectReferenceProvider Include="Ignitor" ProjectPath="$(RepoRoot)src\Components\Ignitor\src\Ignitor.csproj" /> <ProjectReferenceProvider Include="Ignitor" ProjectPath="$(RepoRoot)src\Components\Ignitor\src\Ignitor.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Testing" ProjectPath="$(RepoRoot)src\Testing\src\Microsoft.AspNetCore.Testing.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore" ProjectPath="$(RepoRoot)src\DefaultBuilder\src\Microsoft.AspNetCore.csproj" RefProjectPath="$(RepoRoot)src\DefaultBuilder\ref\Microsoft.AspNetCore.csproj" /> <ProjectReferenceProvider Include="Microsoft.AspNetCore" ProjectPath="$(RepoRoot)src\DefaultBuilder\src\Microsoft.AspNetCore.csproj" RefProjectPath="$(RepoRoot)src\DefaultBuilder\ref\Microsoft.AspNetCore.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.DataProtection.Abstractions" ProjectPath="$(RepoRoot)src\DataProtection\Abstractions\src\Microsoft.AspNetCore.DataProtection.Abstractions.csproj" RefProjectPath="$(RepoRoot)src\DataProtection\Abstractions\ref\Microsoft.AspNetCore.DataProtection.Abstractions.csproj" /> <ProjectReferenceProvider Include="Microsoft.AspNetCore.DataProtection.Abstractions" ProjectPath="$(RepoRoot)src\DataProtection\Abstractions\src\Microsoft.AspNetCore.DataProtection.Abstractions.csproj" RefProjectPath="$(RepoRoot)src\DataProtection\Abstractions\ref\Microsoft.AspNetCore.DataProtection.Abstractions.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Cryptography.Internal" ProjectPath="$(RepoRoot)src\DataProtection\Cryptography.Internal\src\Microsoft.AspNetCore.Cryptography.Internal.csproj" RefProjectPath="$(RepoRoot)src\DataProtection\Cryptography.Internal\ref\Microsoft.AspNetCore.Cryptography.Internal.csproj" /> <ProjectReferenceProvider Include="Microsoft.AspNetCore.Cryptography.Internal" ProjectPath="$(RepoRoot)src\DataProtection\Cryptography.Internal\src\Microsoft.AspNetCore.Cryptography.Internal.csproj" RefProjectPath="$(RepoRoot)src\DataProtection\Cryptography.Internal\ref\Microsoft.AspNetCore.Cryptography.Internal.csproj" />
@ -143,5 +144,14 @@
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Components.Forms" ProjectPath="$(RepoRoot)src\Components\Forms\src\Microsoft.AspNetCore.Components.Forms.csproj" RefProjectPath="$(RepoRoot)src\Components\Forms\ref\Microsoft.AspNetCore.Components.Forms.csproj" /> <ProjectReferenceProvider Include="Microsoft.AspNetCore.Components.Forms" ProjectPath="$(RepoRoot)src\Components\Forms\src\Microsoft.AspNetCore.Components.Forms.csproj" RefProjectPath="$(RepoRoot)src\Components\Forms\ref\Microsoft.AspNetCore.Components.Forms.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Components.Server" ProjectPath="$(RepoRoot)src\Components\Server\src\Microsoft.AspNetCore.Components.Server.csproj" RefProjectPath="$(RepoRoot)src\Components\Server\ref\Microsoft.AspNetCore.Components.Server.csproj" /> <ProjectReferenceProvider Include="Microsoft.AspNetCore.Components.Server" ProjectPath="$(RepoRoot)src\Components\Server\src\Microsoft.AspNetCore.Components.Server.csproj" RefProjectPath="$(RepoRoot)src\Components\Server\ref\Microsoft.AspNetCore.Components.Server.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Components.Web" ProjectPath="$(RepoRoot)src\Components\Web\src\Microsoft.AspNetCore.Components.Web.csproj" RefProjectPath="$(RepoRoot)src\Components\Web\ref\Microsoft.AspNetCore.Components.Web.csproj" /> <ProjectReferenceProvider Include="Microsoft.AspNetCore.Components.Web" ProjectPath="$(RepoRoot)src\Components\Web\src\Microsoft.AspNetCore.Components.Web.csproj" RefProjectPath="$(RepoRoot)src\Components\Web\ref\Microsoft.AspNetCore.Components.Web.csproj" />
<ProjectReferenceProvider Include="Microsoft.Extensions.FileProviders.Embedded" ProjectPath="$(RepoRoot)src\FileProviders\Embedded\src\Microsoft.Extensions.FileProviders.Embedded.csproj" RefProjectPath="$(RepoRoot)src\FileProviders\Embedded\ref\Microsoft.Extensions.FileProviders.Embedded.csproj" />
<ProjectReferenceProvider Include="Microsoft.Extensions.Configuration.KeyPerFile" ProjectPath="$(RepoRoot)src\Configuration.KeyPerFile\src\Microsoft.Extensions.Configuration.KeyPerFile.csproj" RefProjectPath="$(RepoRoot)src\Configuration.KeyPerFile\ref\Microsoft.Extensions.Configuration.KeyPerFile.csproj" />
<ProjectReferenceProvider Include="Microsoft.Extensions.Localization.Abstractions" ProjectPath="$(RepoRoot)src\Localization\Abstractions\src\Microsoft.Extensions.Localization.Abstractions.csproj" RefProjectPath="$(RepoRoot)src\Localization\Abstractions\ref\Microsoft.Extensions.Localization.Abstractions.csproj" />
<ProjectReferenceProvider Include="Microsoft.Extensions.Localization" ProjectPath="$(RepoRoot)src\Localization\Localization\src\Microsoft.Extensions.Localization.csproj" RefProjectPath="$(RepoRoot)src\Localization\Localization\ref\Microsoft.Extensions.Localization.csproj" />
<ProjectReferenceProvider Include="Microsoft.Extensions.ObjectPool" ProjectPath="$(RepoRoot)src\ObjectPool\src\Microsoft.Extensions.ObjectPool.csproj" RefProjectPath="$(RepoRoot)src\ObjectPool\ref\Microsoft.Extensions.ObjectPool.csproj" />
<ProjectReferenceProvider Include="Microsoft.JSInterop" ProjectPath="$(RepoRoot)src\JSInterop\Microsoft.JSInterop\src\Microsoft.JSInterop.csproj" RefProjectPath="$(RepoRoot)src\JSInterop\Microsoft.JSInterop\ref\Microsoft.JSInterop.csproj" />
<ProjectReferenceProvider Include="Microsoft.Extensions.WebEncoders" ProjectPath="$(RepoRoot)src\WebEncoders\src\Microsoft.Extensions.WebEncoders.csproj" RefProjectPath="$(RepoRoot)src\WebEncoders\ref\Microsoft.Extensions.WebEncoders.csproj" />
<ProjectReferenceProvider Include="Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions" ProjectPath="$(RepoRoot)src\HealthChecks\Abstractions\src\Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.csproj" RefProjectPath="$(RepoRoot)src\HealthChecks\Abstractions\ref\Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.csproj" />
<ProjectReferenceProvider Include="Microsoft.Extensions.Diagnostics.HealthChecks" ProjectPath="$(RepoRoot)src\HealthChecks\HealthChecks\src\Microsoft.Extensions.Diagnostics.HealthChecks.csproj" RefProjectPath="$(RepoRoot)src\HealthChecks\HealthChecks\ref\Microsoft.Extensions.Diagnostics.HealthChecks.csproj" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -30,24 +30,18 @@
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="$(MicrosoftExtensionsConfigurationFileExtensionsPackageVersion)" /> <ExternalAspNetCoreAppReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="$(MicrosoftExtensionsConfigurationFileExtensionsPackageVersion)" />
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.Configuration.Ini" Version="$(MicrosoftExtensionsConfigurationIniPackageVersion)" /> <ExternalAspNetCoreAppReference Include="Microsoft.Extensions.Configuration.Ini" Version="$(MicrosoftExtensionsConfigurationIniPackageVersion)" />
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.Configuration.Json" Version="$(MicrosoftExtensionsConfigurationJsonPackageVersion)" /> <ExternalAspNetCoreAppReference Include="Microsoft.Extensions.Configuration.Json" Version="$(MicrosoftExtensionsConfigurationJsonPackageVersion)" />
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.Configuration.KeyPerFile" Version="$(MicrosoftExtensionsConfigurationKeyPerFilePackageVersion)" />
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="$(MicrosoftExtensionsConfigurationUserSecretsPackageVersion)" /> <ExternalAspNetCoreAppReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="$(MicrosoftExtensionsConfigurationUserSecretsPackageVersion)" />
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.Configuration.Xml" Version="$(MicrosoftExtensionsConfigurationXmlPackageVersion)" /> <ExternalAspNetCoreAppReference Include="Microsoft.Extensions.Configuration.Xml" Version="$(MicrosoftExtensionsConfigurationXmlPackageVersion)" />
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.Configuration" Version="$(MicrosoftExtensionsConfigurationPackageVersion)" /> <ExternalAspNetCoreAppReference Include="Microsoft.Extensions.Configuration" Version="$(MicrosoftExtensionsConfigurationPackageVersion)" />
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="$(MicrosoftExtensionsDependencyInjectionAbstractionsPackageVersion)" /> <ExternalAspNetCoreAppReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="$(MicrosoftExtensionsDependencyInjectionAbstractionsPackageVersion)" />
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.DependencyInjection" Version="$(MicrosoftExtensionsDependencyInjectionPackageVersion)" /> <ExternalAspNetCoreAppReference Include="Microsoft.Extensions.DependencyInjection" Version="$(MicrosoftExtensionsDependencyInjectionPackageVersion)" />
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions" Version="$(MicrosoftExtensionsDiagnosticsHealthChecksAbstractionsPackageVersion)" />
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.Diagnostics.HealthChecks" Version="$(MicrosoftExtensionsDiagnosticsHealthChecksPackageVersion)" />
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.FileProviders.Abstractions" Version="$(MicrosoftExtensionsFileProvidersAbstractionsPackageVersion)" /> <ExternalAspNetCoreAppReference Include="Microsoft.Extensions.FileProviders.Abstractions" Version="$(MicrosoftExtensionsFileProvidersAbstractionsPackageVersion)" />
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.FileProviders.Composite" Version="$(MicrosoftExtensionsFileProvidersCompositePackageVersion)" /> <ExternalAspNetCoreAppReference Include="Microsoft.Extensions.FileProviders.Composite" Version="$(MicrosoftExtensionsFileProvidersCompositePackageVersion)" />
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="$(MicrosoftExtensionsFileProvidersEmbeddedPackageVersion)" />
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.FileProviders.Physical" Version="$(MicrosoftExtensionsFileProvidersPhysicalPackageVersion)" /> <ExternalAspNetCoreAppReference Include="Microsoft.Extensions.FileProviders.Physical" Version="$(MicrosoftExtensionsFileProvidersPhysicalPackageVersion)" />
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.FileSystemGlobbing" Version="$(MicrosoftExtensionsFileSystemGlobbingPackageVersion)" /> <ExternalAspNetCoreAppReference Include="Microsoft.Extensions.FileSystemGlobbing" Version="$(MicrosoftExtensionsFileSystemGlobbingPackageVersion)" />
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="$(MicrosoftExtensionsHostingAbstractionsPackageVersion)" /> <ExternalAspNetCoreAppReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="$(MicrosoftExtensionsHostingAbstractionsPackageVersion)" />
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.Hosting" Version="$(MicrosoftExtensionsHostingPackageVersion)" /> <ExternalAspNetCoreAppReference Include="Microsoft.Extensions.Hosting" Version="$(MicrosoftExtensionsHostingPackageVersion)" />
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.Http" Version="$(MicrosoftExtensionsHttpPackageVersion)" /> <ExternalAspNetCoreAppReference Include="Microsoft.Extensions.Http" Version="$(MicrosoftExtensionsHttpPackageVersion)" />
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.Localization.Abstractions" Version="$(MicrosoftExtensionsLocalizationAbstractionsPackageVersion)" />
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.Localization" Version="$(MicrosoftExtensionsLocalizationPackageVersion)" />
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.Logging.Abstractions" Version="$(MicrosoftExtensionsLoggingAbstractionsPackageVersion)" /> <ExternalAspNetCoreAppReference Include="Microsoft.Extensions.Logging.Abstractions" Version="$(MicrosoftExtensionsLoggingAbstractionsPackageVersion)" />
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.Logging.Configuration" Version="$(MicrosoftExtensionsLoggingConfigurationPackageVersion)" /> <ExternalAspNetCoreAppReference Include="Microsoft.Extensions.Logging.Configuration" Version="$(MicrosoftExtensionsLoggingConfigurationPackageVersion)" />
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.Logging.Console" Version="$(MicrosoftExtensionsLoggingConsolePackageVersion)" /> <ExternalAspNetCoreAppReference Include="Microsoft.Extensions.Logging.Console" Version="$(MicrosoftExtensionsLoggingConsolePackageVersion)" />
@ -56,13 +50,10 @@
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.Logging.EventLog" Version="$(MicrosoftExtensionsLoggingEventLogPackageVersion)" /> <ExternalAspNetCoreAppReference Include="Microsoft.Extensions.Logging.EventLog" Version="$(MicrosoftExtensionsLoggingEventLogPackageVersion)" />
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.Logging.TraceSource" Version="$(MicrosoftExtensionsLoggingTraceSourcePackageVersion)" /> <ExternalAspNetCoreAppReference Include="Microsoft.Extensions.Logging.TraceSource" Version="$(MicrosoftExtensionsLoggingTraceSourcePackageVersion)" />
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.Logging" Version="$(MicrosoftExtensionsLoggingPackageVersion)" /> <ExternalAspNetCoreAppReference Include="Microsoft.Extensions.Logging" Version="$(MicrosoftExtensionsLoggingPackageVersion)" />
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.ObjectPool" Version="$(MicrosoftExtensionsObjectPoolPackageVersion)" />
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="$(MicrosoftExtensionsOptionsConfigurationExtensionsPackageVersion)" /> <ExternalAspNetCoreAppReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="$(MicrosoftExtensionsOptionsConfigurationExtensionsPackageVersion)" />
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.Options.DataAnnotations" Version="$(MicrosoftExtensionsOptionsDataAnnotationsPackageVersion)" /> <ExternalAspNetCoreAppReference Include="Microsoft.Extensions.Options.DataAnnotations" Version="$(MicrosoftExtensionsOptionsDataAnnotationsPackageVersion)" />
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.Options" Version="$(MicrosoftExtensionsOptionsPackageVersion)" /> <ExternalAspNetCoreAppReference Include="Microsoft.Extensions.Options" Version="$(MicrosoftExtensionsOptionsPackageVersion)" />
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.Primitives" Version="$(MicrosoftExtensionsPrimitivesPackageVersion)" /> <ExternalAspNetCoreAppReference Include="Microsoft.Extensions.Primitives" Version="$(MicrosoftExtensionsPrimitivesPackageVersion)" />
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.WebEncoders" Version="$(MicrosoftExtensionsWebEncodersPackageVersion)" />
<ExternalAspNetCoreAppReference Include="Microsoft.JSInterop" Version="$(MicrosoftJSInteropPackageVersion)" />
<!-- Dependencies from dotnet/corefx --> <!-- Dependencies from dotnet/corefx -->
<ExternalAspNetCoreAppReference Include="System.IO.Pipelines" Version="$(SystemIOPipelinesPackageVersion)" /> <ExternalAspNetCoreAppReference Include="System.IO.Pipelines" Version="$(SystemIOPipelinesPackageVersion)" />

View File

@ -26,6 +26,15 @@
<AspNetCoreAppReferenceAndPackage Include="Microsoft.AspNetCore.Components" /> <AspNetCoreAppReferenceAndPackage Include="Microsoft.AspNetCore.Components" />
<AspNetCoreAppReferenceAndPackage Include="Microsoft.AspNetCore.Components.Forms" /> <AspNetCoreAppReferenceAndPackage Include="Microsoft.AspNetCore.Components.Forms" />
<AspNetCoreAppReferenceAndPackage Include="Microsoft.AspNetCore.Components.Web" /> <AspNetCoreAppReferenceAndPackage Include="Microsoft.AspNetCore.Components.Web" />
<AspNetCoreAppReferenceAndPackage Include="Microsoft.Extensions.FileProviders.Embedded" />
<AspNetCoreAppReferenceAndPackage Include="Microsoft.Extensions.Configuration.KeyPerFile" />
<AspNetCoreAppReferenceAndPackage Include="Microsoft.Extensions.Localization.Abstractions" />
<AspNetCoreAppReferenceAndPackage Include="Microsoft.Extensions.Localization" />
<AspNetCoreAppReferenceAndPackage Include="Microsoft.Extensions.ObjectPool" />
<AspNetCoreAppReferenceAndPackage Include="Microsoft.JSInterop" />
<AspNetCoreAppReferenceAndPackage Include="Microsoft.Extensions.WebEncoders" />
<AspNetCoreAppReferenceAndPackage Include="Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions" />
<AspNetCoreAppReferenceAndPackage Include="Microsoft.Extensions.Diagnostics.HealthChecks" />
<!-- These assemblies are only in the shared framework --> <!-- These assemblies are only in the shared framework -->
<AspNetCoreAppReference Include="Microsoft.AspNetCore" /> <AspNetCoreAppReference Include="Microsoft.AspNetCore" />

View File

@ -61,14 +61,6 @@
<Uri>https://github.com/dotnet/extensions</Uri> <Uri>https://github.com/dotnet/extensions</Uri>
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha> <Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
</Dependency> </Dependency>
<Dependency Name="Microsoft.AspNetCore.BenchmarkRunner.Sources" Version="5.0.0-preview.1.20114.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
<Uri>https://github.com/dotnet/extensions</Uri>
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.ActivatorUtilities.Sources" Version="5.0.0-preview.1.20114.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
<Uri>https://github.com/dotnet/extensions</Uri>
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Caching.Abstractions" Version="5.0.0-preview.1.20114.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language"> <Dependency Name="Microsoft.Extensions.Caching.Abstractions" Version="5.0.0-preview.1.20114.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
<Uri>https://github.com/dotnet/extensions</Uri> <Uri>https://github.com/dotnet/extensions</Uri>
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha> <Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
@ -85,10 +77,6 @@
<Uri>https://github.com/dotnet/extensions</Uri> <Uri>https://github.com/dotnet/extensions</Uri>
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha> <Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
</Dependency> </Dependency>
<Dependency Name="Microsoft.Extensions.CommandLineUtils.Sources" Version="5.0.0-preview.1.20114.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
<Uri>https://github.com/dotnet/extensions</Uri>
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Configuration.Abstractions" Version="5.0.0-preview.1.20114.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language"> <Dependency Name="Microsoft.Extensions.Configuration.Abstractions" Version="5.0.0-preview.1.20114.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
<Uri>https://github.com/dotnet/extensions</Uri> <Uri>https://github.com/dotnet/extensions</Uri>
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha> <Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
@ -121,10 +109,6 @@
<Uri>https://github.com/dotnet/extensions</Uri> <Uri>https://github.com/dotnet/extensions</Uri>
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha> <Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
</Dependency> </Dependency>
<Dependency Name="Microsoft.Extensions.Configuration.KeyPerFile" Version="5.0.0-preview.1.20114.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
<Uri>https://github.com/dotnet/extensions</Uri>
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Configuration.UserSecrets" Version="5.0.0-preview.1.20114.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language"> <Dependency Name="Microsoft.Extensions.Configuration.UserSecrets" Version="5.0.0-preview.1.20114.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
<Uri>https://github.com/dotnet/extensions</Uri> <Uri>https://github.com/dotnet/extensions</Uri>
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha> <Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
@ -149,14 +133,6 @@
<Uri>https://github.com/dotnet/extensions</Uri> <Uri>https://github.com/dotnet/extensions</Uri>
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha> <Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
</Dependency> </Dependency>
<Dependency Name="Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions" Version="5.0.0-preview.1.20114.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
<Uri>https://github.com/dotnet/extensions</Uri>
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Diagnostics.HealthChecks" Version="5.0.0-preview.1.20114.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
<Uri>https://github.com/dotnet/extensions</Uri>
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.FileProviders.Abstractions" Version="5.0.0-preview.1.20114.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language"> <Dependency Name="Microsoft.Extensions.FileProviders.Abstractions" Version="5.0.0-preview.1.20114.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
<Uri>https://github.com/dotnet/extensions</Uri> <Uri>https://github.com/dotnet/extensions</Uri>
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha> <Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
@ -165,10 +141,6 @@
<Uri>https://github.com/dotnet/extensions</Uri> <Uri>https://github.com/dotnet/extensions</Uri>
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha> <Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
</Dependency> </Dependency>
<Dependency Name="Microsoft.Extensions.FileProviders.Embedded" Version="5.0.0-preview.1.20114.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
<Uri>https://github.com/dotnet/extensions</Uri>
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.FileProviders.Physical" Version="5.0.0-preview.1.20114.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language"> <Dependency Name="Microsoft.Extensions.FileProviders.Physical" Version="5.0.0-preview.1.20114.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
<Uri>https://github.com/dotnet/extensions</Uri> <Uri>https://github.com/dotnet/extensions</Uri>
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha> <Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
@ -177,10 +149,6 @@
<Uri>https://github.com/dotnet/extensions</Uri> <Uri>https://github.com/dotnet/extensions</Uri>
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha> <Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
</Dependency> </Dependency>
<Dependency Name="Microsoft.Extensions.HashCodeCombiner.Sources" Version="5.0.0-preview.1.20114.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
<Uri>https://github.com/dotnet/extensions</Uri>
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Hosting.Abstractions" Version="5.0.0-preview.1.20114.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language"> <Dependency Name="Microsoft.Extensions.Hosting.Abstractions" Version="5.0.0-preview.1.20114.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
<Uri>https://github.com/dotnet/extensions</Uri> <Uri>https://github.com/dotnet/extensions</Uri>
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha> <Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
@ -189,22 +157,10 @@
<Uri>https://github.com/dotnet/extensions</Uri> <Uri>https://github.com/dotnet/extensions</Uri>
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha> <Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
</Dependency> </Dependency>
<Dependency Name="Microsoft.Extensions.HostFactoryResolver.Sources" Version="5.0.0-preview.1.20114.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
<Uri>https://github.com/dotnet/extensions</Uri>
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Http" Version="5.0.0-preview.1.20114.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language"> <Dependency Name="Microsoft.Extensions.Http" Version="5.0.0-preview.1.20114.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
<Uri>https://github.com/dotnet/extensions</Uri> <Uri>https://github.com/dotnet/extensions</Uri>
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha> <Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
</Dependency> </Dependency>
<Dependency Name="Microsoft.Extensions.Localization.Abstractions" Version="5.0.0-preview.1.20114.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
<Uri>https://github.com/dotnet/extensions</Uri>
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Localization" Version="5.0.0-preview.1.20114.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
<Uri>https://github.com/dotnet/extensions</Uri>
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Logging.Abstractions" Version="5.0.0-preview.1.20114.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language"> <Dependency Name="Microsoft.Extensions.Logging.Abstractions" Version="5.0.0-preview.1.20114.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
<Uri>https://github.com/dotnet/extensions</Uri> <Uri>https://github.com/dotnet/extensions</Uri>
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha> <Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
@ -245,10 +201,6 @@
<Uri>https://github.com/dotnet/extensions</Uri> <Uri>https://github.com/dotnet/extensions</Uri>
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha> <Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
</Dependency> </Dependency>
<Dependency Name="Microsoft.Extensions.ObjectPool" Version="5.0.0-preview.1.20114.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
<Uri>https://github.com/dotnet/extensions</Uri>
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Options.ConfigurationExtensions" Version="5.0.0-preview.1.20114.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language"> <Dependency Name="Microsoft.Extensions.Options.ConfigurationExtensions" Version="5.0.0-preview.1.20114.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
<Uri>https://github.com/dotnet/extensions</Uri> <Uri>https://github.com/dotnet/extensions</Uri>
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha> <Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
@ -261,34 +213,14 @@
<Uri>https://github.com/dotnet/extensions</Uri> <Uri>https://github.com/dotnet/extensions</Uri>
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha> <Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
</Dependency> </Dependency>
<Dependency Name="Microsoft.Extensions.ParameterDefaultValue.Sources" Version="5.0.0-preview.1.20114.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
<Uri>https://github.com/dotnet/extensions</Uri>
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Primitives" Version="5.0.0-preview.1.20114.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language"> <Dependency Name="Microsoft.Extensions.Primitives" Version="5.0.0-preview.1.20114.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
<Uri>https://github.com/dotnet/extensions</Uri> <Uri>https://github.com/dotnet/extensions</Uri>
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha> <Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
</Dependency> </Dependency>
<Dependency Name="Microsoft.Extensions.TypeNameHelper.Sources" Version="5.0.0-preview.1.20114.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
<Uri>https://github.com/dotnet/extensions</Uri>
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.ValueStopwatch.Sources" Version="5.0.0-preview.1.20114.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
<Uri>https://github.com/dotnet/extensions</Uri>
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.WebEncoders" Version="5.0.0-preview.1.20114.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
<Uri>https://github.com/dotnet/extensions</Uri>
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
</Dependency>
<Dependency Name="Microsoft.Internal.Extensions.Refs" Version="5.0.0-preview.1.20114.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language"> <Dependency Name="Microsoft.Internal.Extensions.Refs" Version="5.0.0-preview.1.20114.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
<Uri>https://github.com/dotnet/extensions</Uri> <Uri>https://github.com/dotnet/extensions</Uri>
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha> <Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
</Dependency> </Dependency>
<Dependency Name="Microsoft.JSInterop" Version="5.0.0-preview.1.20114.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
<Uri>https://github.com/dotnet/extensions</Uri>
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
</Dependency>
<Dependency Name="Microsoft.Win32.Registry" Version="5.0.0-preview.1.20113.7" CoherentParentDependency="Microsoft.NETCore.App.Runtime.win-x64"> <Dependency Name="Microsoft.Win32.Registry" Version="5.0.0-preview.1.20113.7" CoherentParentDependency="Microsoft.NETCore.App.Runtime.win-x64">
<Uri>https://github.com/dotnet/runtime</Uri> <Uri>https://github.com/dotnet/runtime</Uri>
<Sha>0f3f8e1930c28b67f29990126bc2e8527e959a2e</Sha> <Sha>0f3f8e1930c28b67f29990126bc2e8527e959a2e</Sha>
@ -416,10 +348,6 @@
<Uri>https://github.com/dotnet/arcade</Uri> <Uri>https://github.com/dotnet/arcade</Uri>
<Sha>f50767f96246063f33a6565d318f3adf9058bace</Sha> <Sha>f50767f96246063f33a6565d318f3adf9058bace</Sha>
</Dependency> </Dependency>
<Dependency Name="Microsoft.AspNetCore.Testing" Version="5.0.0-preview.1.20114.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
<Uri>https://github.com/dotnet/extensions</Uri>
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
</Dependency>
<Dependency Name="Microsoft.Net.Compilers.Toolset" Version="3.5.0-beta3-20114-02" CoherentParentDependency="Microsoft.Extensions.Logging"> <Dependency Name="Microsoft.Net.Compilers.Toolset" Version="3.5.0-beta3-20114-02" CoherentParentDependency="Microsoft.Extensions.Logging">
<Uri>https://github.com/dotnet/roslyn</Uri> <Uri>https://github.com/dotnet/roslyn</Uri>
<Sha>92790e24cc2b9f9e336ed0365d610e106c01df88</Sha> <Sha>92790e24cc2b9f9e336ed0365d610e106c01df88</Sha>

View File

@ -98,14 +98,10 @@
<!-- Packages from dotnet/extensions --> <!-- Packages from dotnet/extensions -->
<InternalAspNetCoreAnalyzersPackageVersion>5.0.0-preview.1.20114.1</InternalAspNetCoreAnalyzersPackageVersion> <InternalAspNetCoreAnalyzersPackageVersion>5.0.0-preview.1.20114.1</InternalAspNetCoreAnalyzersPackageVersion>
<MicrosoftAspNetCoreAnalyzerTestingPackageVersion>5.0.0-preview.1.20114.1</MicrosoftAspNetCoreAnalyzerTestingPackageVersion> <MicrosoftAspNetCoreAnalyzerTestingPackageVersion>5.0.0-preview.1.20114.1</MicrosoftAspNetCoreAnalyzerTestingPackageVersion>
<MicrosoftAspNetCoreBenchmarkRunnerSourcesPackageVersion>5.0.0-preview.1.20114.1</MicrosoftAspNetCoreBenchmarkRunnerSourcesPackageVersion>
<MicrosoftAspNetCoreTestingPackageVersion>5.0.0-preview.1.20114.1</MicrosoftAspNetCoreTestingPackageVersion>
<MicrosoftExtensionsActivatorUtilitiesSourcesPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsActivatorUtilitiesSourcesPackageVersion>
<MicrosoftExtensionsCachingAbstractionsPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsCachingAbstractionsPackageVersion> <MicrosoftExtensionsCachingAbstractionsPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsCachingAbstractionsPackageVersion>
<MicrosoftExtensionsCachingMemoryPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsCachingMemoryPackageVersion> <MicrosoftExtensionsCachingMemoryPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsCachingMemoryPackageVersion>
<MicrosoftExtensionsCachingSqlServerPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsCachingSqlServerPackageVersion> <MicrosoftExtensionsCachingSqlServerPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsCachingSqlServerPackageVersion>
<MicrosoftExtensionsCachingStackExchangeRedisPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsCachingStackExchangeRedisPackageVersion> <MicrosoftExtensionsCachingStackExchangeRedisPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsCachingStackExchangeRedisPackageVersion>
<MicrosoftExtensionsCommandLineUtilsSourcesPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsCommandLineUtilsSourcesPackageVersion>
<MicrosoftExtensionsConfigurationAbstractionsPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsConfigurationAbstractionsPackageVersion> <MicrosoftExtensionsConfigurationAbstractionsPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsConfigurationAbstractionsPackageVersion>
<MicrosoftExtensionsConfigurationAzureKeyVaultPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsConfigurationAzureKeyVaultPackageVersion> <MicrosoftExtensionsConfigurationAzureKeyVaultPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsConfigurationAzureKeyVaultPackageVersion>
<MicrosoftExtensionsConfigurationBinderPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsConfigurationBinderPackageVersion> <MicrosoftExtensionsConfigurationBinderPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsConfigurationBinderPackageVersion>
@ -114,27 +110,19 @@
<MicrosoftExtensionsConfigurationFileExtensionsPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsConfigurationFileExtensionsPackageVersion> <MicrosoftExtensionsConfigurationFileExtensionsPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsConfigurationFileExtensionsPackageVersion>
<MicrosoftExtensionsConfigurationIniPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsConfigurationIniPackageVersion> <MicrosoftExtensionsConfigurationIniPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsConfigurationIniPackageVersion>
<MicrosoftExtensionsConfigurationJsonPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsConfigurationJsonPackageVersion> <MicrosoftExtensionsConfigurationJsonPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsConfigurationJsonPackageVersion>
<MicrosoftExtensionsConfigurationKeyPerFilePackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsConfigurationKeyPerFilePackageVersion>
<MicrosoftExtensionsConfigurationPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsConfigurationPackageVersion> <MicrosoftExtensionsConfigurationPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsConfigurationPackageVersion>
<MicrosoftExtensionsConfigurationUserSecretsPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsConfigurationUserSecretsPackageVersion> <MicrosoftExtensionsConfigurationUserSecretsPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsConfigurationUserSecretsPackageVersion>
<MicrosoftExtensionsConfigurationXmlPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsConfigurationXmlPackageVersion> <MicrosoftExtensionsConfigurationXmlPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsConfigurationXmlPackageVersion>
<MicrosoftExtensionsDependencyInjectionAbstractionsPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsDependencyInjectionAbstractionsPackageVersion> <MicrosoftExtensionsDependencyInjectionAbstractionsPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsDependencyInjectionAbstractionsPackageVersion>
<MicrosoftExtensionsDependencyInjectionPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsDependencyInjectionPackageVersion> <MicrosoftExtensionsDependencyInjectionPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsDependencyInjectionPackageVersion>
<MicrosoftExtensionsDiagnosticAdapterPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsDiagnosticAdapterPackageVersion> <MicrosoftExtensionsDiagnosticAdapterPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsDiagnosticAdapterPackageVersion>
<MicrosoftExtensionsDiagnosticsHealthChecksAbstractionsPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsDiagnosticsHealthChecksAbstractionsPackageVersion>
<MicrosoftExtensionsDiagnosticsHealthChecksPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsDiagnosticsHealthChecksPackageVersion>
<MicrosoftExtensionsFileProvidersAbstractionsPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsFileProvidersAbstractionsPackageVersion> <MicrosoftExtensionsFileProvidersAbstractionsPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsFileProvidersAbstractionsPackageVersion>
<MicrosoftExtensionsFileProvidersCompositePackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsFileProvidersCompositePackageVersion> <MicrosoftExtensionsFileProvidersCompositePackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsFileProvidersCompositePackageVersion>
<MicrosoftExtensionsFileProvidersEmbeddedPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsFileProvidersEmbeddedPackageVersion>
<MicrosoftExtensionsFileProvidersPhysicalPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsFileProvidersPhysicalPackageVersion> <MicrosoftExtensionsFileProvidersPhysicalPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsFileProvidersPhysicalPackageVersion>
<MicrosoftExtensionsFileSystemGlobbingPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsFileSystemGlobbingPackageVersion> <MicrosoftExtensionsFileSystemGlobbingPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsFileSystemGlobbingPackageVersion>
<MicrosoftExtensionsHashCodeCombinerSourcesPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsHashCodeCombinerSourcesPackageVersion>
<MicrosoftExtensionsHostingAbstractionsPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsHostingAbstractionsPackageVersion> <MicrosoftExtensionsHostingAbstractionsPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsHostingAbstractionsPackageVersion>
<MicrosoftExtensionsHostingPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsHostingPackageVersion> <MicrosoftExtensionsHostingPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsHostingPackageVersion>
<MicrosoftExtensionsHostFactoryResolverSourcesPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsHostFactoryResolverSourcesPackageVersion>
<MicrosoftExtensionsHttpPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsHttpPackageVersion> <MicrosoftExtensionsHttpPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsHttpPackageVersion>
<MicrosoftExtensionsLocalizationAbstractionsPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsLocalizationAbstractionsPackageVersion>
<MicrosoftExtensionsLocalizationPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsLocalizationPackageVersion>
<MicrosoftExtensionsLoggingAbstractionsPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsLoggingAbstractionsPackageVersion> <MicrosoftExtensionsLoggingAbstractionsPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsLoggingAbstractionsPackageVersion>
<MicrosoftExtensionsLoggingAzureAppServicesPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsLoggingAzureAppServicesPackageVersion> <MicrosoftExtensionsLoggingAzureAppServicesPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsLoggingAzureAppServicesPackageVersion>
<MicrosoftExtensionsLoggingConfigurationPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsLoggingConfigurationPackageVersion> <MicrosoftExtensionsLoggingConfigurationPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsLoggingConfigurationPackageVersion>
@ -145,17 +133,11 @@
<MicrosoftExtensionsLoggingPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsLoggingPackageVersion> <MicrosoftExtensionsLoggingPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsLoggingPackageVersion>
<MicrosoftExtensionsLoggingTestingPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsLoggingTestingPackageVersion> <MicrosoftExtensionsLoggingTestingPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsLoggingTestingPackageVersion>
<MicrosoftExtensionsLoggingTraceSourcePackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsLoggingTraceSourcePackageVersion> <MicrosoftExtensionsLoggingTraceSourcePackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsLoggingTraceSourcePackageVersion>
<MicrosoftExtensionsObjectPoolPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsObjectPoolPackageVersion>
<MicrosoftExtensionsOptionsConfigurationExtensionsPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsOptionsConfigurationExtensionsPackageVersion> <MicrosoftExtensionsOptionsConfigurationExtensionsPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsOptionsConfigurationExtensionsPackageVersion>
<MicrosoftExtensionsOptionsDataAnnotationsPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsOptionsDataAnnotationsPackageVersion> <MicrosoftExtensionsOptionsDataAnnotationsPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsOptionsDataAnnotationsPackageVersion>
<MicrosoftExtensionsOptionsPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsOptionsPackageVersion> <MicrosoftExtensionsOptionsPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsOptionsPackageVersion>
<MicrosoftExtensionsParameterDefaultValueSourcesPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsParameterDefaultValueSourcesPackageVersion>
<MicrosoftExtensionsPrimitivesPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsPrimitivesPackageVersion> <MicrosoftExtensionsPrimitivesPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsPrimitivesPackageVersion>
<MicrosoftExtensionsTypeNameHelperSourcesPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsTypeNameHelperSourcesPackageVersion>
<MicrosoftExtensionsValueStopwatchSourcesPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsValueStopwatchSourcesPackageVersion>
<MicrosoftExtensionsWebEncodersPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsWebEncodersPackageVersion>
<MicrosoftInternalExtensionsRefsPackageVersion>5.0.0-preview.1.20114.1</MicrosoftInternalExtensionsRefsPackageVersion> <MicrosoftInternalExtensionsRefsPackageVersion>5.0.0-preview.1.20114.1</MicrosoftInternalExtensionsRefsPackageVersion>
<MicrosoftJSInteropPackageVersion>5.0.0-preview.1.20114.1</MicrosoftJSInteropPackageVersion>
<!-- Packages from dotnet/efcore --> <!-- Packages from dotnet/efcore -->
<dotnetefPackageVersion>5.0.0-preview.2.20121.2</dotnetefPackageVersion> <dotnetefPackageVersion>5.0.0-preview.2.20121.2</dotnetefPackageVersion>
<MicrosoftEntityFrameworkCoreInMemoryPackageVersion>5.0.0-preview.2.20121.2</MicrosoftEntityFrameworkCoreInMemoryPackageVersion> <MicrosoftEntityFrameworkCoreInMemoryPackageVersion>5.0.0-preview.2.20121.2</MicrosoftEntityFrameworkCoreInMemoryPackageVersion>
@ -198,7 +180,9 @@
<SystemCommandlineExperimentalPackageVersion>0.3.0-alpha.19317.1</SystemCommandlineExperimentalPackageVersion> <SystemCommandlineExperimentalPackageVersion>0.3.0-alpha.19317.1</SystemCommandlineExperimentalPackageVersion>
<SystemComponentModelPackageVersion>4.3.0</SystemComponentModelPackageVersion> <SystemComponentModelPackageVersion>4.3.0</SystemComponentModelPackageVersion>
<SystemNetHttpPackageVersion>4.3.2</SystemNetHttpPackageVersion> <SystemNetHttpPackageVersion>4.3.2</SystemNetHttpPackageVersion>
<SystemRuntimeInteropServicesRuntimeInformationPackageVersion>4.3.0</SystemRuntimeInteropServicesRuntimeInformationPackageVersion>
<SystemThreadingTasksExtensionsPackageVersion>4.5.3</SystemThreadingTasksExtensionsPackageVersion> <SystemThreadingTasksExtensionsPackageVersion>4.5.3</SystemThreadingTasksExtensionsPackageVersion>
<SystemValueTuplePackageVersion>4.5.0</SystemValueTuplePackageVersion>
<!-- Packages developed by @aspnet, but manually updated as necessary. --> <!-- Packages developed by @aspnet, but manually updated as necessary. -->
<LibuvPackageVersion>1.10.0</LibuvPackageVersion> <LibuvPackageVersion>1.10.0</LibuvPackageVersion>
<MicrosoftAspNetWebApiClientPackageVersion>5.2.6</MicrosoftAspNetWebApiClientPackageVersion> <MicrosoftAspNetWebApiClientPackageVersion>5.2.6</MicrosoftAspNetWebApiClientPackageVersion>

View File

@ -119,7 +119,7 @@
<Error Condition="@(_InvalidReferenceToSharedFxOnlyAssembly->Count()) != 0" <Error Condition="@(_InvalidReferenceToSharedFxOnlyAssembly->Count()) != 0"
Text="Cannot reference &quot;%(_InvalidReferenceToSharedFxOnlyAssembly.Identity)&quot; directly because it is part of the shared framework and this project is not. Use &lt;FrameworkReference Include=&quot;Microsoft.AspNetCore.App&quot; /&gt; instead." /> Text="Cannot reference &quot;%(_InvalidReferenceToSharedFxOnlyAssembly.Identity)&quot; directly because it is part of the shared framework and this project is not. Use &lt;FrameworkReference Include=&quot;Microsoft.AspNetCore.App&quot; /&gt; instead." />
<Error Condition="@(_InvalidReferenceToNonSharedFxAssembly->Count()) != 0" <Error Condition="@(_InvalidReferenceToNonSharedFxAssembly->Count()) != 0 AND '$(TargetFramework)' == '$(DefaultNetCoreTargetFramework)'"
Text="Cannot reference &quot;%(_InvalidReferenceToNonSharedFxAssembly.Identity)&quot;. This dependency is not in the shared framework. See docs/SharedFramework.md for instructions on how to modify what is in the shared framework." /> Text="Cannot reference &quot;%(_InvalidReferenceToNonSharedFxAssembly.Identity)&quot;. This dependency is not in the shared framework. See docs/SharedFramework.md for instructions on how to modify what is in the shared framework." />
</Target> </Target>

View File

@ -31,7 +31,6 @@
<Reference Include="Microsoft.Build.Framework" /> <Reference Include="Microsoft.Build.Framework" />
<Reference Include="Microsoft.Build.Utilities.Core" /> <Reference Include="Microsoft.Build.Utilities.Core" />
<Reference Include="Microsoft.CodeAnalysis.Razor" /> <Reference Include="Microsoft.CodeAnalysis.Razor" />
<Reference Include="Microsoft.Extensions.CommandLineUtils.Sources" />
<!-- Avoid CS1705 errors due to mix of assemblies brought in transitively. --> <!-- Avoid CS1705 errors due to mix of assemblies brought in transitively. -->
<Reference Include="Microsoft.AspNetCore.Components" /> <Reference Include="Microsoft.AspNetCore.Components" />
</ItemGroup> </ItemGroup>
@ -40,6 +39,7 @@
<ProjectReference Include="..\..\testassets\StandaloneApp\StandaloneApp.csproj" /> <ProjectReference Include="..\..\testassets\StandaloneApp\StandaloneApp.csproj" />
<Compile Include="$(SharedSourceRoot)test\SkipOnHelixAttribute.cs" /> <Compile Include="$(SharedSourceRoot)test\SkipOnHelixAttribute.cs" />
<Compile Include="$(ComponentsSharedSourceRoot)test\**\*.cs" LinkBase="Helpers" /> <Compile Include="$(ComponentsSharedSourceRoot)test\**\*.cs" LinkBase="Helpers" />
<Compile Include="$(SharedSourceRoot)CommandLineUtils\**\*.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -18,8 +18,9 @@
<Reference Include="Microsoft.AspNetCore.Components.Server" /> <Reference Include="Microsoft.AspNetCore.Components.Server" />
<Reference Include="Microsoft.AspNetCore.ResponseCompression" /> <Reference Include="Microsoft.AspNetCore.ResponseCompression" />
<Reference Include="Microsoft.AspNetCore" /> <Reference Include="Microsoft.AspNetCore" />
<Reference Include="Microsoft.Extensions.CommandLineUtils.Sources" />
<Reference Include="Microsoft.Extensions.Hosting" /> <Reference Include="Microsoft.Extensions.Hosting" />
<Compile Include="$(SharedSourceRoot)CommandLineUtils\**\*.cs" />
</ItemGroup> </ItemGroup>
<!-- Pack settings --> <!-- Pack settings -->

View File

@ -10,7 +10,8 @@
<ItemGroup> <ItemGroup>
<Reference Include="BenchmarkDotNet" /> <Reference Include="BenchmarkDotNet" />
<Reference Include="Microsoft.AspNetCore.Components" /> <Reference Include="Microsoft.AspNetCore.Components" />
<Reference Include="Microsoft.AspNetCore.BenchmarkRunner.Sources" />
<Compile Include="$(SharedSourceRoot)BenchmarkRunner\*.cs" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -10,7 +10,6 @@
<Compile Include="../src/Properties/AssemblyInfo.cs" /> <Compile Include="../src/Properties/AssemblyInfo.cs" />
<Reference Include="Microsoft.Extensions.Logging.Abstractions" /> <Reference Include="Microsoft.Extensions.Logging.Abstractions" />
<Reference Include="Microsoft.Extensions.DependencyInjection.Abstractions" /> <Reference Include="Microsoft.Extensions.DependencyInjection.Abstractions" />
<Reference Include="Microsoft.Extensions.HashCodeCombiner.Sources" />
<Reference Include="System.Buffers" /> <Reference Include="System.Buffers" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == '$(DefaultNetCoreTargetFramework)'"> <ItemGroup Condition="'$(TargetFramework)' == '$(DefaultNetCoreTargetFramework)'">
@ -19,6 +18,5 @@
<Compile Include="../src/Properties/AssemblyInfo.cs" /> <Compile Include="../src/Properties/AssemblyInfo.cs" />
<Reference Include="Microsoft.Extensions.Logging.Abstractions" /> <Reference Include="Microsoft.Extensions.Logging.Abstractions" />
<Reference Include="Microsoft.Extensions.DependencyInjection.Abstractions" /> <Reference Include="Microsoft.Extensions.DependencyInjection.Abstractions" />
<Reference Include="Microsoft.Extensions.HashCodeCombiner.Sources" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -15,7 +15,8 @@
<ItemGroup> <ItemGroup>
<Reference Include="Microsoft.Extensions.Logging.Abstractions" /> <Reference Include="Microsoft.Extensions.Logging.Abstractions" />
<Reference Include="Microsoft.Extensions.DependencyInjection.Abstractions" /> <Reference Include="Microsoft.Extensions.DependencyInjection.Abstractions" />
<Reference Include="Microsoft.Extensions.HashCodeCombiner.Sources" />
<Compile Include="$(SharedSourceRoot)HashCodeCombiner\*.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition="'$(TargetFramework)'=='netstandard2.0'"> <ItemGroup Condition="'$(TargetFramework)'=='netstandard2.0'">
@ -33,12 +34,16 @@
<Target Name="_GetNuspecDependencyPackageVersions"> <Target Name="_GetNuspecDependencyPackageVersions">
<MSBuild Targets="_GetPackageVersionInfo" <MSBuild Targets="_GetPackageVersionInfo"
BuildInParallel="$(BuildInParallel)" BuildInParallel="$(BuildInParallel)"
Projects="../../Analyzers/src/Microsoft.AspNetCore.Components.Analyzers.csproj;../../../Security/Authorization/Core/src/Microsoft.AspNetCore.Authorization.csproj"> Projects="
../../Analyzers/src/Microsoft.AspNetCore.Components.Analyzers.csproj;
../../../JSInterop/Microsoft.JSInterop/src/Microsoft.JSInterop.csproj;
../../../Security/Authorization/Core/src/Microsoft.AspNetCore.Authorization.csproj">
<Output TaskParameter="TargetOutputs" ItemName="_ProjectPathWithVersion" /> <Output TaskParameter="TargetOutputs" ItemName="_ProjectPathWithVersion" />
</MSBuild> </MSBuild>
<ItemGroup> <ItemGroup>
<NuspecProperty Include="@(_ProjectPathWithVersion->WithMetadataValue('PackageId', 'Microsoft.AspnetCore.Components.Analyzers')->'componentAnalyzerPackageVersion=%(PackageVersion)')" /> <NuspecProperty Include="@(_ProjectPathWithVersion->WithMetadataValue('PackageId', 'Microsoft.AspnetCore.Components.Analyzers')->'componentAnalyzerPackageVersion=%(PackageVersion)')" />
<NuspecProperty Include="@(_ProjectPathWithVersion->WithMetadataValue('PackageId', 'Microsoft.AspnetCore.Authorization')->'authorizationPackageVersion=%(PackageVersion)')" /> <NuspecProperty Include="@(_ProjectPathWithVersion->WithMetadataValue('PackageId', 'Microsoft.AspnetCore.Authorization')->'authorizationPackageVersion=%(PackageVersion)')" />
<NuspecProperty Include="@(_ProjectPathWithVersion->WithMetadataValue('PackageId', 'Microsoft.JSInterop')->'jsInteropPackageVersion=%(PackageVersion)')" />
</ItemGroup> </ItemGroup>
</Target> </Target>
@ -50,7 +55,6 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<NuspecProperty Include="jsInteropPackageVersion=$(MicrosoftJSInteropPackageVersion)" />
<NuspecProperty Condition="'$(DotNetBuildFromSource)' != 'true'" Include="systemComponentModelAnnotationsPackageVersion=$(SystemComponentModelAnnotationsPackageVersion)" /> <NuspecProperty Condition="'$(DotNetBuildFromSource)' != 'true'" Include="systemComponentModelAnnotationsPackageVersion=$(SystemComponentModelAnnotationsPackageVersion)" />
<NuspecProperty Include="AssemblyName=$(AssemblyName)" /> <NuspecProperty Include="AssemblyName=$(AssemblyName)" />
<NuspecProperty Include="OutputPath=$(OutputPath)" /> <NuspecProperty Include="OutputPath=$(OutputPath)" />

View File

@ -0,0 +1,6 @@
<Project>
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory)..\, Directory.Build.targets))\Directory.Build.targets" />
<!-- Workaround target import for project references to Microsoft.Extensions.FileProviders.Embedded -->
<Import
Project="$(RepoRoot)src\FileProviders\Embedded\src\build\netstandard2.0\Microsoft.Extensions.FileProviders.Embedded.targets" />
</Project>

View File

@ -1,5 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>$(DefaultNetCoreTargetFramework)</TargetFramework> <TargetFramework>$(DefaultNetCoreTargetFramework)</TargetFramework>
<Description>Runtime server features for ASP.NET Core Components.</Description> <Description>Runtime server features for ASP.NET Core Components.</Description>
@ -10,6 +9,7 @@
<NoWarn>CS0436;$(NoWarn)</NoWarn> <NoWarn>CS0436;$(NoWarn)</NoWarn>
<DefineConstants>$(DefineConstants);ENABLE_UNSAFE_MSGPACK;SPAN_BUILTIN;MESSAGEPACK_INTERNAL;COMPONENTS_SERVER</DefineConstants> <DefineConstants>$(DefineConstants);ENABLE_UNSAFE_MSGPACK;SPAN_BUILTIN;MESSAGEPACK_INTERNAL;COMPONENTS_SERVER</DefineConstants>
<IsPackable>false</IsPackable> <IsPackable>false</IsPackable>
<EmbeddedFilesManifestFileName>Microsoft.Extensions.FileProviders.Embedded.Manifest.xml</EmbeddedFilesManifestFileName>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
@ -22,7 +22,8 @@
<Reference Include="Microsoft.Extensions.FileProviders.Composite" /> <Reference Include="Microsoft.Extensions.FileProviders.Composite" />
<Reference Include="Microsoft.Extensions.FileProviders.Embedded" /> <Reference Include="Microsoft.Extensions.FileProviders.Embedded" />
<Reference Include="Microsoft.Extensions.Logging" /> <Reference Include="Microsoft.Extensions.Logging" />
<Reference Include="Microsoft.Extensions.ValueStopwatch.Sources" PrivateAssets="All" />
<Compile Include="$(SharedSourceRoot)ValueStopwatch\*.cs" />
<!-- Add a project dependency without reference output assemblies to enforce build order --> <!-- Add a project dependency without reference output assemblies to enforce build order -->
<!-- Applying workaround for https://github.com/microsoft/msbuild/issues/2661 and https://github.com/dotnet/sdk/issues/952 --> <!-- Applying workaround for https://github.com/microsoft/msbuild/issues/2661 and https://github.com/dotnet/sdk/issues/952 -->
@ -39,6 +40,15 @@
<MessagePackRoot>$(RepoRoot)src\submodules\MessagePack-CSharp\src\MessagePack.UnityClient\Assets\Scripts\MessagePack\</MessagePackRoot> <MessagePackRoot>$(RepoRoot)src\submodules\MessagePack-CSharp\src\MessagePack.UnityClient\Assets\Scripts\MessagePack\</MessagePackRoot>
</PropertyGroup> </PropertyGroup>
<!-- This workaround is required when referencing FileProviders.Embedded as a project -->
<PropertyGroup>
<_FileProviderTaskAssembly>$(ArtifactsDir)bin\Microsoft.Extensions.FileProviders.Embedded.Manifest.Task\$(Configuration)\netstandard2.0\Microsoft.Extensions.FileProviders.Embedded.Manifest.Task.dll</_FileProviderTaskAssembly>
</PropertyGroup>
<UsingTask
AssemblyFile="$(_FileProviderTaskAssembly)"
TaskName="Microsoft.Extensions.FileProviders.Embedded.Manifest.Task.GenerateEmbeddedResourcesManifest" />
<ItemGroup> <ItemGroup>
<Compile Include="$(ComponentsSharedSourceRoot)src\CacheHeaderSettings.cs" Link="Shared\CacheHeaderSettings.cs" /> <Compile Include="$(ComponentsSharedSourceRoot)src\CacheHeaderSettings.cs" Link="Shared\CacheHeaderSettings.cs" />
<Compile Include="$(ComponentsSharedSourceRoot)src\ArrayBuilder.cs" LinkBase="Circuits" /> <Compile Include="$(ComponentsSharedSourceRoot)src\ArrayBuilder.cs" LinkBase="Circuits" />
@ -84,5 +94,4 @@
<EmbeddedResource Include="$(BlazorServerJSFile)" LogicalName="_framework/%(Filename)%(Extension)" /> <EmbeddedResource Include="$(BlazorServerJSFile)" LogicalName="_framework/%(Filename)%(Extension)" />
<EmbeddedResource Include="$(BlazorServerJSFile).map" LogicalName="_framework/%(Filename)%(Extension)" Condition="Exists('$(BlazorServerJSFile).map')" /> <EmbeddedResource Include="$(BlazorServerJSFile).map" LogicalName="_framework/%(Filename)%(Extension)" Condition="Exists('$(BlazorServerJSFile).map')" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -0,0 +1,17 @@
<!-- This file is automatically generated. -->
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard2.0;$(DefaultNetCoreTargetFramework)</TargetFrameworks>
<TargetFrameworks Condition="'$(DotNetBuildFromSource)' == 'true'">$(DefaultNetCoreTargetFramework)</TargetFrameworks>
</PropertyGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
<Compile Include="Microsoft.Extensions.Configuration.KeyPerFile.netstandard2.0.cs" />
<Reference Include="Microsoft.Extensions.Configuration" />
<Reference Include="Microsoft.Extensions.FileProviders.Physical" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == '$(DefaultNetCoreTargetFramework)'">
<Compile Include="Microsoft.Extensions.Configuration.KeyPerFile.netcoreapp.cs" />
<Reference Include="Microsoft.Extensions.Configuration" />
<Reference Include="Microsoft.Extensions.FileProviders.Physical" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,34 @@
// 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.
namespace Microsoft.Extensions.Configuration
{
public static partial class KeyPerFileConfigurationBuilderExtensions
{
public static Microsoft.Extensions.Configuration.IConfigurationBuilder AddKeyPerFile(this Microsoft.Extensions.Configuration.IConfigurationBuilder builder, System.Action<Microsoft.Extensions.Configuration.KeyPerFile.KeyPerFileConfigurationSource> configureSource) { throw null; }
public static Microsoft.Extensions.Configuration.IConfigurationBuilder AddKeyPerFile(this Microsoft.Extensions.Configuration.IConfigurationBuilder builder, string directoryPath) { throw null; }
public static Microsoft.Extensions.Configuration.IConfigurationBuilder AddKeyPerFile(this Microsoft.Extensions.Configuration.IConfigurationBuilder builder, string directoryPath, bool optional) { throw null; }
public static Microsoft.Extensions.Configuration.IConfigurationBuilder AddKeyPerFile(this Microsoft.Extensions.Configuration.IConfigurationBuilder builder, string directoryPath, bool optional, bool reloadOnChange) { throw null; }
}
}
namespace Microsoft.Extensions.Configuration.KeyPerFile
{
public partial class KeyPerFileConfigurationProvider : Microsoft.Extensions.Configuration.ConfigurationProvider, System.IDisposable
{
public KeyPerFileConfigurationProvider(Microsoft.Extensions.Configuration.KeyPerFile.KeyPerFileConfigurationSource source) { }
public void Dispose() { }
public override void Load() { }
public override string ToString() { throw null; }
}
public partial class KeyPerFileConfigurationSource : Microsoft.Extensions.Configuration.IConfigurationSource
{
public KeyPerFileConfigurationSource() { }
public Microsoft.Extensions.FileProviders.IFileProvider FileProvider { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } }
public System.Func<string, bool> IgnoreCondition { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } }
public string IgnorePrefix { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } }
public bool Optional { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } }
public int ReloadDelay { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } }
public bool ReloadOnChange { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } }
public Microsoft.Extensions.Configuration.IConfigurationProvider Build(Microsoft.Extensions.Configuration.IConfigurationBuilder builder) { throw null; }
}
}

View File

@ -0,0 +1,34 @@
// 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.
namespace Microsoft.Extensions.Configuration
{
public static partial class KeyPerFileConfigurationBuilderExtensions
{
public static Microsoft.Extensions.Configuration.IConfigurationBuilder AddKeyPerFile(this Microsoft.Extensions.Configuration.IConfigurationBuilder builder, System.Action<Microsoft.Extensions.Configuration.KeyPerFile.KeyPerFileConfigurationSource> configureSource) { throw null; }
public static Microsoft.Extensions.Configuration.IConfigurationBuilder AddKeyPerFile(this Microsoft.Extensions.Configuration.IConfigurationBuilder builder, string directoryPath) { throw null; }
public static Microsoft.Extensions.Configuration.IConfigurationBuilder AddKeyPerFile(this Microsoft.Extensions.Configuration.IConfigurationBuilder builder, string directoryPath, bool optional) { throw null; }
public static Microsoft.Extensions.Configuration.IConfigurationBuilder AddKeyPerFile(this Microsoft.Extensions.Configuration.IConfigurationBuilder builder, string directoryPath, bool optional, bool reloadOnChange) { throw null; }
}
}
namespace Microsoft.Extensions.Configuration.KeyPerFile
{
public partial class KeyPerFileConfigurationProvider : Microsoft.Extensions.Configuration.ConfigurationProvider, System.IDisposable
{
public KeyPerFileConfigurationProvider(Microsoft.Extensions.Configuration.KeyPerFile.KeyPerFileConfigurationSource source) { }
public void Dispose() { }
public override void Load() { }
public override string ToString() { throw null; }
}
public partial class KeyPerFileConfigurationSource : Microsoft.Extensions.Configuration.IConfigurationSource
{
public KeyPerFileConfigurationSource() { }
public Microsoft.Extensions.FileProviders.IFileProvider FileProvider { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } }
public System.Func<string, bool> IgnoreCondition { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } }
public string IgnorePrefix { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } }
public bool Optional { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } }
public int ReloadDelay { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } }
public bool ReloadOnChange { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } }
public Microsoft.Extensions.Configuration.IConfigurationProvider Build(Microsoft.Extensions.Configuration.IConfigurationBuilder builder) { throw null; }
}
}

View File

@ -0,0 +1,65 @@
using System;
using System.IO;
using Microsoft.Extensions.Configuration.KeyPerFile;
using Microsoft.Extensions.FileProviders;
namespace Microsoft.Extensions.Configuration
{
/// <summary>
/// Extension methods for registering <see cref="KeyPerFileConfigurationProvider"/> with <see cref="IConfigurationBuilder"/>.
/// </summary>
public static class KeyPerFileConfigurationBuilderExtensions
{
/// <summary>
/// Adds configuration using files from a directory. File names are used as the key,
/// file contents are used as the value.
/// </summary>
/// <param name="builder">The <see cref="IConfigurationBuilder"/> to add to.</param>
/// <param name="directoryPath">The path to the directory.</param>
/// <returns>The <see cref="IConfigurationBuilder"/>.</returns>
public static IConfigurationBuilder AddKeyPerFile(this IConfigurationBuilder builder, string directoryPath)
=> builder.AddKeyPerFile(directoryPath, optional: false, reloadOnChange: false);
/// <summary>
/// Adds configuration using files from a directory. File names are used as the key,
/// file contents are used as the value.
/// </summary>
/// <param name="builder">The <see cref="IConfigurationBuilder"/> to add to.</param>
/// <param name="directoryPath">The path to the directory.</param>
/// <param name="optional">Whether the directory is optional.</param>
/// <returns>The <see cref="IConfigurationBuilder"/>.</returns>
public static IConfigurationBuilder AddKeyPerFile(this IConfigurationBuilder builder, string directoryPath, bool optional)
=> builder.AddKeyPerFile(directoryPath, optional, reloadOnChange: false);
/// <summary>
/// Adds configuration using files from a directory. File names are used as the key,
/// file contents are used as the value.
/// </summary>
/// <param name="builder">The <see cref="IConfigurationBuilder"/> to add to.</param>
/// <param name="directoryPath">The path to the directory.</param>
/// <param name="optional">Whether the directory is optional.</param>
/// <param name="reloadOnChange">Whether the configuration should be reloaded if the files are changed, added or removed.</param>
/// <returns>The <see cref="IConfigurationBuilder"/>.</returns>
public static IConfigurationBuilder AddKeyPerFile(this IConfigurationBuilder builder, string directoryPath, bool optional, bool reloadOnChange)
=> builder.AddKeyPerFile(source =>
{
// Only try to set the file provider if its not optional or the directory exists
if (!optional || Directory.Exists(directoryPath))
{
source.FileProvider = new PhysicalFileProvider(directoryPath);
}
source.Optional = optional;
source.ReloadOnChange = reloadOnChange;
});
/// <summary>
/// Adds configuration using files from a directory. File names are used as the key,
/// file contents are used as the value.
/// </summary>
/// <param name="builder">The <see cref="IConfigurationBuilder"/> to add to.</param>
/// <param name="configureSource">Configures the source.</param>
/// <returns>The <see cref="IConfigurationBuilder"/>.</returns>
public static IConfigurationBuilder AddKeyPerFile(this IConfigurationBuilder builder, Action<KeyPerFileConfigurationSource> configureSource)
=> builder.Add(configureSource);
}
}

View File

@ -0,0 +1,119 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using Microsoft.Extensions.Primitives;
namespace Microsoft.Extensions.Configuration.KeyPerFile
{
/// <summary>
/// A <see cref="ConfigurationProvider"/> that uses a directory's files as configuration key/values.
/// </summary>
public class KeyPerFileConfigurationProvider : ConfigurationProvider, IDisposable
{
private readonly IDisposable _changeTokenRegistration;
KeyPerFileConfigurationSource Source { get; set; }
/// <summary>
/// Initializes a new instance.
/// </summary>
/// <param name="source">The settings.</param>
public KeyPerFileConfigurationProvider(KeyPerFileConfigurationSource source)
{
Source = source ?? throw new ArgumentNullException(nameof(source));
if (Source.ReloadOnChange && Source.FileProvider != null)
{
_changeTokenRegistration = ChangeToken.OnChange(
() => Source.FileProvider.Watch("*"),
() =>
{
Thread.Sleep(Source.ReloadDelay);
Load(reload: true);
});
}
}
private static string NormalizeKey(string key)
=> key.Replace("__", ConfigurationPath.KeyDelimiter);
private static string TrimNewLine(string value)
=> value.EndsWith(Environment.NewLine)
? value.Substring(0, value.Length - Environment.NewLine.Length)
: value;
/// <summary>
/// Loads the configuration values.
/// </summary>
public override void Load()
{
Load(reload: false);
}
private void Load(bool reload)
{
var data = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
if (Source.FileProvider == null)
{
if (Source.Optional || reload) // Always optional on reload
{
Data = data;
return;
}
throw new DirectoryNotFoundException("A non-null file provider for the directory is required when this source is not optional.");
}
var directory = Source.FileProvider.GetDirectoryContents("/");
if (!directory.Exists)
{
if (Source.Optional || reload) // Always optional on reload
{
Data = data;
return;
}
throw new DirectoryNotFoundException("The root directory for the FileProvider doesn't exist and is not optional.");
}
else
{
foreach (var file in directory)
{
if (file.IsDirectory)
{
continue;
}
using var stream = file.CreateReadStream();
using var streamReader = new StreamReader(stream);
if (Source.IgnoreCondition == null || !Source.IgnoreCondition(file.Name))
{
data.Add(NormalizeKey(file.Name), TrimNewLine(streamReader.ReadToEnd()));
}
}
}
Data = data;
}
private string GetDirectoryName()
=> Source.FileProvider?.GetFileInfo("/")?.PhysicalPath ?? "<Unknown>";
/// <summary>
/// Generates a string representing this provider name and relevant details.
/// </summary>
/// <returns> The configuration name. </returns>
public override string ToString()
=> $"{GetType().Name} for files in '{GetDirectoryName()}' ({(Source.Optional ? "Optional" : "Required")})";
/// <inheritdoc />
public void Dispose()
{
_changeTokenRegistration?.Dispose();
}
}
}

View File

@ -0,0 +1,59 @@
using System;
using System.IO;
using Microsoft.Extensions.FileProviders;
namespace Microsoft.Extensions.Configuration.KeyPerFile
{
/// <summary>
/// An <see cref="IConfigurationSource"/> used to configure <see cref="KeyPerFileConfigurationProvider"/>.
/// </summary>
public class KeyPerFileConfigurationSource : IConfigurationSource
{
/// <summary>
/// Constructor;
/// </summary>
public KeyPerFileConfigurationSource()
=> IgnoreCondition = s => IgnorePrefix != null && s.StartsWith(IgnorePrefix);
/// <summary>
/// The FileProvider whos root "/" directory files will be used as configuration data.
/// </summary>
public IFileProvider FileProvider { get; set; }
/// <summary>
/// Files that start with this prefix will be excluded.
/// Defaults to "ignore.".
/// </summary>
public string IgnorePrefix { get; set; } = "ignore.";
/// <summary>
/// Used to determine if a file should be ignored using its name.
/// Defaults to using the IgnorePrefix.
/// </summary>
public Func<string, bool> IgnoreCondition { get; set; }
/// <summary>
/// If false, will throw if the directory doesn't exist.
/// </summary>
public bool Optional { get; set; }
/// <summary>
/// Determines whether the source will be loaded if the underlying file changes.
/// </summary>
public bool ReloadOnChange { get; set; }
/// <summary>
/// Number of milliseconds that reload will wait before calling Load. This helps
/// avoid triggering reload before a file is completely written. Default is 250.
/// </summary>
public int ReloadDelay { get; set; } = 250;
/// <summary>
/// Builds the <see cref="KeyPerFileConfigurationProvider"/> for this source.
/// </summary>
/// <param name="builder">The <see cref="IConfigurationBuilder"/>.</param>
/// <returns>A <see cref="KeyPerFileConfigurationProvider"/></returns>
public IConfigurationProvider Build(IConfigurationBuilder builder)
=> new KeyPerFileConfigurationProvider(this);
}
}

View File

@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Description>Configuration provider that uses files in a directory for Microsoft.Extensions.Configuration.</Description>
<TargetFrameworks>netstandard2.0;$(DefaultNetCoreTargetFramework)</TargetFrameworks>
<TargetFrameworks Condition="'$(DotNetBuildFromSource)' == 'true'">$(DefaultNetCoreTargetFramework)</TargetFrameworks>
<IsPackable>true</IsPackable>
<IsAspNetCoreApp>true</IsAspNetCoreApp>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<PackageTags>configuration</PackageTags>
<NoWarn>$(NoWarn);PKG0001</NoWarn>
</PropertyGroup>
<ItemGroup>
<Reference Include="Microsoft.Extensions.Configuration" />
<Reference Include="Microsoft.Extensions.FileProviders.Physical" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,2 @@

This is a configuration provider that uses a directory's files as data. A file's name is the key and the contents are the value.

View File

@ -0,0 +1,43 @@
// 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 Microsoft.Extensions.Configuration.Test;
using Microsoft.Extensions.FileProviders;
namespace Microsoft.Extensions.Configuration.KeyPerFile.Test
{
public class ConfigurationProviderCommandLineTest : ConfigurationProviderTestBase
{
protected override (IConfigurationProvider Provider, Action Initializer) LoadThroughProvider(
TestSection testConfig)
{
var testFiles = new List<IFileInfo>();
SectionToTestFiles(testFiles, "", testConfig);
var provider = new KeyPerFileConfigurationProvider(
new KeyPerFileConfigurationSource
{
Optional = true,
FileProvider = new TestFileProvider(testFiles.ToArray())
});
return (provider, () => { });
}
private void SectionToTestFiles(List<IFileInfo> testFiles, string sectionName, TestSection section)
{
foreach (var tuple in section.Values.SelectMany(e => e.Value.Expand(e.Key)))
{
testFiles.Add(new TestFile(sectionName + tuple.Key, tuple.Value));
}
foreach (var tuple in section.Sections)
{
SectionToTestFiles(testFiles, sectionName + tuple.Key + "__", tuple.Section);
}
}
}
}

View File

@ -0,0 +1,761 @@
// 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 Microsoft.Extensions.Configuration.Memory;
using Xunit;
namespace Microsoft.Extensions.Configuration.Test
{
public abstract class ConfigurationProviderTestBase
{
[Fact]
public virtual void Load_from_single_provider()
{
var configRoot = BuildConfigRoot(LoadThroughProvider(TestSection.TestConfig));
AssertConfig(configRoot);
}
[Fact]
public virtual void Has_debug_view()
{
var configRoot = BuildConfigRoot(LoadThroughProvider(TestSection.TestConfig));
var providerTag = configRoot.Providers.Single().ToString();
var expected =
$@"Key1=Value1 ({providerTag})
Section1:
Key2=Value12 ({providerTag})
Section2:
Key3=Value123 ({providerTag})
Key3a:
0=ArrayValue0 ({providerTag})
1=ArrayValue1 ({providerTag})
2=ArrayValue2 ({providerTag})
Section3:
Section4:
Key4=Value344 ({providerTag})
";
AssertDebugView(configRoot, expected);
}
[Fact]
public virtual void Null_values_are_included_in_the_config()
{
AssertConfig(BuildConfigRoot(LoadThroughProvider(TestSection.NullsTestConfig)), expectNulls: true, nullValue: "");
}
[Fact]
public virtual void Combine_after_other_provider()
{
AssertConfig(
BuildConfigRoot(
LoadUsingMemoryProvider(TestSection.MissingSection2ValuesConfig),
LoadThroughProvider(TestSection.MissingSection4Config)));
AssertConfig(
BuildConfigRoot(
LoadUsingMemoryProvider(TestSection.MissingSection4Config),
LoadThroughProvider(TestSection.MissingSection2ValuesConfig)));
}
[Fact]
public virtual void Combine_before_other_provider()
{
AssertConfig(
BuildConfigRoot(
LoadThroughProvider(TestSection.MissingSection2ValuesConfig),
LoadUsingMemoryProvider(TestSection.MissingSection4Config)));
AssertConfig(
BuildConfigRoot(
LoadThroughProvider(TestSection.MissingSection4Config),
LoadUsingMemoryProvider(TestSection.MissingSection2ValuesConfig)));
}
[Fact]
public virtual void Second_provider_overrides_values_from_first()
{
AssertConfig(
BuildConfigRoot(
LoadUsingMemoryProvider(TestSection.NoValuesTestConfig),
LoadThroughProvider(TestSection.TestConfig)));
}
[Fact]
public virtual void Combining_from_multiple_providers_is_case_insensitive()
{
AssertConfig(
BuildConfigRoot(
LoadUsingMemoryProvider(TestSection.DifferentCasedTestConfig),
LoadThroughProvider(TestSection.TestConfig)));
}
[Fact]
public virtual void Load_from_single_provider_with_duplicates_throws()
{
AssertFormatOrArgumentException(
() => BuildConfigRoot(LoadThroughProvider(TestSection.DuplicatesTestConfig)));
}
[Fact]
public virtual void Load_from_single_provider_with_differing_case_duplicates_throws()
{
AssertFormatOrArgumentException(
() => BuildConfigRoot(LoadThroughProvider(TestSection.DuplicatesDifferentCaseTestConfig)));
}
private void AssertFormatOrArgumentException(Action test)
{
Exception caught = null;
try
{
test();
}
catch (Exception e)
{
caught = e;
}
Assert.True(caught is ArgumentException
|| caught is FormatException);
}
[Fact]
public virtual void Bind_to_object()
{
var configuration = BuildConfigRoot(LoadThroughProvider(TestSection.TestConfig));
var options = configuration.Get<AsOptions>();
Assert.Equal("Value1", options.Key1);
Assert.Equal("Value12", options.Section1.Key2);
Assert.Equal("Value123", options.Section1.Section2.Key3);
Assert.Equal("Value344", options.Section3.Section4.Key4);
Assert.Equal(new[] { "ArrayValue0", "ArrayValue1", "ArrayValue2" }, options.Section1.Section2.Key3a);
}
public class AsOptions
{
public string Key1 { get; set; }
public Section1AsOptions Section1 { get; set; }
public Section3AsOptions Section3 { get; set; }
}
public class Section1AsOptions
{
public string Key2 { get; set; }
public Section2AsOptions Section2 { get; set; }
}
public class Section2AsOptions
{
public string Key3 { get; set; }
public string[] Key3a { get; set; }
}
public class Section3AsOptions
{
public Section4AsOptions Section4 { get; set; }
}
public class Section4AsOptions
{
public string Key4 { get; set; }
}
protected virtual void AssertDebugView(
IConfigurationRoot config,
string expected)
{
string RemoveLineEnds(string source) => source.Replace("\n", "").Replace("\r", "");
var actual = config.GetDebugView();
Assert.Equal(
RemoveLineEnds(expected),
RemoveLineEnds(actual));
}
protected virtual void AssertConfig(
IConfigurationRoot config,
bool expectNulls = false,
string nullValue = null)
{
var value1 = expectNulls ? nullValue : "Value1";
var value12 = expectNulls ? nullValue : "Value12";
var value123 = expectNulls ? nullValue : "Value123";
var arrayvalue0 = expectNulls ? nullValue : "ArrayValue0";
var arrayvalue1 = expectNulls ? nullValue : "ArrayValue1";
var arrayvalue2 = expectNulls ? nullValue : "ArrayValue2";
var value344 = expectNulls ? nullValue : "Value344";
Assert.Equal(value1, config["Key1"], StringComparer.InvariantCultureIgnoreCase);
Assert.Equal(value12, config["Section1:Key2"], StringComparer.InvariantCultureIgnoreCase);
Assert.Equal(value123, config["Section1:Section2:Key3"], StringComparer.InvariantCultureIgnoreCase);
Assert.Equal(arrayvalue0, config["Section1:Section2:Key3a:0"], StringComparer.InvariantCultureIgnoreCase);
Assert.Equal(arrayvalue1, config["Section1:Section2:Key3a:1"], StringComparer.InvariantCultureIgnoreCase);
Assert.Equal(arrayvalue2, config["Section1:Section2:Key3a:2"], StringComparer.InvariantCultureIgnoreCase);
Assert.Equal(value344, config["Section3:Section4:Key4"], StringComparer.InvariantCultureIgnoreCase);
var section1 = config.GetSection("Section1");
Assert.Equal(value12, section1["Key2"], StringComparer.InvariantCultureIgnoreCase);
Assert.Equal(value123, section1["Section2:Key3"], StringComparer.InvariantCultureIgnoreCase);
Assert.Equal(arrayvalue0, section1["Section2:Key3a:0"], StringComparer.InvariantCultureIgnoreCase);
Assert.Equal(arrayvalue1, section1["Section2:Key3a:1"], StringComparer.InvariantCultureIgnoreCase);
Assert.Equal(arrayvalue2, section1["Section2:Key3a:2"], StringComparer.InvariantCultureIgnoreCase);
Assert.Equal("Section1", section1.Path, StringComparer.InvariantCultureIgnoreCase);
Assert.Null(section1.Value);
var section2 = config.GetSection("Section1:Section2");
Assert.Equal(value123, section2["Key3"], StringComparer.InvariantCultureIgnoreCase);
Assert.Equal(arrayvalue0, section2["Key3a:0"], StringComparer.InvariantCultureIgnoreCase);
Assert.Equal(arrayvalue1, section2["Key3a:1"], StringComparer.InvariantCultureIgnoreCase);
Assert.Equal(arrayvalue2, section2["Key3a:2"], StringComparer.InvariantCultureIgnoreCase);
Assert.Equal("Section1:Section2", section2.Path, StringComparer.InvariantCultureIgnoreCase);
Assert.Null(section2.Value);
section2 = section1.GetSection("Section2");
Assert.Equal(value123, section2["Key3"], StringComparer.InvariantCultureIgnoreCase);
Assert.Equal(arrayvalue0, section2["Key3a:0"], StringComparer.InvariantCultureIgnoreCase);
Assert.Equal(arrayvalue1, section2["Key3a:1"], StringComparer.InvariantCultureIgnoreCase);
Assert.Equal(arrayvalue2, section2["Key3a:2"], StringComparer.InvariantCultureIgnoreCase);
Assert.Equal("Section1:Section2", section2.Path, StringComparer.InvariantCultureIgnoreCase);
Assert.Null(section2.Value);
var section3a = section2.GetSection("Key3a");
Assert.Equal(arrayvalue0, section3a["0"], StringComparer.InvariantCultureIgnoreCase);
Assert.Equal(arrayvalue1, section3a["1"], StringComparer.InvariantCultureIgnoreCase);
Assert.Equal(arrayvalue2, section3a["2"], StringComparer.InvariantCultureIgnoreCase);
Assert.Equal("Section1:Section2:Key3a", section3a.Path, StringComparer.InvariantCultureIgnoreCase);
Assert.Null(section3a.Value);
var section3 = config.GetSection("Section3");
Assert.Equal("Section3", section3.Path, StringComparer.InvariantCultureIgnoreCase);
Assert.Null(section3.Value);
var section4 = config.GetSection("Section3:Section4");
Assert.Equal(value344, section4["Key4"], StringComparer.InvariantCultureIgnoreCase);
Assert.Equal("Section3:Section4", section4.Path, StringComparer.InvariantCultureIgnoreCase);
Assert.Null(section4.Value);
section4 = config.GetSection("Section3").GetSection("Section4");
Assert.Equal(value344, section4["Key4"], StringComparer.InvariantCultureIgnoreCase);
Assert.Equal("Section3:Section4", section4.Path, StringComparer.InvariantCultureIgnoreCase);
Assert.Null(section4.Value);
var sections = config.GetChildren().ToList();
Assert.Equal(3, sections.Count);
Assert.Equal("Key1", sections[0].Key, StringComparer.InvariantCultureIgnoreCase);
Assert.Equal("Key1", sections[0].Path, StringComparer.InvariantCultureIgnoreCase);
Assert.Equal(value1, sections[0].Value, StringComparer.InvariantCultureIgnoreCase);
Assert.Equal("Section1", sections[1].Key, StringComparer.InvariantCultureIgnoreCase);
Assert.Equal("Section1", sections[1].Path, StringComparer.InvariantCultureIgnoreCase);
Assert.Null(sections[1].Value);
Assert.Equal("Section3", sections[2].Key, StringComparer.InvariantCultureIgnoreCase);
Assert.Equal("Section3", sections[2].Path, StringComparer.InvariantCultureIgnoreCase);
Assert.Null(sections[2].Value);
sections = section1.GetChildren().ToList();
Assert.Equal(2, sections.Count);
Assert.Equal("Key2", sections[0].Key, StringComparer.InvariantCultureIgnoreCase);
Assert.Equal("Section1:Key2", sections[0].Path, StringComparer.InvariantCultureIgnoreCase);
Assert.Equal(value12, sections[0].Value, StringComparer.InvariantCultureIgnoreCase);
Assert.Equal("Section2", sections[1].Key, StringComparer.InvariantCultureIgnoreCase);
Assert.Equal("Section1:Section2", sections[1].Path, StringComparer.InvariantCultureIgnoreCase);
Assert.Null(sections[1].Value);
sections = section2.GetChildren().ToList();
Assert.Equal(2, sections.Count);
Assert.Equal("Key3", sections[0].Key, StringComparer.InvariantCultureIgnoreCase);
Assert.Equal("Section1:Section2:Key3", sections[0].Path, StringComparer.InvariantCultureIgnoreCase);
Assert.Equal(value123, sections[0].Value, StringComparer.InvariantCultureIgnoreCase);
Assert.Equal("Key3a", sections[1].Key, StringComparer.InvariantCultureIgnoreCase);
Assert.Equal("Section1:Section2:Key3a", sections[1].Path, StringComparer.InvariantCultureIgnoreCase);
Assert.Null(sections[1].Value);
sections = section3a.GetChildren().ToList();
Assert.Equal(3, sections.Count);
Assert.Equal("0", sections[0].Key, StringComparer.InvariantCultureIgnoreCase);
Assert.Equal("Section1:Section2:Key3a:0", sections[0].Path, StringComparer.InvariantCultureIgnoreCase);
Assert.Equal(arrayvalue0, sections[0].Value, StringComparer.InvariantCultureIgnoreCase);
Assert.Equal("1", sections[1].Key, StringComparer.InvariantCultureIgnoreCase);
Assert.Equal("Section1:Section2:Key3a:1", sections[1].Path, StringComparer.InvariantCultureIgnoreCase);
Assert.Equal(arrayvalue1, sections[1].Value, StringComparer.InvariantCultureIgnoreCase);
Assert.Equal("2", sections[2].Key, StringComparer.InvariantCultureIgnoreCase);
Assert.Equal("Section1:Section2:Key3a:2", sections[2].Path, StringComparer.InvariantCultureIgnoreCase);
Assert.Equal(arrayvalue2, sections[2].Value, StringComparer.InvariantCultureIgnoreCase);
sections = section3.GetChildren().ToList();
Assert.Single(sections);
Assert.Equal("Section4", sections[0].Key, StringComparer.InvariantCultureIgnoreCase);
Assert.Equal("Section3:Section4", sections[0].Path, StringComparer.InvariantCultureIgnoreCase);
Assert.Null(sections[0].Value);
sections = section4.GetChildren().ToList();
Assert.Single(sections);
Assert.Equal("Key4", sections[0].Key, StringComparer.InvariantCultureIgnoreCase);
Assert.Equal("Section3:Section4:Key4", sections[0].Path, StringComparer.InvariantCultureIgnoreCase);
Assert.Equal(value344, sections[0].Value, StringComparer.InvariantCultureIgnoreCase);
}
protected abstract (IConfigurationProvider Provider, Action Initializer) LoadThroughProvider(TestSection testConfig);
protected virtual IConfigurationRoot BuildConfigRoot(
params (IConfigurationProvider Provider, Action Initializer)[] providers)
{
var root = new ConfigurationRoot(providers.Select(e => e.Provider).ToList());
foreach (var initializer in providers.Select(e => e.Initializer))
{
initializer();
}
return root;
}
protected static (IConfigurationProvider Provider, Action Initializer) LoadUsingMemoryProvider(TestSection testConfig)
{
var values = new List<KeyValuePair<string, string>>();
SectionToValues(testConfig, "", values);
return (new MemoryConfigurationProvider(
new MemoryConfigurationSource
{
InitialData = values
}),
() => { });
}
protected static void SectionToValues(
TestSection section,
string sectionName,
IList<KeyValuePair<string, string>> values)
{
foreach (var tuple in section.Values.SelectMany(e => e.Value.Expand(e.Key)))
{
values.Add(new KeyValuePair<string, string>(sectionName + tuple.Key, tuple.Value));
}
foreach (var tuple in section.Sections)
{
SectionToValues(
tuple.Section,
sectionName + tuple.Key + ":",
values);
}
}
protected class TestKeyValue
{
public object Value { get; }
public TestKeyValue(string value)
{
Value = value;
}
public TestKeyValue(string[] values)
{
Value = values;
}
public static implicit operator TestKeyValue(string value) => new TestKeyValue(value);
public static implicit operator TestKeyValue(string[] values) => new TestKeyValue(values);
public string[] AsArray => Value as string[];
public string AsString => Value as string;
public IEnumerable<(string Key, string Value)> Expand(string key)
{
if (AsArray == null)
{
yield return (key, AsString);
}
else
{
for (var i = 0; i < AsArray.Length; i++)
{
yield return ($"{key}:{i}", AsArray[i]);
}
}
}
}
protected class TestSection
{
public IEnumerable<(string Key, TestKeyValue Value)> Values { get; set; }
= Enumerable.Empty<(string, TestKeyValue)>();
public IEnumerable<(string Key, TestSection Section)> Sections { get; set; }
= Enumerable.Empty<(string, TestSection)>();
public static TestSection TestConfig { get; }
= new TestSection
{
Values = new[] { ("Key1", (TestKeyValue)"Value1") },
Sections = new[]
{
("Section1", new TestSection
{
Values = new[] {("Key2", (TestKeyValue)"Value12")},
Sections = new[]
{
("Section2", new TestSection
{
Values = new[]
{
("Key3", (TestKeyValue)"Value123"),
("Key3a", (TestKeyValue)new[] {"ArrayValue0", "ArrayValue1", "ArrayValue2"})
},
})
}
}),
("Section3", new TestSection
{
Sections = new[]
{
("Section4", new TestSection
{
Values = new[] {("Key4", (TestKeyValue)"Value344")}
})
}
})
}
};
public static TestSection NoValuesTestConfig { get; }
= new TestSection
{
Values = new[] { ("Key1", (TestKeyValue)"------") },
Sections = new[]
{
("Section1", new TestSection
{
Values = new[] {("Key2", (TestKeyValue)"-------")},
Sections = new[]
{
("Section2", new TestSection
{
Values = new[]
{
("Key3", (TestKeyValue)"-----"),
("Key3a", (TestKeyValue)new[] {"-----------", "-----------", "-----------"})
},
})
}
}),
("Section3", new TestSection
{
Sections = new[]
{
("Section4", new TestSection
{
Values = new[] {("Key4", (TestKeyValue)"--------")}
})
}
})
}
};
public static TestSection MissingSection2ValuesConfig { get; }
= new TestSection
{
Values = new[] { ("Key1", (TestKeyValue)"Value1") },
Sections = new[]
{
("Section1", new TestSection
{
Values = new[] {("Key2", (TestKeyValue)"Value12")},
Sections = new[]
{
("Section2", new TestSection
{
Values = new[]
{
("Key3a", (TestKeyValue)new[] {"ArrayValue0"})
},
})
}
}),
("Section3", new TestSection
{
Sections = new[]
{
("Section4", new TestSection
{
Values = new[] {("Key4", (TestKeyValue)"Value344")}
})
}
})
}
};
public static TestSection MissingSection4Config { get; }
= new TestSection
{
Values = new[] { ("Key1", (TestKeyValue)"Value1") },
Sections = new[]
{
("Section1", new TestSection
{
Values = new[] {("Key2", (TestKeyValue)"Value12")},
Sections = new[]
{
("Section2", new TestSection
{
Values = new[]
{
("Key3", (TestKeyValue)"Value123"),
("Key3a", (TestKeyValue)new[] {"ArrayValue0", "ArrayValue1", "ArrayValue2"})
},
})
}
}),
("Section3", new TestSection())
}
};
public static TestSection DifferentCasedTestConfig { get; }
= new TestSection
{
Values = new[] { ("KeY1", (TestKeyValue)"Value1") },
Sections = new[]
{
("SectioN1", new TestSection
{
Values = new[] {("KeY2", (TestKeyValue)"Value12")},
Sections = new[]
{
("SectioN2", new TestSection
{
Values = new[]
{
("KeY3", (TestKeyValue)"Value123"),
("KeY3a", (TestKeyValue)new[] {"ArrayValue0", "ArrayValue1", "ArrayValue2"})
},
})
}
}),
("SectioN3", new TestSection
{
Sections = new[]
{
("SectioN4", new TestSection
{
Values = new[] {("KeY4", (TestKeyValue)"Value344")}
})
}
})
}
};
public static TestSection DuplicatesTestConfig { get; }
= new TestSection
{
Values = new[]
{
("Key1", (TestKeyValue)"Value1"),
("Key1", (TestKeyValue)"Value1")
},
Sections = new[]
{
("Section1", new TestSection
{
Values = new[] {("Key2", (TestKeyValue)"Value12")},
Sections = new[]
{
("Section2", new TestSection
{
Values = new[]
{
("Key3", (TestKeyValue)"Value123"),
("Key3a", (TestKeyValue)new[] {"ArrayValue0", "ArrayValue1", "ArrayValue2"})
},
}),
("Section2", new TestSection
{
Values = new[]
{
("Key3", (TestKeyValue)"Value123"),
("Key3a", (TestKeyValue)new[] {"ArrayValue0", "ArrayValue1", "ArrayValue2"})
},
})
}
}),
("Section3", new TestSection
{
Sections = new[]
{
("Section4", new TestSection
{
Values = new[] {("Key4", (TestKeyValue)"Value344")}
})
}
})
}
};
public static TestSection DuplicatesDifferentCaseTestConfig { get; }
= new TestSection
{
Values = new[]
{
("Key1", (TestKeyValue)"Value1"),
("KeY1", (TestKeyValue)"Value1")
},
Sections = new[]
{
("Section1", new TestSection
{
Values = new[] {("Key2", (TestKeyValue)"Value12")},
Sections = new[]
{
("Section2", new TestSection
{
Values = new[]
{
("Key3", (TestKeyValue)"Value123"),
("Key3a", (TestKeyValue)new[] {"ArrayValue0", "ArrayValue1", "ArrayValue2"})
},
}),
("SectioN2", new TestSection
{
Values = new[]
{
("KeY3", (TestKeyValue)"Value123"),
("KeY3a", (TestKeyValue)new[] {"ArrayValue0", "ArrayValue1", "ArrayValue2"})
},
})
}
}),
("Section3", new TestSection
{
Sections = new[]
{
("Section4", new TestSection
{
Values = new[] {("Key4", (TestKeyValue)"Value344")}
})
}
})
}
};
public static TestSection NullsTestConfig { get; }
= new TestSection
{
Values = new[] { ("Key1", new TestKeyValue((string)null)) },
Sections = new[]
{
("Section1", new TestSection
{
Values = new[] {("Key2", new TestKeyValue((string)null))},
Sections = new[]
{
("Section2", new TestSection
{
Values = new[]
{
("Key3", new TestKeyValue((string)null)),
("Key3a", (TestKeyValue)new string[] {null, null, null})
},
})
}
}),
("Section3", new TestSection
{
Sections = new[]
{
("Section4", new TestSection
{
Values = new[] {("Key4", new TestKeyValue((string)null))}
})
}
})
}
};
public static TestSection ExtraValuesTestConfig { get; }
= new TestSection
{
Values = new[]
{
("Key1", (TestKeyValue)"Value1"),
("Key1r", (TestKeyValue)"Value1r")
},
Sections = new[]
{
("Section1", new TestSection
{
Values = new[]
{
("Key2", (TestKeyValue)"Value12"),
("Key2r", (TestKeyValue)"Value12r")
},
Sections = new[]
{
("Section2", new TestSection
{
Values = new[]
{
("Key3", (TestKeyValue)"Value123"),
("Key3a", (TestKeyValue)new[] {"ArrayValue0", "ArrayValue1", "ArrayValue2", "ArrayValue2r"}),
("Key3ar", (TestKeyValue)new[] {"ArrayValue0r"})
},
})
}
}),
("Section3", new TestSection
{
Sections = new[]
{
("Section4", new TestSection
{
Values = new[] {("Key4", (TestKeyValue)"Value344")}
})
}
}),
("Section5r", new TestSection
{
Sections = new[]
{
("Section6r", new TestSection
{
Values = new[] {("Key5r", (TestKeyValue)"Value565r")}
})
}
})
}
};
}
}
}

View File

@ -0,0 +1,416 @@
// 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;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Primitives;
using Xunit;
namespace Microsoft.Extensions.Configuration.KeyPerFile.Test
{
public class KeyPerFileTests
{
[Fact]
public void DoesNotThrowWhenOptionalAndNoSecrets()
{
new ConfigurationBuilder().AddKeyPerFile(o => o.Optional = true).Build();
}
[Fact]
public void DoesNotThrowWhenOptionalAndDirectoryDoesntExist()
{
new ConfigurationBuilder().AddKeyPerFile("nonexistent", true).Build();
}
[Fact]
public void ThrowsWhenNotOptionalAndDirectoryDoesntExist()
{
var e = Assert.Throws<ArgumentException>(() => new ConfigurationBuilder().AddKeyPerFile("nonexistent", false).Build());
Assert.Contains("The path must be absolute.", e.Message);
}
[Fact]
public void CanLoadMultipleSecrets()
{
var testFileProvider = new TestFileProvider(
new TestFile("Secret1", "SecretValue1"),
new TestFile("Secret2", "SecretValue2"));
var config = new ConfigurationBuilder()
.AddKeyPerFile(o => o.FileProvider = testFileProvider)
.Build();
Assert.Equal("SecretValue1", config["Secret1"]);
Assert.Equal("SecretValue2", config["Secret2"]);
}
[Fact]
public void CanLoadMultipleSecretsWithDirectory()
{
var testFileProvider = new TestFileProvider(
new TestFile("Secret1", "SecretValue1"),
new TestFile("Secret2", "SecretValue2"),
new TestFile("directory"));
var config = new ConfigurationBuilder()
.AddKeyPerFile(o => o.FileProvider = testFileProvider)
.Build();
Assert.Equal("SecretValue1", config["Secret1"]);
Assert.Equal("SecretValue2", config["Secret2"]);
}
[Fact]
public void CanLoadNestedKeys()
{
var testFileProvider = new TestFileProvider(
new TestFile("Secret0__Secret1__Secret2__Key", "SecretValue2"),
new TestFile("Secret0__Secret1__Key", "SecretValue1"),
new TestFile("Secret0__Key", "SecretValue0"));
var config = new ConfigurationBuilder()
.AddKeyPerFile(o => o.FileProvider = testFileProvider)
.Build();
Assert.Equal("SecretValue0", config["Secret0:Key"]);
Assert.Equal("SecretValue1", config["Secret0:Secret1:Key"]);
Assert.Equal("SecretValue2", config["Secret0:Secret1:Secret2:Key"]);
}
[Fact]
public void CanIgnoreFilesWithDefault()
{
var testFileProvider = new TestFileProvider(
new TestFile("ignore.Secret0", "SecretValue0"),
new TestFile("ignore.Secret1", "SecretValue1"),
new TestFile("Secret2", "SecretValue2"));
var config = new ConfigurationBuilder()
.AddKeyPerFile(o => o.FileProvider = testFileProvider)
.Build();
Assert.Null(config["ignore.Secret0"]);
Assert.Null(config["ignore.Secret1"]);
Assert.Equal("SecretValue2", config["Secret2"]);
}
[Fact]
public void CanTurnOffDefaultIgnorePrefixWithCondition()
{
var testFileProvider = new TestFileProvider(
new TestFile("ignore.Secret0", "SecretValue0"),
new TestFile("ignore.Secret1", "SecretValue1"),
new TestFile("Secret2", "SecretValue2"));
var config = new ConfigurationBuilder()
.AddKeyPerFile(o =>
{
o.FileProvider = testFileProvider;
o.IgnoreCondition = null;
})
.Build();
Assert.Equal("SecretValue0", config["ignore.Secret0"]);
Assert.Equal("SecretValue1", config["ignore.Secret1"]);
Assert.Equal("SecretValue2", config["Secret2"]);
}
[Fact]
public void CanIgnoreAllWithCondition()
{
var testFileProvider = new TestFileProvider(
new TestFile("Secret0", "SecretValue0"),
new TestFile("Secret1", "SecretValue1"),
new TestFile("Secret2", "SecretValue2"));
var config = new ConfigurationBuilder()
.AddKeyPerFile(o =>
{
o.FileProvider = testFileProvider;
o.IgnoreCondition = s => true;
})
.Build();
Assert.Empty(config.AsEnumerable());
}
[Fact]
public void CanIgnoreFilesWithCustomIgnore()
{
var testFileProvider = new TestFileProvider(
new TestFile("meSecret0", "SecretValue0"),
new TestFile("meSecret1", "SecretValue1"),
new TestFile("Secret2", "SecretValue2"));
var config = new ConfigurationBuilder()
.AddKeyPerFile(o =>
{
o.FileProvider = testFileProvider;
o.IgnorePrefix = "me";
})
.Build();
Assert.Null(config["meSecret0"]);
Assert.Null(config["meSecret1"]);
Assert.Equal("SecretValue2", config["Secret2"]);
}
[Fact]
public void CanUnIgnoreDefaultFiles()
{
var testFileProvider = new TestFileProvider(
new TestFile("ignore.Secret0", "SecretValue0"),
new TestFile("ignore.Secret1", "SecretValue1"),
new TestFile("Secret2", "SecretValue2"));
var config = new ConfigurationBuilder()
.AddKeyPerFile(o =>
{
o.FileProvider = testFileProvider;
o.IgnorePrefix = null;
})
.Build();
Assert.Equal("SecretValue0", config["ignore.Secret0"]);
Assert.Equal("SecretValue1", config["ignore.Secret1"]);
Assert.Equal("SecretValue2", config["Secret2"]);
}
[Fact]
public void BindingDoesNotThrowIfReloadedDuringBinding()
{
var testFileProvider = new TestFileProvider(
new TestFile("Number", "-2"),
new TestFile("Text", "Foo"));
var config = new ConfigurationBuilder()
.AddKeyPerFile(o => o.FileProvider = testFileProvider)
.Build();
MyOptions options = null;
using (var cts = new CancellationTokenSource(TimeSpan.FromMilliseconds(250)))
{
void ReloadLoop()
{
while (!cts.IsCancellationRequested)
{
config.Reload();
}
}
_ = Task.Run(ReloadLoop);
while (!cts.IsCancellationRequested)
{
options = config.Get<MyOptions>();
}
}
Assert.Equal(-2, options.Number);
Assert.Equal("Foo", options.Text);
}
[Fact]
public void ReloadConfigWhenReloadOnChangeIsTrue()
{
var testFileProvider = new TestFileProvider(
new TestFile("Secret1", "SecretValue1"),
new TestFile("Secret2", "SecretValue2"));
var config = new ConfigurationBuilder()
.AddKeyPerFile(o =>
{
o.FileProvider = testFileProvider;
o.ReloadOnChange = true;
}).Build();
Assert.Equal("SecretValue1", config["Secret1"]);
Assert.Equal("SecretValue2", config["Secret2"]);
testFileProvider.ChangeFiles(
new TestFile("Secret1", "NewSecretValue1"),
new TestFile("Secret3", "NewSecretValue3"));
Assert.Equal("NewSecretValue1", config["Secret1"]);
Assert.Null(config["NewSecret2"]);
Assert.Equal("NewSecretValue3", config["Secret3"]);
}
[Fact]
public void SameConfigWhenReloadOnChangeIsFalse()
{
var testFileProvider = new TestFileProvider(
new TestFile("Secret1", "SecretValue1"),
new TestFile("Secret2", "SecretValue2"));
var config = new ConfigurationBuilder()
.AddKeyPerFile(o =>
{
o.FileProvider = testFileProvider;
o.ReloadOnChange = false;
}).Build();
Assert.Equal("SecretValue1", config["Secret1"]);
Assert.Equal("SecretValue2", config["Secret2"]);
testFileProvider.ChangeFiles(
new TestFile("Secret1", "NewSecretValue1"),
new TestFile("Secret3", "NewSecretValue3"));
Assert.Equal("SecretValue1", config["Secret1"]);
Assert.Equal("SecretValue2", config["Secret2"]);
}
[Fact]
public void NoFilesReloadWhenAddedFiles()
{
var testFileProvider = new TestFileProvider();
var config = new ConfigurationBuilder()
.AddKeyPerFile(o =>
{
o.FileProvider = testFileProvider;
o.ReloadOnChange = true;
}).Build();
Assert.Empty(config.AsEnumerable());
testFileProvider.ChangeFiles(
new TestFile("Secret1", "SecretValue1"),
new TestFile("Secret2", "SecretValue2"));
Assert.Equal("SecretValue1", config["Secret1"]);
Assert.Equal("SecretValue2", config["Secret2"]);
}
private sealed class MyOptions
{
public int Number { get; set; }
public string Text { get; set; }
}
}
class TestFileProvider : IFileProvider
{
IDirectoryContents _contents;
MockChangeToken _changeToken;
public TestFileProvider(params IFileInfo[] files)
{
_contents = new TestDirectoryContents(files);
_changeToken = new MockChangeToken();
}
public IDirectoryContents GetDirectoryContents(string subpath) => _contents;
public IFileInfo GetFileInfo(string subpath) => new TestFile("TestDirectory");
public IChangeToken Watch(string filter) => _changeToken;
internal void ChangeFiles(params IFileInfo[] files)
{
_contents = new TestDirectoryContents(files);
_changeToken.RaiseCallback();
}
}
class MockChangeToken : IChangeToken
{
private Action _callback;
public bool ActiveChangeCallbacks => true;
public bool HasChanged => true;
public IDisposable RegisterChangeCallback(Action<object> callback, object state)
{
var disposable = new MockDisposable();
_callback = () => callback(state);
return disposable;
}
internal void RaiseCallback()
{
_callback?.Invoke();
}
}
class MockDisposable : IDisposable
{
public bool Disposed { get; set; }
public void Dispose()
{
Disposed = true;
}
}
class TestDirectoryContents : IDirectoryContents
{
List<IFileInfo> _list;
public TestDirectoryContents(params IFileInfo[] files)
{
_list = new List<IFileInfo>(files);
}
public bool Exists => true;
public IEnumerator<IFileInfo> GetEnumerator() => _list.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
//TODO: Probably need a directory and file type.
class TestFile : IFileInfo
{
private readonly string _name;
private readonly string _contents;
public bool Exists => true;
public bool IsDirectory
{
get;
}
public DateTimeOffset LastModified => throw new NotImplementedException();
public long Length => throw new NotImplementedException();
public string Name => _name;
public string PhysicalPath => "Root/" + Name;
public TestFile(string name)
{
_name = name;
IsDirectory = true;
}
public TestFile(string name, string contents)
{
_name = name;
_contents = contents;
}
public Stream CreateReadStream()
{
if (IsDirectory)
{
throw new InvalidOperationException("Cannot create stream from directory");
}
return _contents == null
? new MemoryStream()
: new MemoryStream(Encoding.UTF8.GetBytes(_contents));
}
}
}

View File

@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>$(DefaultNetCoreTargetFramework);net472</TargetFrameworks>
</PropertyGroup>
<ItemGroup>
<Reference Include="Microsoft.Extensions.Configuration.Abstractions" />
<Reference Include="Microsoft.Extensions.Configuration.Binder" />
<Reference Include="Microsoft.Extensions.Configuration.FileExtensions" />
<Reference Include="Microsoft.Extensions.Configuration.KeyPerFile" />
<Reference Include="Microsoft.Extensions.Configuration" />
<Reference Include="Microsoft.Extensions.FileProviders.Abstractions" />
<Reference Include="Microsoft.Extensions.FileProviders.Physical" />
<Reference Include="Microsoft.Extensions.Primitives" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,17 @@
<!-- This file is automatically generated. -->
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard2.0;$(DefaultNetCoreTargetFramework)</TargetFrameworks>
<TargetFrameworks Condition="'$(DotNetBuildFromSource)' == 'true'">$(DefaultNetCoreTargetFramework)</TargetFrameworks>
</PropertyGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
<Compile Include="Microsoft.Extensions.FileProviders.Embedded.netstandard2.0.cs" />
<Reference Include="Microsoft.Extensions.FileProviders.Abstractions" />
<InternalsVisibleTo Include="Microsoft.Extensions.FileProviders.Embedded.Tests" Key="" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == '$(DefaultNetCoreTargetFramework)'">
<Compile Include="Microsoft.Extensions.FileProviders.Embedded.netcoreapp.cs" />
<Reference Include="Microsoft.Extensions.FileProviders.Abstractions" />
<InternalsVisibleTo Include="Microsoft.Extensions.FileProviders.Embedded.Tests" Key="" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,39 @@
// 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.
namespace Microsoft.Extensions.FileProviders
{
public partial class EmbeddedFileProvider : Microsoft.Extensions.FileProviders.IFileProvider
{
public EmbeddedFileProvider(System.Reflection.Assembly assembly) { }
public EmbeddedFileProvider(System.Reflection.Assembly assembly, string baseNamespace) { }
public Microsoft.Extensions.FileProviders.IDirectoryContents GetDirectoryContents(string subpath) { throw null; }
public Microsoft.Extensions.FileProviders.IFileInfo GetFileInfo(string subpath) { throw null; }
public Microsoft.Extensions.Primitives.IChangeToken Watch(string pattern) { throw null; }
}
public partial class ManifestEmbeddedFileProvider : Microsoft.Extensions.FileProviders.IFileProvider
{
public ManifestEmbeddedFileProvider(System.Reflection.Assembly assembly) { }
public ManifestEmbeddedFileProvider(System.Reflection.Assembly assembly, string root) { }
public ManifestEmbeddedFileProvider(System.Reflection.Assembly assembly, string root, System.DateTimeOffset lastModified) { }
public ManifestEmbeddedFileProvider(System.Reflection.Assembly assembly, string root, string manifestName, System.DateTimeOffset lastModified) { }
public System.Reflection.Assembly Assembly { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } }
public Microsoft.Extensions.FileProviders.IDirectoryContents GetDirectoryContents(string subpath) { throw null; }
public Microsoft.Extensions.FileProviders.IFileInfo GetFileInfo(string subpath) { throw null; }
public Microsoft.Extensions.Primitives.IChangeToken Watch(string filter) { throw null; }
}
}
namespace Microsoft.Extensions.FileProviders.Embedded
{
public partial class EmbeddedResourceFileInfo : Microsoft.Extensions.FileProviders.IFileInfo
{
public EmbeddedResourceFileInfo(System.Reflection.Assembly assembly, string resourcePath, string name, System.DateTimeOffset lastModified) { }
public bool Exists { get { throw null; } }
public bool IsDirectory { get { throw null; } }
public System.DateTimeOffset LastModified { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } }
public long Length { get { throw null; } }
public string Name { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } }
public string PhysicalPath { get { throw null; } }
public System.IO.Stream CreateReadStream() { throw null; }
}
}

View File

@ -0,0 +1,39 @@
// 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.
namespace Microsoft.Extensions.FileProviders
{
public partial class EmbeddedFileProvider : Microsoft.Extensions.FileProviders.IFileProvider
{
public EmbeddedFileProvider(System.Reflection.Assembly assembly) { }
public EmbeddedFileProvider(System.Reflection.Assembly assembly, string baseNamespace) { }
public Microsoft.Extensions.FileProviders.IDirectoryContents GetDirectoryContents(string subpath) { throw null; }
public Microsoft.Extensions.FileProviders.IFileInfo GetFileInfo(string subpath) { throw null; }
public Microsoft.Extensions.Primitives.IChangeToken Watch(string pattern) { throw null; }
}
public partial class ManifestEmbeddedFileProvider : Microsoft.Extensions.FileProviders.IFileProvider
{
public ManifestEmbeddedFileProvider(System.Reflection.Assembly assembly) { }
public ManifestEmbeddedFileProvider(System.Reflection.Assembly assembly, string root) { }
public ManifestEmbeddedFileProvider(System.Reflection.Assembly assembly, string root, System.DateTimeOffset lastModified) { }
public ManifestEmbeddedFileProvider(System.Reflection.Assembly assembly, string root, string manifestName, System.DateTimeOffset lastModified) { }
public System.Reflection.Assembly Assembly { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } }
public Microsoft.Extensions.FileProviders.IDirectoryContents GetDirectoryContents(string subpath) { throw null; }
public Microsoft.Extensions.FileProviders.IFileInfo GetFileInfo(string subpath) { throw null; }
public Microsoft.Extensions.Primitives.IChangeToken Watch(string filter) { throw null; }
}
}
namespace Microsoft.Extensions.FileProviders.Embedded
{
public partial class EmbeddedResourceFileInfo : Microsoft.Extensions.FileProviders.IFileInfo
{
public EmbeddedResourceFileInfo(System.Reflection.Assembly assembly, string resourcePath, string name, System.DateTimeOffset lastModified) { }
public bool Exists { get { throw null; } }
public bool IsDirectory { get { throw null; } }
public System.DateTimeOffset LastModified { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } }
public long Length { get { throw null; } }
public string Name { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } }
public string PhysicalPath { get { throw null; } }
public System.IO.Stream CreateReadStream() { throw null; }
}
}

View File

@ -0,0 +1,181 @@
// 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.Text;
using Microsoft.Extensions.FileProviders.Embedded;
using Microsoft.Extensions.Primitives;
namespace Microsoft.Extensions.FileProviders
{
/// <summary>
/// Looks up files using embedded resources in the specified assembly.
/// This file provider is case sensitive.
/// </summary>
public class EmbeddedFileProvider : IFileProvider
{
private static readonly char[] _invalidFileNameChars = Path.GetInvalidFileNameChars()
.Where(c => c != '/' && c != '\\').ToArray();
private readonly Assembly _assembly;
private readonly string _baseNamespace;
private readonly DateTimeOffset _lastModified;
/// <summary>
/// Initializes a new instance of the <see cref="EmbeddedFileProvider" /> class using the specified
/// assembly with the base namespace defaulting to the assembly name.
/// </summary>
/// <param name="assembly">The assembly that contains the embedded resources.</param>
public EmbeddedFileProvider(Assembly assembly)
: this(assembly, assembly?.GetName()?.Name)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="EmbeddedFileProvider" /> class using the specified
/// assembly and base namespace.
/// </summary>
/// <param name="assembly">The assembly that contains the embedded resources.</param>
/// <param name="baseNamespace">The base namespace that contains the embedded resources.</param>
public EmbeddedFileProvider(Assembly assembly, string baseNamespace)
{
if (assembly == null)
{
throw new ArgumentNullException("assembly");
}
_baseNamespace = string.IsNullOrEmpty(baseNamespace) ? string.Empty : baseNamespace + ".";
_assembly = assembly;
_lastModified = DateTimeOffset.UtcNow;
if (!string.IsNullOrEmpty(_assembly.Location))
{
try
{
_lastModified = File.GetLastWriteTimeUtc(_assembly.Location);
}
catch (PathTooLongException)
{
}
catch (UnauthorizedAccessException)
{
}
}
}
/// <summary>
/// Locates a file at the given path.
/// </summary>
/// <param name="subpath">The path that identifies the file. </param>
/// <returns>
/// The file information. Caller must check Exists property. A <see cref="NotFoundFileInfo" /> if the file could
/// not be found.
/// </returns>
public IFileInfo GetFileInfo(string subpath)
{
if (string.IsNullOrEmpty(subpath))
{
return new NotFoundFileInfo(subpath);
}
var builder = new StringBuilder(_baseNamespace.Length + subpath.Length);
builder.Append(_baseNamespace);
// Relative paths starting with a leading slash okay
if (subpath.StartsWith("/", StringComparison.Ordinal))
{
builder.Append(subpath, 1, subpath.Length - 1);
}
else
{
builder.Append(subpath);
}
for (var i = _baseNamespace.Length; i < builder.Length; i++)
{
if (builder[i] == '/' || builder[i] == '\\')
{
builder[i] = '.';
}
}
var resourcePath = builder.ToString();
if (HasInvalidPathChars(resourcePath))
{
return new NotFoundFileInfo(resourcePath);
}
var name = Path.GetFileName(subpath);
if (_assembly.GetManifestResourceInfo(resourcePath) == null)
{
return new NotFoundFileInfo(name);
}
return new EmbeddedResourceFileInfo(_assembly, resourcePath, name, _lastModified);
}
/// <summary>
/// Enumerate a directory at the given path, if any.
/// This file provider uses a flat directory structure. Everything under the base namespace is considered to be one
/// directory.
/// </summary>
/// <param name="subpath">The path that identifies the directory</param>
/// <returns>
/// Contents of the directory. Caller must check Exists property. A <see cref="NotFoundDirectoryContents" /> if no
/// resources were found that match <paramref name="subpath" />
/// </returns>
public IDirectoryContents GetDirectoryContents(string subpath)
{
// The file name is assumed to be the remainder of the resource name.
if (subpath == null)
{
return NotFoundDirectoryContents.Singleton;
}
// EmbeddedFileProvider only supports a flat file structure at the base namespace.
if (subpath.Length != 0 && !string.Equals(subpath, "/", StringComparison.Ordinal))
{
return NotFoundDirectoryContents.Singleton;
}
var entries = new List<IFileInfo>();
// TODO: The list of resources in an assembly isn't going to change. Consider caching.
var resources = _assembly.GetManifestResourceNames();
for (var i = 0; i < resources.Length; i++)
{
var resourceName = resources[i];
if (resourceName.StartsWith(_baseNamespace, StringComparison.Ordinal))
{
entries.Add(new EmbeddedResourceFileInfo(
_assembly,
resourceName,
resourceName.Substring(_baseNamespace.Length),
_lastModified));
}
}
return new EnumerableDirectoryContents(entries);
}
/// <summary>
/// Embedded files do not change.
/// </summary>
/// <param name="pattern">This parameter is ignored</param>
/// <returns>A <see cref="NullChangeToken" /></returns>
public IChangeToken Watch(string pattern)
{
return NullChangeToken.Singleton;
}
private static bool HasInvalidPathChars(string path)
{
return path.IndexOfAny(_invalidFileNameChars) != -1;
}
}
}

View File

@ -0,0 +1,94 @@
// 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;
namespace Microsoft.Extensions.FileProviders.Embedded
{
/// <summary>
/// Represents a file embedded in an assembly.
/// </summary>
public class EmbeddedResourceFileInfo : IFileInfo
{
private readonly Assembly _assembly;
private readonly string _resourcePath;
private long? _length;
/// <summary>
/// Initializes a new instance of <see cref="EmbeddedFileProvider"/> for an assembly using <paramref name="resourcePath"/> as the base
/// </summary>
/// <param name="assembly">The assembly that contains the embedded resource</param>
/// <param name="resourcePath">The path to the embedded resource</param>
/// <param name="name">An arbitrary name for this instance</param>
/// <param name="lastModified">The <see cref="DateTimeOffset" /> to use for <see cref="LastModified" /></param>
public EmbeddedResourceFileInfo(
Assembly assembly,
string resourcePath,
string name,
DateTimeOffset lastModified)
{
_assembly = assembly;
_resourcePath = resourcePath;
Name = name;
LastModified = lastModified;
}
/// <summary>
/// Always true.
/// </summary>
public bool Exists => true;
/// <summary>
/// The length, in bytes, of the embedded resource
/// </summary>
public long Length
{
get
{
if (!_length.HasValue)
{
using (var stream = _assembly.GetManifestResourceStream(_resourcePath))
{
_length = stream.Length;
}
}
return _length.Value;
}
}
/// <summary>
/// Always null.
/// </summary>
public string PhysicalPath => null;
/// <summary>
/// The name of embedded file
/// </summary>
public string Name { get; }
/// <summary>
/// The time, in UTC, when the <see cref="EmbeddedFileProvider"/> was created
/// </summary>
public DateTimeOffset LastModified { get; }
/// <summary>
/// Always false.
/// </summary>
public bool IsDirectory => false;
/// <inheritdoc />
public Stream CreateReadStream()
{
var stream = _assembly.GetManifestResourceStream(_resourcePath);
if (!_length.HasValue)
{
_length = stream.Length;
}
return stream;
}
}
}

View File

@ -0,0 +1,39 @@
// 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;
using System.Collections.Generic;
namespace Microsoft.Extensions.FileProviders.Embedded
{
internal class EnumerableDirectoryContents : IDirectoryContents
{
private readonly IEnumerable<IFileInfo> _entries;
public EnumerableDirectoryContents(IEnumerable<IFileInfo> entries)
{
if (entries == null)
{
throw new ArgumentNullException(nameof(entries));
}
_entries = entries;
}
public bool Exists
{
get { return true; }
}
public IEnumerator<IFileInfo> GetEnumerator()
{
return _entries.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return _entries.GetEnumerator();
}
}
}

View File

@ -0,0 +1,91 @@
// 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.Diagnostics;
using System.IO;
using System.Linq;
using Microsoft.Extensions.Primitives;
namespace Microsoft.Extensions.FileProviders.Embedded.Manifest
{
internal class EmbeddedFilesManifest
{
private static readonly char[] _invalidFileNameChars = Path.GetInvalidFileNameChars()
.Where(c => c != Path.DirectorySeparatorChar && c != Path.AltDirectorySeparatorChar).ToArray();
private static readonly char[] _separators = new char[] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar };
private readonly ManifestDirectory _rootDirectory;
internal EmbeddedFilesManifest(ManifestDirectory rootDirectory)
{
if (rootDirectory == null)
{
throw new ArgumentNullException(nameof(rootDirectory));
}
_rootDirectory = rootDirectory;
}
internal ManifestEntry ResolveEntry(string path)
{
if (string.IsNullOrEmpty(path) || HasInvalidPathChars(path))
{
return null;
}
// trimmed is a string without leading nor trailing path separators
// so if we find an empty string while iterating over the segments
// we know for sure the path is invalid and we treat it as the above
// case by returning null.
// Examples of invalid paths are: //wwwroot /\wwwroot //wwwroot//jquery.js
var trimmed = RemoveLeadingAndTrailingDirectorySeparators(path);
// Paths consisting only of a single path separator like / or \ are ok.
if (trimmed.Length == 0)
{
return _rootDirectory;
}
var tokenizer = new StringTokenizer(trimmed, _separators);
ManifestEntry currentEntry = _rootDirectory;
foreach (var segment in tokenizer)
{
if (segment.Equals(""))
{
return null;
}
currentEntry = currentEntry.Traverse(segment);
}
return currentEntry;
}
private static StringSegment RemoveLeadingAndTrailingDirectorySeparators(string path)
{
Debug.Assert(path.Length > 0);
var start = Array.IndexOf(_separators, path[0]) == -1 ? 0 : 1;
if (start == path.Length)
{
return StringSegment.Empty;
}
var end = Array.IndexOf(_separators, path[path.Length - 1]) == -1 ? path.Length : path.Length - 1;
var trimmed = new StringSegment(path, start, end - start);
return trimmed;
}
internal EmbeddedFilesManifest Scope(string path)
{
if (ResolveEntry(path) is ManifestDirectory directory && directory != ManifestEntry.UnknownPath)
{
return new EmbeddedFilesManifest(directory.ToRootDirectory());
}
throw new InvalidOperationException($"Invalid path: '{path}'");
}
private static bool HasInvalidPathChars(string path) => path.IndexOfAny(_invalidFileNameChars) != -1;
}
}

View File

@ -0,0 +1,127 @@
// 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 Microsoft.Extensions.Primitives;
namespace Microsoft.Extensions.FileProviders.Embedded.Manifest
{
internal class ManifestDirectory : ManifestEntry
{
protected ManifestDirectory(string name, ManifestEntry[] children)
: base(name)
{
if (children == null)
{
throw new ArgumentNullException(nameof(children));
}
Children = children;
}
public IReadOnlyList<ManifestEntry> Children { get; protected set; }
public override ManifestEntry Traverse(StringSegment segment)
{
if (segment.Equals(".", StringComparison.Ordinal))
{
return this;
}
if (segment.Equals("..", StringComparison.Ordinal))
{
return Parent;
}
foreach (var child in Children)
{
if (segment.Equals(child.Name, StringComparison.OrdinalIgnoreCase))
{
return child;
}
}
return UnknownPath;
}
public virtual ManifestDirectory ToRootDirectory() => CreateRootDirectory(CopyChildren());
public static ManifestDirectory CreateDirectory(string name, ManifestEntry[] children)
{
if (string.IsNullOrWhiteSpace(name))
{
throw new ArgumentException($"'{nameof(name)}' must not be null, empty or whitespace.", nameof(name));
}
if (children == null)
{
throw new ArgumentNullException(nameof(children));
}
var result = new ManifestDirectory(name, children);
ValidateChildrenAndSetParent(children, result);
return result;
}
public static ManifestRootDirectory CreateRootDirectory(ManifestEntry[] children)
{
if (children == null)
{
throw new ArgumentNullException(nameof(children));
}
var result = new ManifestRootDirectory(children);
ValidateChildrenAndSetParent(children, result);
return result;
}
internal static void ValidateChildrenAndSetParent(ManifestEntry[] children, ManifestDirectory parent)
{
foreach (var child in children)
{
if (child == UnknownPath)
{
throw new InvalidOperationException($"Invalid entry type '{nameof(ManifestSinkDirectory)}'");
}
if (child is ManifestRootDirectory)
{
throw new InvalidOperationException($"Can't add a root folder as a child");
}
child.SetParent(parent);
}
}
private ManifestEntry[] CopyChildren()
{
var list = new List<ManifestEntry>();
for (int i = 0; i < Children.Count; i++)
{
var child = Children[i];
switch (child)
{
case ManifestSinkDirectory s:
case ManifestRootDirectory r:
throw new InvalidOperationException("Unexpected manifest node.");
case ManifestDirectory d:
var grandChildren = d.CopyChildren();
var newDirectory = CreateDirectory(d.Name, grandChildren);
list.Add(newDirectory);
break;
case ManifestFile f:
var file = new ManifestFile(f.Name, f.ResourcePath);
list.Add(file);
break;
default:
throw new InvalidOperationException("Unexpected manifest node.");
}
}
return list.ToArray();
}
}
}

View File

@ -0,0 +1,72 @@
// 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;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace Microsoft.Extensions.FileProviders.Embedded.Manifest
{
internal class ManifestDirectoryContents : IDirectoryContents
{
private readonly DateTimeOffset _lastModified;
private IFileInfo[] _entries;
public ManifestDirectoryContents(Assembly assembly, ManifestDirectory directory, DateTimeOffset lastModified)
{
if (assembly == null)
{
throw new ArgumentNullException(nameof(assembly));
}
if (directory == null)
{
throw new ArgumentNullException(nameof(directory));
}
Assembly = assembly;
Directory = directory;
_lastModified = lastModified;
}
public bool Exists => true;
public Assembly Assembly { get; }
public ManifestDirectory Directory { get; }
public IEnumerator<IFileInfo> GetEnumerator()
{
return EnsureEntries().GetEnumerator();
IReadOnlyList<IFileInfo> EnsureEntries() => _entries = _entries ?? ResolveEntries().ToArray();
IEnumerable<IFileInfo> ResolveEntries()
{
if (Directory == ManifestEntry.UnknownPath)
{
yield break;
}
foreach (var entry in Directory.Children)
{
switch (entry)
{
case ManifestFile f:
yield return new ManifestFileInfo(Assembly, f, _lastModified);
break;
case ManifestDirectory d:
yield return new ManifestDirectoryInfo(d, _lastModified);
break;
default:
throw new InvalidOperationException("Unknown entry type");
}
}
}
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
}

View File

@ -0,0 +1,39 @@
// 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;
namespace Microsoft.Extensions.FileProviders.Embedded.Manifest
{
internal class ManifestDirectoryInfo : IFileInfo
{
public ManifestDirectoryInfo(ManifestDirectory directory, DateTimeOffset lastModified)
{
if (directory == null)
{
throw new ArgumentNullException(nameof(directory));
}
Directory = directory;
LastModified = lastModified;
}
public bool Exists => true;
public long Length => -1;
public string PhysicalPath => null;
public string Name => Directory.Name;
public DateTimeOffset LastModified { get; }
public bool IsDirectory => true;
public ManifestDirectory Directory { get; }
public Stream CreateReadStream() =>
throw new InvalidOperationException("Cannot create a stream for a directory.");
}
}

View File

@ -0,0 +1,34 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.Extensions.Primitives;
namespace Microsoft.Extensions.FileProviders.Embedded.Manifest
{
internal abstract class ManifestEntry
{
public ManifestEntry(string name)
{
Name = name;
}
public ManifestEntry Parent { get; private set; }
public string Name { get; }
public static ManifestEntry UnknownPath { get; } = ManifestSinkDirectory.Instance;
protected internal virtual void SetParent(ManifestDirectory directory)
{
if (Parent != null)
{
throw new InvalidOperationException("Directory already has a parent.");
}
Parent = directory;
}
public abstract ManifestEntry Traverse(StringSegment segment);
}
}

View File

@ -0,0 +1,31 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.Extensions.Primitives;
namespace Microsoft.Extensions.FileProviders.Embedded.Manifest
{
internal class ManifestFile : ManifestEntry
{
public ManifestFile(string name, string resourcePath)
: base(name)
{
if (string.IsNullOrWhiteSpace(name))
{
throw new ArgumentException($"'{nameof(name)}' must not be null, empty or whitespace.", nameof(name));
}
if (string.IsNullOrWhiteSpace(resourcePath))
{
throw new ArgumentException($"'{nameof(resourcePath)}' must not be null, empty or whitespace.", nameof(resourcePath));
}
ResourcePath = resourcePath;
}
public string ResourcePath { get; }
public override ManifestEntry Traverse(StringSegment segment) => UnknownPath;
}
}

View File

@ -0,0 +1,71 @@
// 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;
namespace Microsoft.Extensions.FileProviders.Embedded.Manifest
{
internal class ManifestFileInfo : IFileInfo
{
private long? _length;
public ManifestFileInfo(Assembly assembly, ManifestFile file, DateTimeOffset lastModified)
{
if (assembly == null)
{
throw new ArgumentNullException(nameof(assembly));
}
if (file == null)
{
throw new ArgumentNullException(nameof(file));
}
Assembly = assembly;
ManifestFile = file;
LastModified = lastModified;
}
public Assembly Assembly { get; }
public ManifestFile ManifestFile { get; }
public bool Exists => true;
public long Length => EnsureLength();
public string PhysicalPath => null;
public string Name => ManifestFile.Name;
public DateTimeOffset LastModified { get; }
public bool IsDirectory => false;
private long EnsureLength()
{
if (_length == null)
{
using (var stream = Assembly.GetManifestResourceStream(ManifestFile.ResourcePath))
{
_length = stream.Length;
}
}
return _length.Value;
}
public Stream CreateReadStream()
{
var stream = Assembly.GetManifestResourceStream(ManifestFile.ResourcePath);
if (!_length.HasValue)
{
_length = stream.Length;
}
return stream;
}
}
}

View File

@ -0,0 +1,159 @@
// 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 System.Runtime.CompilerServices;
using System.Xml;
using System.Xml.Linq;
namespace Microsoft.Extensions.FileProviders.Embedded.Manifest
{
internal static class ManifestParser
{
private static readonly string DefaultManifestName = "Microsoft.Extensions.FileProviders.Embedded.Manifest.xml";
public static EmbeddedFilesManifest Parse(Assembly assembly)
{
return Parse(assembly, DefaultManifestName);
}
public static EmbeddedFilesManifest Parse(Assembly assembly, string name)
{
if (assembly == null)
{
throw new ArgumentNullException(nameof(assembly));
}
if (name == null)
{
throw new ArgumentNullException(nameof(name));
}
var stream = assembly.GetManifestResourceStream(name);
if (stream == null)
{
throw new InvalidOperationException($"Could not load the embedded file manifest " +
$"'{name}' for assembly '{assembly.GetName().Name}'.");
}
var document = XDocument.Load(stream);
var manifest = EnsureElement(document, "Manifest");
var manifestVersion = EnsureElement(manifest, "ManifestVersion");
var version = EnsureText(manifestVersion);
if (!string.Equals("1.0", version, StringComparison.Ordinal))
{
throw new InvalidOperationException($"The embedded file manifest '{name}' for " +
$"assembly '{assembly.GetName().Name}' specifies an unsupported file format" +
$" version: '{version}'.");
}
var fileSystem = EnsureElement(manifest, "FileSystem");
var entries = fileSystem.Elements();
var entriesList = new List<ManifestEntry>();
foreach (var element in entries)
{
var entry = BuildEntry(element);
entriesList.Add(entry);
}
ValidateEntries(entriesList);
var rootDirectory = ManifestDirectory.CreateRootDirectory(entriesList.ToArray());
return new EmbeddedFilesManifest(rootDirectory);
}
private static void ValidateEntries(List<ManifestEntry> entriesList)
{
for (int i = 0; i < entriesList.Count - 1; i++)
{
for (int j = i + 1; j < entriesList.Count; j++)
{
if (string.Equals(entriesList[i].Name, entriesList[j].Name, StringComparison.OrdinalIgnoreCase))
{
throw new InvalidOperationException(
"Found two entries with the same name but different casing:" +
$" '{entriesList[i].Name}' and '{entriesList[j]}'");
}
}
}
}
private static ManifestEntry BuildEntry(XElement element)
{
RuntimeHelpers.EnsureSufficientExecutionStack();
if (element.NodeType != XmlNodeType.Element)
{
throw new InvalidOperationException($"Invalid manifest format. Expected a 'File' or a 'Directory' node:" +
$" '{element.ToString()}'");
}
if (string.Equals(element.Name.LocalName, "File", StringComparison.Ordinal))
{
var entryName = EnsureName(element);
var path = EnsureElement(element, "ResourcePath");
var pathValue = EnsureText(path);
return new ManifestFile(entryName, pathValue);
}
if (string.Equals(element.Name.LocalName, "Directory", StringComparison.Ordinal))
{
var directoryName = EnsureName(element);
var children = new List<ManifestEntry>();
foreach (var child in element.Elements())
{
children.Add(BuildEntry(child));
}
ValidateEntries(children);
return ManifestDirectory.CreateDirectory(directoryName, children.ToArray());
}
throw new InvalidOperationException($"Invalid manifest format.Expected a 'File' or a 'Directory' node. " +
$"Got '{element.Name.LocalName}' instead.");
}
private static XElement EnsureElement(XContainer container, string elementName)
{
var element = container.Element(elementName);
if (element == null)
{
throw new InvalidOperationException($"Invalid manifest format. Missing '{elementName}' element name");
}
return element;
}
private static string EnsureName(XElement element)
{
var value = element.Attribute("Name")?.Value;
if (value == null)
{
throw new InvalidOperationException($"Invalid manifest format. '{element.Name}' must contain a 'Name' attribute.");
}
return value;
}
private static string EnsureText(XElement element)
{
if (element.Elements().Count() == 0 &&
!element.IsEmpty &&
element.Nodes().Count() == 1 &&
element.FirstNode.NodeType == XmlNodeType.Text)
{
return element.Value;
}
throw new InvalidOperationException(
$"Invalid manifest format. '{element.Name.LocalName}' must contain " +
$"a text value. '{element.Value}'");
}
}
}

View File

@ -0,0 +1,16 @@
// 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.
namespace Microsoft.Extensions.FileProviders.Embedded.Manifest
{
internal class ManifestRootDirectory : ManifestDirectory
{
public ManifestRootDirectory(ManifestEntry[] children)
: base(name: null, children: children)
{
SetParent(ManifestSinkDirectory.Instance);
}
public override ManifestDirectory ToRootDirectory() => this;
}
}

View File

@ -0,0 +1,22 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.Extensions.Primitives;
namespace Microsoft.Extensions.FileProviders.Embedded.Manifest
{
internal class ManifestSinkDirectory : ManifestDirectory
{
private ManifestSinkDirectory()
: base(name: null, children: Array.Empty<ManifestEntry>())
{
SetParent(this);
Children = new[] { this };
}
public static ManifestDirectory Instance { get; } = new ManifestSinkDirectory();
public override ManifestEntry Traverse(StringSegment segment) => this;
}
}

View File

@ -0,0 +1,153 @@
// 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 Microsoft.Extensions.FileProviders.Embedded.Manifest;
using Microsoft.Extensions.Primitives;
namespace Microsoft.Extensions.FileProviders
{
/// <summary>
/// An embedded file provider that uses a manifest compiled in the assembly to
/// reconstruct the original paths of the embedded files when they were embedded
/// into the assembly.
/// </summary>
public class ManifestEmbeddedFileProvider : IFileProvider
{
private readonly DateTimeOffset _lastModified;
/// <summary>
/// Initializes a new instance of <see cref="ManifestEmbeddedFileProvider"/>.
/// </summary>
/// <param name="assembly">The assembly containing the embedded files.</param>
public ManifestEmbeddedFileProvider(Assembly assembly)
: this(assembly, ManifestParser.Parse(assembly), ResolveLastModified(assembly)) { }
/// <summary>
/// Initializes a new instance of <see cref="ManifestEmbeddedFileProvider"/>.
/// </summary>
/// <param name="assembly">The assembly containing the embedded files.</param>
/// <param name="root">The relative path from the root of the manifest to use as root for the provider.</param>
public ManifestEmbeddedFileProvider(Assembly assembly, string root)
: this(assembly, root, ResolveLastModified(assembly))
{
}
/// <summary>
/// Initializes a new instance of <see cref="ManifestEmbeddedFileProvider"/>.
/// </summary>
/// <param name="assembly">The assembly containing the embedded files.</param>
/// <param name="root">The relative path from the root of the manifest to use as root for the provider.</param>
/// <param name="lastModified">The LastModified date to use on the <see cref="IFileInfo"/> instances
/// returned by this <see cref="IFileProvider"/>.</param>
public ManifestEmbeddedFileProvider(Assembly assembly, string root, DateTimeOffset lastModified)
: this(assembly, ManifestParser.Parse(assembly).Scope(root), lastModified)
{
}
/// <summary>
/// Initializes a new instance of <see cref="ManifestEmbeddedFileProvider"/>.
/// </summary>
/// <param name="assembly">The assembly containing the embedded files.</param>
/// <param name="root">The relative path from the root of the manifest to use as root for the provider.</param>
/// <param name="manifestName">The name of the embedded resource containing the manifest.</param>
/// <param name="lastModified">The LastModified date to use on the <see cref="IFileInfo"/> instances
/// returned by this <see cref="IFileProvider"/>.</param>
public ManifestEmbeddedFileProvider(Assembly assembly, string root, string manifestName, DateTimeOffset lastModified)
: this(assembly, ManifestParser.Parse(assembly, manifestName).Scope(root), lastModified)
{
}
internal ManifestEmbeddedFileProvider(Assembly assembly, EmbeddedFilesManifest manifest, DateTimeOffset lastModified)
{
if (assembly == null)
{
throw new ArgumentNullException(nameof(assembly));
}
if (manifest == null)
{
throw new ArgumentNullException(nameof(manifest));
}
Assembly = assembly;
Manifest = manifest;
_lastModified = lastModified;
}
/// <summary>
/// Gets the <see cref="Assembly"/> for this provider.
/// </summary>
public Assembly Assembly { get; }
internal EmbeddedFilesManifest Manifest { get; }
/// <inheritdoc />
public IDirectoryContents GetDirectoryContents(string subpath)
{
var entry = Manifest.ResolveEntry(subpath);
if (entry == null || entry == ManifestEntry.UnknownPath)
{
return NotFoundDirectoryContents.Singleton;
}
if (!(entry is ManifestDirectory directory))
{
return NotFoundDirectoryContents.Singleton;
}
return new ManifestDirectoryContents(Assembly, directory, _lastModified);
}
/// <inheritdoc />
public IFileInfo GetFileInfo(string subpath)
{
var entry = Manifest.ResolveEntry(subpath);
switch (entry)
{
case null:
return new NotFoundFileInfo(subpath);
case ManifestFile f:
return new ManifestFileInfo(Assembly, f, _lastModified);
case ManifestDirectory d when d != ManifestEntry.UnknownPath:
return new NotFoundFileInfo(d.Name);
}
return new NotFoundFileInfo(subpath);
}
/// <inheritdoc />
public IChangeToken Watch(string filter)
{
if (filter == null)
{
throw new ArgumentNullException(nameof(filter));
}
return NullChangeToken.Singleton;
}
private static DateTimeOffset ResolveLastModified(Assembly assembly)
{
var result = DateTimeOffset.UtcNow;
if (!string.IsNullOrEmpty(assembly.Location))
{
try
{
result = File.GetLastWriteTimeUtc(assembly.Location);
}
catch (PathTooLongException)
{
}
catch (UnauthorizedAccessException)
{
}
}
return result;
}
}
}

View File

@ -0,0 +1,38 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<RootNamespace>Microsoft.Extensions.FileProviders</RootNamespace>
<Description>File provider for files in embedded resources for Microsoft.Extensions.FileProviders.</Description>
<TargetFrameworks>netstandard2.0;$(DefaultNetCoreTargetFramework)</TargetFrameworks>
<NuspecFile>$(MSBuildProjectName).multitarget.nuspec</NuspecFile>
<TargetFrameworks Condition="'$(DotNetBuildFromSource)' == 'true'">$(DefaultNetCoreTargetFramework)</TargetFrameworks>
<NuspecFile Condition="'$(DotNetBuildFromSource)' == 'true'">$(MSBuildProjectName).netcoreapp.nuspec</NuspecFile>
<IsPackable>true</IsPackable>
<IsAspNetCoreApp>true</IsAspNetCoreApp>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<PackageTags>files;filesystem</PackageTags>
<NoPackageAnalysis>true</NoPackageAnalysis>
</PropertyGroup>
<ItemGroup>
<InternalsVisibleTo Include="Microsoft.Extensions.FileProviders.Embedded.Tests" />
</ItemGroup>
<ItemGroup>
<Reference Include="Microsoft.Extensions.FileProviders.Abstractions" />
<ProjectReference Include="..\..\Manifest.MSBuildTask\src\Microsoft.Extensions.FileProviders.Embedded.Manifest.Task.csproj" PrivateAssets="All" ReferenceOutputAssembly="false" />
</ItemGroup>
<ItemGroup>
<SignedPackageFile Include="$(TargetPath)" Certificate="$(AssemblySigningCertName)" />
<SignedPackageFile Include="Microsoft.Extensions.FileProviders.Embedded.Manifest.Task.dll" Certificate="$(AssemblySigningCertName)" />
</ItemGroup>
<ItemGroup>
<NuspecProperty Include="AssemblyName=$(AssemblyName)" />
<NuspecProperty Include="OutputPath=$(OutputPath)" />
<NuspecProperty Include="AbstractionsPackageVersion=$(MicrosoftExtensionsFileProvidersAbstractionsPackageVersion)" />
<NuspecProperty Include="TaskAssemblyNetStandard=$(ArtifactsDir)bin\$(AssemblyName).Manifest.Task\$(Configuration)\netstandard2.0\$(AssemblyName).Manifest.Task.dll"/>
<NuspecProperty Include="TaskSymbolNetStandard=$(ArtifactsDir)bin\$(AssemblyName).Manifest.Task\$(Configuration)\netstandard2.0\$(AssemblyName).Manifest.Task.pdb" Condition="'$(DebugType)'!='embedded'"/>
</ItemGroup>
</Project>

View File

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2012/06/nuspec.xsd">
<metadata>
$CommonMetadataElements$
<dependencies>
<group targetFramework=".NETCoreApp5.0">
<dependency id="Microsoft.Extensions.FileProviders.Abstractions" version="$AbstractionsPackageVersion$" exclude="Build,Analyzers" />
</group>
<group targetFramework=".NETStandard2.0">
<dependency id="Microsoft.Extensions.FileProviders.Abstractions" version="$AbstractionsPackageVersion$" exclude="Build,Analyzers" />
</group>
</dependencies>
</metadata>
<files>
$CommonFileElements$
<file src="$OutputPath$**\$AssemblyName$.dll" target="lib\" />
<file src="$OutputPath$**\$AssemblyName$.pdb" target="lib\" />
<file src="$OutputPath$**\$AssemblyName$.xml" target="lib\" />
<file src="build\**\*" target="build\" />
<file src="buildMultiTargeting\**\*" target="buildMultiTargeting\" />
<file src="$TaskAssemblyNetStandard$" target="tasks\netstandard2.0\$AssemblyName$.Manifest.Task.dll" />
<file src="$TaskSymbolNetStandard$" target="tasks\netstandard2.0\$AssemblyName$.Manifest.Task.pdb" />
</files>
</package>

View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2012/06/nuspec.xsd">
<metadata>
$CommonMetadataElements$
<dependencies>
<group targetFramework=".NETCoreApp5.0">
<dependency id="Microsoft.Extensions.FileProviders.Abstractions" version="$AbstractionsPackageVersion$" exclude="Build,Analyzers" />
</group>
</dependencies>
</metadata>
<files>
$CommonFileElements$
<file src="$OutputPath$**\$AssemblyName$.dll" target="lib\" />
<file src="$OutputPath$**\$AssemblyName$.pdb" target="lib\" />
<file src="$OutputPath$**\$AssemblyName$.xml" target="lib\" />
<file src="build\**\*" target="build\" />
<file src="buildMultiTargeting\**\*" target="buildMultiTargeting\" />
<file src="$TaskAssemblyNetStandard$" target="tasks\netstandard2.0\$AssemblyName$.Manifest.Task.dll" />
<file src="$TaskSymbolNetStandard$" target="tasks\netstandard2.0\$AssemblyName$.Manifest.Task.pdb" />
</files>
</package>

View File

@ -0,0 +1,15 @@
<Project TreatAsLocalProperty="_FileProviderTaskFolder;_FileProviderTaskAssembly">
<PropertyGroup>
<GenerateEmbeddedFilesManifest Condition="'$(GenerateEmbeddedFilesManifest)' == ''">false</GenerateEmbeddedFilesManifest>
<EmbeddedFilesManifestFileName Condition="'$(EmbeddedFilesManifestFileName)' == ''">Microsoft.Extensions.FileProviders.Embedded.Manifest.xml</EmbeddedFilesManifestFileName>
</PropertyGroup>
<PropertyGroup>
<_FileProviderTaskAssembly>$(MSBuildThisFileDirectory)..\..\tasks\netstandard2.0\Microsoft.Extensions.FileProviders.Embedded.Manifest.Task.dll</_FileProviderTaskAssembly>
</PropertyGroup>
<UsingTask
AssemblyFile="$(_FileProviderTaskAssembly)"
TaskName="Microsoft.Extensions.FileProviders.Embedded.Manifest.Task.GenerateEmbeddedResourcesManifest" />
</Project>

View File

@ -0,0 +1,69 @@
<Project>
<PropertyGroup>
<PrepareResourceNamesDependsOn>_CalculateEmbeddedFilesManifestInputs;$(PrepareResourceNamesDependsOn)</PrepareResourceNamesDependsOn>
</PropertyGroup>
<Target
Name="_CalculateEmbeddedFilesManifestInputs"
Condition="'$(GenerateEmbeddedFilesManifest)' == 'true'">
<PropertyGroup>
<_GeneratedManifestFile>$(IntermediateOutputPath)$(EmbeddedFilesManifestFileName)</_GeneratedManifestFile>
</PropertyGroup>
<ItemGroup>
<_FilesForManifest Include="@(EmbeddedResource)" />
<_FilesForManifest Remove="@(EmbeddedResource->WithMetadataValue('ExcludeFromManifest','true'))" />
</ItemGroup>
<Warning
Text="GenerateEmbeddedFilesManifest was set, but no EmbeddedResource items were found that could be added to the manifest."
Condition="@(_FilesForManifest->Count()) == 0" />
<ItemGroup Condition="@(_FilesForManifest->Count()) != 0">
<EmbeddedResource
Include="$(_GeneratedManifestFile)"
LogicalName="$(EmbeddedFilesManifestFileName)" />
</ItemGroup>
</Target>
<Target Name="_CreateGeneratedManifestInfoInputsCacheFile" DependsOnTargets="_CalculateEmbeddedFilesManifestInputs">
<PropertyGroup>
<_GeneratedManifestInfoInputsCacheFile>$(IntermediateOutputPath)$(MSBuildProjectName).EmbeddedFilesManifest.cache</_GeneratedManifestInfoInputsCacheFile>
</PropertyGroup>
<Hash ItemsToHash="@(_FilesForManifest)">
<Output TaskParameter="HashResult" PropertyName="_EmbeddedGeneratedManifestHash" />
</Hash>
<WriteLinesToFile
Lines="$(_EmbeddedGeneratedManifestHash)"
File="$(_GeneratedManifestInfoInputsCacheFile)"
Overwrite="True"
WriteOnlyWhenDifferent="True" />
<ItemGroup>
<FileWrites Include="$(_GeneratedManifestInfoInputsCacheFile)" />
</ItemGroup>
</Target>
<Target
Name="_GenerateEmbeddedFilesManifest"
DependsOnTargets="_CreateGeneratedManifestInfoInputsCacheFile"
AfterTargets="PrepareResourceNames"
Condition="'$(GenerateEmbeddedFilesManifest)' == 'true' AND @(_FilesForManifest->Count()) != 0"
Inputs="$(_GeneratedManifestInfoInputsCacheFile)"
Outputs="$(_GeneratedManifestFile)">
<ItemGroup>
<!-- Rebuild _FilesForManifest since PrepareResourceNames would have updated EmbeddedResource. -->
<_FilesForManifest Remove="@(_FilesForManifest)" />
<_FilesForManifest Include="@(EmbeddedResource)" />
<_FilesForManifest Remove="@(EmbeddedResource->WithMetadataValue('ExcludeFromManifest','true'))" />
</ItemGroup>
<GenerateEmbeddedResourcesManifest
EmbeddedFiles="@(_FilesForManifest)"
ManifestFile="$(_GeneratedManifestFile)" />
</Target>
</Project>

View File

@ -0,0 +1,3 @@
<Project>
<Import Project="..\build\netstandard2.0\Microsoft.Extensions.FileProviders.Embedded.props" />
</Project>

View File

@ -0,0 +1,3 @@
<Project>
<Import Project="..\build\netstandard2.0\Microsoft.Extensions.FileProviders.Embedded.targets" />
</Project>

View File

@ -0,0 +1,231 @@
// 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.Testing;
using Xunit;
namespace Microsoft.Extensions.FileProviders.Embedded.Tests
{
public class EmbeddedFileProviderTests
{
private static readonly string Namespace = typeof(EmbeddedFileProviderTests).Namespace;
[Fact]
public void ConstructorWithNullAssemblyThrowsArgumentException()
{
Assert.Throws<ArgumentNullException>(() => new EmbeddedFileProvider(null));
}
[Fact]
public void GetFileInfo_ReturnsNotFoundFileInfo_IfFileDoesNotExist()
{
// Arrange
var provider = new EmbeddedFileProvider(GetType().GetTypeInfo().Assembly);
// Act
var fileInfo = provider.GetFileInfo("DoesNotExist.Txt");
// Assert
Assert.NotNull(fileInfo);
Assert.False(fileInfo.Exists);
}
[Theory]
[InlineData("File.txt")]
[InlineData("/File.txt")]
public void GetFileInfo_ReturnsFilesAtRoot(string filePath)
{
// Arrange
var provider = new EmbeddedFileProvider(GetType().GetTypeInfo().Assembly);
var expectedFileLength = 8;
// Act
var fileInfo = provider.GetFileInfo(filePath);
// Assert
Assert.NotNull(fileInfo);
Assert.True(fileInfo.Exists);
Assert.NotEqual(default(DateTimeOffset), fileInfo.LastModified);
Assert.Equal(expectedFileLength, fileInfo.Length);
Assert.False(fileInfo.IsDirectory);
Assert.Null(fileInfo.PhysicalPath);
Assert.Equal("File.txt", fileInfo.Name);
}
[Fact]
public void GetFileInfo_ReturnsNotFoundFileInfo_IfFileDoesNotExistUnderSpecifiedNamespace()
{
// Arrange
var provider = new EmbeddedFileProvider(GetType().GetTypeInfo().Assembly, Namespace + ".SubNamespace");
// Act
var fileInfo = provider.GetFileInfo("File.txt");
// Assert
Assert.NotNull(fileInfo);
Assert.False(fileInfo.Exists);
}
[Fact]
public void GetFileInfo_ReturnsNotFoundIfPathStartsWithBackSlash()
{
// Arrange
var provider = new EmbeddedFileProvider(GetType().GetTypeInfo().Assembly);
// Act
var fileInfo = provider.GetFileInfo("\\File.txt");
// Assert
Assert.NotNull(fileInfo);
Assert.False(fileInfo.Exists);
}
public static TheoryData GetFileInfo_LocatesFilesUnderSpecifiedNamespaceData
{
get
{
var theoryData = new TheoryData<string>
{
"ResourcesInSubdirectory/File3.txt"
};
if (TestPlatformHelper.IsWindows)
{
theoryData.Add("ResourcesInSubdirectory\\File3.txt");
}
return theoryData;
}
}
[Theory]
[MemberData(nameof(GetFileInfo_LocatesFilesUnderSpecifiedNamespaceData))]
public void GetFileInfo_LocatesFilesUnderSpecifiedNamespace(string path)
{
// Arrange
var provider = new EmbeddedFileProvider(GetType().GetTypeInfo().Assembly, Namespace + ".Resources");
// Act
var fileInfo = provider.GetFileInfo(path);
// Assert
Assert.NotNull(fileInfo);
Assert.True(fileInfo.Exists);
Assert.NotEqual(default(DateTimeOffset), fileInfo.LastModified);
Assert.True(fileInfo.Length > 0);
Assert.False(fileInfo.IsDirectory);
Assert.Null(fileInfo.PhysicalPath);
Assert.Equal("File3.txt", fileInfo.Name);
}
public static TheoryData GetFileInfo_LocatesFilesUnderSubDirectoriesData
{
get
{
var theoryData = new TheoryData<string>
{
"Resources/File.txt"
};
if (TestPlatformHelper.IsWindows)
{
theoryData.Add("Resources\\File.txt");
}
return theoryData;
}
}
[Theory]
[MemberData(nameof(GetFileInfo_LocatesFilesUnderSubDirectoriesData))]
public void GetFileInfo_LocatesFilesUnderSubDirectories(string path)
{
// Arrange
var provider = new EmbeddedFileProvider(GetType().GetTypeInfo().Assembly);
// Act
var fileInfo = provider.GetFileInfo(path);
// Assert
Assert.NotNull(fileInfo);
Assert.True(fileInfo.Exists);
Assert.NotEqual(default(DateTimeOffset), fileInfo.LastModified);
Assert.True(fileInfo.Length > 0);
Assert.False(fileInfo.IsDirectory);
Assert.Null(fileInfo.PhysicalPath);
Assert.Equal("File.txt", fileInfo.Name);
}
[Theory]
[InlineData("")]
[InlineData("/")]
public void GetDirectoryContents_ReturnsAllFilesInFileSystem(string path)
{
// Arrange
var provider = new EmbeddedFileProvider(GetType().GetTypeInfo().Assembly, Namespace + ".Resources");
// Act
var files = provider.GetDirectoryContents(path);
// Assert
Assert.Collection(files.OrderBy(f => f.Name, StringComparer.Ordinal),
file => Assert.Equal("File.txt", file.Name),
file => Assert.Equal("ResourcesInSubdirectory.File3.txt", file.Name));
Assert.False(provider.GetDirectoryContents("file").Exists);
Assert.False(provider.GetDirectoryContents("file/").Exists);
Assert.False(provider.GetDirectoryContents("file.txt").Exists);
Assert.False(provider.GetDirectoryContents("file/txt").Exists);
}
[Fact]
public void GetDirectoryContents_ReturnsEmptySequence_IfResourcesDoNotExistUnderNamespace()
{
// Arrange
var provider = new EmbeddedFileProvider(GetType().GetTypeInfo().Assembly, "Unknown.Namespace");
// Act
var files = provider.GetDirectoryContents(string.Empty);
// Assert
Assert.NotNull(files);
Assert.True(files.Exists);
Assert.Empty(files);
}
[Theory]
[InlineData("Resources")]
[InlineData("/Resources")]
public void GetDirectoryContents_ReturnsNotFoundDirectoryContents_IfHierarchicalPathIsSpecified(string path)
{
// Arrange
var provider = new EmbeddedFileProvider(GetType().GetTypeInfo().Assembly);
// Act
var files = provider.GetDirectoryContents(path);
// Assert
Assert.NotNull(files);
Assert.False(files.Exists);
Assert.Empty(files);
}
[Fact]
public void Watch_ReturnsNoOpTrigger()
{
// Arange
var provider = new EmbeddedFileProvider(GetType().GetTypeInfo().Assembly);
// Act
var token = provider.Watch("Resources/File.txt");
// Assert
Assert.NotNull(token);
Assert.False(token.ActiveChangeCallbacks);
Assert.False(token.HasChanged);
}
}
}

View File

@ -0,0 +1 @@
Hello

View File

@ -0,0 +1,34 @@
// 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;
namespace Microsoft.Extensions.FileProviders
{
internal class FileInfoComparer : IEqualityComparer<IFileInfo>
{
public static FileInfoComparer Instance { get; set; } = new FileInfoComparer();
public bool Equals(IFileInfo x, IFileInfo y)
{
if (x == null && y == null)
{
return true;
}
if ((x == null && y != null) || (x != null && y == null))
{
return false;
}
return x.Exists == y.Exists &&
x.IsDirectory == y.IsDirectory &&
x.Length == y.Length &&
string.Equals(x.Name, y.Name, StringComparison.Ordinal) &&
string.Equals(x.PhysicalPath, y.PhysicalPath, StringComparison.Ordinal);
}
public int GetHashCode(IFileInfo obj) => 0;
}
}

View File

@ -0,0 +1,58 @@
// 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 Xunit;
namespace Microsoft.Extensions.FileProviders.Embedded.Manifest
{
public class EmbeddedFilesManifestTests
{
[Theory]
[InlineData("/wwwroot//jquery.validate.js")]
[InlineData("//wwwroot/jquery.validate.js")]
public void ResolveEntry_IgnoresInvalidPaths(string path)
{
// Arrange
var manifest = new EmbeddedFilesManifest(
ManifestDirectory.CreateRootDirectory(
new[]
{
ManifestDirectory.CreateDirectory("wwwroot",
new[]
{
new ManifestFile("jquery.validate.js","wwwroot.jquery.validate.js")
})
}));
// Act
var entry = manifest.ResolveEntry(path);
// Assert
Assert.Null(entry);
}
[Theory]
[InlineData("/")]
[InlineData("./")]
[InlineData("/wwwroot/jquery.validate.js")]
[InlineData("/wwwroot/")]
public void ResolveEntry_AllowsSingleDirectorySeparator(string path)
{
// Arrange
var manifest = new EmbeddedFilesManifest(
ManifestDirectory.CreateRootDirectory(
new[]
{
ManifestDirectory.CreateDirectory("wwwroot",
new[]
{
new ManifestFile("jquery.validate.js","wwwroot.jquery.validate.js")
})
}));
// Act
var entry = manifest.ResolveEntry(path);
// Assert
Assert.NotNull(entry);
}
}
}

View File

@ -0,0 +1,113 @@
// 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 Xunit;
namespace Microsoft.Extensions.FileProviders.Embedded.Manifest
{
public class ManifestEntryTests
{
[Fact]
public void TraversingAFile_ReturnsUnknownPath()
{
// Arrange
var file = new ManifestFile("a", "a.b.c");
// Act
var result = file.Traverse(".");
// Assert
Assert.Equal(ManifestEntry.UnknownPath, result);
}
[Fact]
public void TraversingANonExistingFile_ReturnsUnknownPath()
{
// Arrange
var directory = ManifestDirectory.CreateDirectory("a", Array.Empty<ManifestEntry>());
// Act
var result = directory.Traverse("missing.txt");
// Assert
Assert.Equal(ManifestEntry.UnknownPath, result);
}
[Fact]
public void TraversingWithDot_ReturnsSelf()
{
// Arrange
var directory = ManifestDirectory.CreateDirectory("a", Array.Empty<ManifestEntry>());
// Act
var result = directory.Traverse(".");
// Assert
Assert.Same(directory, result);
}
[Fact]
public void TraversingWithDotDot_ReturnsParent()
{
// Arrange
var childDirectory = ManifestDirectory.CreateDirectory("b", Array.Empty<ManifestEntry>());
var directory = ManifestDirectory.CreateDirectory("a", new[] { childDirectory });
// Act
var result = childDirectory.Traverse("..");
// Assert
Assert.Equal(directory, result);
}
[Fact]
public void TraversingRootDirectoryWithDotDot_ReturnsSinkDirectory()
{
// Arrange
var directory = ManifestDirectory.CreateRootDirectory(Array.Empty<ManifestEntry>());
// Act
var result = directory.Traverse("..");
// Assert
Assert.Equal(ManifestEntry.UnknownPath, result);
}
[Fact]
public void ScopingAFolderAndTryingToGetAScopedFile_ReturnsSinkDirectory()
{
// Arrange
var directory = ManifestDirectory.CreateRootDirectory(new[] {
ManifestDirectory.CreateDirectory("a",
new[] { new ManifestFile("test1.txt", "text.txt") }),
ManifestDirectory.CreateDirectory("b",
new[] { new ManifestFile("test2.txt", "test2.txt") }) });
var newRoot = ((ManifestDirectory)directory.Traverse("a")).ToRootDirectory();
// Act
var result = newRoot.Traverse("../b/test.txt");
// Assert
Assert.Same(ManifestEntry.UnknownPath, result);
}
[Theory]
[InlineData("..")]
[InlineData(".")]
[InlineData("file.txt")]
[InlineData("folder")]
public void TraversingUnknownPath_ReturnsSinkDirectory(string path)
{
// Arrange
var directory = ManifestEntry.UnknownPath;
// Act
var result = directory.Traverse(path);
// Assert
Assert.Equal(ManifestEntry.UnknownPath, result);
}
}
}

View File

@ -0,0 +1,116 @@
// 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 Xunit;
namespace Microsoft.Extensions.FileProviders.Embedded.Manifest
{
public class ManifestParserTests
{
[Fact]
public void Parse_UsesDefaultManifestNameForManifest()
{
// Arrange
var assembly = new TestAssembly(
TestEntry.Directory("unused",
TestEntry.File("sample.txt")));
// Act
var manifest = ManifestParser.Parse(assembly);
// Assert
Assert.NotNull(manifest);
}
[Fact]
public void Parse_FindsManifestWithCustomName()
{
// Arrange
var assembly = new TestAssembly(
TestEntry.Directory("unused",
TestEntry.File("sample.txt")),
manifestName: "Manifest.xml");
// Act
var manifest = ManifestParser.Parse(assembly, "Manifest.xml");
// Assert
Assert.NotNull(manifest);
}
[Fact]
public void Parse_ThrowsForEntriesWithDifferentCasing()
{
// Arrange
var assembly = new TestAssembly(
TestEntry.Directory("unused",
TestEntry.File("sample.txt"),
TestEntry.File("SAMPLE.TXT")));
// Act & Assert
Assert.Throws<InvalidOperationException>(() => ManifestParser.Parse(assembly));
}
[Theory]
[MemberData(nameof(MalformedManifests))]
public void Parse_ThrowsForInvalidManifests(string invalidManifest)
{
// Arrange
var assembly = new TestAssembly(invalidManifest);
// Act & Assert
Assert.Throws<InvalidOperationException>(() => ManifestParser.Parse(assembly));
}
public static TheoryData<string> MalformedManifests =>
new TheoryData<string>
{
"<Manifest></Manifest>",
"<Manifest><ManifestVersion></ManifestVersion></Manifest>",
"<Manifest><ManifestVersion /></Manifest>",
"<Manifest><ManifestVersion><Version>2.0</Version></ManifestVersion></Manifest>",
"<Manifest><ManifestVersion>2.0</ManifestVersion></Manifest>",
@"<Manifest><ManifestVersion>1.0</ManifestVersion>
<FileSystem><File><ResourcePath>path</ResourcePath></File></FileSystem></Manifest>",
@"<Manifest><ManifestVersion>1.0</ManifestVersion>
<FileSystem><File Name=""sample.txt""><ResourcePath></ResourcePath></File></FileSystem></Manifest>",
@"<Manifest><ManifestVersion>1.0</ManifestVersion>
<FileSystem><File Name=""sample.txt"">sample.txt</File></FileSystem></Manifest>",
@"<Manifest><ManifestVersion>1.0</ManifestVersion>
<FileSystem><Directory></Directory></FileSystem></Manifest>",
@"<Manifest><ManifestVersion>1.0</ManifestVersion>
<FileSystem><Directory Name=""wwwroot""><Unknown /></Directory></FileSystem></Manifest>"
};
[Theory]
[MemberData(nameof(ManifestsWithAdditionalData))]
public void Parse_IgnoresAdditionalDataOnFileAndDirectoryNodes(string manifest)
{
// Arrange
var assembly = new TestAssembly(manifest);
// Act
var result = ManifestParser.Parse(assembly);
// Assert
Assert.NotNull(result);
}
public static TheoryData<string> ManifestsWithAdditionalData =>
new TheoryData<string>
{
@"<Manifest><ManifestVersion>1.0</ManifestVersion>
<FileSystem><Directory Name=""wwwroot"" AdditionalAttribute=""value""></Directory></FileSystem></Manifest>",
@"<Manifest><ManifestVersion>1.0</ManifestVersion>
<FileSystem><Directory Name=""wwwroot"" AdditionalAttribute=""value"">
<File Name=""sample.txt"" AdditionalValue=""value""><ResourcePath something=""abc"">path</ResourcePath><hash>1234</hash></File>
</Directory></FileSystem></Manifest>"
};
}
}

View File

@ -0,0 +1,41 @@
// 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.Xml.Linq;
namespace Microsoft.Extensions.FileProviders.Embedded.Manifest
{
class TestEntry
{
public bool IsFile => ResourcePath != null;
public string Name { get; set; }
public TestEntry[] Children { get; set; }
public string ResourcePath { get; set; }
public static TestEntry Directory(string name, params TestEntry[] entries) =>
new TestEntry() { Name = name, Children = entries };
public static TestEntry File(string name, string path = null) =>
new TestEntry() { Name = name, ResourcePath = path ?? name };
public XElement ToXElement() => IsFile ?
new XElement("File", new XAttribute("Name", Name), new XElement("ResourcePath", ResourcePath)) :
new XElement("Directory", new XAttribute("Name", Name), Children.Select(c => c.ToXElement()));
public IEnumerable<TestEntry> GetFiles()
{
if (IsFile)
{
return Enumerable.Empty<TestEntry>();
}
var files = Children.Where(c => c.IsFile).ToArray();
var otherFiles = Children.Where(c => !c.IsFile).SelectMany(d => d.GetFiles()).ToArray();
return files.Concat(otherFiles).ToArray();
}
}
}

View File

@ -0,0 +1,428 @@
// 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.Linq;
using Microsoft.Extensions.FileProviders.Embedded.Manifest;
using Xunit;
namespace Microsoft.Extensions.FileProviders
{
public class ManifestEmbeddedFileProviderTests
{
[Fact]
public void GetFileInfo_CanResolveSimpleFiles()
{
// Arrange
var assembly = new TestAssembly(
TestEntry.Directory("unused",
TestEntry.File("jquery.validate.js"),
TestEntry.File("jquery.min.js"),
TestEntry.File("site.css")));
// Act
var provider = new ManifestEmbeddedFileProvider(assembly);
// Assert
var jqueryValidate = provider.GetFileInfo("jquery.validate.js");
Assert.True(jqueryValidate.Exists);
Assert.False(jqueryValidate.IsDirectory);
Assert.Equal("jquery.validate.js", jqueryValidate.Name);
Assert.Null(jqueryValidate.PhysicalPath);
Assert.Equal(0, jqueryValidate.Length);
var jqueryMin = provider.GetFileInfo("jquery.min.js");
Assert.True(jqueryMin.Exists);
Assert.False(jqueryMin.IsDirectory);
Assert.Equal("jquery.min.js", jqueryMin.Name);
Assert.Null(jqueryMin.PhysicalPath);
Assert.Equal(0, jqueryMin.Length);
var siteCss = provider.GetFileInfo("site.css");
Assert.True(siteCss.Exists);
Assert.False(siteCss.IsDirectory);
Assert.Equal("site.css", siteCss.Name);
Assert.Null(siteCss.PhysicalPath);
Assert.Equal(0, siteCss.Length);
}
[Fact]
public void GetFileInfo_CanResolveFilesInsideAFolder()
{
// Arrange
var assembly = new TestAssembly(
TestEntry.Directory("unused",
TestEntry.Directory("wwwroot",
TestEntry.File("jquery.validate.js"),
TestEntry.File("jquery.min.js"),
TestEntry.File("site.css"))));
// Act
var provider = new ManifestEmbeddedFileProvider(assembly);
// Assert
var jqueryValidate = provider.GetFileInfo(Path.Combine("wwwroot", "jquery.validate.js"));
Assert.True(jqueryValidate.Exists);
Assert.False(jqueryValidate.IsDirectory);
Assert.Equal("jquery.validate.js", jqueryValidate.Name);
Assert.Null(jqueryValidate.PhysicalPath);
Assert.Equal(0, jqueryValidate.Length);
var jqueryMin = provider.GetFileInfo(Path.Combine("wwwroot", "jquery.min.js"));
Assert.True(jqueryMin.Exists);
Assert.False(jqueryMin.IsDirectory);
Assert.Equal("jquery.min.js", jqueryMin.Name);
Assert.Null(jqueryMin.PhysicalPath);
Assert.Equal(0, jqueryMin.Length);
var siteCss = provider.GetFileInfo(Path.Combine("wwwroot", "site.css"));
Assert.True(siteCss.Exists);
Assert.False(siteCss.IsDirectory);
Assert.Equal("site.css", siteCss.Name);
Assert.Null(siteCss.PhysicalPath);
Assert.Equal(0, siteCss.Length);
}
[Fact]
public void GetFileInfo_ResolveNonExistingFile_ReturnsNotFoundFileInfo()
{
// Arrange
var assembly = new TestAssembly(
TestEntry.Directory("unused",
TestEntry.Directory("wwwroot",
TestEntry.File("jquery.validate.js"),
TestEntry.File("jquery.min.js"),
TestEntry.File("site.css"))));
var provider = new ManifestEmbeddedFileProvider(assembly);
// Act
var file = provider.GetFileInfo("some/non/existing/file.txt");
// Assert
Assert.IsType<NotFoundFileInfo>(file);
}
[Fact]
public void GetFileInfo_ResolveNonExistingDirectory_ReturnsNotFoundFileInfo()
{
// Arrange
var assembly = new TestAssembly(
TestEntry.Directory("unused",
TestEntry.Directory("wwwroot",
TestEntry.File("jquery.validate.js"),
TestEntry.File("jquery.min.js"),
TestEntry.File("site.css"))));
var provider = new ManifestEmbeddedFileProvider(assembly);
// Act
var file = provider.GetFileInfo("some");
// Assert
Assert.IsType<NotFoundFileInfo>(file);
}
[Fact]
public void GetFileInfo_ResolveExistingDirectory_ReturnsNotFoundFileInfo()
{
// Arrange
var assembly = new TestAssembly(
TestEntry.Directory("unused",
TestEntry.Directory("wwwroot",
TestEntry.File("jquery.validate.js"),
TestEntry.File("jquery.min.js"),
TestEntry.File("site.css"))));
var provider = new ManifestEmbeddedFileProvider(assembly);
// Act
var file = provider.GetFileInfo("wwwroot");
// Assert
Assert.IsType<NotFoundFileInfo>(file);
}
[Theory]
[InlineData("WWWROOT", "JQUERY.VALIDATE.JS")]
[InlineData("WwWRoOT", "JQuERY.VALiDATE.js")]
public void GetFileInfo_ResolvesFiles_WithDifferentCasing(string folder, string file)
{
// Arrange
var assembly = new TestAssembly(
TestEntry.Directory("unused",
TestEntry.Directory("wwwroot",
TestEntry.File("jquery.validate.js"),
TestEntry.File("jquery.min.js"),
TestEntry.File("site.css"))));
// Act
var provider = new ManifestEmbeddedFileProvider(assembly);
// Assert
var jqueryValidate = provider.GetFileInfo(Path.Combine(folder, file));
Assert.True(jqueryValidate.Exists);
Assert.False(jqueryValidate.IsDirectory);
Assert.Equal("jquery.validate.js", jqueryValidate.Name);
Assert.Null(jqueryValidate.PhysicalPath);
Assert.Equal(0, jqueryValidate.Length);
}
[Fact]
public void GetFileInfo_AllowsLeadingDots_OnThePath()
{
// Arrange
var assembly = new TestAssembly(
TestEntry.Directory("unused",
TestEntry.Directory("wwwroot",
TestEntry.File("jquery.validate.js"),
TestEntry.File("jquery.min.js"),
TestEntry.File("site.css"))));
// Act
var provider = new ManifestEmbeddedFileProvider(assembly);
// Assert
var jqueryValidate = provider.GetFileInfo(Path.Combine(".", "wwwroot", "jquery.validate.js"));
Assert.True(jqueryValidate.Exists);
Assert.False(jqueryValidate.IsDirectory);
Assert.Equal("jquery.validate.js", jqueryValidate.Name);
Assert.Null(jqueryValidate.PhysicalPath);
Assert.Equal(0, jqueryValidate.Length);
}
[Fact]
public void GetFileInfo_EscapingFromTheRootFolder_ReturnsNotFound()
{
// Arrange
var assembly = new TestAssembly(
TestEntry.Directory("unused",
TestEntry.Directory("wwwroot",
TestEntry.File("jquery.validate.js"),
TestEntry.File("jquery.min.js"),
TestEntry.File("site.css"))));
// Act
var provider = new ManifestEmbeddedFileProvider(assembly);
// Assert
var jqueryValidate = provider.GetFileInfo(Path.Combine("..", "wwwroot", "jquery.validate.js"));
Assert.IsType<NotFoundFileInfo>(jqueryValidate);
}
[Theory]
[InlineData("wwwroot/jquery?validate.js")]
[InlineData("wwwroot/jquery*validate.js")]
[InlineData("wwwroot/jquery:validate.js")]
[InlineData("wwwroot/jquery<validate.js")]
[InlineData("wwwroot/jquery>validate.js")]
[InlineData("wwwroot/jquery\0validate.js")]
public void GetFileInfo_ReturnsNotFoundfileInfo_ForPathsWithInvalidCharacters(string path)
{
// Arrange
var assembly = new TestAssembly(
TestEntry.Directory("unused",
TestEntry.Directory("wwwroot",
TestEntry.File("jquery.validate.js"),
TestEntry.File("jquery.min.js"),
TestEntry.File("site.css"))));
// Act
var provider = new ManifestEmbeddedFileProvider(assembly);
// Assert
var file = provider.GetFileInfo(path);
Assert.IsType<NotFoundFileInfo>(file);
Assert.Equal(path, file.Name);
}
[Fact]
public void GetDirectoryContents_CanEnumerateExistingFolders()
{
// Arrange
var assembly = new TestAssembly(
TestEntry.Directory("unused",
TestEntry.Directory("wwwroot",
TestEntry.File("jquery.validate.js"),
TestEntry.File("jquery.min.js"),
TestEntry.File("site.css"))));
var provider = new ManifestEmbeddedFileProvider(assembly);
var expectedContents = new[]
{
CreateTestFileInfo("jquery.validate.js"),
CreateTestFileInfo("jquery.min.js"),
CreateTestFileInfo("site.css")
};
// Act
var contents = provider.GetDirectoryContents("wwwroot").ToArray();
// Assert
Assert.Equal(expectedContents, contents, FileInfoComparer.Instance);
}
[Fact]
public void GetDirectoryContents_EnumeratesOnlyAGivenLevel()
{
// Arrange
var assembly = new TestAssembly(
TestEntry.Directory("unused",
TestEntry.Directory("wwwroot",
TestEntry.File("jquery.validate.js"),
TestEntry.File("jquery.min.js"),
TestEntry.File("site.css"))));
var provider = new ManifestEmbeddedFileProvider(assembly);
var expectedContents = new[]
{
CreateTestFileInfo("wwwroot", isDirectory: true)
};
// Act
var contents = provider.GetDirectoryContents(".").ToArray();
// Assert
Assert.Equal(expectedContents, contents, FileInfoComparer.Instance);
}
[Fact]
public void GetDirectoryContents_EnumeratesFilesAndDirectoriesOnAGivenPath()
{
// Arrange
var assembly = new TestAssembly(
TestEntry.Directory("unused",
TestEntry.Directory("wwwroot"),
TestEntry.File("site.css")));
var provider = new ManifestEmbeddedFileProvider(assembly);
var expectedContents = new[]
{
CreateTestFileInfo("wwwroot", isDirectory: true),
CreateTestFileInfo("site.css")
};
// Act
var contents = provider.GetDirectoryContents(".").ToArray();
// Assert
Assert.Equal(expectedContents, contents, FileInfoComparer.Instance);
}
[Fact]
public void GetDirectoryContents_ReturnsNoEntries_ForNonExistingDirectories()
{
// Arrange
var assembly = new TestAssembly(
TestEntry.Directory("unused",
TestEntry.Directory("wwwroot"),
TestEntry.File("site.css")));
var provider = new ManifestEmbeddedFileProvider(assembly);
// Act
var contents = provider.GetDirectoryContents("non-existing");
// Assert
Assert.IsType<NotFoundDirectoryContents>(contents);
}
[Fact]
public void GetDirectoryContents_ReturnsNoEntries_ForFilePaths()
{
// Arrange
var assembly = new TestAssembly(
TestEntry.Directory("unused",
TestEntry.Directory("wwwroot"),
TestEntry.File("site.css")));
var provider = new ManifestEmbeddedFileProvider(assembly);
// Act
var contents = provider.GetDirectoryContents("site.css");
// Assert
Assert.IsType<NotFoundDirectoryContents>(contents);
}
[Theory]
[InlineData("wwwro*t")]
[InlineData("wwwro?t")]
[InlineData("wwwro:t")]
[InlineData("wwwro<t")]
[InlineData("wwwro>t")]
[InlineData("wwwro\0t")]
public void GetDirectoryContents_ReturnsNotFoundDirectoryContents_ForPathsWithInvalidCharacters(string path)
{
// Arrange
var assembly = new TestAssembly(
TestEntry.Directory("unused",
TestEntry.Directory("wwwroot",
TestEntry.File("jquery.validate.js"),
TestEntry.File("jquery.min.js"),
TestEntry.File("site.css"))));
// Act
var provider = new ManifestEmbeddedFileProvider(assembly);
// Assert
var directory = provider.GetDirectoryContents(path);
Assert.IsType<NotFoundDirectoryContents>(directory);
}
[Fact]
public void Contructor_CanScopeManifestToAFolder()
{
// Arrange
var assembly = new TestAssembly(
TestEntry.Directory("unused",
TestEntry.Directory("wwwroot",
TestEntry.File("jquery.validate.js")),
TestEntry.File("site.css")));
var provider = new ManifestEmbeddedFileProvider(assembly);
var scopedProvider = new ManifestEmbeddedFileProvider(assembly, provider.Manifest.Scope("wwwroot"), DateTimeOffset.UtcNow);
// Act
var jqueryValidate = scopedProvider.GetFileInfo("jquery.validate.js");
// Assert
Assert.True(jqueryValidate.Exists);
Assert.False(jqueryValidate.IsDirectory);
Assert.Equal("jquery.validate.js", jqueryValidate.Name);
Assert.Null(jqueryValidate.PhysicalPath);
Assert.Equal(0, jqueryValidate.Length);
}
[Theory]
[InlineData("wwwroot/jquery.validate.js")]
[InlineData("../wwwroot/jquery.validate.js")]
[InlineData("site.css")]
[InlineData("../site.css")]
public void ScopedFileProvider_DoesNotReturnFilesOutOfScope(string path)
{
// Arrange
var assembly = new TestAssembly(
TestEntry.Directory("unused",
TestEntry.Directory("wwwroot",
TestEntry.File("jquery.validate.js")),
TestEntry.File("site.css")));
var provider = new ManifestEmbeddedFileProvider(assembly);
var scopedProvider = new ManifestEmbeddedFileProvider(assembly, provider.Manifest.Scope("wwwroot"), DateTimeOffset.UtcNow);
// Act
var jqueryValidate = scopedProvider.GetFileInfo(path);
// Assert
Assert.IsType<NotFoundFileInfo>(jqueryValidate);
}
private IFileInfo CreateTestFileInfo(string name, bool isDirectory = false) =>
new TestFileInfo(name, isDirectory);
}
}

View File

@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>$(DefaultNetCoreTargetFramework);net472</TargetFrameworks>
</PropertyGroup>
<ItemGroup>
<EmbeddedResource Include="File.txt;sub\**\*;Resources\**\*" />
</ItemGroup>
<ItemGroup>
<Reference Include="Microsoft.Extensions.FileProviders.Abstractions" />
<Reference Include="Microsoft.Extensions.FileProviders.Embedded" />
<Reference Include="Microsoft.Extensions.Primitives" />
</ItemGroup>
</Project>

View File

@ -0,0 +1 @@
Resources-Hello

View File

@ -0,0 +1 @@
Hello3

View File

@ -0,0 +1,69 @@
// 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.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using Microsoft.Extensions.FileProviders.Embedded.Manifest;
namespace Microsoft.Extensions.FileProviders
{
internal class TestAssembly : Assembly
{
public TestAssembly(string manifest, string manifestName = "Microsoft.Extensions.FileProviders.Embedded.Manifest.xml")
{
ManifestStream = new MemoryStream();
using (var writer = new StreamWriter(ManifestStream, Encoding.UTF8, 1024, leaveOpen: true))
{
writer.Write(manifest);
}
ManifestStream.Seek(0, SeekOrigin.Begin);
ManifestName = manifestName;
}
public TestAssembly(TestEntry entry, string manifestName = "Microsoft.Extensions.FileProviders.Embedded.Manifest.xml")
{
ManifestName = manifestName;
var manifest = new XDocument(
new XDeclaration("1.0", "utf-8", "yes"),
new XElement("Manifest",
new XElement("ManifestVersion", "1.0"),
new XElement("FileSystem", entry.Children.Select(c => c.ToXElement()))));
ManifestStream = new MemoryStream();
using (var writer = XmlWriter.Create(ManifestStream, new XmlWriterSettings { CloseOutput = false }))
{
manifest.WriteTo(writer);
}
ManifestStream.Seek(0, SeekOrigin.Begin);
Files = entry.GetFiles().Select(f => f.ResourcePath).ToArray();
}
public string ManifestName { get; }
public MemoryStream ManifestStream { get; private set; }
public string[] Files { get; private set; }
public override Stream GetManifestResourceStream(string name)
{
if (string.Equals(ManifestName, name))
{
return ManifestStream;
}
return Files.Contains(name) ? Stream.Null : null;
}
public override string Location => null;
public override AssemblyName GetName()
{
return new AssemblyName("TestAssembly");
}
}
}

View File

@ -0,0 +1,34 @@
// 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;
namespace Microsoft.Extensions.FileProviders
{
internal class TestFileInfo : IFileInfo
{
private readonly string _name;
private readonly bool _isDirectory;
public TestFileInfo(string name, bool isDirectory)
{
_name = name;
_isDirectory = isDirectory;
}
public bool Exists => true;
public long Length => _isDirectory ? -1 : 0;
public string PhysicalPath => null;
public string Name => _name;
public DateTimeOffset LastModified => throw new NotImplementedException();
public bool IsDirectory => _isDirectory;
public Stream CreateReadStream() => Stream.Null;
}
}

View File

@ -0,0 +1 @@
Hello2

Binary file not shown.

View File

@ -0,0 +1,21 @@
// 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;
namespace Microsoft.Extensions.FileProviders.Embedded.Manifest.Task
{
public class EmbeddedItem : IEquatable<EmbeddedItem>
{
public string ManifestFilePath { get; set; }
public string AssemblyResourceName { get; set; }
public bool Equals(EmbeddedItem other) =>
string.Equals(ManifestFilePath, other?.ManifestFilePath, StringComparison.Ordinal) &&
string.Equals(AssemblyResourceName, other?.AssemblyResourceName, StringComparison.Ordinal);
public override bool Equals(object obj) => Equals(obj as EmbeddedItem);
public override int GetHashCode() => ManifestFilePath.GetHashCode() ^ AssemblyResourceName.GetHashCode();
}
}

View File

@ -0,0 +1,121 @@
// 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.Diagnostics;
namespace Microsoft.Extensions.FileProviders.Embedded.Manifest.Task
{
/// <summary>
/// This API supports infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
[DebuggerDisplay("{Name,nq}")]
public class Entry : IEquatable<Entry>
{
public bool IsFile { get; private set; }
public string Name { get; private set; }
public string AssemblyResourceName { get; private set; }
public ISet<Entry> Children { get; } = new SortedSet<Entry>(NameComparer.Instance);
public static Entry Directory(string name) =>
new Entry { Name = name };
public static Entry File(string name, string assemblyResourceName) =>
new Entry { Name = name, AssemblyResourceName = assemblyResourceName, IsFile = true };
internal void AddChild(Entry child)
{
if (IsFile)
{
throw new InvalidOperationException("Tried to add children to a file.");
}
if (Children.Contains(child))
{
throw new InvalidOperationException($"An item with the name '{child.Name}' already exists.");
}
Children.Add(child);
}
internal Entry GetDirectory(string currentSegment)
{
if (IsFile)
{
throw new InvalidOperationException("Tried to get a directory from a file.");
}
foreach (var child in Children)
{
if (child.HasName(currentSegment))
{
if (child.IsFile)
{
throw new InvalidOperationException("Tried to find a directory but found a file instead");
}
else
{
return child;
}
}
}
return null;
}
public bool Equals(Entry other)
{
if (other == null || !other.HasName(Name) || other.IsFile != IsFile)
{
return false;
}
if (IsFile)
{
return string.Equals(other.AssemblyResourceName, AssemblyResourceName, StringComparison.Ordinal);
}
else
{
return SameChildren(Children, other.Children);
}
}
private bool HasName(string currentSegment)
{
return string.Equals(Name, currentSegment, StringComparison.Ordinal);
}
private bool SameChildren(ISet<Entry> left, ISet<Entry> right)
{
if (left.Count != right.Count)
{
return false;
}
var le = left.GetEnumerator();
var re = right.GetEnumerator();
while (le.MoveNext() && re.MoveNext())
{
if (!le.Current.Equals(re.Current))
{
return false;
}
}
return true;
}
private class NameComparer : IComparer<Entry>
{
public static NameComparer Instance { get; } = new NameComparer();
public int Compare(Entry x, Entry y) =>
string.Compare(x?.Name, y?.Name, StringComparison.Ordinal);
}
}
}

View File

@ -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.Linq;
using System.Text;
using System.Xml;
using Microsoft.Build.Framework;
namespace Microsoft.Extensions.FileProviders.Embedded.Manifest.Task
{
/// <summary>
/// Task for generating a manifest file out of the embedded resources in an
/// assembly.
/// </summary>
public class GenerateEmbeddedResourcesManifest : Microsoft.Build.Utilities.Task
{
private const string LogicalName = "LogicalName";
private const string ManifestResourceName = "ManifestResourceName";
private const string TargetPath = "TargetPath";
[Required]
public ITaskItem[] EmbeddedFiles { get; set; }
[Required]
public string ManifestFile { get; set; }
/// <inheritdoc />
public override bool Execute()
{
var processedItems = CreateEmbeddedItems(EmbeddedFiles);
var manifest = BuildManifest(processedItems);
var document = manifest.ToXmlDocument();
var settings = new XmlWriterSettings()
{
Encoding = Encoding.UTF8,
CloseOutput = true
};
using (var xmlWriter = GetXmlWriter(settings))
{
document.WriteTo(xmlWriter);
}
return true;
}
protected virtual XmlWriter GetXmlWriter(XmlWriterSettings settings)
{
if (settings == null)
{
throw new ArgumentNullException(nameof(settings));
}
var fileStream = new FileStream(ManifestFile, FileMode.Create);
return XmlWriter.Create(fileStream, settings);
}
public EmbeddedItem[] CreateEmbeddedItems(params ITaskItem[] items)
{
if (items == null)
{
throw new ArgumentNullException(nameof(items));
}
return items.Select(er => new EmbeddedItem
{
ManifestFilePath = GetManifestPath(er),
AssemblyResourceName = GetAssemblyResourceName(er)
}).ToArray();
}
public Manifest BuildManifest(EmbeddedItem[] processedItems)
{
if (processedItems == null)
{
throw new ArgumentNullException(nameof(processedItems));
}
var manifest = new Manifest();
foreach (var item in processedItems)
{
manifest.AddElement(item.ManifestFilePath, item.AssemblyResourceName);
}
return manifest;
}
private string GetManifestPath(ITaskItem taskItem) => string.Equals(taskItem.GetMetadata(LogicalName), taskItem.GetMetadata(ManifestResourceName)) ?
taskItem.GetMetadata(TargetPath) :
NormalizePath(taskItem.GetMetadata(LogicalName));
private string GetAssemblyResourceName(ITaskItem taskItem) => string.Equals(taskItem.GetMetadata(LogicalName), taskItem.GetMetadata(ManifestResourceName)) ?
taskItem.GetMetadata(ManifestResourceName) :
taskItem.GetMetadata(LogicalName);
private string NormalizePath(string path) => Path.DirectorySeparatorChar == '\\' ?
path.Replace("/", "\\") : path.Replace("\\", "/");
}
}

View File

@ -0,0 +1,84 @@
// 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.IO;
using System.Linq;
using System.Xml.Linq;
namespace Microsoft.Extensions.FileProviders.Embedded.Manifest.Task
{
public class Manifest
{
public Entry Root { get; set; } = Entry.Directory("");
public void AddElement(string originalPath, string assemblyResourceName)
{
if (originalPath == null)
{
throw new System.ArgumentNullException(nameof(originalPath));
}
if (assemblyResourceName == null)
{
throw new System.ArgumentNullException(nameof(assemblyResourceName));
}
var paths = originalPath.Split(Path.DirectorySeparatorChar);
var current = Root;
for (int i = 0; i < paths.Length - 1; i++)
{
var currentSegment = paths[i];
var next = current.GetDirectory(currentSegment);
if (next == null)
{
next = Entry.Directory(currentSegment);
current.AddChild(next);
}
current = next;
}
current.AddChild(Entry.File(paths[paths.Length - 1], assemblyResourceName));
}
public XDocument ToXmlDocument()
{
var document = new XDocument(new XDeclaration("1.0", "utf-8", "yes"));
var root = new XElement(ElementNames.Root,
new XElement(ElementNames.ManifestVersion, "1.0"),
new XElement(ElementNames.FileSystem,
Root.Children.Select(e => BuildNode(e))));
document.Add(root);
return document;
}
private XElement BuildNode(Entry entry)
{
if (entry.IsFile)
{
return new XElement(ElementNames.File,
new XAttribute(ElementNames.Name, entry.Name),
new XElement(ElementNames.ResourcePath, entry.AssemblyResourceName));
}
else
{
var directory = new XElement(ElementNames.Directory, new XAttribute(ElementNames.Name, entry.Name));
directory.Add(entry.Children.Select(c => BuildNode(c)));
return directory;
}
}
private class ElementNames
{
public static readonly string Directory = "Directory";
public static readonly string Name = "Name";
public static readonly string FileSystem = "FileSystem";
public static readonly string Root = "Manifest";
public static readonly string File = "File";
public static readonly string ResourcePath = "ResourcePath";
public static readonly string ManifestVersion = "ManifestVersion";
}
}
}

View File

@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Description>MSBuild task to generate a manifest that can be used by Microsoft.Extensions.FileProviders.Embedded to preserve
metadata of the files embedded in the assembly at compilation time.</Description>
<TargetFramework>netstandard2.0</TargetFramework>
<GenerateDocumentationFile>false</GenerateDocumentationFile>
<IsShippingAssembly>true</IsShippingAssembly>
<IsPackable>false</IsPackable>
<IsImplementationProject>false</IsImplementationProject>
<ExcludeFromSourceBuild>false</ExcludeFromSourceBuild>
</PropertyGroup>
<ItemGroup>
<Reference Include="Microsoft.Build.Framework" />
<Reference Include="Microsoft.Build.Utilities.Core" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,387 @@
// 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.Text;
using System.Xml;
using System.Xml.Linq;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
using Xunit;
namespace Microsoft.Extensions.FileProviders.Embedded.Manifest.Task
{
public class GenerateEmbeddedResourcesManifestTest
{
[Fact]
public void CreateEmbeddedItems_MapsMetadataFromEmbeddedResources_UsesTheTargetPath()
{
// Arrange
var task = new TestGenerateEmbeddedResourcesManifest();
var embeddedFiles = CreateEmbeddedResource(
CreateMetadata(@"lib\js\jquery.validate.js"));
var expectedItems = new[]
{
CreateEmbeddedItem(@"lib\js\jquery.validate.js","lib.js.jquery.validate.js")
};
// Act
var embeddedItems = task.CreateEmbeddedItems(embeddedFiles);
// Assert
Assert.Equal(expectedItems, embeddedItems);
}
[Fact]
public void CreateEmbeddedItems_MapsMetadataFromEmbeddedResources_WithLogicalName()
{
// Arrange
var task = new TestGenerateEmbeddedResourcesManifest();
var DirectorySeparator = (Path.DirectorySeparatorChar == '\\' ? '/' : '\\');
var embeddedFiles = CreateEmbeddedResource(
CreateMetadata("site.css", null, "site.css"),
CreateMetadata("lib/jquery.validate.js", null, $"dist{DirectorySeparator}jquery.validate.js"));
var expectedItems = new[]
{
CreateEmbeddedItem("site.css","site.css"),
CreateEmbeddedItem(Path.Combine("dist","jquery.validate.js"),$"dist{DirectorySeparator}jquery.validate.js")
};
// Act
var embeddedItems = task.CreateEmbeddedItems(embeddedFiles);
// Assert
Assert.Equal(expectedItems, embeddedItems);
}
[Fact]
public void BuildManifest_CanCreatesManifest_ForTopLevelFiles()
{
// Arrange
var task = new TestGenerateEmbeddedResourcesManifest();
var embeddedFiles = CreateEmbeddedResource(
CreateMetadata("jquery.validate.js"),
CreateMetadata("jquery.min.js"),
CreateMetadata("Site.css"));
var manifestFiles = task.CreateEmbeddedItems(embeddedFiles);
var expectedManifest = new Manifest()
{
Root = Entry.Directory("").AddRange(
Entry.File("jquery.validate.js", "jquery.validate.js"),
Entry.File("jquery.min.js", "jquery.min.js"),
Entry.File("Site.css", "Site.css"))
};
// Act
var manifest = task.BuildManifest(manifestFiles);
// Assert
Assert.Equal(expectedManifest, manifest, ManifestComparer.Instance);
}
[Fact]
public void BuildManifest_CanCreatesManifest_ForFilesWithinAFolder()
{
// Arrange
var task = new TestGenerateEmbeddedResourcesManifest();
var embeddedFiles = CreateEmbeddedResource(
CreateMetadata(Path.Combine("wwwroot", "js", "jquery.validate.js")),
CreateMetadata(Path.Combine("wwwroot", "js", "jquery.min.js")),
CreateMetadata(Path.Combine("wwwroot", "css", "Site.css")),
CreateMetadata(Path.Combine("Areas", "Identity", "Views", "Account", "Index.cshtml")));
var manifestFiles = task.CreateEmbeddedItems(embeddedFiles);
var expectedManifest = new Manifest()
{
Root = Entry.Directory("").AddRange(
Entry.Directory("wwwroot").AddRange(
Entry.Directory("js").AddRange(
Entry.File("jquery.validate.js", "wwwroot.js.jquery.validate.js"),
Entry.File("jquery.min.js", "wwwroot.js.jquery.min.js")),
Entry.Directory("css").AddRange(
Entry.File("Site.css", "wwwroot.css.Site.css"))),
Entry.Directory("Areas").AddRange(
Entry.Directory("Identity").AddRange(
Entry.Directory("Views").AddRange(
Entry.Directory("Account").AddRange(
Entry.File("Index.cshtml", "Areas.Identity.Views.Account.Index.cshtml"))))))
};
// Act
var manifest = task.BuildManifest(manifestFiles);
// Assert
Assert.Equal(expectedManifest, manifest, ManifestComparer.Instance);
}
[Fact]
public void BuildManifest_RespectsEntriesWithLogicalName()
{
// Arrange
var task = new TestGenerateEmbeddedResourcesManifest();
var embeddedFiles = CreateEmbeddedResource(
CreateMetadata("jquery.validate.js", null, @"wwwroot\lib\js\jquery.validate.js"),
CreateMetadata("jquery.min.js", null, @"wwwroot\lib/js\jquery.min.js"),
CreateMetadata("Site.css", null, "wwwroot/lib/css/site.css"));
var manifestFiles = task.CreateEmbeddedItems(embeddedFiles);
var expectedManifest = new Manifest()
{
Root = Entry.Directory("").AddRange(
Entry.Directory("wwwroot").AddRange(
Entry.Directory("lib").AddRange(
Entry.Directory("js").AddRange(
Entry.File("jquery.validate.js", @"wwwroot\lib\js\jquery.validate.js"),
Entry.File("jquery.min.js", @"wwwroot\lib/js\jquery.min.js")),
Entry.Directory("css").AddRange(
Entry.File("site.css", "wwwroot/lib/css/site.css")))))
};
// Act
var manifest = task.BuildManifest(manifestFiles);
// Assert
Assert.Equal(expectedManifest, manifest, ManifestComparer.Instance);
}
[Fact]
public void BuildManifest_SupportsFilesAndFoldersWithDifferentCasing()
{
// Arrange
var task = new TestGenerateEmbeddedResourcesManifest();
var embeddedFiles = CreateEmbeddedResource(
CreateMetadata(Path.Combine("A", "b", "c.txt")),
CreateMetadata(Path.Combine("A", "B", "c.txt")),
CreateMetadata(Path.Combine("A", "B", "C.txt")),
CreateMetadata(Path.Combine("A", "b", "C.txt")),
CreateMetadata(Path.Combine("A", "d")),
CreateMetadata(Path.Combine("A", "D", "e.txt")));
var manifestFiles = task.CreateEmbeddedItems(embeddedFiles);
var expectedManifest = new Manifest()
{
Root = Entry.Directory("").AddRange(
Entry.Directory("A").AddRange(
Entry.Directory("b").AddRange(
Entry.File("c.txt", @"A.b.c.txt"),
Entry.File("C.txt", @"A.b.C.txt")),
Entry.Directory("B").AddRange(
Entry.File("c.txt", @"A.B.c.txt"),
Entry.File("C.txt", @"A.B.C.txt")),
Entry.Directory("D").AddRange(
Entry.File("e.txt", "A.D.e.txt")),
Entry.File("d", "A.d")))
};
// Act
var manifest = task.BuildManifest(manifestFiles);
// Assert
Assert.Equal(expectedManifest, manifest, ManifestComparer.Instance);
}
[Fact]
public void BuildManifest_ThrowsInvalidOperationException_WhenTryingToAddAFileWithTheSameNameAsAFolder()
{
// Arrange
var task = new TestGenerateEmbeddedResourcesManifest();
var embeddedFiles = CreateEmbeddedResource(
CreateMetadata(Path.Combine("A", "b", "c.txt")),
CreateMetadata(Path.Combine("A", "b")));
var manifestFiles = task.CreateEmbeddedItems(embeddedFiles);
// Act & Assert
Assert.Throws<InvalidOperationException>(() => task.BuildManifest(manifestFiles));
}
[Fact]
public void BuildManifest_ThrowsInvalidOperationException_WhenTryingToAddAFolderWithTheSameNameAsAFile()
{
// Arrange
var task = new TestGenerateEmbeddedResourcesManifest();
var embeddedFiles = CreateEmbeddedResource(
CreateMetadata(Path.Combine("A", "b")),
CreateMetadata(Path.Combine("A", "b", "c.txt")));
var manifestFiles = task.CreateEmbeddedItems(embeddedFiles);
// Act & Assert
Assert.Throws<InvalidOperationException>(() => task.BuildManifest(manifestFiles));
}
[Fact]
public void ToXmlDocument_GeneratesTheCorrectXmlDocument()
{
// Arrange
var manifest = new Manifest()
{
Root = Entry.Directory("").AddRange(
Entry.Directory("A").AddRange(
Entry.Directory("b").AddRange(
Entry.File("c.txt", @"A.b.c.txt"),
Entry.File("C.txt", @"A.b.C.txt")),
Entry.Directory("B").AddRange(
Entry.File("c.txt", @"A.B.c.txt"),
Entry.File("C.txt", @"A.B.C.txt")),
Entry.Directory("D").AddRange(
Entry.File("e.txt", "A.D.e.txt")),
Entry.File("d", "A.d")))
};
var expectedDocument = new XDocument(
new XDeclaration("1.0", "utf-8", "yes"),
new XElement("Manifest",
new XElement("ManifestVersion", "1.0"),
new XElement("FileSystem",
new XElement("Directory", new XAttribute("Name", "A"),
new XElement("Directory", new XAttribute("Name", "B"),
new XElement("File", new XAttribute("Name", "C.txt"), new XElement("ResourcePath", "A.B.C.txt")),
new XElement("File", new XAttribute("Name", "c.txt"), new XElement("ResourcePath", "A.B.c.txt"))),
new XElement("Directory", new XAttribute("Name", "D"),
new XElement("File", new XAttribute("Name", "e.txt"), new XElement("ResourcePath", "A.D.e.txt"))),
new XElement("Directory", new XAttribute("Name", "b"),
new XElement("File", new XAttribute("Name", "C.txt"), new XElement("ResourcePath", "A.b.C.txt")),
new XElement("File", new XAttribute("Name", "c.txt"), new XElement("ResourcePath", "A.b.c.txt"))),
new XElement("File", new XAttribute("Name", "d"), new XElement("ResourcePath", "A.d"))))));
// Act
var document = manifest.ToXmlDocument();
// Assert
Assert.Equal(expectedDocument.ToString(), document.ToString());
}
[Fact]
public void Execute_WritesManifest_ToOutputFile()
{
// Arrange
var task = new TestGenerateEmbeddedResourcesManifest();
var embeddedFiles = CreateEmbeddedResource(
CreateMetadata(Path.Combine("A", "b", "c.txt")),
CreateMetadata(Path.Combine("A", "B", "c.txt")),
CreateMetadata(Path.Combine("A", "B", "C.txt")),
CreateMetadata(Path.Combine("A", "b", "C.txt")),
CreateMetadata(Path.Combine("A", "d")),
CreateMetadata(Path.Combine("A", "D", "e.txt")));
task.EmbeddedFiles = embeddedFiles;
task.ManifestFile = Path.Combine("obj", "debug", "netstandard2.0");
var expectedDocument = new XDocument(
new XDeclaration("1.0", "utf-8", "yes"),
new XElement("Manifest",
new XElement("ManifestVersion", "1.0"),
new XElement("FileSystem",
new XElement("Directory", new XAttribute("Name", "A"),
new XElement("Directory", new XAttribute("Name", "B"),
new XElement("File", new XAttribute("Name", "C.txt"), new XElement("ResourcePath", "A.B.C.txt")),
new XElement("File", new XAttribute("Name", "c.txt"), new XElement("ResourcePath", "A.B.c.txt"))),
new XElement("Directory", new XAttribute("Name", "D"),
new XElement("File", new XAttribute("Name", "e.txt"), new XElement("ResourcePath", "A.D.e.txt"))),
new XElement("Directory", new XAttribute("Name", "b"),
new XElement("File", new XAttribute("Name", "C.txt"), new XElement("ResourcePath", "A.b.C.txt")),
new XElement("File", new XAttribute("Name", "c.txt"), new XElement("ResourcePath", "A.b.c.txt"))),
new XElement("File", new XAttribute("Name", "d"), new XElement("ResourcePath", "A.d"))))));
var expectedOutput = new MemoryStream();
var writer = XmlWriter.Create(expectedOutput, new XmlWriterSettings { Encoding = Encoding.UTF8 });
expectedDocument.WriteTo(writer);
writer.Flush();
expectedOutput.Seek(0, SeekOrigin.Begin);
// Act
task.Execute();
// Assert
task.Output.Seek(0, SeekOrigin.Begin);
using (var expectedReader = new StreamReader(expectedOutput))
{
using (var reader = new StreamReader(task.Output))
{
Assert.Equal(expectedReader.ReadToEnd(), reader.ReadToEnd());
}
}
}
private EmbeddedItem CreateEmbeddedItem(string manifestPath, string assemblyName) =>
new EmbeddedItem
{
ManifestFilePath = manifestPath,
AssemblyResourceName = assemblyName
};
public class TestGenerateEmbeddedResourcesManifest
: GenerateEmbeddedResourcesManifest
{
public TestGenerateEmbeddedResourcesManifest()
: this(new MemoryStream())
{
}
public TestGenerateEmbeddedResourcesManifest(Stream output)
{
Output = output;
}
public Stream Output { get; }
protected override XmlWriter GetXmlWriter(XmlWriterSettings settings)
{
settings.CloseOutput = false;
return XmlWriter.Create(Output, settings);
}
}
private ITaskItem[] CreateEmbeddedResource(params IDictionary<string, string>[] files) =>
files.Select(f => CreateTaskItem(f)).ToArray();
private ITaskItem CreateTaskItem(IDictionary<string, string> metadata)
{
var result = new TaskItem();
foreach (var kvp in metadata)
{
result.SetMetadata(kvp.Key, kvp.Value);
}
return result;
}
private static IDictionary<string, string>
CreateMetadata(
string targetPath,
string manifestResourceName = null,
string logicalName = null) =>
new Dictionary<string, string>
{
["TargetPath"] = targetPath,
["ManifestResourceName"] = manifestResourceName ?? targetPath.Replace("/", ".").Replace("\\", "."),
["LogicalName"] = logicalName ?? targetPath.Replace("/", ".").Replace("\\", "."),
};
private class ManifestComparer : IEqualityComparer<Manifest>
{
public static IEqualityComparer<Manifest> Instance { get; } = new ManifestComparer();
public bool Equals(Manifest x, Manifest y)
{
return x.Root.Equals(y.Root);
}
public int GetHashCode(Manifest obj)
{
return obj.Root.GetHashCode();
}
}
}
}

View File

@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>$(DefaultNetCoreTargetFramework)</TargetFramework>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\src\Microsoft.Extensions.FileProviders.Embedded.Manifest.Task.csproj" />
</ItemGroup>
<ItemGroup>
<Reference Include="Microsoft.Build.Framework" />
<Reference Include="Microsoft.Build.Utilities.Core" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,18 @@
// 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.
namespace Microsoft.Extensions.FileProviders.Embedded.Manifest.Task
{
internal static class SetExtensions
{
public static Entry AddRange(this Entry source, params Entry[] elements)
{
foreach (var element in elements)
{
source.Children.Add(element);
}
return source;
}
}
}

View File

@ -118,6 +118,12 @@ This package is an internal implementation of the .NET Core SDK and is not meant
<Output TaskParameter="JoinResult" ItemName="_ReferencedExtensionsRefAssemblies" /> <Output TaskParameter="JoinResult" ItemName="_ReferencedExtensionsRefAssemblies" />
</JoinItems> </JoinItems>
<!-- _DuplicatedExtensionsRefAssemblies represents ref assemblies that are present in Microsoft.Internal.Extensions.Refs package and also are built in this repo. -->
<!-- This should be temporary while we migrate sources since there should be no duplication when sources are deleted from extensions. -->
<JoinItems Left="@(_ReferencedExtensionsRefAssemblies)" Right="@(_ResolvedProjectReferencePaths)" LeftKey="Filename" RightKey="Filename" ItemSpecToUse="Left">
<Output TaskParameter="JoinResult" ItemName="_DuplicatedExtensionsRefAssemblies" />
</JoinItems>
<JoinItems Left="@(_ReferencedExtensionsRefAssemblies)" Right="@(ExternalAspNetCoreAppReference)" LeftKey="Filename" RightKey="Identity" ItemSpecToUse="Left"> <JoinItems Left="@(_ReferencedExtensionsRefAssemblies)" Right="@(ExternalAspNetCoreAppReference)" LeftKey="Filename" RightKey="Identity" ItemSpecToUse="Left">
<Output TaskParameter="JoinResult" ItemName="_SelectedExtensionsRefs" /> <Output TaskParameter="JoinResult" ItemName="_SelectedExtensionsRefs" />
</JoinItems> </JoinItems>
@ -127,12 +133,15 @@ This package is an internal implementation of the .NET Core SDK and is not meant
</JoinItems> </JoinItems>
<ItemGroup> <ItemGroup>
<!-- Do not exclude the ref assemblies that we built in this repo if it's also included in Microsoft.Internal.Extensions.Refs -->
<_ReferencedExtensionsRefAssembliesToExclude Include="@(_ReferencedExtensionsRefAssemblies)" Exclude="@(_DuplicatedExtensionsRefAssemblies)" />
<!-- Exclude transitive external dependencies that are not directly referenced by projects in AspNetCore or Extensions --> <!-- Exclude transitive external dependencies that are not directly referenced by projects in AspNetCore or Extensions -->
<AspNetCoreReferenceAssemblyPath <AspNetCoreReferenceAssemblyPath
Include="@(ReferencePathWithRefAssemblies)" Include="@(ReferencePathWithRefAssemblies)"
Condition="'%(ReferencePathWithRefAssemblies.IsReferenceAssembly)' != 'false'" Condition="'%(ReferencePathWithRefAssemblies.IsReferenceAssembly)' != 'false'"
Exclude=" Exclude="
@(_ReferencedExtensionsRefAssemblies); @(_ReferencedExtensionsRefAssembliesToExclude);
@(ReferencePathWithRefAssemblies->WithMetadataValue('NuGetPackageId', 'Microsoft.NETCore.App.Ref')); @(ReferencePathWithRefAssemblies->WithMetadataValue('NuGetPackageId', 'Microsoft.NETCore.App.Ref'));
@(ReferencePathWithRefAssemblies->WithMetadataValue('NuGetPackageId', 'System.Security.Cryptography.Pkcs')); @(ReferencePathWithRefAssemblies->WithMetadataValue('NuGetPackageId', 'System.Security.Cryptography.Pkcs'));
@(ReferencePathWithRefAssemblies->WithMetadataValue('NuGetPackageId', 'System.Drawing.Common')); @(ReferencePathWithRefAssemblies->WithMetadataValue('NuGetPackageId', 'System.Drawing.Common'));

View File

@ -0,0 +1,13 @@
<!-- This file is automatically generated. -->
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard2.0;$(DefaultNetCoreTargetFramework)</TargetFrameworks>
<TargetFrameworks Condition="'$(DotNetBuildFromSource)' == 'true'">$(DefaultNetCoreTargetFramework)</TargetFrameworks>
</PropertyGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
<Compile Include="Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.netstandard2.0.cs" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == '$(DefaultNetCoreTargetFramework)'">
<Compile Include="Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.netcoreapp.cs" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,72 @@
// 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.
namespace Microsoft.Extensions.Diagnostics.HealthChecks
{
public sealed partial class HealthCheckContext
{
public HealthCheckContext() { }
public Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckRegistration Registration { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } }
}
public sealed partial class HealthCheckRegistration
{
public HealthCheckRegistration(string name, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck instance, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus, System.Collections.Generic.IEnumerable<string> tags) { }
public HealthCheckRegistration(string name, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck instance, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus, System.Collections.Generic.IEnumerable<string> tags, System.TimeSpan? timeout) { }
public HealthCheckRegistration(string name, System.Func<System.IServiceProvider, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck> factory, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus, System.Collections.Generic.IEnumerable<string> tags) { }
public HealthCheckRegistration(string name, System.Func<System.IServiceProvider, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck> factory, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus, System.Collections.Generic.IEnumerable<string> tags, System.TimeSpan? timeout) { }
public System.Func<System.IServiceProvider, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck> Factory { get { throw null; } set { } }
public Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus FailureStatus { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } }
public string Name { get { throw null; } set { } }
public System.Collections.Generic.ISet<string> Tags { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } }
public System.TimeSpan Timeout { get { throw null; } set { } }
}
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
public partial struct HealthCheckResult
{
private object _dummy;
private int _dummyPrimitive;
public HealthCheckResult(Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus status, string description = null, System.Exception exception = null, System.Collections.Generic.IReadOnlyDictionary<string, object> data = null) { throw null; }
public System.Collections.Generic.IReadOnlyDictionary<string, object> Data { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } }
public string Description { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } }
public System.Exception Exception { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } }
public Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus Status { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } }
public static Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckResult Degraded(string description = null, System.Exception exception = null, System.Collections.Generic.IReadOnlyDictionary<string, object> data = null) { throw null; }
public static Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckResult Healthy(string description = null, System.Collections.Generic.IReadOnlyDictionary<string, object> data = null) { throw null; }
public static Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckResult Unhealthy(string description = null, System.Exception exception = null, System.Collections.Generic.IReadOnlyDictionary<string, object> data = null) { throw null; }
}
public sealed partial class HealthReport
{
public HealthReport(System.Collections.Generic.IReadOnlyDictionary<string, Microsoft.Extensions.Diagnostics.HealthChecks.HealthReportEntry> entries, System.TimeSpan totalDuration) { }
public System.Collections.Generic.IReadOnlyDictionary<string, Microsoft.Extensions.Diagnostics.HealthChecks.HealthReportEntry> Entries { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } }
public Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus Status { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } }
public System.TimeSpan TotalDuration { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } }
}
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
public partial struct HealthReportEntry
{
private object _dummy;
private int _dummyPrimitive;
public HealthReportEntry(Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus status, string description, System.TimeSpan duration, System.Exception exception, System.Collections.Generic.IReadOnlyDictionary<string, object> data) { throw null; }
public HealthReportEntry(Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus status, string description, System.TimeSpan duration, System.Exception exception, System.Collections.Generic.IReadOnlyDictionary<string, object> data, System.Collections.Generic.IEnumerable<string> tags = null) { throw null; }
public System.Collections.Generic.IReadOnlyDictionary<string, object> Data { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } }
public string Description { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } }
public System.TimeSpan Duration { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } }
public System.Exception Exception { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } }
public Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus Status { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } }
public System.Collections.Generic.IEnumerable<string> Tags { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } }
}
public enum HealthStatus
{
Unhealthy = 0,
Degraded = 1,
Healthy = 2,
}
public partial interface IHealthCheck
{
System.Threading.Tasks.Task<Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckResult> CheckHealthAsync(Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckContext context, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken));
}
public partial interface IHealthCheckPublisher
{
System.Threading.Tasks.Task PublishAsync(Microsoft.Extensions.Diagnostics.HealthChecks.HealthReport report, System.Threading.CancellationToken cancellationToken);
}
}

View File

@ -0,0 +1,72 @@
// 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.
namespace Microsoft.Extensions.Diagnostics.HealthChecks
{
public sealed partial class HealthCheckContext
{
public HealthCheckContext() { }
public Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckRegistration Registration { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } }
}
public sealed partial class HealthCheckRegistration
{
public HealthCheckRegistration(string name, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck instance, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus, System.Collections.Generic.IEnumerable<string> tags) { }
public HealthCheckRegistration(string name, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck instance, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus, System.Collections.Generic.IEnumerable<string> tags, System.TimeSpan? timeout) { }
public HealthCheckRegistration(string name, System.Func<System.IServiceProvider, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck> factory, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus, System.Collections.Generic.IEnumerable<string> tags) { }
public HealthCheckRegistration(string name, System.Func<System.IServiceProvider, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck> factory, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus, System.Collections.Generic.IEnumerable<string> tags, System.TimeSpan? timeout) { }
public System.Func<System.IServiceProvider, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck> Factory { get { throw null; } set { } }
public Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus FailureStatus { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } }
public string Name { get { throw null; } set { } }
public System.Collections.Generic.ISet<string> Tags { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } }
public System.TimeSpan Timeout { get { throw null; } set { } }
}
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
public partial struct HealthCheckResult
{
private object _dummy;
private int _dummyPrimitive;
public HealthCheckResult(Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus status, string description = null, System.Exception exception = null, System.Collections.Generic.IReadOnlyDictionary<string, object> data = null) { throw null; }
public System.Collections.Generic.IReadOnlyDictionary<string, object> Data { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } }
public string Description { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } }
public System.Exception Exception { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } }
public Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus Status { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } }
public static Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckResult Degraded(string description = null, System.Exception exception = null, System.Collections.Generic.IReadOnlyDictionary<string, object> data = null) { throw null; }
public static Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckResult Healthy(string description = null, System.Collections.Generic.IReadOnlyDictionary<string, object> data = null) { throw null; }
public static Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckResult Unhealthy(string description = null, System.Exception exception = null, System.Collections.Generic.IReadOnlyDictionary<string, object> data = null) { throw null; }
}
public sealed partial class HealthReport
{
public HealthReport(System.Collections.Generic.IReadOnlyDictionary<string, Microsoft.Extensions.Diagnostics.HealthChecks.HealthReportEntry> entries, System.TimeSpan totalDuration) { }
public System.Collections.Generic.IReadOnlyDictionary<string, Microsoft.Extensions.Diagnostics.HealthChecks.HealthReportEntry> Entries { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } }
public Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus Status { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } }
public System.TimeSpan TotalDuration { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } }
}
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
public partial struct HealthReportEntry
{
private object _dummy;
private int _dummyPrimitive;
public HealthReportEntry(Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus status, string description, System.TimeSpan duration, System.Exception exception, System.Collections.Generic.IReadOnlyDictionary<string, object> data) { throw null; }
public HealthReportEntry(Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus status, string description, System.TimeSpan duration, System.Exception exception, System.Collections.Generic.IReadOnlyDictionary<string, object> data, System.Collections.Generic.IEnumerable<string> tags = null) { throw null; }
public System.Collections.Generic.IReadOnlyDictionary<string, object> Data { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } }
public string Description { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } }
public System.TimeSpan Duration { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } }
public System.Exception Exception { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } }
public Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus Status { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } }
public System.Collections.Generic.IEnumerable<string> Tags { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } }
}
public enum HealthStatus
{
Unhealthy = 0,
Degraded = 1,
Healthy = 2,
}
public partial interface IHealthCheck
{
System.Threading.Tasks.Task<Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckResult> CheckHealthAsync(Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckContext context, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken));
}
public partial interface IHealthCheckPublisher
{
System.Threading.Tasks.Task PublishAsync(Microsoft.Extensions.Diagnostics.HealthChecks.HealthReport report, System.Threading.CancellationToken cancellationToken);
}
}

View File

@ -0,0 +1,13 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.Extensions.Diagnostics.HealthChecks
{
public sealed class HealthCheckContext
{
/// <summary>
/// Gets or sets the <see cref="HealthCheckRegistration"/> of the currently executing <see cref="IHealthCheck"/>.
/// </summary>
public HealthCheckRegistration Registration { get; set; }
}
}

View File

@ -0,0 +1,199 @@
// 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;
namespace Microsoft.Extensions.Diagnostics.HealthChecks
{
/// <summary>
/// Represent the registration information associated with an <see cref="IHealthCheck"/> implementation.
/// </summary>
/// <remarks>
/// <para>
/// The health check registration is provided as a separate object so that application developers can customize
/// how health check implementations are configured.
/// </para>
/// <para>
/// The registration is provided to an <see cref="IHealthCheck"/> implementation during execution through
/// <see cref="HealthCheckContext.Registration"/>. This allows a health check implementation to access named
/// options or perform other operations based on the registered name.
/// </para>
/// </remarks>
public sealed class HealthCheckRegistration
{
private Func<IServiceProvider, IHealthCheck> _factory;
private string _name;
private TimeSpan _timeout;
/// <summary>
/// Creates a new <see cref="T:Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckRegistration" /> for an existing <see cref="T:Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck" /> instance.
/// </summary>
/// <param name="name">The health check name.</param>
/// <param name="instance">The <see cref="T:Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck" /> instance.</param>
/// <param name="failureStatus">
/// The <see cref="T:Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus" /> that should be reported upon failure of the health check. If the provided value
/// is <c>null</c>, then <see cref="F:Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus.Unhealthy" /> will be reported.
/// </param>
/// <param name="tags">A list of tags that can be used for filtering health checks.</param>
public HealthCheckRegistration(string name, IHealthCheck instance, HealthStatus? failureStatus, IEnumerable<string> tags)
: this(name, instance, failureStatus, tags, default)
{
}
/// <summary>
/// Creates a new <see cref="HealthCheckRegistration"/> for an existing <see cref="IHealthCheck"/> instance.
/// </summary>
/// <param name="name">The health check name.</param>
/// <param name="instance">The <see cref="IHealthCheck"/> instance.</param>
/// <param name="failureStatus">
/// The <see cref="HealthStatus"/> that should be reported upon failure of the health check. If the provided value
/// is <c>null</c>, then <see cref="HealthStatus.Unhealthy"/> will be reported.
/// </param>
/// <param name="tags">A list of tags that can be used for filtering health checks.</param>
/// <param name="timeout">An optional <see cref="TimeSpan"/> representing the timeout of the check.</param>
public HealthCheckRegistration(string name, IHealthCheck instance, HealthStatus? failureStatus, IEnumerable<string> tags, TimeSpan? timeout)
{
if (name == null)
{
throw new ArgumentNullException(nameof(name));
}
if (instance == null)
{
throw new ArgumentNullException(nameof(instance));
}
if (timeout <= TimeSpan.Zero && timeout != System.Threading.Timeout.InfiniteTimeSpan)
{
throw new ArgumentOutOfRangeException(nameof(timeout));
}
Name = name;
FailureStatus = failureStatus ?? HealthStatus.Unhealthy;
Tags = new HashSet<string>(tags ?? Array.Empty<string>(), StringComparer.OrdinalIgnoreCase);
Factory = (_) => instance;
Timeout = timeout ?? System.Threading.Timeout.InfiniteTimeSpan;
}
/// <summary>
/// Creates a new <see cref="HealthCheckRegistration"/> for an existing <see cref="IHealthCheck"/> instance.
/// </summary>
/// <param name="name">The health check name.</param>
/// <param name="factory">A delegate used to create the <see cref="IHealthCheck"/> instance.</param>
/// <param name="failureStatus">
/// The <see cref="HealthStatus"/> that should be reported when the health check reports a failure. If the provided value
/// is <c>null</c>, then <see cref="HealthStatus.Unhealthy"/> will be reported.
/// </param>
/// <param name="tags">A list of tags that can be used for filtering health checks.</param>
public HealthCheckRegistration(
string name,
Func<IServiceProvider, IHealthCheck> factory,
HealthStatus? failureStatus,
IEnumerable<string> tags)
: this(name, factory, failureStatus, tags, default)
{
}
/// <summary>
/// Creates a new <see cref="HealthCheckRegistration"/> for an existing <see cref="IHealthCheck"/> instance.
/// </summary>
/// <param name="name">The health check name.</param>
/// <param name="factory">A delegate used to create the <see cref="IHealthCheck"/> instance.</param>
/// <param name="failureStatus">
/// The <see cref="HealthStatus"/> that should be reported when the health check reports a failure. If the provided value
/// is <c>null</c>, then <see cref="HealthStatus.Unhealthy"/> will be reported.
/// </param>
/// <param name="tags">A list of tags that can be used for filtering health checks.</param>
/// <param name="timeout">An optional <see cref="TimeSpan"/> representing the timeout of the check.</param>
public HealthCheckRegistration(
string name,
Func<IServiceProvider, IHealthCheck> factory,
HealthStatus? failureStatus,
IEnumerable<string> tags,
TimeSpan? timeout)
{
if (name == null)
{
throw new ArgumentNullException(nameof(name));
}
if (factory == null)
{
throw new ArgumentNullException(nameof(factory));
}
if (timeout <= TimeSpan.Zero && timeout != System.Threading.Timeout.InfiniteTimeSpan)
{
throw new ArgumentOutOfRangeException(nameof(timeout));
}
Name = name;
FailureStatus = failureStatus ?? HealthStatus.Unhealthy;
Tags = new HashSet<string>(tags ?? Array.Empty<string>(), StringComparer.OrdinalIgnoreCase);
Factory = factory;
Timeout = timeout ?? System.Threading.Timeout.InfiniteTimeSpan;
}
/// <summary>
/// Gets or sets a delegate used to create the <see cref="IHealthCheck"/> instance.
/// </summary>
public Func<IServiceProvider, IHealthCheck> Factory
{
get => _factory;
set
{
if (value == null)
{
throw new ArgumentNullException(nameof(value));
}
_factory = value;
}
}
/// <summary>
/// Gets or sets the <see cref="HealthStatus"/> that should be reported upon failure of the health check.
/// </summary>
public HealthStatus FailureStatus { get; set; }
/// <summary>
/// Gets or sets the timeout used for the test.
/// </summary>
public TimeSpan Timeout
{
get => _timeout;
set
{
if (value <= TimeSpan.Zero && value != System.Threading.Timeout.InfiniteTimeSpan)
{
throw new ArgumentOutOfRangeException(nameof(value));
}
_timeout = value;
}
}
/// <summary>
/// Gets or sets the health check name.
/// </summary>
public string Name
{
get => _name;
set
{
if (value == null)
{
throw new ArgumentNullException(nameof(value));
}
_name = value;
}
}
/// <summary>
/// Gets a list of tags that can be used for filtering health checks.
/// </summary>
public ISet<string> Tags { get; }
}
}

View File

@ -0,0 +1,88 @@
// 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;
namespace Microsoft.Extensions.Diagnostics.HealthChecks
{
/// <summary>
/// Represents the result of a health check.
/// </summary>
public struct HealthCheckResult
{
private static readonly IReadOnlyDictionary<string, object> _emptyReadOnlyDictionary = new Dictionary<string, object>();
/// <summary>
/// Creates a new <see cref="HealthCheckResult"/> with the specified values for <paramref name="status"/>,
/// <paramref name="exception"/>, <paramref name="description"/>, and <paramref name="data"/>.
/// </summary>
/// <param name="status">A value indicating the status of the component that was checked.</param>
/// <param name="description">A human-readable description of the status of the component that was checked.</param>
/// <param name="exception">An <see cref="Exception"/> representing the exception that was thrown when checking for status (if any).</param>
/// <param name="data">Additional key-value pairs describing the health of the component.</param>
public HealthCheckResult(HealthStatus status, string description = null, Exception exception = null, IReadOnlyDictionary<string, object> data = null)
{
Status = status;
Description = description;
Exception = exception;
Data = data ?? _emptyReadOnlyDictionary;
}
/// <summary>
/// Gets additional key-value pairs describing the health of the component.
/// </summary>
public IReadOnlyDictionary<string, object> Data { get; }
/// <summary>
/// Gets a human-readable description of the status of the component that was checked.
/// </summary>
public string Description { get; }
/// <summary>
/// Gets an <see cref="Exception"/> representing the exception that was thrown when checking for status (if any).
/// </summary>
public Exception Exception { get; }
/// <summary>
/// Gets a value indicating the status of the component that was checked.
/// </summary>
public HealthStatus Status { get; }
/// <summary>
/// Creates a <see cref="HealthCheckResult"/> representing a healthy component.
/// </summary>
/// <param name="description">A human-readable description of the status of the component that was checked. Optional.</param>
/// <param name="data">Additional key-value pairs describing the health of the component. Optional.</param>
/// <returns>A <see cref="HealthCheckResult"/> representing a healthy component.</returns>
public static HealthCheckResult Healthy(string description = null, IReadOnlyDictionary<string, object> data = null)
{
return new HealthCheckResult(status: HealthStatus.Healthy, description, exception: null, data);
}
/// <summary>
/// Creates a <see cref="HealthCheckResult"/> representing a degraded component.
/// </summary>
/// <param name="description">A human-readable description of the status of the component that was checked. Optional.</param>
/// <param name="exception">An <see cref="Exception"/> representing the exception that was thrown when checking for status. Optional.</param>
/// <param name="data">Additional key-value pairs describing the health of the component. Optional.</param>
/// <returns>A <see cref="HealthCheckResult"/> representing a degraged component.</returns>
public static HealthCheckResult Degraded(string description = null, Exception exception = null, IReadOnlyDictionary<string, object> data = null)
{
return new HealthCheckResult(status: HealthStatus.Degraded, description, exception: exception, data);
}
/// <summary>
/// Creates a <see cref="HealthCheckResult"/> representing an unhealthy component.
/// </summary>
/// <param name="description">A human-readable description of the status of the component that was checked. Optional.</param>
/// <param name="exception">An <see cref="Exception"/> representing the exception that was thrown when checking for status. Optional.</param>
/// <param name="data">Additional key-value pairs describing the health of the component. Optional.</param>
/// <returns>A <see cref="HealthCheckResult"/> representing an unhealthy component.</returns>
public static HealthCheckResult Unhealthy(string description = null, Exception exception = null, IReadOnlyDictionary<string, object> data = null)
{
return new HealthCheckResult(status: HealthStatus.Unhealthy, description, exception, data);
}
}
}

View File

@ -0,0 +1,68 @@
// 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;
namespace Microsoft.Extensions.Diagnostics.HealthChecks
{
/// <summary>
/// Represents the result of executing a group of <see cref="IHealthCheck"/> instances.
/// </summary>
public sealed class HealthReport
{
/// <summary>
/// Create a new <see cref="HealthReport"/> from the specified results.
/// </summary>
/// <param name="entries">A <see cref="IReadOnlyDictionary{TKey, T}"/> containing the results from each health check.</param>
/// <param name="totalDuration">A value indicating the time the health check service took to execute.</param>
public HealthReport(IReadOnlyDictionary<string, HealthReportEntry> entries, TimeSpan totalDuration)
{
Entries = entries;
Status = CalculateAggregateStatus(entries.Values);
TotalDuration = totalDuration;
}
/// <summary>
/// A <see cref="IReadOnlyDictionary{TKey, T}"/> containing the results from each health check.
/// </summary>
/// <remarks>
/// The keys in this dictionary map the name of each executed health check to a <see cref="HealthReportEntry"/> for the
/// result data returned from the corresponding health check.
/// </remarks>
public IReadOnlyDictionary<string, HealthReportEntry> Entries { get; }
/// <summary>
/// Gets a <see cref="HealthStatus"/> representing the aggregate status of all the health checks. The value of <see cref="Status"/>
/// will be the most severe status reported by a health check. If no checks were executed, the value is always <see cref="HealthStatus.Healthy"/>.
/// </summary>
public HealthStatus Status { get; }
/// <summary>
/// Gets the time the health check service took to execute.
/// </summary>
public TimeSpan TotalDuration { get; }
private HealthStatus CalculateAggregateStatus(IEnumerable<HealthReportEntry> entries)
{
// This is basically a Min() check, but we know the possible range, so we don't need to walk the whole list
var currentValue = HealthStatus.Healthy;
foreach (var entry in entries)
{
if (currentValue > entry.Status)
{
currentValue = entry.Status;
}
if (currentValue == HealthStatus.Unhealthy)
{
// Game over, man! Game over!
// (We hit the worst possible status, so there's no need to keep iterating)
return currentValue;
}
}
return currentValue;
}
}
}

View File

@ -0,0 +1,82 @@
// 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;
namespace Microsoft.Extensions.Diagnostics.HealthChecks
{
/// <summary>
/// Represents an entry in a <see cref="HealthReport"/>. Corresponds to the result of a single <see cref="IHealthCheck"/>.
/// </summary>
public struct HealthReportEntry
{
private static readonly IReadOnlyDictionary<string, object> _emptyReadOnlyDictionary = new Dictionary<string, object>();
/// <summary>
/// Creates a new <see cref="HealthReportEntry"/> with the specified values for <paramref name="status"/>, <paramref name="exception"/>,
/// <paramref name="description"/>, and <paramref name="data"/>.
/// </summary>
/// <param name="status">A value indicating the health status of the component that was checked.</param>
/// <param name="description">A human-readable description of the status of the component that was checked.</param>
/// <param name="duration">A value indicating the health execution duration.</param>
/// <param name="exception">An <see cref="Exception"/> representing the exception that was thrown when checking for status (if any).</param>
/// <param name="data">Additional key-value pairs describing the health of the component.</param>
public HealthReportEntry(HealthStatus status, string description, TimeSpan duration, Exception exception, IReadOnlyDictionary<string, object> data)
: this(status, description, duration, exception, data, null)
{
}
/// <summary>
/// Creates a new <see cref="HealthReportEntry"/> with the specified values for <paramref name="status"/>, <paramref name="exception"/>,
/// <paramref name="description"/>, and <paramref name="data"/>.
/// </summary>
/// <param name="status">A value indicating the health status of the component that was checked.</param>
/// <param name="description">A human-readable description of the status of the component that was checked.</param>
/// <param name="duration">A value indicating the health execution duration.</param>
/// <param name="exception">An <see cref="Exception"/> representing the exception that was thrown when checking for status (if any).</param>
/// <param name="data">Additional key-value pairs describing the health of the component.</param>
/// <param name="tags">Tags associated with the health check that generated the report entry.</param>
public HealthReportEntry(HealthStatus status, string description, TimeSpan duration, Exception exception, IReadOnlyDictionary<string, object> data, IEnumerable<string> tags = null)
{
Status = status;
Description = description;
Duration = duration;
Exception = exception;
Data = data ?? _emptyReadOnlyDictionary;
Tags = tags ?? Enumerable.Empty<string>();
}
/// <summary>
/// Gets additional key-value pairs describing the health of the component.
/// </summary>
public IReadOnlyDictionary<string, object> Data { get; }
/// <summary>
/// Gets a human-readable description of the status of the component that was checked.
/// </summary>
public string Description { get; }
/// <summary>
/// Gets the health check execution duration.
/// </summary>
public TimeSpan Duration { get; }
/// <summary>
/// Gets an <see cref="System.Exception"/> representing the exception that was thrown when checking for status (if any).
/// </summary>
public Exception Exception { get; }
/// <summary>
/// Gets the health status of the component that was checked.
/// </summary>
public HealthStatus Status { get; }
/// <summary>
/// Gets the tags associated with the health check.
/// </summary>
public IEnumerable<string> Tags { get; }
}
}

View File

@ -0,0 +1,37 @@
// 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.
namespace Microsoft.Extensions.Diagnostics.HealthChecks
{
/// <summary>
/// Represents the reported status of a health check result.
/// </summary>
/// <remarks>
/// <para>
/// A status of <see cref="Unhealthy"/> should be considered the default value for a failing health check. Application
/// developers may configure a health check to report a different status as desired.
/// </para>
/// <para>
/// The values of this enum or ordered from least healthy to most healthy. So <see cref="HealthStatus.Degraded"/> is
/// greater than <see cref="HealthStatus.Unhealthy"/> but less than <see cref="HealthStatus.Healthy"/>.
/// </para>
/// </remarks>
public enum HealthStatus
{
/// <summary>
/// Indicates that the health check determined that the component was unhealthy, or an unhandled
/// exception was thrown while executing the health check.
/// </summary>
Unhealthy = 0,
/// <summary>
/// Indicates that the health check determined that the component was in a degraded state.
/// </summary>
Degraded = 1,
/// <summary>
/// Indicates that the health check determined that the component was healthy.
/// </summary>
Healthy = 2,
}
}

View File

@ -0,0 +1,23 @@
// 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.Threading;
using System.Threading.Tasks;
namespace Microsoft.Extensions.Diagnostics.HealthChecks
{
/// <summary>
/// Represents a health check, which can be used to check the status of a component in the application, such as a backend service, database or some internal
/// state.
/// </summary>
public interface IHealthCheck
{
/// <summary>
/// Runs the health check, returning the status of the component being checked.
/// </summary>
/// <param name="context">A context object associated with the current execution.</param>
/// <param name="cancellationToken">A <see cref="CancellationToken"/> that can be used to cancel the health check.</param>
/// <returns>A <see cref="Task{HealthCheckResult}"/> that completes when the health check has finished, yielding the status of the component being checked.</returns>
Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default);
}
}

View File

@ -0,0 +1,39 @@
// 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.Threading;
using System.Threading.Tasks;
namespace Microsoft.Extensions.Diagnostics.HealthChecks
{
/// <summary>
/// Represents a publisher of <see cref="HealthReport"/> information.
/// </summary>
/// <remarks>
/// <para>
/// The default health checks implementation provided an <c>IHostedService</c> implementation that can
/// be used to execute health checks at regular intervals and provide the resulting <see cref="HealthReport"/>
/// data to all registered <see cref="IHealthCheckPublisher"/> instances.
/// </para>
/// <para>
/// To provide an <see cref="IHealthCheckPublisher"/> implementation, register an instance or type as a singleton
/// service in the dependency injection container.
/// </para>
/// <para>
/// <see cref="IHealthCheckPublisher"/> instances are provided with a <see cref="HealthReport"/> after executing
/// health checks in a background thread. The use of <see cref="IHealthCheckPublisher"/> depend on hosting in
/// an application using <c>IWebHost</c> or generic host (<c>IHost</c>). Execution of <see cref="IHealthCheckPublisher"/>
/// instance is not related to execution of health checks via a middleware.
/// </para>
/// </remarks>
public interface IHealthCheckPublisher
{
/// <summary>
/// Publishes the provided <paramref name="report"/>.
/// </summary>
/// <param name="report">The <see cref="HealthReport"/>. The result of executing a set of health checks.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/>.</param>
/// <returns>A <see cref="Task"/> which will complete when publishing is complete.</returns>
Task PublishAsync(HealthReport report, CancellationToken cancellationToken);
}
}

View File

@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Description>Abstractions for defining health checks in .NET applications
Commonly Used Types
Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck
</Description>
<RootNamespace>Microsoft.Extensions.Diagnostics.HealthChecks</RootNamespace>
<TargetFrameworks>netstandard2.0;$(DefaultNetCoreTargetFramework)</TargetFrameworks>
<TargetFrameworks Condition="'$(DotNetBuildFromSource)' == 'true'">$(DefaultNetCoreTargetFramework)</TargetFrameworks>
<NoWarn>$(NoWarn);CS1591</NoWarn>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<PackageTags>diagnostics;healthchecks</PackageTags>
<IsPackable>true</IsPackable>
<IsAspNetCoreApp>true</IsAspNetCoreApp>
</PropertyGroup>
</Project>

View File

@ -0,0 +1,21 @@
<!-- This file is automatically generated. -->
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard2.0;$(DefaultNetCoreTargetFramework)</TargetFrameworks>
<TargetFrameworks Condition="'$(DotNetBuildFromSource)' == 'true'">$(DefaultNetCoreTargetFramework)</TargetFrameworks>
</PropertyGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
<Compile Include="Microsoft.Extensions.Diagnostics.HealthChecks.netstandard2.0.cs" />
<Reference Include="Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions" />
<Reference Include="Microsoft.Extensions.Hosting.Abstractions" />
<Reference Include="Microsoft.Extensions.Options" />
<InternalsVisibleTo Include="Microsoft.Extensions.Diagnostics.HealthChecks.Tests" Key="" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == '$(DefaultNetCoreTargetFramework)'">
<Compile Include="Microsoft.Extensions.Diagnostics.HealthChecks.netcoreapp.cs" />
<Reference Include="Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions" />
<Reference Include="Microsoft.Extensions.Hosting.Abstractions" />
<Reference Include="Microsoft.Extensions.Options" />
<InternalsVisibleTo Include="Microsoft.Extensions.Diagnostics.HealthChecks.Tests" Key="" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,59 @@
// 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.
namespace Microsoft.Extensions.DependencyInjection
{
public static partial class HealthChecksBuilderAddCheckExtensions
{
public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck instance, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus, System.Collections.Generic.IEnumerable<string> tags) { throw null; }
public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck instance, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus = default(Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus?), System.Collections.Generic.IEnumerable<string> tags = null, System.TimeSpan? timeout = default(System.TimeSpan?)) { throw null; }
public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddCheck<T>(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus, System.Collections.Generic.IEnumerable<string> tags) where T : class, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck { throw null; }
public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddCheck<T>(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus = default(Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus?), System.Collections.Generic.IEnumerable<string> tags = null, System.TimeSpan? timeout = default(System.TimeSpan?)) where T : class, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck { throw null; }
public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddTypeActivatedCheck<T>(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus, System.Collections.Generic.IEnumerable<string> tags, params object[] args) where T : class, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck { throw null; }
public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddTypeActivatedCheck<T>(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus, System.Collections.Generic.IEnumerable<string> tags, System.TimeSpan timeout, params object[] args) where T : class, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck { throw null; }
public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddTypeActivatedCheck<T>(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus, params object[] args) where T : class, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck { throw null; }
public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddTypeActivatedCheck<T>(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, params object[] args) where T : class, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck { throw null; }
}
public static partial class HealthChecksBuilderDelegateExtensions
{
public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddAsyncCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, System.Func<System.Threading.CancellationToken, System.Threading.Tasks.Task<Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckResult>> check, System.Collections.Generic.IEnumerable<string> tags) { throw null; }
public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddAsyncCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, System.Func<System.Threading.CancellationToken, System.Threading.Tasks.Task<Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckResult>> check, System.Collections.Generic.IEnumerable<string> tags = null, System.TimeSpan? timeout = default(System.TimeSpan?)) { throw null; }
public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddAsyncCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, System.Func<System.Threading.Tasks.Task<Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckResult>> check, System.Collections.Generic.IEnumerable<string> tags) { throw null; }
public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddAsyncCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, System.Func<System.Threading.Tasks.Task<Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckResult>> check, System.Collections.Generic.IEnumerable<string> tags = null, System.TimeSpan? timeout = default(System.TimeSpan?)) { throw null; }
public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, System.Func<Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckResult> check, System.Collections.Generic.IEnumerable<string> tags) { throw null; }
public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, System.Func<Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckResult> check, System.Collections.Generic.IEnumerable<string> tags = null, System.TimeSpan? timeout = default(System.TimeSpan?)) { throw null; }
public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, System.Func<System.Threading.CancellationToken, Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckResult> check, System.Collections.Generic.IEnumerable<string> tags) { throw null; }
public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, System.Func<System.Threading.CancellationToken, Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckResult> check, System.Collections.Generic.IEnumerable<string> tags = null, System.TimeSpan? timeout = default(System.TimeSpan?)) { throw null; }
}
public static partial class HealthCheckServiceCollectionExtensions
{
public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddHealthChecks(this Microsoft.Extensions.DependencyInjection.IServiceCollection services) { throw null; }
}
public partial interface IHealthChecksBuilder
{
Microsoft.Extensions.DependencyInjection.IServiceCollection Services { get; }
Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder Add(Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckRegistration registration);
}
}
namespace Microsoft.Extensions.Diagnostics.HealthChecks
{
public sealed partial class HealthCheckPublisherOptions
{
public HealthCheckPublisherOptions() { }
public System.TimeSpan Delay { get { throw null; } set { } }
public System.TimeSpan Period { get { throw null; } set { } }
public System.Func<Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckRegistration, bool> Predicate { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } }
public System.TimeSpan Timeout { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } }
}
public abstract partial class HealthCheckService
{
protected HealthCheckService() { }
public abstract System.Threading.Tasks.Task<Microsoft.Extensions.Diagnostics.HealthChecks.HealthReport> CheckHealthAsync(System.Func<Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckRegistration, bool> predicate, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken));
public System.Threading.Tasks.Task<Microsoft.Extensions.Diagnostics.HealthChecks.HealthReport> CheckHealthAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
}
public sealed partial class HealthCheckServiceOptions
{
public HealthCheckServiceOptions() { }
public System.Collections.Generic.ICollection<Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckRegistration> Registrations { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } }
}
}

View File

@ -0,0 +1,59 @@
// 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.
namespace Microsoft.Extensions.DependencyInjection
{
public static partial class HealthChecksBuilderAddCheckExtensions
{
public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck instance, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus, System.Collections.Generic.IEnumerable<string> tags) { throw null; }
public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck instance, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus = default(Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus?), System.Collections.Generic.IEnumerable<string> tags = null, System.TimeSpan? timeout = default(System.TimeSpan?)) { throw null; }
public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddCheck<T>(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus, System.Collections.Generic.IEnumerable<string> tags) where T : class, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck { throw null; }
public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddCheck<T>(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus = default(Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus?), System.Collections.Generic.IEnumerable<string> tags = null, System.TimeSpan? timeout = default(System.TimeSpan?)) where T : class, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck { throw null; }
public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddTypeActivatedCheck<T>(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus, System.Collections.Generic.IEnumerable<string> tags, params object[] args) where T : class, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck { throw null; }
public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddTypeActivatedCheck<T>(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus, System.Collections.Generic.IEnumerable<string> tags, System.TimeSpan timeout, params object[] args) where T : class, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck { throw null; }
public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddTypeActivatedCheck<T>(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus, params object[] args) where T : class, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck { throw null; }
public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddTypeActivatedCheck<T>(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, params object[] args) where T : class, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck { throw null; }
}
public static partial class HealthChecksBuilderDelegateExtensions
{
public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddAsyncCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, System.Func<System.Threading.CancellationToken, System.Threading.Tasks.Task<Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckResult>> check, System.Collections.Generic.IEnumerable<string> tags) { throw null; }
public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddAsyncCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, System.Func<System.Threading.CancellationToken, System.Threading.Tasks.Task<Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckResult>> check, System.Collections.Generic.IEnumerable<string> tags = null, System.TimeSpan? timeout = default(System.TimeSpan?)) { throw null; }
public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddAsyncCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, System.Func<System.Threading.Tasks.Task<Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckResult>> check, System.Collections.Generic.IEnumerable<string> tags) { throw null; }
public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddAsyncCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, System.Func<System.Threading.Tasks.Task<Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckResult>> check, System.Collections.Generic.IEnumerable<string> tags = null, System.TimeSpan? timeout = default(System.TimeSpan?)) { throw null; }
public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, System.Func<Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckResult> check, System.Collections.Generic.IEnumerable<string> tags) { throw null; }
public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, System.Func<Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckResult> check, System.Collections.Generic.IEnumerable<string> tags = null, System.TimeSpan? timeout = default(System.TimeSpan?)) { throw null; }
public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, System.Func<System.Threading.CancellationToken, Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckResult> check, System.Collections.Generic.IEnumerable<string> tags) { throw null; }
public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, System.Func<System.Threading.CancellationToken, Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckResult> check, System.Collections.Generic.IEnumerable<string> tags = null, System.TimeSpan? timeout = default(System.TimeSpan?)) { throw null; }
}
public static partial class HealthCheckServiceCollectionExtensions
{
public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddHealthChecks(this Microsoft.Extensions.DependencyInjection.IServiceCollection services) { throw null; }
}
public partial interface IHealthChecksBuilder
{
Microsoft.Extensions.DependencyInjection.IServiceCollection Services { get; }
Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder Add(Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckRegistration registration);
}
}
namespace Microsoft.Extensions.Diagnostics.HealthChecks
{
public sealed partial class HealthCheckPublisherOptions
{
public HealthCheckPublisherOptions() { }
public System.TimeSpan Delay { get { throw null; } set { } }
public System.TimeSpan Period { get { throw null; } set { } }
public System.Func<Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckRegistration, bool> Predicate { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } }
public System.TimeSpan Timeout { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } }
}
public abstract partial class HealthCheckService
{
protected HealthCheckService() { }
public abstract System.Threading.Tasks.Task<Microsoft.Extensions.Diagnostics.HealthChecks.HealthReport> CheckHealthAsync(System.Func<Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckRegistration, bool> predicate, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken));
public System.Threading.Tasks.Task<Microsoft.Extensions.Diagnostics.HealthChecks.HealthReport> CheckHealthAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
}
public sealed partial class HealthCheckServiceOptions
{
public HealthCheckServiceOptions() { }
public System.Collections.Generic.ICollection<Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckRegistration> Registrations { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } }
}
}

View File

@ -0,0 +1,346 @@
// 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;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Internal;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace Microsoft.Extensions.Diagnostics.HealthChecks
{
internal class DefaultHealthCheckService : HealthCheckService
{
private readonly IServiceScopeFactory _scopeFactory;
private readonly IOptions<HealthCheckServiceOptions> _options;
private readonly ILogger<DefaultHealthCheckService> _logger;
public DefaultHealthCheckService(
IServiceScopeFactory scopeFactory,
IOptions<HealthCheckServiceOptions> options,
ILogger<DefaultHealthCheckService> logger)
{
_scopeFactory = scopeFactory ?? throw new ArgumentNullException(nameof(scopeFactory));
_options = options ?? throw new ArgumentNullException(nameof(options));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
// We're specifically going out of our way to do this at startup time. We want to make sure you
// get any kind of health-check related error as early as possible. Waiting until someone
// actually tries to **run** health checks would be real baaaaad.
ValidateRegistrations(_options.Value.Registrations);
}
public override async Task<HealthReport> CheckHealthAsync(
Func<HealthCheckRegistration, bool> predicate,
CancellationToken cancellationToken = default)
{
var registrations = _options.Value.Registrations;
if (predicate != null)
{
registrations = registrations.Where(predicate).ToArray();
}
var totalTime = ValueStopwatch.StartNew();
Log.HealthCheckProcessingBegin(_logger);
var tasks = new Task<HealthReportEntry>[registrations.Count];
var index = 0;
using (var scope = _scopeFactory.CreateScope())
{
foreach (var registration in registrations)
{
tasks[index++] = Task.Run(() => RunCheckAsync(scope, registration, cancellationToken), cancellationToken);
}
await Task.WhenAll(tasks).ConfigureAwait(false);
}
index = 0;
var entries = new Dictionary<string, HealthReportEntry>(StringComparer.OrdinalIgnoreCase);
foreach (var registration in registrations)
{
entries[registration.Name] = tasks[index++].Result;
}
var totalElapsedTime = totalTime.GetElapsedTime();
var report = new HealthReport(entries, totalElapsedTime);
Log.HealthCheckProcessingEnd(_logger, report.Status, totalElapsedTime);
return report;
}
private async Task<HealthReportEntry> RunCheckAsync(IServiceScope scope, HealthCheckRegistration registration, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
var healthCheck = registration.Factory(scope.ServiceProvider);
// If the health check does things like make Database queries using EF or backend HTTP calls,
// it may be valuable to know that logs it generates are part of a health check. So we start a scope.
using (_logger.BeginScope(new HealthCheckLogScope(registration.Name)))
{
var stopwatch = ValueStopwatch.StartNew();
var context = new HealthCheckContext { Registration = registration };
Log.HealthCheckBegin(_logger, registration);
HealthReportEntry entry;
CancellationTokenSource timeoutCancellationTokenSource = null;
try
{
HealthCheckResult result;
var checkCancellationToken = cancellationToken;
if (registration.Timeout > TimeSpan.Zero)
{
timeoutCancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
timeoutCancellationTokenSource.CancelAfter(registration.Timeout);
checkCancellationToken = timeoutCancellationTokenSource.Token;
}
result = await healthCheck.CheckHealthAsync(context, checkCancellationToken).ConfigureAwait(false);
var duration = stopwatch.GetElapsedTime();
entry = new HealthReportEntry(
status: result.Status,
description: result.Description,
duration: duration,
exception: result.Exception,
data: result.Data,
tags: registration.Tags);
Log.HealthCheckEnd(_logger, registration, entry, duration);
Log.HealthCheckData(_logger, registration, entry);
}
catch (OperationCanceledException ex) when (!cancellationToken.IsCancellationRequested)
{
var duration = stopwatch.GetElapsedTime();
entry = new HealthReportEntry(
status: HealthStatus.Unhealthy,
description: "A timeout occurred while running check.",
duration: duration,
exception: ex,
data: null);
Log.HealthCheckError(_logger, registration, ex, duration);
}
// Allow cancellation to propagate if it's not a timeout.
catch (Exception ex) when (ex as OperationCanceledException == null)
{
var duration = stopwatch.GetElapsedTime();
entry = new HealthReportEntry(
status: HealthStatus.Unhealthy,
description: ex.Message,
duration: duration,
exception: ex,
data: null);
Log.HealthCheckError(_logger, registration, ex, duration);
}
finally
{
timeoutCancellationTokenSource?.Dispose();
}
return entry;
}
}
private static void ValidateRegistrations(IEnumerable<HealthCheckRegistration> registrations)
{
// Scan the list for duplicate names to provide a better error if there are duplicates.
var duplicateNames = registrations
.GroupBy(c => c.Name, StringComparer.OrdinalIgnoreCase)
.Where(g => g.Count() > 1)
.Select(g => g.Key)
.ToList();
if (duplicateNames.Count > 0)
{
throw new ArgumentException($"Duplicate health checks were registered with the name(s): {string.Join(", ", duplicateNames)}", nameof(registrations));
}
}
internal static class EventIds
{
public static readonly EventId HealthCheckProcessingBegin = new EventId(100, "HealthCheckProcessingBegin");
public static readonly EventId HealthCheckProcessingEnd = new EventId(101, "HealthCheckProcessingEnd");
public static readonly EventId HealthCheckBegin = new EventId(102, "HealthCheckBegin");
public static readonly EventId HealthCheckEnd = new EventId(103, "HealthCheckEnd");
public static readonly EventId HealthCheckError = new EventId(104, "HealthCheckError");
public static readonly EventId HealthCheckData = new EventId(105, "HealthCheckData");
}
private static class Log
{
private static readonly Action<ILogger, Exception> _healthCheckProcessingBegin = LoggerMessage.Define(
LogLevel.Debug,
EventIds.HealthCheckProcessingBegin,
"Running health checks");
private static readonly Action<ILogger, double, HealthStatus, Exception> _healthCheckProcessingEnd = LoggerMessage.Define<double, HealthStatus>(
LogLevel.Debug,
EventIds.HealthCheckProcessingEnd,
"Health check processing completed after {ElapsedMilliseconds}ms with combined status {HealthStatus}");
private static readonly Action<ILogger, string, Exception> _healthCheckBegin = LoggerMessage.Define<string>(
LogLevel.Debug,
EventIds.HealthCheckBegin,
"Running health check {HealthCheckName}");
// These are separate so they can have different log levels
private static readonly string HealthCheckEndText = "Health check {HealthCheckName} completed after {ElapsedMilliseconds}ms with status {HealthStatus} and '{HealthCheckDescription}'";
private static readonly Action<ILogger, string, double, HealthStatus, string, Exception> _healthCheckEndHealthy = LoggerMessage.Define<string, double, HealthStatus, string>(
LogLevel.Debug,
EventIds.HealthCheckEnd,
HealthCheckEndText);
private static readonly Action<ILogger, string, double, HealthStatus, string, Exception> _healthCheckEndDegraded = LoggerMessage.Define<string, double, HealthStatus, string>(
LogLevel.Warning,
EventIds.HealthCheckEnd,
HealthCheckEndText);
private static readonly Action<ILogger, string, double, HealthStatus, string, Exception> _healthCheckEndUnhealthy = LoggerMessage.Define<string, double, HealthStatus, string>(
LogLevel.Error,
EventIds.HealthCheckEnd,
HealthCheckEndText);
private static readonly Action<ILogger, string, double, HealthStatus, string, Exception> _healthCheckEndFailed = LoggerMessage.Define<string, double, HealthStatus, string>(
LogLevel.Error,
EventIds.HealthCheckEnd,
HealthCheckEndText);
private static readonly Action<ILogger, string, double, Exception> _healthCheckError = LoggerMessage.Define<string, double>(
LogLevel.Error,
EventIds.HealthCheckError,
"Health check {HealthCheckName} threw an unhandled exception after {ElapsedMilliseconds}ms");
public static void HealthCheckProcessingBegin(ILogger logger)
{
_healthCheckProcessingBegin(logger, null);
}
public static void HealthCheckProcessingEnd(ILogger logger, HealthStatus status, TimeSpan duration)
{
_healthCheckProcessingEnd(logger, duration.TotalMilliseconds, status, null);
}
public static void HealthCheckBegin(ILogger logger, HealthCheckRegistration registration)
{
_healthCheckBegin(logger, registration.Name, null);
}
public static void HealthCheckEnd(ILogger logger, HealthCheckRegistration registration, HealthReportEntry entry, TimeSpan duration)
{
switch (entry.Status)
{
case HealthStatus.Healthy:
_healthCheckEndHealthy(logger, registration.Name, duration.TotalMilliseconds, entry.Status, entry.Description, null);
break;
case HealthStatus.Degraded:
_healthCheckEndDegraded(logger, registration.Name, duration.TotalMilliseconds, entry.Status, entry.Description, null);
break;
case HealthStatus.Unhealthy:
_healthCheckEndUnhealthy(logger, registration.Name, duration.TotalMilliseconds, entry.Status, entry.Description, null);
break;
}
}
public static void HealthCheckError(ILogger logger, HealthCheckRegistration registration, Exception exception, TimeSpan duration)
{
_healthCheckError(logger, registration.Name, duration.TotalMilliseconds, exception);
}
public static void HealthCheckData(ILogger logger, HealthCheckRegistration registration, HealthReportEntry entry)
{
if (entry.Data.Count > 0 && logger.IsEnabled(LogLevel.Debug))
{
logger.Log(
LogLevel.Debug,
EventIds.HealthCheckData,
new HealthCheckDataLogValue(registration.Name, entry.Data),
null,
(state, ex) => state.ToString());
}
}
}
internal class HealthCheckDataLogValue : IReadOnlyList<KeyValuePair<string, object>>
{
private readonly string _name;
private readonly List<KeyValuePair<string, object>> _values;
private string _formatted;
public HealthCheckDataLogValue(string name, IReadOnlyDictionary<string, object> values)
{
_name = name;
_values = values.ToList();
// We add the name as a kvp so that you can filter by health check name in the logs.
// This is the same parameter name used in the other logs.
_values.Add(new KeyValuePair<string, object>("HealthCheckName", name));
}
public KeyValuePair<string, object> this[int index]
{
get
{
if (index < 0 || index >= Count)
{
throw new IndexOutOfRangeException(nameof(index));
}
return _values[index];
}
}
public int Count => _values.Count;
public IEnumerator<KeyValuePair<string, object>> GetEnumerator()
{
return _values.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return _values.GetEnumerator();
}
public override string ToString()
{
if (_formatted == null)
{
var builder = new StringBuilder();
builder.AppendLine($"Health check data for {_name}:");
var values = _values;
for (var i = 0; i < values.Count; i++)
{
var kvp = values[i];
builder.Append(" ");
builder.Append(kvp.Key);
builder.Append(": ");
builder.AppendLine(kvp.Value?.ToString());
}
_formatted = builder.ToString();
}
return _formatted;
}
}
}
}

View File

@ -0,0 +1,35 @@
// 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.Threading;
using System.Threading.Tasks;
namespace Microsoft.Extensions.Diagnostics.HealthChecks
{
/// <summary>
/// A simple implementation of <see cref="IHealthCheck"/> which uses a provided delegate to
/// implement the check.
/// </summary>
internal sealed class DelegateHealthCheck : IHealthCheck
{
private readonly Func<CancellationToken, Task<HealthCheckResult>> _check;
/// <summary>
/// Create an instance of <see cref="DelegateHealthCheck"/> from the specified delegate.
/// </summary>
/// <param name="check">A delegate which provides the code to execute when the health check is run.</param>
public DelegateHealthCheck(Func<CancellationToken, Task<HealthCheckResult>> check)
{
_check = check ?? throw new ArgumentNullException(nameof(check));
}
/// <summary>
/// Runs the health check, returning the status of the component being checked.
/// </summary>
/// <param name="context">A context object associated with the current execution.</param>
/// <param name="cancellationToken">A <see cref="CancellationToken"/> that can be used to cancel the health check.</param>
/// <returns>A <see cref="Task{HealthCheckResult}"/> that completes when the health check has finished, yielding the status of the component being checked.</returns>
public Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default) => _check(cancellationToken);
}
}

View File

@ -0,0 +1,33 @@
// 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 Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Diagnostics.HealthChecks;
using Microsoft.Extensions.Hosting;
namespace Microsoft.Extensions.DependencyInjection
{
/// <summary>
/// Provides extension methods for registering <see cref="HealthCheckService"/> in an <see cref="IServiceCollection"/>.
/// </summary>
public static class HealthCheckServiceCollectionExtensions
{
/// <summary>
/// Adds the <see cref="HealthCheckService"/> to the container, using the provided delegate to register
/// health checks.
/// </summary>
/// <remarks>
/// This operation is idempotent - multiple invocations will still only result in a single
/// <see cref="HealthCheckService"/> instance in the <see cref="IServiceCollection"/>. It can be invoked
/// multiple times in order to get access to the <see cref="IHealthChecksBuilder"/> in multiple places.
/// </remarks>
/// <param name="services">The <see cref="IServiceCollection"/> to add the <see cref="HealthCheckService"/> to.</param>
/// <returns>An instance of <see cref="IHealthChecksBuilder"/> from which health checks can be registered.</returns>
public static IHealthChecksBuilder AddHealthChecks(this IServiceCollection services)
{
services.TryAddSingleton<HealthCheckService, DefaultHealthCheckService>();
services.TryAddEnumerable(ServiceDescriptor.Singleton<IHostedService, HealthCheckPublisherHostedService>());
return new HealthChecksBuilder(services);
}
}
}

View File

@ -0,0 +1,33 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.Extensions.Diagnostics.HealthChecks;
namespace Microsoft.Extensions.DependencyInjection
{
internal class HealthChecksBuilder : IHealthChecksBuilder
{
public HealthChecksBuilder(IServiceCollection services)
{
Services = services;
}
public IServiceCollection Services { get; }
public IHealthChecksBuilder Add(HealthCheckRegistration registration)
{
if (registration == null)
{
throw new ArgumentNullException(nameof(registration));
}
Services.Configure<HealthCheckServiceOptions>(options =>
{
options.Registrations.Add(registration);
});
return this;
}
}
}

View File

@ -0,0 +1,285 @@
// 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 Microsoft.Extensions.Diagnostics.HealthChecks;
namespace Microsoft.Extensions.DependencyInjection
{
/// <summary>
/// Provides basic extension methods for registering <see cref="IHealthCheck"/> instances in an <see cref="IHealthChecksBuilder"/>.
/// </summary>
public static class HealthChecksBuilderAddCheckExtensions
{
/// <summary>
/// Adds a new health check with the specified name and implementation.
/// </summary>
/// <param name="builder">The <see cref="IHealthChecksBuilder"/>.</param>
/// <param name="name">The name of the health check.</param>
/// <param name="instance">An <see cref="IHealthCheck"/> instance.</param>
/// <param name="failureStatus">
/// The <see cref="HealthStatus"/> that should be reported when the health check reports a failure. If the provided value
/// is <c>null</c>, then <see cref="HealthStatus.Unhealthy"/> will be reported.
/// </param>
/// <param name="tags">A list of tags that can be used to filter health checks.</param>
/// <returns>The <see cref="IHealthChecksBuilder"/>.</returns>
// 2.0 BACKCOMPAT OVERLOAD -- DO NOT TOUCH
public static IHealthChecksBuilder AddCheck(
this IHealthChecksBuilder builder,
string name,
IHealthCheck instance,
HealthStatus? failureStatus,
IEnumerable<string> tags)
{
return AddCheck(builder, name, instance, failureStatus, tags, default);
}
/// <summary>
/// Adds a new health check with the specified name and implementation.
/// </summary>
/// <param name="builder">The <see cref="IHealthChecksBuilder"/>.</param>
/// <param name="name">The name of the health check.</param>
/// <param name="instance">An <see cref="IHealthCheck"/> instance.</param>
/// <param name="failureStatus">
/// The <see cref="HealthStatus"/> that should be reported when the health check reports a failure. If the provided value
/// is <c>null</c>, then <see cref="HealthStatus.Unhealthy"/> will be reported.
/// </param>
/// <param name="tags">A list of tags that can be used to filter health checks.</param>
/// <param name="timeout">An optional <see cref="TimeSpan"/> representing the timeout of the check.</param>
/// <returns>The <see cref="IHealthChecksBuilder"/>.</returns>
public static IHealthChecksBuilder AddCheck(
this IHealthChecksBuilder builder,
string name,
IHealthCheck instance,
HealthStatus? failureStatus = null,
IEnumerable<string> tags = null,
TimeSpan? timeout = null)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
if (name == null)
{
throw new ArgumentNullException(nameof(name));
}
if (instance == null)
{
throw new ArgumentNullException(nameof(instance));
}
return builder.Add(new HealthCheckRegistration(name, instance, failureStatus, tags, timeout));
}
/// <summary>
/// Adds a new health check with the specified name and implementation.
/// </summary>
/// <typeparam name="T">The health check implementation type.</typeparam>
/// <param name="builder">The <see cref="IHealthChecksBuilder"/>.</param>
/// <param name="name">The name of the health check.</param>
/// <param name="failureStatus">
/// The <see cref="HealthStatus"/> that should be reported when the health check reports a failure. If the provided value
/// is <c>null</c>, then <see cref="HealthStatus.Unhealthy"/> will be reported.
/// </param>
/// <param name="tags">A list of tags that can be used to filter health checks.</param>
/// <returns>The <see cref="IHealthChecksBuilder"/>.</returns>
/// <remarks>
/// This method will use <see cref="ActivatorUtilities.GetServiceOrCreateInstance{T}(IServiceProvider)"/> to create the health check
/// instance when needed. If a service of type <typeparamref name="T"/> is registered in the dependency injection container
/// with any lifetime it will be used. Otherwise an instance of type <typeparamref name="T"/> will be constructed with
/// access to services from the dependency injection container.
/// </remarks>
// 2.0 BACKCOMPAT OVERLOAD -- DO NOT TOUCH
public static IHealthChecksBuilder AddCheck<T>(
this IHealthChecksBuilder builder,
string name,
HealthStatus? failureStatus,
IEnumerable<string> tags) where T : class, IHealthCheck
{
return AddCheck<T>(builder, name, failureStatus, tags, default);
}
/// <summary>
/// Adds a new health check with the specified name and implementation.
/// </summary>
/// <typeparam name="T">The health check implementation type.</typeparam>
/// <param name="builder">The <see cref="IHealthChecksBuilder"/>.</param>
/// <param name="name">The name of the health check.</param>
/// <param name="failureStatus">
/// The <see cref="HealthStatus"/> that should be reported when the health check reports a failure. If the provided value
/// is <c>null</c>, then <see cref="HealthStatus.Unhealthy"/> will be reported.
/// </param>
/// <param name="tags">A list of tags that can be used to filter health checks.</param>
/// <param name="timeout">An optional <see cref="TimeSpan"/> representing the timeout of the check.</param>
/// <returns>The <see cref="IHealthChecksBuilder"/>.</returns>
/// <remarks>
/// This method will use <see cref="ActivatorUtilities.GetServiceOrCreateInstance{T}(IServiceProvider)"/> to create the health check
/// instance when needed. If a service of type <typeparamref name="T"/> is registered in the dependency injection container
/// with any lifetime it will be used. Otherwise an instance of type <typeparamref name="T"/> will be constructed with
/// access to services from the dependency injection container.
/// </remarks>
public static IHealthChecksBuilder AddCheck<T>(
this IHealthChecksBuilder builder,
string name,
HealthStatus? failureStatus = null,
IEnumerable<string> tags = null,
TimeSpan? timeout = null) where T : class, IHealthCheck
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
if (name == null)
{
throw new ArgumentNullException(nameof(name));
}
return builder.Add(new HealthCheckRegistration(name, s => ActivatorUtilities.GetServiceOrCreateInstance<T>(s), failureStatus, tags, timeout));
}
// NOTE: AddTypeActivatedCheck has overloads rather than default parameters values, because default parameter values don't
// play super well with params.
/// <summary>
/// Adds a new type activated health check with the specified name and implementation.
/// </summary>
/// <typeparam name="T">The health check implementation type.</typeparam>
/// <param name="builder">The <see cref="IHealthChecksBuilder"/>.</param>
/// <param name="name">The name of the health check.</param>
/// <param name="args">Additional arguments to provide to the constructor.</param>
/// <returns>The <see cref="IHealthChecksBuilder"/>.</returns>
/// <remarks>
/// This method will use <see cref="ActivatorUtilities.CreateInstance{T}(IServiceProvider, object[])"/> to create the health check
/// instance when needed. Additional arguments can be provided to the constructor via <paramref name="args"/>.
/// </remarks>
public static IHealthChecksBuilder AddTypeActivatedCheck<T>(this IHealthChecksBuilder builder, string name, params object[] args) where T : class, IHealthCheck
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
if (name == null)
{
throw new ArgumentNullException(nameof(name));
}
return AddTypeActivatedCheck<T>(builder, name, failureStatus: null, tags: null, args);
}
/// <summary>
/// Adds a new type activated health check with the specified name and implementation.
/// </summary>
/// <typeparam name="T">The health check implementation type.</typeparam>
/// <param name="builder">The <see cref="IHealthChecksBuilder"/>.</param>
/// <param name="name">The name of the health check.</param>
/// <param name="failureStatus">
/// The <see cref="HealthStatus"/> that should be reported when the health check reports a failure. If the provided value
/// is <c>null</c>, then <see cref="HealthStatus.Unhealthy"/> will be reported.
/// </param>
/// <param name="args">Additional arguments to provide to the constructor.</param>
/// <returns>The <see cref="IHealthChecksBuilder"/>.</returns>
/// <remarks>
/// This method will use <see cref="ActivatorUtilities.CreateInstance{T}(IServiceProvider, object[])"/> to create the health check
/// instance when needed. Additional arguments can be provided to the constructor via <paramref name="args"/>.
/// </remarks>
public static IHealthChecksBuilder AddTypeActivatedCheck<T>(
this IHealthChecksBuilder builder,
string name,
HealthStatus? failureStatus,
params object[] args) where T : class, IHealthCheck
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
if (name == null)
{
throw new ArgumentNullException(nameof(name));
}
return AddTypeActivatedCheck<T>(builder, name, failureStatus, tags: null, args);
}
/// <summary>
/// Adds a new type activated health check with the specified name and implementation.
/// </summary>
/// <typeparam name="T">The health check implementation type.</typeparam>
/// <param name="builder">The <see cref="IHealthChecksBuilder"/>.</param>
/// <param name="name">The name of the health check.</param>
/// <param name="failureStatus">
/// The <see cref="HealthStatus"/> that should be reported when the health check reports a failure. If the provided value
/// is <c>null</c>, then <see cref="HealthStatus.Unhealthy"/> will be reported.
/// </param>
/// <param name="tags">A list of tags that can be used to filter health checks.</param>
/// <param name="args">Additional arguments to provide to the constructor.</param>
/// <returns>The <see cref="IHealthChecksBuilder"/>.</returns>
/// <remarks>
/// This method will use <see cref="ActivatorUtilities.CreateInstance{T}(IServiceProvider, object[])"/> to create the health check
/// instance when needed. Additional arguments can be provided to the constructor via <paramref name="args"/>.
/// </remarks>
public static IHealthChecksBuilder AddTypeActivatedCheck<T>(
this IHealthChecksBuilder builder,
string name,
HealthStatus? failureStatus,
IEnumerable<string> tags,
params object[] args) where T : class, IHealthCheck
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
if (name == null)
{
throw new ArgumentNullException(nameof(name));
}
return builder.Add(new HealthCheckRegistration(name, s => ActivatorUtilities.CreateInstance<T>(s, args), failureStatus, tags));
}
/// <summary>
/// Adds a new type activated health check with the specified name and implementation.
/// </summary>
/// <typeparam name="T">The health check implementation type.</typeparam>
/// <param name="builder">The <see cref="IHealthChecksBuilder"/>.</param>
/// <param name="name">The name of the health check.</param>
/// <param name="failureStatus">
/// The <see cref="HealthStatus"/> that should be reported when the health check reports a failure. If the provided value
/// is <c>null</c>, then <see cref="HealthStatus.Unhealthy"/> will be reported.
/// </param>
/// <param name="tags">A list of tags that can be used to filter health checks.</param>
/// <param name="args">Additional arguments to provide to the constructor.</param>
/// <param name="timeout">A <see cref="TimeSpan"/> representing the timeout of the check.</param>
/// <returns>The <see cref="IHealthChecksBuilder"/>.</returns>
/// <remarks>
/// This method will use <see cref="ActivatorUtilities.CreateInstance{T}(IServiceProvider, object[])"/> to create the health check
/// instance when needed. Additional arguments can be provided to the constructor via <paramref name="args"/>.
/// </remarks>
public static IHealthChecksBuilder AddTypeActivatedCheck<T>(
this IHealthChecksBuilder builder,
string name,
HealthStatus? failureStatus,
IEnumerable<string> tags,
TimeSpan timeout,
params object[] args) where T : class, IHealthCheck
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
if (name == null)
{
throw new ArgumentNullException(nameof(name));
}
return builder.Add(new HealthCheckRegistration(name, s => ActivatorUtilities.CreateInstance<T>(s, args), failureStatus, tags, timeout));
}
}
}

View File

@ -0,0 +1,229 @@
// 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.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Diagnostics.HealthChecks;
namespace Microsoft.Extensions.DependencyInjection
{
/// <summary>
/// Provides extension methods for registering delegates with the <see cref="IHealthChecksBuilder"/>.
/// </summary>
public static class HealthChecksBuilderDelegateExtensions
{
/// <summary>
/// Adds a new health check with the specified name and implementation.
/// </summary>
/// <param name="builder">The <see cref="IHealthChecksBuilder"/>.</param>
/// <param name="name">The name of the health check.</param>
/// <param name="tags">A list of tags that can be used to filter health checks.</param>
/// <param name="check">A delegate that provides the health check implementation.</param>
/// <returns>The <see cref="IHealthChecksBuilder"/>.</returns>
// 2.0 BACKCOMPAT OVERLOAD -- DO NOT TOUCH
public static IHealthChecksBuilder AddCheck(
this IHealthChecksBuilder builder,
string name,
Func<HealthCheckResult> check,
IEnumerable<string> tags)
{
return AddCheck(builder, name, check, tags, default);
}
/// <summary>
/// Adds a new health check with the specified name and implementation.
/// </summary>
/// <param name="builder">The <see cref="IHealthChecksBuilder"/>.</param>
/// <param name="name">The name of the health check.</param>
/// <param name="tags">A list of tags that can be used to filter health checks.</param>
/// <param name="check">A delegate that provides the health check implementation.</param>
/// <param name="timeout">An optional <see cref="TimeSpan"/> representing the timeout of the check.</param>
/// <returns>The <see cref="IHealthChecksBuilder"/>.</returns>
public static IHealthChecksBuilder AddCheck(
this IHealthChecksBuilder builder,
string name,
Func<HealthCheckResult> check,
IEnumerable<string> tags = null,
TimeSpan? timeout = default)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
if (name == null)
{
throw new ArgumentNullException(nameof(name));
}
if (check == null)
{
throw new ArgumentNullException(nameof(check));
}
var instance = new DelegateHealthCheck((ct) => Task.FromResult(check()));
return builder.Add(new HealthCheckRegistration(name, instance, failureStatus: null, tags, timeout));
}
/// <summary>
/// Adds a new health check with the specified name and implementation.
/// </summary>
/// <param name="builder">The <see cref="IHealthChecksBuilder"/>.</param>
/// <param name="name">The name of the health check.</param>
/// <param name="tags">A list of tags that can be used to filter health checks.</param>
/// <param name="check">A delegate that provides the health check implementation.</param>
/// <returns>The <see cref="IHealthChecksBuilder"/>.</returns>
// 2.0 BACKCOMPAT OVERLOAD -- DO NOT TOUCH
public static IHealthChecksBuilder AddCheck(
this IHealthChecksBuilder builder,
string name,
Func<CancellationToken, HealthCheckResult> check,
IEnumerable<string> tags)
{
return AddCheck(builder, name, check, tags, default);
}
/// <summary>
/// Adds a new health check with the specified name and implementation.
/// </summary>
/// <param name="builder">The <see cref="IHealthChecksBuilder"/>.</param>
/// <param name="name">The name of the health check.</param>
/// <param name="tags">A list of tags that can be used to filter health checks.</param>
/// <param name="check">A delegate that provides the health check implementation.</param>
/// <param name="timeout">An optional <see cref="TimeSpan"/> representing the timeout of the check.</param>
/// <returns>The <see cref="IHealthChecksBuilder"/>.</returns>
public static IHealthChecksBuilder AddCheck(
this IHealthChecksBuilder builder,
string name,
Func<CancellationToken, HealthCheckResult> check,
IEnumerable<string> tags = null,
TimeSpan? timeout = default)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
if (name == null)
{
throw new ArgumentNullException(nameof(name));
}
if (check == null)
{
throw new ArgumentNullException(nameof(check));
}
var instance = new DelegateHealthCheck((ct) => Task.FromResult(check(ct)));
return builder.Add(new HealthCheckRegistration(name, instance, failureStatus: null, tags, timeout));
}
/// <summary>
/// Adds a new health check with the specified name and implementation.
/// </summary>
/// <param name="builder">The <see cref="IHealthChecksBuilder"/>.</param>
/// <param name="name">The name of the health check.</param>
/// <param name="tags">A list of tags that can be used to filter health checks.</param>
/// <param name="check">A delegate that provides the health check implementation.</param>
/// <returns>The <see cref="IHealthChecksBuilder"/>.</returns>
// 2.0 BACKCOMPAT OVERLOAD -- DO NOT TOUCH
public static IHealthChecksBuilder AddAsyncCheck(
this IHealthChecksBuilder builder,
string name,
Func<Task<HealthCheckResult>> check,
IEnumerable<string> tags)
{
return AddAsyncCheck(builder, name, check, tags, default);
}
/// <summary>
/// Adds a new health check with the specified name and implementation.
/// </summary>
/// <param name="builder">The <see cref="IHealthChecksBuilder"/>.</param>
/// <param name="name">The name of the health check.</param>
/// <param name="tags">A list of tags that can be used to filter health checks.</param>
/// <param name="check">A delegate that provides the health check implementation.</param>
/// <param name="timeout">An optional <see cref="TimeSpan"/> representing the timeout of the check.</param>
/// <returns>The <see cref="IHealthChecksBuilder"/>.</returns>
public static IHealthChecksBuilder AddAsyncCheck(
this IHealthChecksBuilder builder,
string name,
Func<Task<HealthCheckResult>> check,
IEnumerable<string> tags = null,
TimeSpan? timeout = default)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
if (name == null)
{
throw new ArgumentNullException(nameof(name));
}
if (check == null)
{
throw new ArgumentNullException(nameof(check));
}
var instance = new DelegateHealthCheck((ct) => check());
return builder.Add(new HealthCheckRegistration(name, instance, failureStatus: null, tags, timeout));
}
/// <summary>
/// Adds a new health check with the specified name and implementation.
/// </summary>
/// <param name="builder">The <see cref="IHealthChecksBuilder"/>.</param>
/// <param name="name">The name of the health check.</param>
/// <param name="tags">A list of tags that can be used to filter health checks.</param>
/// <param name="check">A delegate that provides the health check implementation.</param>
/// <returns>The <see cref="IHealthChecksBuilder"/>.</returns>
// 2.0 BACKCOMPAT OVERLOAD -- DO NOT TOUCH
public static IHealthChecksBuilder AddAsyncCheck(
this IHealthChecksBuilder builder,
string name,
Func<CancellationToken, Task<HealthCheckResult>> check,
IEnumerable<string> tags)
{
return AddAsyncCheck(builder, name, check, tags, default);
}
/// <summary>
/// Adds a new health check with the specified name and implementation.
/// </summary>
/// <param name="builder">The <see cref="IHealthChecksBuilder"/>.</param>
/// <param name="name">The name of the health check.</param>
/// <param name="tags">A list of tags that can be used to filter health checks.</param>
/// <param name="check">A delegate that provides the health check implementation.</param>
/// <param name="timeout">An optional <see cref="TimeSpan"/> representing the timeout of the check.</param>
/// <returns>The <see cref="IHealthChecksBuilder"/>.</returns>
public static IHealthChecksBuilder AddAsyncCheck(
this IHealthChecksBuilder builder,
string name,
Func<CancellationToken, Task<HealthCheckResult>> check,
IEnumerable<string> tags = null,
TimeSpan? timeout = default)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
if (name == null)
{
throw new ArgumentNullException(nameof(name));
}
if (check == null)
{
throw new ArgumentNullException(nameof(check));
}
var instance = new DelegateHealthCheck((ct) => check(ct));
return builder.Add(new HealthCheckRegistration(name, instance, failureStatus: null, tags, timeout));
}
}
}

View File

@ -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 Microsoft.Extensions.Diagnostics.HealthChecks;
namespace Microsoft.Extensions.DependencyInjection
{
/// <summary>
/// A builder used to register health checks.
/// </summary>
public interface IHealthChecksBuilder
{
/// <summary>
/// Adds a <see cref="HealthCheckRegistration"/> for a health check.
/// </summary>
/// <param name="registration">The <see cref="HealthCheckRegistration"/>.</param>
IHealthChecksBuilder Add(HealthCheckRegistration registration);
/// <summary>
/// Gets the <see cref="IServiceCollection"/> into which <see cref="IHealthCheck"/> instances should be registered.
/// </summary>
IServiceCollection Services { get; }
}
}

View File

@ -0,0 +1,48 @@
// 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;
using System.Collections.Generic;
namespace Microsoft.Extensions.Diagnostics.HealthChecks
{
internal class HealthCheckLogScope : IReadOnlyList<KeyValuePair<string, object>>
{
public string HealthCheckName { get; }
int IReadOnlyCollection<KeyValuePair<string, object>>.Count { get; } = 1;
KeyValuePair<string, object> IReadOnlyList<KeyValuePair<string, object>>.this[int index]
{
get
{
if (index == 0)
{
return new KeyValuePair<string, object>(nameof(HealthCheckName), HealthCheckName);
}
throw new ArgumentOutOfRangeException(nameof(index));
}
}
/// <summary>
/// Creates a new instance of <see cref="HealthCheckLogScope"/> with the provided name.
/// </summary>
/// <param name="healthCheckName">The name of the health check being executed.</param>
public HealthCheckLogScope(string healthCheckName)
{
HealthCheckName = healthCheckName;
}
IEnumerator<KeyValuePair<string, object>> IEnumerable<KeyValuePair<string, object>>.GetEnumerator()
{
yield return new KeyValuePair<string, object>(nameof(HealthCheckName), HealthCheckName);
}
IEnumerator IEnumerable.GetEnumerator()
{
return ((IEnumerable<KeyValuePair<string, object>>)this).GetEnumerator();
}
}
}

View File

@ -0,0 +1,262 @@
// 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.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Internal;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace Microsoft.Extensions.Diagnostics.HealthChecks
{
internal sealed class HealthCheckPublisherHostedService : IHostedService
{
private readonly HealthCheckService _healthCheckService;
private readonly IOptions<HealthCheckPublisherOptions> _options;
private readonly ILogger _logger;
private readonly IHealthCheckPublisher[] _publishers;
private CancellationTokenSource _stopping;
private Timer _timer;
public HealthCheckPublisherHostedService(
HealthCheckService healthCheckService,
IOptions<HealthCheckPublisherOptions> options,
ILogger<HealthCheckPublisherHostedService> logger,
IEnumerable<IHealthCheckPublisher> publishers)
{
if (healthCheckService == null)
{
throw new ArgumentNullException(nameof(healthCheckService));
}
if (options == null)
{
throw new ArgumentNullException(nameof(options));
}
if (logger == null)
{
throw new ArgumentNullException(nameof(logger));
}
if (publishers == null)
{
throw new ArgumentNullException(nameof(publishers));
}
_healthCheckService = healthCheckService;
_options = options;
_logger = logger;
_publishers = publishers.ToArray();
_stopping = new CancellationTokenSource();
}
internal bool IsStopping => _stopping.IsCancellationRequested;
internal bool IsTimerRunning => _timer != null;
public Task StartAsync(CancellationToken cancellationToken = default)
{
if (_publishers.Length == 0)
{
return Task.CompletedTask;
}
// IMPORTANT - make sure this is the last thing that happens in this method. The timer can
// fire before other code runs.
_timer = NonCapturingTimer.Create(Timer_Tick, null, dueTime: _options.Value.Delay, period: _options.Value.Period);
return Task.CompletedTask;
}
public Task StopAsync(CancellationToken cancellationToken = default)
{
try
{
_stopping.Cancel();
}
catch
{
// Ignore exceptions thrown as a result of a cancellation.
}
if (_publishers.Length == 0)
{
return Task.CompletedTask;
}
_timer?.Dispose();
_timer = null;
return Task.CompletedTask;
}
// Yes, async void. We need to be async. We need to be void. We handle the exceptions in RunAsync
private async void Timer_Tick(object state)
{
await RunAsync();
}
// Internal for testing
internal async Task RunAsync()
{
var duration = ValueStopwatch.StartNew();
Logger.HealthCheckPublisherProcessingBegin(_logger);
CancellationTokenSource cancellation = null;
try
{
var timeout = _options.Value.Timeout;
cancellation = CancellationTokenSource.CreateLinkedTokenSource(_stopping.Token);
cancellation.CancelAfter(timeout);
await RunAsyncCore(cancellation.Token);
Logger.HealthCheckPublisherProcessingEnd(_logger, duration.GetElapsedTime());
}
catch (OperationCanceledException) when (IsStopping)
{
// This is a cancellation - if the app is shutting down we want to ignore it. Otherwise, it's
// a timeout and we want to log it.
}
catch (Exception ex)
{
// This is an error, publishing failed.
Logger.HealthCheckPublisherProcessingEnd(_logger, duration.GetElapsedTime(), ex);
}
finally
{
cancellation.Dispose();
}
}
private async Task RunAsyncCore(CancellationToken cancellationToken)
{
// Forcibly yield - we want to unblock the timer thread.
await Task.Yield();
// The health checks service does it's own logging, and doesn't throw exceptions.
var report = await _healthCheckService.CheckHealthAsync(_options.Value.Predicate, cancellationToken);
var publishers = _publishers;
var tasks = new Task[publishers.Length];
for (var i = 0; i < publishers.Length; i++)
{
tasks[i] = RunPublisherAsync(publishers[i], report, cancellationToken);
}
await Task.WhenAll(tasks);
}
private async Task RunPublisherAsync(IHealthCheckPublisher publisher, HealthReport report, CancellationToken cancellationToken)
{
var duration = ValueStopwatch.StartNew();
try
{
Logger.HealthCheckPublisherBegin(_logger, publisher);
await publisher.PublishAsync(report, cancellationToken);
Logger.HealthCheckPublisherEnd(_logger, publisher, duration.GetElapsedTime());
}
catch (OperationCanceledException) when (IsStopping)
{
// This is a cancellation - if the app is shutting down we want to ignore it. Otherwise, it's
// a timeout and we want to log it.
}
catch (OperationCanceledException ocex)
{
Logger.HealthCheckPublisherTimeout(_logger, publisher, duration.GetElapsedTime());
throw ocex;
}
catch (Exception ex)
{
Logger.HealthCheckPublisherError(_logger, publisher, duration.GetElapsedTime(), ex);
throw ex;
}
}
internal static class EventIds
{
public static readonly EventId HealthCheckPublisherProcessingBegin = new EventId(100, "HealthCheckPublisherProcessingBegin");
public static readonly EventId HealthCheckPublisherProcessingEnd = new EventId(101, "HealthCheckPublisherProcessingEnd");
public static readonly EventId HealthCheckPublisherProcessingError = new EventId(101, "HealthCheckPublisherProcessingError");
public static readonly EventId HealthCheckPublisherBegin = new EventId(102, "HealthCheckPublisherBegin");
public static readonly EventId HealthCheckPublisherEnd = new EventId(103, "HealthCheckPublisherEnd");
public static readonly EventId HealthCheckPublisherError = new EventId(104, "HealthCheckPublisherError");
public static readonly EventId HealthCheckPublisherTimeout = new EventId(104, "HealthCheckPublisherTimeout");
}
private static class Logger
{
private static readonly Action<ILogger, Exception> _healthCheckPublisherProcessingBegin = LoggerMessage.Define(
LogLevel.Debug,
EventIds.HealthCheckPublisherProcessingBegin,
"Running health check publishers");
private static readonly Action<ILogger, double, Exception> _healthCheckPublisherProcessingEnd = LoggerMessage.Define<double>(
LogLevel.Debug,
EventIds.HealthCheckPublisherProcessingEnd,
"Health check publisher processing completed after {ElapsedMilliseconds}ms");
private static readonly Action<ILogger, IHealthCheckPublisher, Exception> _healthCheckPublisherBegin = LoggerMessage.Define<IHealthCheckPublisher>(
LogLevel.Debug,
EventIds.HealthCheckPublisherBegin,
"Running health check publisher '{HealthCheckPublisher}'");
private static readonly Action<ILogger, IHealthCheckPublisher, double, Exception> _healthCheckPublisherEnd = LoggerMessage.Define<IHealthCheckPublisher, double>(
LogLevel.Debug,
EventIds.HealthCheckPublisherEnd,
"Health check '{HealthCheckPublisher}' completed after {ElapsedMilliseconds}ms");
private static readonly Action<ILogger, IHealthCheckPublisher, double, Exception> _healthCheckPublisherError = LoggerMessage.Define<IHealthCheckPublisher, double>(
LogLevel.Error,
EventIds.HealthCheckPublisherError,
"Health check {HealthCheckPublisher} threw an unhandled exception after {ElapsedMilliseconds}ms");
private static readonly Action<ILogger, IHealthCheckPublisher, double, Exception> _healthCheckPublisherTimeout = LoggerMessage.Define<IHealthCheckPublisher, double>(
LogLevel.Error,
EventIds.HealthCheckPublisherTimeout,
"Health check {HealthCheckPublisher} was canceled after {ElapsedMilliseconds}ms");
public static void HealthCheckPublisherProcessingBegin(ILogger logger)
{
_healthCheckPublisherProcessingBegin(logger, null);
}
public static void HealthCheckPublisherProcessingEnd(ILogger logger, TimeSpan duration, Exception exception = null)
{
_healthCheckPublisherProcessingEnd(logger, duration.TotalMilliseconds, exception);
}
public static void HealthCheckPublisherBegin(ILogger logger, IHealthCheckPublisher publisher)
{
_healthCheckPublisherBegin(logger, publisher, null);
}
public static void HealthCheckPublisherEnd(ILogger logger, IHealthCheckPublisher publisher, TimeSpan duration)
{
_healthCheckPublisherEnd(logger, publisher, duration.TotalMilliseconds, null);
}
public static void HealthCheckPublisherError(ILogger logger, IHealthCheckPublisher publisher, TimeSpan duration, Exception exception)
{
_healthCheckPublisherError(logger, publisher, duration.TotalMilliseconds, exception);
}
public static void HealthCheckPublisherTimeout(ILogger logger, IHealthCheckPublisher publisher, TimeSpan duration)
{
_healthCheckPublisherTimeout(logger, publisher, duration.TotalMilliseconds, null);
}
}
}
}

View File

@ -0,0 +1,84 @@
// 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;
namespace Microsoft.Extensions.Diagnostics.HealthChecks
{
/// <summary>
/// Options for the default service that executes <see cref="IHealthCheckPublisher"/> instances.
/// </summary>
public sealed class HealthCheckPublisherOptions
{
private TimeSpan _delay;
private TimeSpan _period;
public HealthCheckPublisherOptions()
{
_delay = TimeSpan.FromSeconds(5);
_period = TimeSpan.FromSeconds(30);
}
/// <summary>
/// Gets or sets the initial delay applied after the application starts before executing
/// <see cref="IHealthCheckPublisher"/> instances. The delay is applied once at startup, and does
/// not apply to subsequent iterations. The default value is 5 seconds.
/// </summary>
public TimeSpan Delay
{
get => _delay;
set
{
if (value == System.Threading.Timeout.InfiniteTimeSpan)
{
throw new ArgumentException($"The {nameof(Delay)} must not be infinite.", nameof(value));
}
_delay = value;
}
}
/// <summary>
/// Gets or sets the period of <see cref="IHealthCheckPublisher"/> execution. The default value is
/// 30 seconds.
/// </summary>
/// <remarks>
/// The <see cref="Period"/> cannot be set to a value lower than 1 second.
/// </remarks>
public TimeSpan Period
{
get => _period;
set
{
if (value < TimeSpan.FromSeconds(1))
{
throw new ArgumentException($"The {nameof(Period)} must be greater than or equal to one second.", nameof(value));
}
if (value == System.Threading.Timeout.InfiniteTimeSpan)
{
throw new ArgumentException($"The {nameof(Period)} must not be infinite.", nameof(value));
}
_period = value;
}
}
/// <summary>
/// Gets or sets a predicate that is used to filter the set of health checks executed.
/// </summary>
/// <remarks>
/// If <see cref="Predicate"/> is <c>null</c>, the health check publisher service will run all
/// registered health checks - this is the default behavior. To run a subset of health checks,
/// provide a function that filters the set of checks. The predicate will be evaluated each period.
/// </remarks>
public Func<HealthCheckRegistration, bool> Predicate { get; set; }
/// <summary>
/// Gets or sets the timeout for executing the health checks an all <see cref="IHealthCheckPublisher"/>
/// instances. Use <see cref="System.Threading.Timeout.InfiniteTimeSpan"/> to execute with no timeout.
/// The default value is 30 seconds.
/// </summary>
public TimeSpan Timeout { get; set; } = TimeSpan.FromSeconds(30);
}
}

Some files were not shown because too many files have changed in this diff Show More