Merge pull request #19033 from dotnet/johluo/migrating-extensions
Migrating sources from dotnet/extensions
This commit is contained in:
commit
5a0526dfd9
|
|
@ -109,6 +109,7 @@
|
|||
$(RepoRoot)src\Components\Web.JS\Microsoft.AspNetCore.Components.Web.JS.npmproj;
|
||||
$(RepoRoot)src\SignalR\**\*.npmproj;
|
||||
$(RepoRoot)src\Middleware\**\*.npmproj;
|
||||
$(RepoRoot)src\JSInterop\**\*.npmproj;
|
||||
"
|
||||
RestoreInParallel="false"
|
||||
Exclude="@(ProjectToExclude)" />
|
||||
|
|
@ -152,6 +153,14 @@
|
|||
$(RepoRoot)src\SignalR\**\*.csproj;
|
||||
$(RepoRoot)src\Components\**\*.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\testassets\*\*.csproj;
|
||||
"
|
||||
|
|
@ -181,6 +190,14 @@
|
|||
$(RepoRoot)src\Azure\**\src\*.csproj;
|
||||
$(RepoRoot)src\SignalR\**\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="
|
||||
@(ProjectToBuild);
|
||||
|
|
|
|||
|
|
@ -30,10 +30,8 @@ and are generated based on the last package release.
|
|||
<LatestPackageReference Include="Microsoft.CSharp" Version="$(MicrosoftCSharpPackageVersion)" />
|
||||
<LatestPackageReference Include="Microsoft.DotNet.GenAPI" Version="$(MicrosoftDotNetGenApiPackageVersion)" />
|
||||
<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.Memory" Version="$(MicrosoftExtensionsCachingMemoryPackageVersion)" />
|
||||
<LatestPackageReference Include="Microsoft.Extensions.CommandLineUtils.Sources" Version="$(MicrosoftExtensionsCommandLineUtilsSourcesPackageVersion)" />
|
||||
<LatestPackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="$(MicrosoftExtensionsConfigurationAbstractionsPackageVersion)" />
|
||||
<LatestPackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="$(MicrosoftExtensionsConfigurationBinderPackageVersion)" />
|
||||
<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.Ini" Version="$(MicrosoftExtensionsConfigurationIniPackageVersion)" />
|
||||
<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.Xml" Version="$(MicrosoftExtensionsConfigurationXmlPackageVersion)" />
|
||||
<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.DependencyModel" Version="$(MicrosoftExtensionsDependencyModelPackageVersion)" />
|
||||
<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.Composite" Version="$(MicrosoftExtensionsFileProvidersCompositePackageVersion)" />
|
||||
<LatestPackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="$(MicrosoftExtensionsFileProvidersEmbeddedPackageVersion)" />
|
||||
<LatestPackageReference Include="Microsoft.Extensions.FileProviders.Physical" Version="$(MicrosoftExtensionsFileProvidersPhysicalPackageVersion)" />
|
||||
<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" Version="$(MicrosoftExtensionsHostingPackageVersion)" />
|
||||
<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.Configuration" Version="$(MicrosoftExtensionsLoggingConfigurationPackageVersion)" />
|
||||
<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.TraceSource" Version="$(MicrosoftExtensionsLoggingTraceSourcePackageVersion)" />
|
||||
<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.DataAnnotations" Version="$(MicrosoftExtensionsOptionsDataAnnotationsPackageVersion)" />
|
||||
<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.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.SystemEvents" Version="$(MicrosoftWin32SystemEventsPackageVersion)" />
|
||||
<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.Reflection.Metadata" Version="$(SystemReflectionMetadataPackageVersion)" />
|
||||
<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.Pkcs" Version="$(SystemSecurityCryptographyPkcsPackageVersion)" />
|
||||
<LatestPackageReference Include="System.Security.Cryptography.Xml" Version="$(SystemSecurityCryptographyXmlPackageVersion)" />
|
||||
<LatestPackageReference Include="System.Security.Permissions" Version="$(SystemSecurityPermissionsPackageVersion)" />
|
||||
<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.Threading.Channels" Version="$(SystemThreadingChannelsPackageVersion)" />
|
||||
<LatestPackageReference Include="System.Windows.Extensions" Version="$(SystemWindowsExtensionsPackageVersion)" />
|
||||
<LatestPackageReference Include="System.ValueTuple" Version="$(SystemValueTuplePackageVersion)" />
|
||||
|
||||
<!-- Runtime packages required for crossgen -->
|
||||
<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.3.1.x64" 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.Testing" Version="$(MicrosoftAspNetCoreTestingPackageVersion)" />
|
||||
<LatestPackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="$(MicrosoftBclAsyncInterfacesPackageVersion)" />
|
||||
<LatestPackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="$(MicrosoftEntityFrameworkCoreInMemoryPackageVersion)" />
|
||||
<LatestPackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="$(MicrosoftEntityFrameworkCoreRelationalPackageVersion)" />
|
||||
|
|
|
|||
|
|
@ -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.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="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.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" />
|
||||
|
|
@ -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.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.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>
|
||||
</Project>
|
||||
|
|
|
|||
|
|
@ -30,24 +30,18 @@
|
|||
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="$(MicrosoftExtensionsConfigurationFileExtensionsPackageVersion)" />
|
||||
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.Configuration.Ini" Version="$(MicrosoftExtensionsConfigurationIniPackageVersion)" />
|
||||
<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.Xml" Version="$(MicrosoftExtensionsConfigurationXmlPackageVersion)" />
|
||||
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.Configuration" Version="$(MicrosoftExtensionsConfigurationPackageVersion)" />
|
||||
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="$(MicrosoftExtensionsDependencyInjectionAbstractionsPackageVersion)" />
|
||||
<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.Composite" Version="$(MicrosoftExtensionsFileProvidersCompositePackageVersion)" />
|
||||
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="$(MicrosoftExtensionsFileProvidersEmbeddedPackageVersion)" />
|
||||
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.FileProviders.Physical" Version="$(MicrosoftExtensionsFileProvidersPhysicalPackageVersion)" />
|
||||
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.FileSystemGlobbing" Version="$(MicrosoftExtensionsFileSystemGlobbingPackageVersion)" />
|
||||
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="$(MicrosoftExtensionsHostingAbstractionsPackageVersion)" />
|
||||
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.Hosting" Version="$(MicrosoftExtensionsHostingPackageVersion)" />
|
||||
<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.Configuration" Version="$(MicrosoftExtensionsLoggingConfigurationPackageVersion)" />
|
||||
<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.TraceSource" Version="$(MicrosoftExtensionsLoggingTraceSourcePackageVersion)" />
|
||||
<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.DataAnnotations" Version="$(MicrosoftExtensionsOptionsDataAnnotationsPackageVersion)" />
|
||||
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.Options" Version="$(MicrosoftExtensionsOptionsPackageVersion)" />
|
||||
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.Primitives" Version="$(MicrosoftExtensionsPrimitivesPackageVersion)" />
|
||||
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.WebEncoders" Version="$(MicrosoftExtensionsWebEncodersPackageVersion)" />
|
||||
<ExternalAspNetCoreAppReference Include="Microsoft.JSInterop" Version="$(MicrosoftJSInteropPackageVersion)" />
|
||||
|
||||
<!-- Dependencies from dotnet/corefx -->
|
||||
<ExternalAspNetCoreAppReference Include="System.IO.Pipelines" Version="$(SystemIOPipelinesPackageVersion)" />
|
||||
|
|
|
|||
|
|
@ -26,6 +26,15 @@
|
|||
<AspNetCoreAppReferenceAndPackage Include="Microsoft.AspNetCore.Components" />
|
||||
<AspNetCoreAppReferenceAndPackage Include="Microsoft.AspNetCore.Components.Forms" />
|
||||
<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 -->
|
||||
<AspNetCoreAppReference Include="Microsoft.AspNetCore" />
|
||||
|
|
|
|||
|
|
@ -61,14 +61,6 @@
|
|||
<Uri>https://github.com/dotnet/extensions</Uri>
|
||||
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
|
||||
</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">
|
||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
||||
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
|
||||
|
|
@ -85,10 +77,6 @@
|
|||
<Uri>https://github.com/dotnet/extensions</Uri>
|
||||
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
|
||||
</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">
|
||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
||||
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
|
||||
|
|
@ -121,10 +109,6 @@
|
|||
<Uri>https://github.com/dotnet/extensions</Uri>
|
||||
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
|
||||
</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">
|
||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
||||
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
|
||||
|
|
@ -149,14 +133,6 @@
|
|||
<Uri>https://github.com/dotnet/extensions</Uri>
|
||||
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
|
||||
</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">
|
||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
||||
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
|
||||
|
|
@ -165,10 +141,6 @@
|
|||
<Uri>https://github.com/dotnet/extensions</Uri>
|
||||
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
|
||||
</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">
|
||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
||||
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
|
||||
|
|
@ -177,10 +149,6 @@
|
|||
<Uri>https://github.com/dotnet/extensions</Uri>
|
||||
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
|
||||
</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">
|
||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
||||
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
|
||||
|
|
@ -189,22 +157,10 @@
|
|||
<Uri>https://github.com/dotnet/extensions</Uri>
|
||||
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
|
||||
</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">
|
||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
||||
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
|
||||
</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">
|
||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
||||
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
|
||||
|
|
@ -245,10 +201,6 @@
|
|||
<Uri>https://github.com/dotnet/extensions</Uri>
|
||||
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
|
||||
</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">
|
||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
||||
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
|
||||
|
|
@ -261,34 +213,14 @@
|
|||
<Uri>https://github.com/dotnet/extensions</Uri>
|
||||
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
|
||||
</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">
|
||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
||||
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
|
||||
</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">
|
||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
||||
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
|
||||
</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">
|
||||
<Uri>https://github.com/dotnet/runtime</Uri>
|
||||
<Sha>0f3f8e1930c28b67f29990126bc2e8527e959a2e</Sha>
|
||||
|
|
@ -416,10 +348,6 @@
|
|||
<Uri>https://github.com/dotnet/arcade</Uri>
|
||||
<Sha>f50767f96246063f33a6565d318f3adf9058bace</Sha>
|
||||
</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">
|
||||
<Uri>https://github.com/dotnet/roslyn</Uri>
|
||||
<Sha>92790e24cc2b9f9e336ed0365d610e106c01df88</Sha>
|
||||
|
|
|
|||
|
|
@ -98,14 +98,10 @@
|
|||
<!-- Packages from dotnet/extensions -->
|
||||
<InternalAspNetCoreAnalyzersPackageVersion>5.0.0-preview.1.20114.1</InternalAspNetCoreAnalyzersPackageVersion>
|
||||
<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>
|
||||
<MicrosoftExtensionsCachingMemoryPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsCachingMemoryPackageVersion>
|
||||
<MicrosoftExtensionsCachingSqlServerPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsCachingSqlServerPackageVersion>
|
||||
<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>
|
||||
<MicrosoftExtensionsConfigurationAzureKeyVaultPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsConfigurationAzureKeyVaultPackageVersion>
|
||||
<MicrosoftExtensionsConfigurationBinderPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsConfigurationBinderPackageVersion>
|
||||
|
|
@ -114,27 +110,19 @@
|
|||
<MicrosoftExtensionsConfigurationFileExtensionsPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsConfigurationFileExtensionsPackageVersion>
|
||||
<MicrosoftExtensionsConfigurationIniPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsConfigurationIniPackageVersion>
|
||||
<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>
|
||||
<MicrosoftExtensionsConfigurationUserSecretsPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsConfigurationUserSecretsPackageVersion>
|
||||
<MicrosoftExtensionsConfigurationXmlPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsConfigurationXmlPackageVersion>
|
||||
<MicrosoftExtensionsDependencyInjectionAbstractionsPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsDependencyInjectionAbstractionsPackageVersion>
|
||||
<MicrosoftExtensionsDependencyInjectionPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsDependencyInjectionPackageVersion>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<MicrosoftExtensionsLoggingAzureAppServicesPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsLoggingAzureAppServicesPackageVersion>
|
||||
<MicrosoftExtensionsLoggingConfigurationPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsLoggingConfigurationPackageVersion>
|
||||
|
|
@ -145,17 +133,11 @@
|
|||
<MicrosoftExtensionsLoggingPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsLoggingPackageVersion>
|
||||
<MicrosoftExtensionsLoggingTestingPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsLoggingTestingPackageVersion>
|
||||
<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>
|
||||
<MicrosoftExtensionsOptionsDataAnnotationsPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsOptionsDataAnnotationsPackageVersion>
|
||||
<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>
|
||||
<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>
|
||||
<MicrosoftJSInteropPackageVersion>5.0.0-preview.1.20114.1</MicrosoftJSInteropPackageVersion>
|
||||
<!-- Packages from dotnet/efcore -->
|
||||
<dotnetefPackageVersion>5.0.0-preview.2.20121.2</dotnetefPackageVersion>
|
||||
<MicrosoftEntityFrameworkCoreInMemoryPackageVersion>5.0.0-preview.2.20121.2</MicrosoftEntityFrameworkCoreInMemoryPackageVersion>
|
||||
|
|
@ -198,7 +180,9 @@
|
|||
<SystemCommandlineExperimentalPackageVersion>0.3.0-alpha.19317.1</SystemCommandlineExperimentalPackageVersion>
|
||||
<SystemComponentModelPackageVersion>4.3.0</SystemComponentModelPackageVersion>
|
||||
<SystemNetHttpPackageVersion>4.3.2</SystemNetHttpPackageVersion>
|
||||
<SystemRuntimeInteropServicesRuntimeInformationPackageVersion>4.3.0</SystemRuntimeInteropServicesRuntimeInformationPackageVersion>
|
||||
<SystemThreadingTasksExtensionsPackageVersion>4.5.3</SystemThreadingTasksExtensionsPackageVersion>
|
||||
<SystemValueTuplePackageVersion>4.5.0</SystemValueTuplePackageVersion>
|
||||
<!-- Packages developed by @aspnet, but manually updated as necessary. -->
|
||||
<LibuvPackageVersion>1.10.0</LibuvPackageVersion>
|
||||
<MicrosoftAspNetWebApiClientPackageVersion>5.2.6</MicrosoftAspNetWebApiClientPackageVersion>
|
||||
|
|
|
|||
|
|
@ -119,7 +119,7 @@
|
|||
<Error Condition="@(_InvalidReferenceToSharedFxOnlyAssembly->Count()) != 0"
|
||||
Text="Cannot reference "%(_InvalidReferenceToSharedFxOnlyAssembly.Identity)" directly because it is part of the shared framework and this project is not. Use <FrameworkReference Include="Microsoft.AspNetCore.App" /> instead." />
|
||||
|
||||
<Error Condition="@(_InvalidReferenceToNonSharedFxAssembly->Count()) != 0"
|
||||
<Error Condition="@(_InvalidReferenceToNonSharedFxAssembly->Count()) != 0 AND '$(TargetFramework)' == '$(DefaultNetCoreTargetFramework)'"
|
||||
Text="Cannot reference "%(_InvalidReferenceToNonSharedFxAssembly.Identity)". 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>
|
||||
|
||||
|
|
|
|||
|
|
@ -31,7 +31,6 @@
|
|||
<Reference Include="Microsoft.Build.Framework" />
|
||||
<Reference Include="Microsoft.Build.Utilities.Core" />
|
||||
<Reference Include="Microsoft.CodeAnalysis.Razor" />
|
||||
<Reference Include="Microsoft.Extensions.CommandLineUtils.Sources" />
|
||||
<!-- Avoid CS1705 errors due to mix of assemblies brought in transitively. -->
|
||||
<Reference Include="Microsoft.AspNetCore.Components" />
|
||||
</ItemGroup>
|
||||
|
|
@ -40,6 +39,7 @@
|
|||
<ProjectReference Include="..\..\testassets\StandaloneApp\StandaloneApp.csproj" />
|
||||
<Compile Include="$(SharedSourceRoot)test\SkipOnHelixAttribute.cs" />
|
||||
<Compile Include="$(ComponentsSharedSourceRoot)test\**\*.cs" LinkBase="Helpers" />
|
||||
<Compile Include="$(SharedSourceRoot)CommandLineUtils\**\*.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
|||
|
|
@ -18,8 +18,9 @@
|
|||
<Reference Include="Microsoft.AspNetCore.Components.Server" />
|
||||
<Reference Include="Microsoft.AspNetCore.ResponseCompression" />
|
||||
<Reference Include="Microsoft.AspNetCore" />
|
||||
<Reference Include="Microsoft.Extensions.CommandLineUtils.Sources" />
|
||||
<Reference Include="Microsoft.Extensions.Hosting" />
|
||||
|
||||
<Compile Include="$(SharedSourceRoot)CommandLineUtils\**\*.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Pack settings -->
|
||||
|
|
|
|||
|
|
@ -10,7 +10,8 @@
|
|||
<ItemGroup>
|
||||
<Reference Include="BenchmarkDotNet" />
|
||||
<Reference Include="Microsoft.AspNetCore.Components" />
|
||||
<Reference Include="Microsoft.AspNetCore.BenchmarkRunner.Sources" />
|
||||
|
||||
<Compile Include="$(SharedSourceRoot)BenchmarkRunner\*.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@
|
|||
<Compile Include="../src/Properties/AssemblyInfo.cs" />
|
||||
<Reference Include="Microsoft.Extensions.Logging.Abstractions" />
|
||||
<Reference Include="Microsoft.Extensions.DependencyInjection.Abstractions" />
|
||||
<Reference Include="Microsoft.Extensions.HashCodeCombiner.Sources" />
|
||||
<Reference Include="System.Buffers" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition="'$(TargetFramework)' == '$(DefaultNetCoreTargetFramework)'">
|
||||
|
|
@ -19,6 +18,5 @@
|
|||
<Compile Include="../src/Properties/AssemblyInfo.cs" />
|
||||
<Reference Include="Microsoft.Extensions.Logging.Abstractions" />
|
||||
<Reference Include="Microsoft.Extensions.DependencyInjection.Abstractions" />
|
||||
<Reference Include="Microsoft.Extensions.HashCodeCombiner.Sources" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
|
|
|||
|
|
@ -15,7 +15,8 @@
|
|||
<ItemGroup>
|
||||
<Reference Include="Microsoft.Extensions.Logging.Abstractions" />
|
||||
<Reference Include="Microsoft.Extensions.DependencyInjection.Abstractions" />
|
||||
<Reference Include="Microsoft.Extensions.HashCodeCombiner.Sources" />
|
||||
|
||||
<Compile Include="$(SharedSourceRoot)HashCodeCombiner\*.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)'=='netstandard2.0'">
|
||||
|
|
@ -33,12 +34,16 @@
|
|||
<Target Name="_GetNuspecDependencyPackageVersions">
|
||||
<MSBuild Targets="_GetPackageVersionInfo"
|
||||
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" />
|
||||
</MSBuild>
|
||||
<ItemGroup>
|
||||
<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.JSInterop')->'jsInteropPackageVersion=%(PackageVersion)')" />
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
|
||||
|
|
@ -50,7 +55,6 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<NuspecProperty Include="jsInteropPackageVersion=$(MicrosoftJSInteropPackageVersion)" />
|
||||
<NuspecProperty Condition="'$(DotNetBuildFromSource)' != 'true'" Include="systemComponentModelAnnotationsPackageVersion=$(SystemComponentModelAnnotationsPackageVersion)" />
|
||||
<NuspecProperty Include="AssemblyName=$(AssemblyName)" />
|
||||
<NuspecProperty Include="OutputPath=$(OutputPath)" />
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
@ -1,5 +1,4 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>$(DefaultNetCoreTargetFramework)</TargetFramework>
|
||||
<Description>Runtime server features for ASP.NET Core Components.</Description>
|
||||
|
|
@ -10,6 +9,7 @@
|
|||
<NoWarn>CS0436;$(NoWarn)</NoWarn>
|
||||
<DefineConstants>$(DefineConstants);ENABLE_UNSAFE_MSGPACK;SPAN_BUILTIN;MESSAGEPACK_INTERNAL;COMPONENTS_SERVER</DefineConstants>
|
||||
<IsPackable>false</IsPackable>
|
||||
<EmbeddedFilesManifestFileName>Microsoft.Extensions.FileProviders.Embedded.Manifest.xml</EmbeddedFilesManifestFileName>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
@ -22,7 +22,8 @@
|
|||
<Reference Include="Microsoft.Extensions.FileProviders.Composite" />
|
||||
<Reference Include="Microsoft.Extensions.FileProviders.Embedded" />
|
||||
<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 -->
|
||||
<!-- 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>
|
||||
</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>
|
||||
<Compile Include="$(ComponentsSharedSourceRoot)src\CacheHeaderSettings.cs" Link="Shared\CacheHeaderSettings.cs" />
|
||||
<Compile Include="$(ComponentsSharedSourceRoot)src\ArrayBuilder.cs" LinkBase="Circuits" />
|
||||
|
|
@ -84,5 +94,4 @@
|
|||
<EmbeddedResource Include="$(BlazorServerJSFile)" LogicalName="_framework/%(Filename)%(Extension)" />
|
||||
<EmbeddedResource Include="$(BlazorServerJSFile).map" LogicalName="_framework/%(Filename)%(Extension)" Condition="Exists('$(BlazorServerJSFile).map')" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
|
@ -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.
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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")}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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.");
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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}'");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
<Project>
|
||||
<Import Project="..\build\netstandard2.0\Microsoft.Extensions.FileProviders.Embedded.props" />
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
<Project>
|
||||
<Import Project="..\build\netstandard2.0\Microsoft.Extensions.FileProviders.Embedded.targets" />
|
||||
</Project>
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
Hello
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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>"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
|
@ -0,0 +1 @@
|
|||
Resources-Hello
|
||||
|
|
@ -0,0 +1 @@
|
|||
Hello3
|
||||
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
Hello2
|
||||
Binary file not shown.
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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("\\", "/");
|
||||
}
|
||||
}
|
||||
|
|
@ -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";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -118,6 +118,12 @@ This package is an internal implementation of the .NET Core SDK and is not meant
|
|||
<Output TaskParameter="JoinResult" ItemName="_ReferencedExtensionsRefAssemblies" />
|
||||
</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">
|
||||
<Output TaskParameter="JoinResult" ItemName="_SelectedExtensionsRefs" />
|
||||
</JoinItems>
|
||||
|
|
@ -127,12 +133,15 @@ This package is an internal implementation of the .NET Core SDK and is not meant
|
|||
</JoinItems>
|
||||
|
||||
<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 -->
|
||||
<AspNetCoreReferenceAssemblyPath
|
||||
Include="@(ReferencePathWithRefAssemblies)"
|
||||
Condition="'%(ReferencePathWithRefAssemblies.IsReferenceAssembly)' != 'false'"
|
||||
Exclude="
|
||||
@(_ReferencedExtensionsRefAssemblies);
|
||||
@(_ReferencedExtensionsRefAssembliesToExclude);
|
||||
@(ReferencePathWithRefAssemblies->WithMetadataValue('NuGetPackageId', 'Microsoft.NETCore.App.Ref'));
|
||||
@(ReferencePathWithRefAssemblies->WithMetadataValue('NuGetPackageId', 'System.Security.Cryptography.Pkcs'));
|
||||
@(ReferencePathWithRefAssemblies->WithMetadataValue('NuGetPackageId', 'System.Drawing.Common'));
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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; } }
|
||||
}
|
||||
}
|
||||
|
|
@ -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; } }
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
Loading…
Reference in New Issue