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\Components\Web.JS\Microsoft.AspNetCore.Components.Web.JS.npmproj;
|
||||||
$(RepoRoot)src\SignalR\**\*.npmproj;
|
$(RepoRoot)src\SignalR\**\*.npmproj;
|
||||||
$(RepoRoot)src\Middleware\**\*.npmproj;
|
$(RepoRoot)src\Middleware\**\*.npmproj;
|
||||||
|
$(RepoRoot)src\JSInterop\**\*.npmproj;
|
||||||
"
|
"
|
||||||
RestoreInParallel="false"
|
RestoreInParallel="false"
|
||||||
Exclude="@(ProjectToExclude)" />
|
Exclude="@(ProjectToExclude)" />
|
||||||
|
|
@ -152,6 +153,14 @@
|
||||||
$(RepoRoot)src\SignalR\**\*.csproj;
|
$(RepoRoot)src\SignalR\**\*.csproj;
|
||||||
$(RepoRoot)src\Components\**\*.csproj;
|
$(RepoRoot)src\Components\**\*.csproj;
|
||||||
$(RepoRoot)src\Analyzers\**\*.csproj;
|
$(RepoRoot)src\Analyzers\**\*.csproj;
|
||||||
|
$(RepoRoot)src\FileProviders\**\*.csproj;
|
||||||
|
$(RepoRoot)src\Configuration.KeyPerFile\**\*.csproj;
|
||||||
|
$(RepoRoot)src\Localization\**\*.csproj;
|
||||||
|
$(RepoRoot)src\ObjectPool\**\*.csproj;
|
||||||
|
$(RepoRoot)src\JSInterop\**\*.csproj;
|
||||||
|
$(RepoRoot)src\WebEncoders\**\*.csproj;
|
||||||
|
$(RepoRoot)src\HealthChecks\**\*.csproj;
|
||||||
|
$(RepoRoot)src\Testing\**\*.csproj;
|
||||||
$(RepoRoot)src\ProjectTemplates\*\*.csproj;
|
$(RepoRoot)src\ProjectTemplates\*\*.csproj;
|
||||||
$(RepoRoot)src\ProjectTemplates\testassets\*\*.csproj;
|
$(RepoRoot)src\ProjectTemplates\testassets\*\*.csproj;
|
||||||
"
|
"
|
||||||
|
|
@ -181,6 +190,14 @@
|
||||||
$(RepoRoot)src\Azure\**\src\*.csproj;
|
$(RepoRoot)src\Azure\**\src\*.csproj;
|
||||||
$(RepoRoot)src\SignalR\**\src\*.csproj;
|
$(RepoRoot)src\SignalR\**\src\*.csproj;
|
||||||
$(RepoRoot)src\Components\**\src\*.csproj;
|
$(RepoRoot)src\Components\**\src\*.csproj;
|
||||||
|
$(RepoRoot)src\FileProviders\**\src\*.csproj;
|
||||||
|
$(RepoRoot)src\Configuration.KeyPerFile\**\src\*.csproj;
|
||||||
|
$(RepoRoot)src\Localization\**\src\*.csproj;
|
||||||
|
$(RepoRoot)src\ObjectPool\**\src\*.csproj;
|
||||||
|
$(RepoRoot)src\JSInterop\**\src\*.csproj;
|
||||||
|
$(RepoRoot)src\WebEncoders\**\src\*.csproj;
|
||||||
|
$(RepoRoot)src\HealthChecks\**\src\*.csproj;
|
||||||
|
$(RepoRoot)src\Testing\**\src\*.csproj;
|
||||||
"
|
"
|
||||||
Exclude="
|
Exclude="
|
||||||
@(ProjectToBuild);
|
@(ProjectToBuild);
|
||||||
|
|
|
||||||
|
|
@ -30,10 +30,8 @@ and are generated based on the last package release.
|
||||||
<LatestPackageReference Include="Microsoft.CSharp" Version="$(MicrosoftCSharpPackageVersion)" />
|
<LatestPackageReference Include="Microsoft.CSharp" Version="$(MicrosoftCSharpPackageVersion)" />
|
||||||
<LatestPackageReference Include="Microsoft.DotNet.GenAPI" Version="$(MicrosoftDotNetGenApiPackageVersion)" />
|
<LatestPackageReference Include="Microsoft.DotNet.GenAPI" Version="$(MicrosoftDotNetGenApiPackageVersion)" />
|
||||||
<LatestPackageReference Include="Microsoft.DotNet.PlatformAbstractions" Version="$(MicrosoftDotNetPlatformAbstractionsPackageVersion)" />
|
<LatestPackageReference Include="Microsoft.DotNet.PlatformAbstractions" Version="$(MicrosoftDotNetPlatformAbstractionsPackageVersion)" />
|
||||||
<LatestPackageReference Include="Microsoft.Extensions.ActivatorUtilities.Sources" Version="$(MicrosoftExtensionsActivatorUtilitiesSourcesPackageVersion)" />
|
|
||||||
<LatestPackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="$(MicrosoftExtensionsCachingAbstractionsPackageVersion)" />
|
<LatestPackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="$(MicrosoftExtensionsCachingAbstractionsPackageVersion)" />
|
||||||
<LatestPackageReference Include="Microsoft.Extensions.Caching.Memory" Version="$(MicrosoftExtensionsCachingMemoryPackageVersion)" />
|
<LatestPackageReference Include="Microsoft.Extensions.Caching.Memory" Version="$(MicrosoftExtensionsCachingMemoryPackageVersion)" />
|
||||||
<LatestPackageReference Include="Microsoft.Extensions.CommandLineUtils.Sources" Version="$(MicrosoftExtensionsCommandLineUtilsSourcesPackageVersion)" />
|
|
||||||
<LatestPackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="$(MicrosoftExtensionsConfigurationAbstractionsPackageVersion)" />
|
<LatestPackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="$(MicrosoftExtensionsConfigurationAbstractionsPackageVersion)" />
|
||||||
<LatestPackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="$(MicrosoftExtensionsConfigurationBinderPackageVersion)" />
|
<LatestPackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="$(MicrosoftExtensionsConfigurationBinderPackageVersion)" />
|
||||||
<LatestPackageReference Include="Microsoft.Extensions.Configuration.CommandLine" Version="$(MicrosoftExtensionsConfigurationCommandLinePackageVersion)" />
|
<LatestPackageReference Include="Microsoft.Extensions.Configuration.CommandLine" Version="$(MicrosoftExtensionsConfigurationCommandLinePackageVersion)" />
|
||||||
|
|
@ -41,7 +39,6 @@ and are generated based on the last package release.
|
||||||
<LatestPackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="$(MicrosoftExtensionsConfigurationFileExtensionsPackageVersion)" />
|
<LatestPackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="$(MicrosoftExtensionsConfigurationFileExtensionsPackageVersion)" />
|
||||||
<LatestPackageReference Include="Microsoft.Extensions.Configuration.Ini" Version="$(MicrosoftExtensionsConfigurationIniPackageVersion)" />
|
<LatestPackageReference Include="Microsoft.Extensions.Configuration.Ini" Version="$(MicrosoftExtensionsConfigurationIniPackageVersion)" />
|
||||||
<LatestPackageReference Include="Microsoft.Extensions.Configuration.Json" Version="$(MicrosoftExtensionsConfigurationJsonPackageVersion)" />
|
<LatestPackageReference Include="Microsoft.Extensions.Configuration.Json" Version="$(MicrosoftExtensionsConfigurationJsonPackageVersion)" />
|
||||||
<LatestPackageReference Include="Microsoft.Extensions.Configuration.KeyPerFile" Version="$(MicrosoftExtensionsConfigurationKeyPerFilePackageVersion)" />
|
|
||||||
<LatestPackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="$(MicrosoftExtensionsConfigurationUserSecretsPackageVersion)" />
|
<LatestPackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="$(MicrosoftExtensionsConfigurationUserSecretsPackageVersion)" />
|
||||||
<LatestPackageReference Include="Microsoft.Extensions.Configuration.Xml" Version="$(MicrosoftExtensionsConfigurationXmlPackageVersion)" />
|
<LatestPackageReference Include="Microsoft.Extensions.Configuration.Xml" Version="$(MicrosoftExtensionsConfigurationXmlPackageVersion)" />
|
||||||
<LatestPackageReference Include="Microsoft.Extensions.Configuration" Version="$(MicrosoftExtensionsConfigurationPackageVersion)" />
|
<LatestPackageReference Include="Microsoft.Extensions.Configuration" Version="$(MicrosoftExtensionsConfigurationPackageVersion)" />
|
||||||
|
|
@ -49,20 +46,13 @@ and are generated based on the last package release.
|
||||||
<LatestPackageReference Include="Microsoft.Extensions.DependencyInjection" Version="$(MicrosoftExtensionsDependencyInjectionPackageVersion)" />
|
<LatestPackageReference Include="Microsoft.Extensions.DependencyInjection" Version="$(MicrosoftExtensionsDependencyInjectionPackageVersion)" />
|
||||||
<LatestPackageReference Include="Microsoft.Extensions.DependencyModel" Version="$(MicrosoftExtensionsDependencyModelPackageVersion)" />
|
<LatestPackageReference Include="Microsoft.Extensions.DependencyModel" Version="$(MicrosoftExtensionsDependencyModelPackageVersion)" />
|
||||||
<LatestPackageReference Include="Microsoft.Extensions.DiagnosticAdapter" Version="$(MicrosoftExtensionsDiagnosticAdapterPackageVersion)" />
|
<LatestPackageReference Include="Microsoft.Extensions.DiagnosticAdapter" Version="$(MicrosoftExtensionsDiagnosticAdapterPackageVersion)" />
|
||||||
<LatestPackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions" Version="$(MicrosoftExtensionsDiagnosticsHealthChecksAbstractionsPackageVersion)" />
|
|
||||||
<LatestPackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks" Version="$(MicrosoftExtensionsDiagnosticsHealthChecksPackageVersion)" />
|
|
||||||
<LatestPackageReference Include="Microsoft.Extensions.FileProviders.Abstractions" Version="$(MicrosoftExtensionsFileProvidersAbstractionsPackageVersion)" />
|
<LatestPackageReference Include="Microsoft.Extensions.FileProviders.Abstractions" Version="$(MicrosoftExtensionsFileProvidersAbstractionsPackageVersion)" />
|
||||||
<LatestPackageReference Include="Microsoft.Extensions.FileProviders.Composite" Version="$(MicrosoftExtensionsFileProvidersCompositePackageVersion)" />
|
<LatestPackageReference Include="Microsoft.Extensions.FileProviders.Composite" Version="$(MicrosoftExtensionsFileProvidersCompositePackageVersion)" />
|
||||||
<LatestPackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="$(MicrosoftExtensionsFileProvidersEmbeddedPackageVersion)" />
|
|
||||||
<LatestPackageReference Include="Microsoft.Extensions.FileProviders.Physical" Version="$(MicrosoftExtensionsFileProvidersPhysicalPackageVersion)" />
|
<LatestPackageReference Include="Microsoft.Extensions.FileProviders.Physical" Version="$(MicrosoftExtensionsFileProvidersPhysicalPackageVersion)" />
|
||||||
<LatestPackageReference Include="Microsoft.Extensions.FileSystemGlobbing" Version="$(MicrosoftExtensionsFileSystemGlobbingPackageVersion)" />
|
<LatestPackageReference Include="Microsoft.Extensions.FileSystemGlobbing" Version="$(MicrosoftExtensionsFileSystemGlobbingPackageVersion)" />
|
||||||
<LatestPackageReference Include="Microsoft.Extensions.HashCodeCombiner.Sources" Version="$(MicrosoftExtensionsHashCodeCombinerSourcesPackageVersion)" />
|
|
||||||
<LatestPackageReference Include="Microsoft.Extensions.HostFactoryResolver.Sources" Version="$(MicrosoftExtensionsHostFactoryResolverSourcesPackageVersion)" />
|
|
||||||
<LatestPackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="$(MicrosoftExtensionsHostingAbstractionsPackageVersion)" />
|
<LatestPackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="$(MicrosoftExtensionsHostingAbstractionsPackageVersion)" />
|
||||||
<LatestPackageReference Include="Microsoft.Extensions.Hosting" Version="$(MicrosoftExtensionsHostingPackageVersion)" />
|
<LatestPackageReference Include="Microsoft.Extensions.Hosting" Version="$(MicrosoftExtensionsHostingPackageVersion)" />
|
||||||
<LatestPackageReference Include="Microsoft.Extensions.Http" Version="$(MicrosoftExtensionsHttpPackageVersion)" />
|
<LatestPackageReference Include="Microsoft.Extensions.Http" Version="$(MicrosoftExtensionsHttpPackageVersion)" />
|
||||||
<LatestPackageReference Include="Microsoft.Extensions.Localization.Abstractions" Version="$(MicrosoftExtensionsLocalizationAbstractionsPackageVersion)" />
|
|
||||||
<LatestPackageReference Include="Microsoft.Extensions.Localization" Version="$(MicrosoftExtensionsLocalizationPackageVersion)" />
|
|
||||||
<LatestPackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="$(MicrosoftExtensionsLoggingAbstractionsPackageVersion)" />
|
<LatestPackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="$(MicrosoftExtensionsLoggingAbstractionsPackageVersion)" />
|
||||||
<LatestPackageReference Include="Microsoft.Extensions.Logging.Configuration" Version="$(MicrosoftExtensionsLoggingConfigurationPackageVersion)" />
|
<LatestPackageReference Include="Microsoft.Extensions.Logging.Configuration" Version="$(MicrosoftExtensionsLoggingConfigurationPackageVersion)" />
|
||||||
<LatestPackageReference Include="Microsoft.Extensions.Logging.Console" Version="$(MicrosoftExtensionsLoggingConsolePackageVersion)" />
|
<LatestPackageReference Include="Microsoft.Extensions.Logging.Console" Version="$(MicrosoftExtensionsLoggingConsolePackageVersion)" />
|
||||||
|
|
@ -71,16 +61,10 @@ and are generated based on the last package release.
|
||||||
<LatestPackageReference Include="Microsoft.Extensions.Logging.EventLog" Version="$(MicrosoftExtensionsLoggingEventLogPackageVersion)" />
|
<LatestPackageReference Include="Microsoft.Extensions.Logging.EventLog" Version="$(MicrosoftExtensionsLoggingEventLogPackageVersion)" />
|
||||||
<LatestPackageReference Include="Microsoft.Extensions.Logging.TraceSource" Version="$(MicrosoftExtensionsLoggingTraceSourcePackageVersion)" />
|
<LatestPackageReference Include="Microsoft.Extensions.Logging.TraceSource" Version="$(MicrosoftExtensionsLoggingTraceSourcePackageVersion)" />
|
||||||
<LatestPackageReference Include="Microsoft.Extensions.Logging" Version="$(MicrosoftExtensionsLoggingPackageVersion)" />
|
<LatestPackageReference Include="Microsoft.Extensions.Logging" Version="$(MicrosoftExtensionsLoggingPackageVersion)" />
|
||||||
<LatestPackageReference Include="Microsoft.Extensions.ObjectPool" Version="$(MicrosoftExtensionsObjectPoolPackageVersion)" />
|
|
||||||
<LatestPackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="$(MicrosoftExtensionsOptionsConfigurationExtensionsPackageVersion)" />
|
<LatestPackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="$(MicrosoftExtensionsOptionsConfigurationExtensionsPackageVersion)" />
|
||||||
<LatestPackageReference Include="Microsoft.Extensions.Options.DataAnnotations" Version="$(MicrosoftExtensionsOptionsDataAnnotationsPackageVersion)" />
|
<LatestPackageReference Include="Microsoft.Extensions.Options.DataAnnotations" Version="$(MicrosoftExtensionsOptionsDataAnnotationsPackageVersion)" />
|
||||||
<LatestPackageReference Include="Microsoft.Extensions.Options" Version="$(MicrosoftExtensionsOptionsPackageVersion)" />
|
<LatestPackageReference Include="Microsoft.Extensions.Options" Version="$(MicrosoftExtensionsOptionsPackageVersion)" />
|
||||||
<LatestPackageReference Include="Microsoft.Extensions.ParameterDefaultValue.Sources" Version="$(MicrosoftExtensionsParameterDefaultValueSourcesPackageVersion)" />
|
|
||||||
<LatestPackageReference Include="Microsoft.Extensions.Primitives" Version="$(MicrosoftExtensionsPrimitivesPackageVersion)" />
|
<LatestPackageReference Include="Microsoft.Extensions.Primitives" Version="$(MicrosoftExtensionsPrimitivesPackageVersion)" />
|
||||||
<LatestPackageReference Include="Microsoft.Extensions.TypeNameHelper.Sources" Version="$(MicrosoftExtensionsTypeNameHelperSourcesPackageVersion)" />
|
|
||||||
<LatestPackageReference Include="Microsoft.Extensions.ValueStopWatch.Sources" Version="$(MicrosoftExtensionsValueStopwatchSourcesPackageVersion)" />
|
|
||||||
<LatestPackageReference Include="Microsoft.Extensions.WebEncoders" Version="$(MicrosoftExtensionsWebEncodersPackageVersion)" />
|
|
||||||
<LatestPackageReference Include="Microsoft.JSInterop" Version="$(MicrosoftJSInteropPackageVersion)" />
|
|
||||||
<LatestPackageReference Include="Microsoft.Win32.Registry" Version="$(MicrosoftWin32RegistryPackageVersion)" />
|
<LatestPackageReference Include="Microsoft.Win32.Registry" Version="$(MicrosoftWin32RegistryPackageVersion)" />
|
||||||
<LatestPackageReference Include="Microsoft.Win32.SystemEvents" Version="$(MicrosoftWin32SystemEventsPackageVersion)" />
|
<LatestPackageReference Include="Microsoft.Win32.SystemEvents" Version="$(MicrosoftWin32SystemEventsPackageVersion)" />
|
||||||
<LatestPackageReference Include="System.Buffers" Version="$(SystemBuffersPackageVersion)" />
|
<LatestPackageReference Include="System.Buffers" Version="$(SystemBuffersPackageVersion)" />
|
||||||
|
|
@ -94,14 +78,17 @@ and are generated based on the last package release.
|
||||||
<LatestPackageReference Include="System.Net.Http" Version="$(SystemNetHttpPackageVersion)" />
|
<LatestPackageReference Include="System.Net.Http" Version="$(SystemNetHttpPackageVersion)" />
|
||||||
<LatestPackageReference Include="System.Reflection.Metadata" Version="$(SystemReflectionMetadataPackageVersion)" />
|
<LatestPackageReference Include="System.Reflection.Metadata" Version="$(SystemReflectionMetadataPackageVersion)" />
|
||||||
<LatestPackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="$(SystemRuntimeCompilerServicesUnsafePackageVersion)" />
|
<LatestPackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="$(SystemRuntimeCompilerServicesUnsafePackageVersion)" />
|
||||||
|
<LatestPackageReference Include="System.Runtime.InteropServices.RuntimeInformation" Version="$(SystemRuntimeInteropServicesRuntimeInformationPackageVersion)" />
|
||||||
<LatestPackageReference Include="System.Security.Cryptography.Cng" Version="$(SystemSecurityCryptographyCngPackageVersion)" />
|
<LatestPackageReference Include="System.Security.Cryptography.Cng" Version="$(SystemSecurityCryptographyCngPackageVersion)" />
|
||||||
<LatestPackageReference Include="System.Security.Cryptography.Pkcs" Version="$(SystemSecurityCryptographyPkcsPackageVersion)" />
|
<LatestPackageReference Include="System.Security.Cryptography.Pkcs" Version="$(SystemSecurityCryptographyPkcsPackageVersion)" />
|
||||||
<LatestPackageReference Include="System.Security.Cryptography.Xml" Version="$(SystemSecurityCryptographyXmlPackageVersion)" />
|
<LatestPackageReference Include="System.Security.Cryptography.Xml" Version="$(SystemSecurityCryptographyXmlPackageVersion)" />
|
||||||
<LatestPackageReference Include="System.Security.Permissions" Version="$(SystemSecurityPermissionsPackageVersion)" />
|
<LatestPackageReference Include="System.Security.Permissions" Version="$(SystemSecurityPermissionsPackageVersion)" />
|
||||||
<LatestPackageReference Include="System.Security.Principal.Windows" Version="$(SystemSecurityPrincipalWindowsPackageVersion)" />
|
<LatestPackageReference Include="System.Security.Principal.Windows" Version="$(SystemSecurityPrincipalWindowsPackageVersion)" />
|
||||||
|
<LatestPackageReference Include="System.Text.Encodings.Web" Version="$(SystemTextEncodingsWebPackageVersion)" />
|
||||||
<LatestPackageReference Include="System.Text.Json" Version="$(SystemTextJsonPackageVersion)" />
|
<LatestPackageReference Include="System.Text.Json" Version="$(SystemTextJsonPackageVersion)" />
|
||||||
<LatestPackageReference Include="System.Threading.Channels" Version="$(SystemThreadingChannelsPackageVersion)" />
|
<LatestPackageReference Include="System.Threading.Channels" Version="$(SystemThreadingChannelsPackageVersion)" />
|
||||||
<LatestPackageReference Include="System.Windows.Extensions" Version="$(SystemWindowsExtensionsPackageVersion)" />
|
<LatestPackageReference Include="System.Windows.Extensions" Version="$(SystemWindowsExtensionsPackageVersion)" />
|
||||||
|
<LatestPackageReference Include="System.ValueTuple" Version="$(SystemValueTuplePackageVersion)" />
|
||||||
|
|
||||||
<!-- Runtime packages required for crossgen -->
|
<!-- Runtime packages required for crossgen -->
|
||||||
<LatestPackageReference Include="microsoft.netcore.app.runtime.win-x64" Version="$(MicrosoftNETCoreAppRuntimeVersion)" />
|
<LatestPackageReference Include="microsoft.netcore.app.runtime.win-x64" Version="$(MicrosoftNETCoreAppRuntimeVersion)" />
|
||||||
|
|
@ -122,9 +109,7 @@ and are generated based on the last package release.
|
||||||
<LatestPackageReference Include="Microsoft.AspNetCore.AzureAppServices.SiteExtension.2.2" Version="$(MicrosoftAspNetCoreAzureAppServicesSiteExtension22PackageVersion)" />
|
<LatestPackageReference Include="Microsoft.AspNetCore.AzureAppServices.SiteExtension.2.2" Version="$(MicrosoftAspNetCoreAzureAppServicesSiteExtension22PackageVersion)" />
|
||||||
<LatestPackageReference Include="Microsoft.AspNetCore.AzureAppServices.SiteExtension.3.1.x64" Version="$(MicrosoftAspNetCoreAzureAppServicesSiteExtension31PackageVersion)" />
|
<LatestPackageReference Include="Microsoft.AspNetCore.AzureAppServices.SiteExtension.3.1.x64" Version="$(MicrosoftAspNetCoreAzureAppServicesSiteExtension31PackageVersion)" />
|
||||||
<LatestPackageReference Include="Microsoft.AspNetCore.AzureAppServices.SiteExtension.3.1.x86" Version="$(MicrosoftAspNetCoreAzureAppServicesSiteExtension31PackageVersion)" />
|
<LatestPackageReference Include="Microsoft.AspNetCore.AzureAppServices.SiteExtension.3.1.x86" Version="$(MicrosoftAspNetCoreAzureAppServicesSiteExtension31PackageVersion)" />
|
||||||
<LatestPackageReference Include="Microsoft.AspNetCore.BenchmarkRunner.Sources" Version="$(MicrosoftAspNetCoreBenchmarkRunnerSourcesPackageVersion)" />
|
|
||||||
<LatestPackageReference Include="Microsoft.AspNetCore.Blazor.Mono" Version="$(MicrosoftAspNetCoreBlazorMonoPackageVersion)" />
|
<LatestPackageReference Include="Microsoft.AspNetCore.Blazor.Mono" Version="$(MicrosoftAspNetCoreBlazorMonoPackageVersion)" />
|
||||||
<LatestPackageReference Include="Microsoft.AspNetCore.Testing" Version="$(MicrosoftAspNetCoreTestingPackageVersion)" />
|
|
||||||
<LatestPackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="$(MicrosoftBclAsyncInterfacesPackageVersion)" />
|
<LatestPackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="$(MicrosoftBclAsyncInterfacesPackageVersion)" />
|
||||||
<LatestPackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="$(MicrosoftEntityFrameworkCoreInMemoryPackageVersion)" />
|
<LatestPackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="$(MicrosoftEntityFrameworkCoreInMemoryPackageVersion)" />
|
||||||
<LatestPackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="$(MicrosoftEntityFrameworkCoreRelationalPackageVersion)" />
|
<LatestPackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="$(MicrosoftEntityFrameworkCoreRelationalPackageVersion)" />
|
||||||
|
|
|
||||||
|
|
@ -63,6 +63,7 @@
|
||||||
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Blazor.Server" ProjectPath="$(RepoRoot)src\Components\Blazor\Server\src\Microsoft.AspNetCore.Blazor.Server.csproj" />
|
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Blazor.Server" ProjectPath="$(RepoRoot)src\Components\Blazor\Server\src\Microsoft.AspNetCore.Blazor.Server.csproj" />
|
||||||
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Blazor.DataAnnotations.Validation" ProjectPath="$(RepoRoot)src\Components\Blazor\Validation\src\Microsoft.AspNetCore.Blazor.DataAnnotations.Validation.csproj" />
|
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Blazor.DataAnnotations.Validation" ProjectPath="$(RepoRoot)src\Components\Blazor\Validation\src\Microsoft.AspNetCore.Blazor.DataAnnotations.Validation.csproj" />
|
||||||
<ProjectReferenceProvider Include="Ignitor" ProjectPath="$(RepoRoot)src\Components\Ignitor\src\Ignitor.csproj" />
|
<ProjectReferenceProvider Include="Ignitor" ProjectPath="$(RepoRoot)src\Components\Ignitor\src\Ignitor.csproj" />
|
||||||
|
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Testing" ProjectPath="$(RepoRoot)src\Testing\src\Microsoft.AspNetCore.Testing.csproj" />
|
||||||
<ProjectReferenceProvider Include="Microsoft.AspNetCore" ProjectPath="$(RepoRoot)src\DefaultBuilder\src\Microsoft.AspNetCore.csproj" RefProjectPath="$(RepoRoot)src\DefaultBuilder\ref\Microsoft.AspNetCore.csproj" />
|
<ProjectReferenceProvider Include="Microsoft.AspNetCore" ProjectPath="$(RepoRoot)src\DefaultBuilder\src\Microsoft.AspNetCore.csproj" RefProjectPath="$(RepoRoot)src\DefaultBuilder\ref\Microsoft.AspNetCore.csproj" />
|
||||||
<ProjectReferenceProvider Include="Microsoft.AspNetCore.DataProtection.Abstractions" ProjectPath="$(RepoRoot)src\DataProtection\Abstractions\src\Microsoft.AspNetCore.DataProtection.Abstractions.csproj" RefProjectPath="$(RepoRoot)src\DataProtection\Abstractions\ref\Microsoft.AspNetCore.DataProtection.Abstractions.csproj" />
|
<ProjectReferenceProvider Include="Microsoft.AspNetCore.DataProtection.Abstractions" ProjectPath="$(RepoRoot)src\DataProtection\Abstractions\src\Microsoft.AspNetCore.DataProtection.Abstractions.csproj" RefProjectPath="$(RepoRoot)src\DataProtection\Abstractions\ref\Microsoft.AspNetCore.DataProtection.Abstractions.csproj" />
|
||||||
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Cryptography.Internal" ProjectPath="$(RepoRoot)src\DataProtection\Cryptography.Internal\src\Microsoft.AspNetCore.Cryptography.Internal.csproj" RefProjectPath="$(RepoRoot)src\DataProtection\Cryptography.Internal\ref\Microsoft.AspNetCore.Cryptography.Internal.csproj" />
|
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Cryptography.Internal" ProjectPath="$(RepoRoot)src\DataProtection\Cryptography.Internal\src\Microsoft.AspNetCore.Cryptography.Internal.csproj" RefProjectPath="$(RepoRoot)src\DataProtection\Cryptography.Internal\ref\Microsoft.AspNetCore.Cryptography.Internal.csproj" />
|
||||||
|
|
@ -143,5 +144,14 @@
|
||||||
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Components.Forms" ProjectPath="$(RepoRoot)src\Components\Forms\src\Microsoft.AspNetCore.Components.Forms.csproj" RefProjectPath="$(RepoRoot)src\Components\Forms\ref\Microsoft.AspNetCore.Components.Forms.csproj" />
|
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Components.Forms" ProjectPath="$(RepoRoot)src\Components\Forms\src\Microsoft.AspNetCore.Components.Forms.csproj" RefProjectPath="$(RepoRoot)src\Components\Forms\ref\Microsoft.AspNetCore.Components.Forms.csproj" />
|
||||||
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Components.Server" ProjectPath="$(RepoRoot)src\Components\Server\src\Microsoft.AspNetCore.Components.Server.csproj" RefProjectPath="$(RepoRoot)src\Components\Server\ref\Microsoft.AspNetCore.Components.Server.csproj" />
|
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Components.Server" ProjectPath="$(RepoRoot)src\Components\Server\src\Microsoft.AspNetCore.Components.Server.csproj" RefProjectPath="$(RepoRoot)src\Components\Server\ref\Microsoft.AspNetCore.Components.Server.csproj" />
|
||||||
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Components.Web" ProjectPath="$(RepoRoot)src\Components\Web\src\Microsoft.AspNetCore.Components.Web.csproj" RefProjectPath="$(RepoRoot)src\Components\Web\ref\Microsoft.AspNetCore.Components.Web.csproj" />
|
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Components.Web" ProjectPath="$(RepoRoot)src\Components\Web\src\Microsoft.AspNetCore.Components.Web.csproj" RefProjectPath="$(RepoRoot)src\Components\Web\ref\Microsoft.AspNetCore.Components.Web.csproj" />
|
||||||
|
<ProjectReferenceProvider Include="Microsoft.Extensions.FileProviders.Embedded" ProjectPath="$(RepoRoot)src\FileProviders\Embedded\src\Microsoft.Extensions.FileProviders.Embedded.csproj" RefProjectPath="$(RepoRoot)src\FileProviders\Embedded\ref\Microsoft.Extensions.FileProviders.Embedded.csproj" />
|
||||||
|
<ProjectReferenceProvider Include="Microsoft.Extensions.Configuration.KeyPerFile" ProjectPath="$(RepoRoot)src\Configuration.KeyPerFile\src\Microsoft.Extensions.Configuration.KeyPerFile.csproj" RefProjectPath="$(RepoRoot)src\Configuration.KeyPerFile\ref\Microsoft.Extensions.Configuration.KeyPerFile.csproj" />
|
||||||
|
<ProjectReferenceProvider Include="Microsoft.Extensions.Localization.Abstractions" ProjectPath="$(RepoRoot)src\Localization\Abstractions\src\Microsoft.Extensions.Localization.Abstractions.csproj" RefProjectPath="$(RepoRoot)src\Localization\Abstractions\ref\Microsoft.Extensions.Localization.Abstractions.csproj" />
|
||||||
|
<ProjectReferenceProvider Include="Microsoft.Extensions.Localization" ProjectPath="$(RepoRoot)src\Localization\Localization\src\Microsoft.Extensions.Localization.csproj" RefProjectPath="$(RepoRoot)src\Localization\Localization\ref\Microsoft.Extensions.Localization.csproj" />
|
||||||
|
<ProjectReferenceProvider Include="Microsoft.Extensions.ObjectPool" ProjectPath="$(RepoRoot)src\ObjectPool\src\Microsoft.Extensions.ObjectPool.csproj" RefProjectPath="$(RepoRoot)src\ObjectPool\ref\Microsoft.Extensions.ObjectPool.csproj" />
|
||||||
|
<ProjectReferenceProvider Include="Microsoft.JSInterop" ProjectPath="$(RepoRoot)src\JSInterop\Microsoft.JSInterop\src\Microsoft.JSInterop.csproj" RefProjectPath="$(RepoRoot)src\JSInterop\Microsoft.JSInterop\ref\Microsoft.JSInterop.csproj" />
|
||||||
|
<ProjectReferenceProvider Include="Microsoft.Extensions.WebEncoders" ProjectPath="$(RepoRoot)src\WebEncoders\src\Microsoft.Extensions.WebEncoders.csproj" RefProjectPath="$(RepoRoot)src\WebEncoders\ref\Microsoft.Extensions.WebEncoders.csproj" />
|
||||||
|
<ProjectReferenceProvider Include="Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions" ProjectPath="$(RepoRoot)src\HealthChecks\Abstractions\src\Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.csproj" RefProjectPath="$(RepoRoot)src\HealthChecks\Abstractions\ref\Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.csproj" />
|
||||||
|
<ProjectReferenceProvider Include="Microsoft.Extensions.Diagnostics.HealthChecks" ProjectPath="$(RepoRoot)src\HealthChecks\HealthChecks\src\Microsoft.Extensions.Diagnostics.HealthChecks.csproj" RefProjectPath="$(RepoRoot)src\HealthChecks\HealthChecks\ref\Microsoft.Extensions.Diagnostics.HealthChecks.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
||||||
|
|
@ -30,24 +30,18 @@
|
||||||
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="$(MicrosoftExtensionsConfigurationFileExtensionsPackageVersion)" />
|
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="$(MicrosoftExtensionsConfigurationFileExtensionsPackageVersion)" />
|
||||||
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.Configuration.Ini" Version="$(MicrosoftExtensionsConfigurationIniPackageVersion)" />
|
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.Configuration.Ini" Version="$(MicrosoftExtensionsConfigurationIniPackageVersion)" />
|
||||||
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.Configuration.Json" Version="$(MicrosoftExtensionsConfigurationJsonPackageVersion)" />
|
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.Configuration.Json" Version="$(MicrosoftExtensionsConfigurationJsonPackageVersion)" />
|
||||||
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.Configuration.KeyPerFile" Version="$(MicrosoftExtensionsConfigurationKeyPerFilePackageVersion)" />
|
|
||||||
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="$(MicrosoftExtensionsConfigurationUserSecretsPackageVersion)" />
|
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="$(MicrosoftExtensionsConfigurationUserSecretsPackageVersion)" />
|
||||||
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.Configuration.Xml" Version="$(MicrosoftExtensionsConfigurationXmlPackageVersion)" />
|
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.Configuration.Xml" Version="$(MicrosoftExtensionsConfigurationXmlPackageVersion)" />
|
||||||
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.Configuration" Version="$(MicrosoftExtensionsConfigurationPackageVersion)" />
|
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.Configuration" Version="$(MicrosoftExtensionsConfigurationPackageVersion)" />
|
||||||
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="$(MicrosoftExtensionsDependencyInjectionAbstractionsPackageVersion)" />
|
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="$(MicrosoftExtensionsDependencyInjectionAbstractionsPackageVersion)" />
|
||||||
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.DependencyInjection" Version="$(MicrosoftExtensionsDependencyInjectionPackageVersion)" />
|
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.DependencyInjection" Version="$(MicrosoftExtensionsDependencyInjectionPackageVersion)" />
|
||||||
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions" Version="$(MicrosoftExtensionsDiagnosticsHealthChecksAbstractionsPackageVersion)" />
|
|
||||||
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.Diagnostics.HealthChecks" Version="$(MicrosoftExtensionsDiagnosticsHealthChecksPackageVersion)" />
|
|
||||||
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.FileProviders.Abstractions" Version="$(MicrosoftExtensionsFileProvidersAbstractionsPackageVersion)" />
|
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.FileProviders.Abstractions" Version="$(MicrosoftExtensionsFileProvidersAbstractionsPackageVersion)" />
|
||||||
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.FileProviders.Composite" Version="$(MicrosoftExtensionsFileProvidersCompositePackageVersion)" />
|
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.FileProviders.Composite" Version="$(MicrosoftExtensionsFileProvidersCompositePackageVersion)" />
|
||||||
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="$(MicrosoftExtensionsFileProvidersEmbeddedPackageVersion)" />
|
|
||||||
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.FileProviders.Physical" Version="$(MicrosoftExtensionsFileProvidersPhysicalPackageVersion)" />
|
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.FileProviders.Physical" Version="$(MicrosoftExtensionsFileProvidersPhysicalPackageVersion)" />
|
||||||
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.FileSystemGlobbing" Version="$(MicrosoftExtensionsFileSystemGlobbingPackageVersion)" />
|
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.FileSystemGlobbing" Version="$(MicrosoftExtensionsFileSystemGlobbingPackageVersion)" />
|
||||||
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="$(MicrosoftExtensionsHostingAbstractionsPackageVersion)" />
|
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="$(MicrosoftExtensionsHostingAbstractionsPackageVersion)" />
|
||||||
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.Hosting" Version="$(MicrosoftExtensionsHostingPackageVersion)" />
|
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.Hosting" Version="$(MicrosoftExtensionsHostingPackageVersion)" />
|
||||||
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.Http" Version="$(MicrosoftExtensionsHttpPackageVersion)" />
|
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.Http" Version="$(MicrosoftExtensionsHttpPackageVersion)" />
|
||||||
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.Localization.Abstractions" Version="$(MicrosoftExtensionsLocalizationAbstractionsPackageVersion)" />
|
|
||||||
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.Localization" Version="$(MicrosoftExtensionsLocalizationPackageVersion)" />
|
|
||||||
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.Logging.Abstractions" Version="$(MicrosoftExtensionsLoggingAbstractionsPackageVersion)" />
|
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.Logging.Abstractions" Version="$(MicrosoftExtensionsLoggingAbstractionsPackageVersion)" />
|
||||||
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.Logging.Configuration" Version="$(MicrosoftExtensionsLoggingConfigurationPackageVersion)" />
|
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.Logging.Configuration" Version="$(MicrosoftExtensionsLoggingConfigurationPackageVersion)" />
|
||||||
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.Logging.Console" Version="$(MicrosoftExtensionsLoggingConsolePackageVersion)" />
|
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.Logging.Console" Version="$(MicrosoftExtensionsLoggingConsolePackageVersion)" />
|
||||||
|
|
@ -56,13 +50,10 @@
|
||||||
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.Logging.EventLog" Version="$(MicrosoftExtensionsLoggingEventLogPackageVersion)" />
|
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.Logging.EventLog" Version="$(MicrosoftExtensionsLoggingEventLogPackageVersion)" />
|
||||||
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.Logging.TraceSource" Version="$(MicrosoftExtensionsLoggingTraceSourcePackageVersion)" />
|
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.Logging.TraceSource" Version="$(MicrosoftExtensionsLoggingTraceSourcePackageVersion)" />
|
||||||
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.Logging" Version="$(MicrosoftExtensionsLoggingPackageVersion)" />
|
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.Logging" Version="$(MicrosoftExtensionsLoggingPackageVersion)" />
|
||||||
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.ObjectPool" Version="$(MicrosoftExtensionsObjectPoolPackageVersion)" />
|
|
||||||
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="$(MicrosoftExtensionsOptionsConfigurationExtensionsPackageVersion)" />
|
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="$(MicrosoftExtensionsOptionsConfigurationExtensionsPackageVersion)" />
|
||||||
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.Options.DataAnnotations" Version="$(MicrosoftExtensionsOptionsDataAnnotationsPackageVersion)" />
|
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.Options.DataAnnotations" Version="$(MicrosoftExtensionsOptionsDataAnnotationsPackageVersion)" />
|
||||||
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.Options" Version="$(MicrosoftExtensionsOptionsPackageVersion)" />
|
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.Options" Version="$(MicrosoftExtensionsOptionsPackageVersion)" />
|
||||||
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.Primitives" Version="$(MicrosoftExtensionsPrimitivesPackageVersion)" />
|
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.Primitives" Version="$(MicrosoftExtensionsPrimitivesPackageVersion)" />
|
||||||
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.WebEncoders" Version="$(MicrosoftExtensionsWebEncodersPackageVersion)" />
|
|
||||||
<ExternalAspNetCoreAppReference Include="Microsoft.JSInterop" Version="$(MicrosoftJSInteropPackageVersion)" />
|
|
||||||
|
|
||||||
<!-- Dependencies from dotnet/corefx -->
|
<!-- Dependencies from dotnet/corefx -->
|
||||||
<ExternalAspNetCoreAppReference Include="System.IO.Pipelines" Version="$(SystemIOPipelinesPackageVersion)" />
|
<ExternalAspNetCoreAppReference Include="System.IO.Pipelines" Version="$(SystemIOPipelinesPackageVersion)" />
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,15 @@
|
||||||
<AspNetCoreAppReferenceAndPackage Include="Microsoft.AspNetCore.Components" />
|
<AspNetCoreAppReferenceAndPackage Include="Microsoft.AspNetCore.Components" />
|
||||||
<AspNetCoreAppReferenceAndPackage Include="Microsoft.AspNetCore.Components.Forms" />
|
<AspNetCoreAppReferenceAndPackage Include="Microsoft.AspNetCore.Components.Forms" />
|
||||||
<AspNetCoreAppReferenceAndPackage Include="Microsoft.AspNetCore.Components.Web" />
|
<AspNetCoreAppReferenceAndPackage Include="Microsoft.AspNetCore.Components.Web" />
|
||||||
|
<AspNetCoreAppReferenceAndPackage Include="Microsoft.Extensions.FileProviders.Embedded" />
|
||||||
|
<AspNetCoreAppReferenceAndPackage Include="Microsoft.Extensions.Configuration.KeyPerFile" />
|
||||||
|
<AspNetCoreAppReferenceAndPackage Include="Microsoft.Extensions.Localization.Abstractions" />
|
||||||
|
<AspNetCoreAppReferenceAndPackage Include="Microsoft.Extensions.Localization" />
|
||||||
|
<AspNetCoreAppReferenceAndPackage Include="Microsoft.Extensions.ObjectPool" />
|
||||||
|
<AspNetCoreAppReferenceAndPackage Include="Microsoft.JSInterop" />
|
||||||
|
<AspNetCoreAppReferenceAndPackage Include="Microsoft.Extensions.WebEncoders" />
|
||||||
|
<AspNetCoreAppReferenceAndPackage Include="Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions" />
|
||||||
|
<AspNetCoreAppReferenceAndPackage Include="Microsoft.Extensions.Diagnostics.HealthChecks" />
|
||||||
|
|
||||||
<!-- These assemblies are only in the shared framework -->
|
<!-- These assemblies are only in the shared framework -->
|
||||||
<AspNetCoreAppReference Include="Microsoft.AspNetCore" />
|
<AspNetCoreAppReference Include="Microsoft.AspNetCore" />
|
||||||
|
|
|
||||||
|
|
@ -61,14 +61,6 @@
|
||||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
<Uri>https://github.com/dotnet/extensions</Uri>
|
||||||
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
|
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
|
||||||
</Dependency>
|
</Dependency>
|
||||||
<Dependency Name="Microsoft.AspNetCore.BenchmarkRunner.Sources" Version="5.0.0-preview.1.20114.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
|
|
||||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
|
||||||
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
|
|
||||||
</Dependency>
|
|
||||||
<Dependency Name="Microsoft.Extensions.ActivatorUtilities.Sources" Version="5.0.0-preview.1.20114.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
|
|
||||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
|
||||||
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
|
|
||||||
</Dependency>
|
|
||||||
<Dependency Name="Microsoft.Extensions.Caching.Abstractions" Version="5.0.0-preview.1.20114.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
|
<Dependency Name="Microsoft.Extensions.Caching.Abstractions" Version="5.0.0-preview.1.20114.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
|
||||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
<Uri>https://github.com/dotnet/extensions</Uri>
|
||||||
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
|
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
|
||||||
|
|
@ -85,10 +77,6 @@
|
||||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
<Uri>https://github.com/dotnet/extensions</Uri>
|
||||||
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
|
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
|
||||||
</Dependency>
|
</Dependency>
|
||||||
<Dependency Name="Microsoft.Extensions.CommandLineUtils.Sources" Version="5.0.0-preview.1.20114.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
|
|
||||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
|
||||||
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
|
|
||||||
</Dependency>
|
|
||||||
<Dependency Name="Microsoft.Extensions.Configuration.Abstractions" Version="5.0.0-preview.1.20114.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
|
<Dependency Name="Microsoft.Extensions.Configuration.Abstractions" Version="5.0.0-preview.1.20114.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
|
||||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
<Uri>https://github.com/dotnet/extensions</Uri>
|
||||||
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
|
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
|
||||||
|
|
@ -121,10 +109,6 @@
|
||||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
<Uri>https://github.com/dotnet/extensions</Uri>
|
||||||
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
|
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
|
||||||
</Dependency>
|
</Dependency>
|
||||||
<Dependency Name="Microsoft.Extensions.Configuration.KeyPerFile" Version="5.0.0-preview.1.20114.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
|
|
||||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
|
||||||
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
|
|
||||||
</Dependency>
|
|
||||||
<Dependency Name="Microsoft.Extensions.Configuration.UserSecrets" Version="5.0.0-preview.1.20114.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
|
<Dependency Name="Microsoft.Extensions.Configuration.UserSecrets" Version="5.0.0-preview.1.20114.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
|
||||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
<Uri>https://github.com/dotnet/extensions</Uri>
|
||||||
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
|
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
|
||||||
|
|
@ -149,14 +133,6 @@
|
||||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
<Uri>https://github.com/dotnet/extensions</Uri>
|
||||||
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
|
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
|
||||||
</Dependency>
|
</Dependency>
|
||||||
<Dependency Name="Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions" Version="5.0.0-preview.1.20114.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
|
|
||||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
|
||||||
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
|
|
||||||
</Dependency>
|
|
||||||
<Dependency Name="Microsoft.Extensions.Diagnostics.HealthChecks" Version="5.0.0-preview.1.20114.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
|
|
||||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
|
||||||
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
|
|
||||||
</Dependency>
|
|
||||||
<Dependency Name="Microsoft.Extensions.FileProviders.Abstractions" Version="5.0.0-preview.1.20114.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
|
<Dependency Name="Microsoft.Extensions.FileProviders.Abstractions" Version="5.0.0-preview.1.20114.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
|
||||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
<Uri>https://github.com/dotnet/extensions</Uri>
|
||||||
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
|
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
|
||||||
|
|
@ -165,10 +141,6 @@
|
||||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
<Uri>https://github.com/dotnet/extensions</Uri>
|
||||||
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
|
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
|
||||||
</Dependency>
|
</Dependency>
|
||||||
<Dependency Name="Microsoft.Extensions.FileProviders.Embedded" Version="5.0.0-preview.1.20114.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
|
|
||||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
|
||||||
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
|
|
||||||
</Dependency>
|
|
||||||
<Dependency Name="Microsoft.Extensions.FileProviders.Physical" Version="5.0.0-preview.1.20114.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
|
<Dependency Name="Microsoft.Extensions.FileProviders.Physical" Version="5.0.0-preview.1.20114.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
|
||||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
<Uri>https://github.com/dotnet/extensions</Uri>
|
||||||
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
|
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
|
||||||
|
|
@ -177,10 +149,6 @@
|
||||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
<Uri>https://github.com/dotnet/extensions</Uri>
|
||||||
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
|
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
|
||||||
</Dependency>
|
</Dependency>
|
||||||
<Dependency Name="Microsoft.Extensions.HashCodeCombiner.Sources" Version="5.0.0-preview.1.20114.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
|
|
||||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
|
||||||
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
|
|
||||||
</Dependency>
|
|
||||||
<Dependency Name="Microsoft.Extensions.Hosting.Abstractions" Version="5.0.0-preview.1.20114.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
|
<Dependency Name="Microsoft.Extensions.Hosting.Abstractions" Version="5.0.0-preview.1.20114.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
|
||||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
<Uri>https://github.com/dotnet/extensions</Uri>
|
||||||
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
|
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
|
||||||
|
|
@ -189,22 +157,10 @@
|
||||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
<Uri>https://github.com/dotnet/extensions</Uri>
|
||||||
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
|
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
|
||||||
</Dependency>
|
</Dependency>
|
||||||
<Dependency Name="Microsoft.Extensions.HostFactoryResolver.Sources" Version="5.0.0-preview.1.20114.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
|
|
||||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
|
||||||
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
|
|
||||||
</Dependency>
|
|
||||||
<Dependency Name="Microsoft.Extensions.Http" Version="5.0.0-preview.1.20114.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
|
<Dependency Name="Microsoft.Extensions.Http" Version="5.0.0-preview.1.20114.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
|
||||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
<Uri>https://github.com/dotnet/extensions</Uri>
|
||||||
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
|
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
|
||||||
</Dependency>
|
</Dependency>
|
||||||
<Dependency Name="Microsoft.Extensions.Localization.Abstractions" Version="5.0.0-preview.1.20114.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
|
|
||||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
|
||||||
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
|
|
||||||
</Dependency>
|
|
||||||
<Dependency Name="Microsoft.Extensions.Localization" Version="5.0.0-preview.1.20114.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
|
|
||||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
|
||||||
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
|
|
||||||
</Dependency>
|
|
||||||
<Dependency Name="Microsoft.Extensions.Logging.Abstractions" Version="5.0.0-preview.1.20114.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
|
<Dependency Name="Microsoft.Extensions.Logging.Abstractions" Version="5.0.0-preview.1.20114.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
|
||||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
<Uri>https://github.com/dotnet/extensions</Uri>
|
||||||
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
|
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
|
||||||
|
|
@ -245,10 +201,6 @@
|
||||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
<Uri>https://github.com/dotnet/extensions</Uri>
|
||||||
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
|
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
|
||||||
</Dependency>
|
</Dependency>
|
||||||
<Dependency Name="Microsoft.Extensions.ObjectPool" Version="5.0.0-preview.1.20114.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
|
|
||||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
|
||||||
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
|
|
||||||
</Dependency>
|
|
||||||
<Dependency Name="Microsoft.Extensions.Options.ConfigurationExtensions" Version="5.0.0-preview.1.20114.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
|
<Dependency Name="Microsoft.Extensions.Options.ConfigurationExtensions" Version="5.0.0-preview.1.20114.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
|
||||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
<Uri>https://github.com/dotnet/extensions</Uri>
|
||||||
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
|
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
|
||||||
|
|
@ -261,34 +213,14 @@
|
||||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
<Uri>https://github.com/dotnet/extensions</Uri>
|
||||||
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
|
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
|
||||||
</Dependency>
|
</Dependency>
|
||||||
<Dependency Name="Microsoft.Extensions.ParameterDefaultValue.Sources" Version="5.0.0-preview.1.20114.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
|
|
||||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
|
||||||
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
|
|
||||||
</Dependency>
|
|
||||||
<Dependency Name="Microsoft.Extensions.Primitives" Version="5.0.0-preview.1.20114.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
|
<Dependency Name="Microsoft.Extensions.Primitives" Version="5.0.0-preview.1.20114.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
|
||||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
<Uri>https://github.com/dotnet/extensions</Uri>
|
||||||
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
|
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
|
||||||
</Dependency>
|
</Dependency>
|
||||||
<Dependency Name="Microsoft.Extensions.TypeNameHelper.Sources" Version="5.0.0-preview.1.20114.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
|
|
||||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
|
||||||
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
|
|
||||||
</Dependency>
|
|
||||||
<Dependency Name="Microsoft.Extensions.ValueStopwatch.Sources" Version="5.0.0-preview.1.20114.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
|
|
||||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
|
||||||
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
|
|
||||||
</Dependency>
|
|
||||||
<Dependency Name="Microsoft.Extensions.WebEncoders" Version="5.0.0-preview.1.20114.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
|
|
||||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
|
||||||
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
|
|
||||||
</Dependency>
|
|
||||||
<Dependency Name="Microsoft.Internal.Extensions.Refs" Version="5.0.0-preview.1.20114.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
|
<Dependency Name="Microsoft.Internal.Extensions.Refs" Version="5.0.0-preview.1.20114.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
|
||||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
<Uri>https://github.com/dotnet/extensions</Uri>
|
||||||
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
|
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
|
||||||
</Dependency>
|
</Dependency>
|
||||||
<Dependency Name="Microsoft.JSInterop" Version="5.0.0-preview.1.20114.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
|
|
||||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
|
||||||
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
|
|
||||||
</Dependency>
|
|
||||||
<Dependency Name="Microsoft.Win32.Registry" Version="5.0.0-preview.1.20113.7" CoherentParentDependency="Microsoft.NETCore.App.Runtime.win-x64">
|
<Dependency Name="Microsoft.Win32.Registry" Version="5.0.0-preview.1.20113.7" CoherentParentDependency="Microsoft.NETCore.App.Runtime.win-x64">
|
||||||
<Uri>https://github.com/dotnet/runtime</Uri>
|
<Uri>https://github.com/dotnet/runtime</Uri>
|
||||||
<Sha>0f3f8e1930c28b67f29990126bc2e8527e959a2e</Sha>
|
<Sha>0f3f8e1930c28b67f29990126bc2e8527e959a2e</Sha>
|
||||||
|
|
@ -416,10 +348,6 @@
|
||||||
<Uri>https://github.com/dotnet/arcade</Uri>
|
<Uri>https://github.com/dotnet/arcade</Uri>
|
||||||
<Sha>f50767f96246063f33a6565d318f3adf9058bace</Sha>
|
<Sha>f50767f96246063f33a6565d318f3adf9058bace</Sha>
|
||||||
</Dependency>
|
</Dependency>
|
||||||
<Dependency Name="Microsoft.AspNetCore.Testing" Version="5.0.0-preview.1.20114.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
|
|
||||||
<Uri>https://github.com/dotnet/extensions</Uri>
|
|
||||||
<Sha>396aff55e0b4628a7a44375e4b72e5d19a6e37ab</Sha>
|
|
||||||
</Dependency>
|
|
||||||
<Dependency Name="Microsoft.Net.Compilers.Toolset" Version="3.5.0-beta3-20114-02" CoherentParentDependency="Microsoft.Extensions.Logging">
|
<Dependency Name="Microsoft.Net.Compilers.Toolset" Version="3.5.0-beta3-20114-02" CoherentParentDependency="Microsoft.Extensions.Logging">
|
||||||
<Uri>https://github.com/dotnet/roslyn</Uri>
|
<Uri>https://github.com/dotnet/roslyn</Uri>
|
||||||
<Sha>92790e24cc2b9f9e336ed0365d610e106c01df88</Sha>
|
<Sha>92790e24cc2b9f9e336ed0365d610e106c01df88</Sha>
|
||||||
|
|
|
||||||
|
|
@ -98,14 +98,10 @@
|
||||||
<!-- Packages from dotnet/extensions -->
|
<!-- Packages from dotnet/extensions -->
|
||||||
<InternalAspNetCoreAnalyzersPackageVersion>5.0.0-preview.1.20114.1</InternalAspNetCoreAnalyzersPackageVersion>
|
<InternalAspNetCoreAnalyzersPackageVersion>5.0.0-preview.1.20114.1</InternalAspNetCoreAnalyzersPackageVersion>
|
||||||
<MicrosoftAspNetCoreAnalyzerTestingPackageVersion>5.0.0-preview.1.20114.1</MicrosoftAspNetCoreAnalyzerTestingPackageVersion>
|
<MicrosoftAspNetCoreAnalyzerTestingPackageVersion>5.0.0-preview.1.20114.1</MicrosoftAspNetCoreAnalyzerTestingPackageVersion>
|
||||||
<MicrosoftAspNetCoreBenchmarkRunnerSourcesPackageVersion>5.0.0-preview.1.20114.1</MicrosoftAspNetCoreBenchmarkRunnerSourcesPackageVersion>
|
|
||||||
<MicrosoftAspNetCoreTestingPackageVersion>5.0.0-preview.1.20114.1</MicrosoftAspNetCoreTestingPackageVersion>
|
|
||||||
<MicrosoftExtensionsActivatorUtilitiesSourcesPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsActivatorUtilitiesSourcesPackageVersion>
|
|
||||||
<MicrosoftExtensionsCachingAbstractionsPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsCachingAbstractionsPackageVersion>
|
<MicrosoftExtensionsCachingAbstractionsPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsCachingAbstractionsPackageVersion>
|
||||||
<MicrosoftExtensionsCachingMemoryPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsCachingMemoryPackageVersion>
|
<MicrosoftExtensionsCachingMemoryPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsCachingMemoryPackageVersion>
|
||||||
<MicrosoftExtensionsCachingSqlServerPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsCachingSqlServerPackageVersion>
|
<MicrosoftExtensionsCachingSqlServerPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsCachingSqlServerPackageVersion>
|
||||||
<MicrosoftExtensionsCachingStackExchangeRedisPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsCachingStackExchangeRedisPackageVersion>
|
<MicrosoftExtensionsCachingStackExchangeRedisPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsCachingStackExchangeRedisPackageVersion>
|
||||||
<MicrosoftExtensionsCommandLineUtilsSourcesPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsCommandLineUtilsSourcesPackageVersion>
|
|
||||||
<MicrosoftExtensionsConfigurationAbstractionsPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsConfigurationAbstractionsPackageVersion>
|
<MicrosoftExtensionsConfigurationAbstractionsPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsConfigurationAbstractionsPackageVersion>
|
||||||
<MicrosoftExtensionsConfigurationAzureKeyVaultPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsConfigurationAzureKeyVaultPackageVersion>
|
<MicrosoftExtensionsConfigurationAzureKeyVaultPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsConfigurationAzureKeyVaultPackageVersion>
|
||||||
<MicrosoftExtensionsConfigurationBinderPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsConfigurationBinderPackageVersion>
|
<MicrosoftExtensionsConfigurationBinderPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsConfigurationBinderPackageVersion>
|
||||||
|
|
@ -114,27 +110,19 @@
|
||||||
<MicrosoftExtensionsConfigurationFileExtensionsPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsConfigurationFileExtensionsPackageVersion>
|
<MicrosoftExtensionsConfigurationFileExtensionsPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsConfigurationFileExtensionsPackageVersion>
|
||||||
<MicrosoftExtensionsConfigurationIniPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsConfigurationIniPackageVersion>
|
<MicrosoftExtensionsConfigurationIniPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsConfigurationIniPackageVersion>
|
||||||
<MicrosoftExtensionsConfigurationJsonPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsConfigurationJsonPackageVersion>
|
<MicrosoftExtensionsConfigurationJsonPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsConfigurationJsonPackageVersion>
|
||||||
<MicrosoftExtensionsConfigurationKeyPerFilePackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsConfigurationKeyPerFilePackageVersion>
|
|
||||||
<MicrosoftExtensionsConfigurationPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsConfigurationPackageVersion>
|
<MicrosoftExtensionsConfigurationPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsConfigurationPackageVersion>
|
||||||
<MicrosoftExtensionsConfigurationUserSecretsPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsConfigurationUserSecretsPackageVersion>
|
<MicrosoftExtensionsConfigurationUserSecretsPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsConfigurationUserSecretsPackageVersion>
|
||||||
<MicrosoftExtensionsConfigurationXmlPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsConfigurationXmlPackageVersion>
|
<MicrosoftExtensionsConfigurationXmlPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsConfigurationXmlPackageVersion>
|
||||||
<MicrosoftExtensionsDependencyInjectionAbstractionsPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsDependencyInjectionAbstractionsPackageVersion>
|
<MicrosoftExtensionsDependencyInjectionAbstractionsPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsDependencyInjectionAbstractionsPackageVersion>
|
||||||
<MicrosoftExtensionsDependencyInjectionPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsDependencyInjectionPackageVersion>
|
<MicrosoftExtensionsDependencyInjectionPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsDependencyInjectionPackageVersion>
|
||||||
<MicrosoftExtensionsDiagnosticAdapterPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsDiagnosticAdapterPackageVersion>
|
<MicrosoftExtensionsDiagnosticAdapterPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsDiagnosticAdapterPackageVersion>
|
||||||
<MicrosoftExtensionsDiagnosticsHealthChecksAbstractionsPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsDiagnosticsHealthChecksAbstractionsPackageVersion>
|
|
||||||
<MicrosoftExtensionsDiagnosticsHealthChecksPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsDiagnosticsHealthChecksPackageVersion>
|
|
||||||
<MicrosoftExtensionsFileProvidersAbstractionsPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsFileProvidersAbstractionsPackageVersion>
|
<MicrosoftExtensionsFileProvidersAbstractionsPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsFileProvidersAbstractionsPackageVersion>
|
||||||
<MicrosoftExtensionsFileProvidersCompositePackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsFileProvidersCompositePackageVersion>
|
<MicrosoftExtensionsFileProvidersCompositePackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsFileProvidersCompositePackageVersion>
|
||||||
<MicrosoftExtensionsFileProvidersEmbeddedPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsFileProvidersEmbeddedPackageVersion>
|
|
||||||
<MicrosoftExtensionsFileProvidersPhysicalPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsFileProvidersPhysicalPackageVersion>
|
<MicrosoftExtensionsFileProvidersPhysicalPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsFileProvidersPhysicalPackageVersion>
|
||||||
<MicrosoftExtensionsFileSystemGlobbingPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsFileSystemGlobbingPackageVersion>
|
<MicrosoftExtensionsFileSystemGlobbingPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsFileSystemGlobbingPackageVersion>
|
||||||
<MicrosoftExtensionsHashCodeCombinerSourcesPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsHashCodeCombinerSourcesPackageVersion>
|
|
||||||
<MicrosoftExtensionsHostingAbstractionsPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsHostingAbstractionsPackageVersion>
|
<MicrosoftExtensionsHostingAbstractionsPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsHostingAbstractionsPackageVersion>
|
||||||
<MicrosoftExtensionsHostingPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsHostingPackageVersion>
|
<MicrosoftExtensionsHostingPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsHostingPackageVersion>
|
||||||
<MicrosoftExtensionsHostFactoryResolverSourcesPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsHostFactoryResolverSourcesPackageVersion>
|
|
||||||
<MicrosoftExtensionsHttpPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsHttpPackageVersion>
|
<MicrosoftExtensionsHttpPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsHttpPackageVersion>
|
||||||
<MicrosoftExtensionsLocalizationAbstractionsPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsLocalizationAbstractionsPackageVersion>
|
|
||||||
<MicrosoftExtensionsLocalizationPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsLocalizationPackageVersion>
|
|
||||||
<MicrosoftExtensionsLoggingAbstractionsPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsLoggingAbstractionsPackageVersion>
|
<MicrosoftExtensionsLoggingAbstractionsPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsLoggingAbstractionsPackageVersion>
|
||||||
<MicrosoftExtensionsLoggingAzureAppServicesPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsLoggingAzureAppServicesPackageVersion>
|
<MicrosoftExtensionsLoggingAzureAppServicesPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsLoggingAzureAppServicesPackageVersion>
|
||||||
<MicrosoftExtensionsLoggingConfigurationPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsLoggingConfigurationPackageVersion>
|
<MicrosoftExtensionsLoggingConfigurationPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsLoggingConfigurationPackageVersion>
|
||||||
|
|
@ -145,17 +133,11 @@
|
||||||
<MicrosoftExtensionsLoggingPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsLoggingPackageVersion>
|
<MicrosoftExtensionsLoggingPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsLoggingPackageVersion>
|
||||||
<MicrosoftExtensionsLoggingTestingPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsLoggingTestingPackageVersion>
|
<MicrosoftExtensionsLoggingTestingPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsLoggingTestingPackageVersion>
|
||||||
<MicrosoftExtensionsLoggingTraceSourcePackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsLoggingTraceSourcePackageVersion>
|
<MicrosoftExtensionsLoggingTraceSourcePackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsLoggingTraceSourcePackageVersion>
|
||||||
<MicrosoftExtensionsObjectPoolPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsObjectPoolPackageVersion>
|
|
||||||
<MicrosoftExtensionsOptionsConfigurationExtensionsPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsOptionsConfigurationExtensionsPackageVersion>
|
<MicrosoftExtensionsOptionsConfigurationExtensionsPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsOptionsConfigurationExtensionsPackageVersion>
|
||||||
<MicrosoftExtensionsOptionsDataAnnotationsPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsOptionsDataAnnotationsPackageVersion>
|
<MicrosoftExtensionsOptionsDataAnnotationsPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsOptionsDataAnnotationsPackageVersion>
|
||||||
<MicrosoftExtensionsOptionsPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsOptionsPackageVersion>
|
<MicrosoftExtensionsOptionsPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsOptionsPackageVersion>
|
||||||
<MicrosoftExtensionsParameterDefaultValueSourcesPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsParameterDefaultValueSourcesPackageVersion>
|
|
||||||
<MicrosoftExtensionsPrimitivesPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsPrimitivesPackageVersion>
|
<MicrosoftExtensionsPrimitivesPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsPrimitivesPackageVersion>
|
||||||
<MicrosoftExtensionsTypeNameHelperSourcesPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsTypeNameHelperSourcesPackageVersion>
|
|
||||||
<MicrosoftExtensionsValueStopwatchSourcesPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsValueStopwatchSourcesPackageVersion>
|
|
||||||
<MicrosoftExtensionsWebEncodersPackageVersion>5.0.0-preview.1.20114.1</MicrosoftExtensionsWebEncodersPackageVersion>
|
|
||||||
<MicrosoftInternalExtensionsRefsPackageVersion>5.0.0-preview.1.20114.1</MicrosoftInternalExtensionsRefsPackageVersion>
|
<MicrosoftInternalExtensionsRefsPackageVersion>5.0.0-preview.1.20114.1</MicrosoftInternalExtensionsRefsPackageVersion>
|
||||||
<MicrosoftJSInteropPackageVersion>5.0.0-preview.1.20114.1</MicrosoftJSInteropPackageVersion>
|
|
||||||
<!-- Packages from dotnet/efcore -->
|
<!-- Packages from dotnet/efcore -->
|
||||||
<dotnetefPackageVersion>5.0.0-preview.2.20121.2</dotnetefPackageVersion>
|
<dotnetefPackageVersion>5.0.0-preview.2.20121.2</dotnetefPackageVersion>
|
||||||
<MicrosoftEntityFrameworkCoreInMemoryPackageVersion>5.0.0-preview.2.20121.2</MicrosoftEntityFrameworkCoreInMemoryPackageVersion>
|
<MicrosoftEntityFrameworkCoreInMemoryPackageVersion>5.0.0-preview.2.20121.2</MicrosoftEntityFrameworkCoreInMemoryPackageVersion>
|
||||||
|
|
@ -198,7 +180,9 @@
|
||||||
<SystemCommandlineExperimentalPackageVersion>0.3.0-alpha.19317.1</SystemCommandlineExperimentalPackageVersion>
|
<SystemCommandlineExperimentalPackageVersion>0.3.0-alpha.19317.1</SystemCommandlineExperimentalPackageVersion>
|
||||||
<SystemComponentModelPackageVersion>4.3.0</SystemComponentModelPackageVersion>
|
<SystemComponentModelPackageVersion>4.3.0</SystemComponentModelPackageVersion>
|
||||||
<SystemNetHttpPackageVersion>4.3.2</SystemNetHttpPackageVersion>
|
<SystemNetHttpPackageVersion>4.3.2</SystemNetHttpPackageVersion>
|
||||||
|
<SystemRuntimeInteropServicesRuntimeInformationPackageVersion>4.3.0</SystemRuntimeInteropServicesRuntimeInformationPackageVersion>
|
||||||
<SystemThreadingTasksExtensionsPackageVersion>4.5.3</SystemThreadingTasksExtensionsPackageVersion>
|
<SystemThreadingTasksExtensionsPackageVersion>4.5.3</SystemThreadingTasksExtensionsPackageVersion>
|
||||||
|
<SystemValueTuplePackageVersion>4.5.0</SystemValueTuplePackageVersion>
|
||||||
<!-- Packages developed by @aspnet, but manually updated as necessary. -->
|
<!-- Packages developed by @aspnet, but manually updated as necessary. -->
|
||||||
<LibuvPackageVersion>1.10.0</LibuvPackageVersion>
|
<LibuvPackageVersion>1.10.0</LibuvPackageVersion>
|
||||||
<MicrosoftAspNetWebApiClientPackageVersion>5.2.6</MicrosoftAspNetWebApiClientPackageVersion>
|
<MicrosoftAspNetWebApiClientPackageVersion>5.2.6</MicrosoftAspNetWebApiClientPackageVersion>
|
||||||
|
|
|
||||||
|
|
@ -119,7 +119,7 @@
|
||||||
<Error Condition="@(_InvalidReferenceToSharedFxOnlyAssembly->Count()) != 0"
|
<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." />
|
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." />
|
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>
|
</Target>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,6 @@
|
||||||
<Reference Include="Microsoft.Build.Framework" />
|
<Reference Include="Microsoft.Build.Framework" />
|
||||||
<Reference Include="Microsoft.Build.Utilities.Core" />
|
<Reference Include="Microsoft.Build.Utilities.Core" />
|
||||||
<Reference Include="Microsoft.CodeAnalysis.Razor" />
|
<Reference Include="Microsoft.CodeAnalysis.Razor" />
|
||||||
<Reference Include="Microsoft.Extensions.CommandLineUtils.Sources" />
|
|
||||||
<!-- Avoid CS1705 errors due to mix of assemblies brought in transitively. -->
|
<!-- Avoid CS1705 errors due to mix of assemblies brought in transitively. -->
|
||||||
<Reference Include="Microsoft.AspNetCore.Components" />
|
<Reference Include="Microsoft.AspNetCore.Components" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
@ -40,6 +39,7 @@
|
||||||
<ProjectReference Include="..\..\testassets\StandaloneApp\StandaloneApp.csproj" />
|
<ProjectReference Include="..\..\testassets\StandaloneApp\StandaloneApp.csproj" />
|
||||||
<Compile Include="$(SharedSourceRoot)test\SkipOnHelixAttribute.cs" />
|
<Compile Include="$(SharedSourceRoot)test\SkipOnHelixAttribute.cs" />
|
||||||
<Compile Include="$(ComponentsSharedSourceRoot)test\**\*.cs" LinkBase="Helpers" />
|
<Compile Include="$(ComponentsSharedSourceRoot)test\**\*.cs" LinkBase="Helpers" />
|
||||||
|
<Compile Include="$(SharedSourceRoot)CommandLineUtils\**\*.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
|
||||||
|
|
@ -18,8 +18,9 @@
|
||||||
<Reference Include="Microsoft.AspNetCore.Components.Server" />
|
<Reference Include="Microsoft.AspNetCore.Components.Server" />
|
||||||
<Reference Include="Microsoft.AspNetCore.ResponseCompression" />
|
<Reference Include="Microsoft.AspNetCore.ResponseCompression" />
|
||||||
<Reference Include="Microsoft.AspNetCore" />
|
<Reference Include="Microsoft.AspNetCore" />
|
||||||
<Reference Include="Microsoft.Extensions.CommandLineUtils.Sources" />
|
|
||||||
<Reference Include="Microsoft.Extensions.Hosting" />
|
<Reference Include="Microsoft.Extensions.Hosting" />
|
||||||
|
|
||||||
|
<Compile Include="$(SharedSourceRoot)CommandLineUtils\**\*.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<!-- Pack settings -->
|
<!-- Pack settings -->
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,8 @@
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="BenchmarkDotNet" />
|
<Reference Include="BenchmarkDotNet" />
|
||||||
<Reference Include="Microsoft.AspNetCore.Components" />
|
<Reference Include="Microsoft.AspNetCore.Components" />
|
||||||
<Reference Include="Microsoft.AspNetCore.BenchmarkRunner.Sources" />
|
|
||||||
|
<Compile Include="$(SharedSourceRoot)BenchmarkRunner\*.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,6 @@
|
||||||
<Compile Include="../src/Properties/AssemblyInfo.cs" />
|
<Compile Include="../src/Properties/AssemblyInfo.cs" />
|
||||||
<Reference Include="Microsoft.Extensions.Logging.Abstractions" />
|
<Reference Include="Microsoft.Extensions.Logging.Abstractions" />
|
||||||
<Reference Include="Microsoft.Extensions.DependencyInjection.Abstractions" />
|
<Reference Include="Microsoft.Extensions.DependencyInjection.Abstractions" />
|
||||||
<Reference Include="Microsoft.Extensions.HashCodeCombiner.Sources" />
|
|
||||||
<Reference Include="System.Buffers" />
|
<Reference Include="System.Buffers" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup Condition="'$(TargetFramework)' == '$(DefaultNetCoreTargetFramework)'">
|
<ItemGroup Condition="'$(TargetFramework)' == '$(DefaultNetCoreTargetFramework)'">
|
||||||
|
|
@ -19,6 +18,5 @@
|
||||||
<Compile Include="../src/Properties/AssemblyInfo.cs" />
|
<Compile Include="../src/Properties/AssemblyInfo.cs" />
|
||||||
<Reference Include="Microsoft.Extensions.Logging.Abstractions" />
|
<Reference Include="Microsoft.Extensions.Logging.Abstractions" />
|
||||||
<Reference Include="Microsoft.Extensions.DependencyInjection.Abstractions" />
|
<Reference Include="Microsoft.Extensions.DependencyInjection.Abstractions" />
|
||||||
<Reference Include="Microsoft.Extensions.HashCodeCombiner.Sources" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,8 @@
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="Microsoft.Extensions.Logging.Abstractions" />
|
<Reference Include="Microsoft.Extensions.Logging.Abstractions" />
|
||||||
<Reference Include="Microsoft.Extensions.DependencyInjection.Abstractions" />
|
<Reference Include="Microsoft.Extensions.DependencyInjection.Abstractions" />
|
||||||
<Reference Include="Microsoft.Extensions.HashCodeCombiner.Sources" />
|
|
||||||
|
<Compile Include="$(SharedSourceRoot)HashCodeCombiner\*.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup Condition="'$(TargetFramework)'=='netstandard2.0'">
|
<ItemGroup Condition="'$(TargetFramework)'=='netstandard2.0'">
|
||||||
|
|
@ -33,12 +34,16 @@
|
||||||
<Target Name="_GetNuspecDependencyPackageVersions">
|
<Target Name="_GetNuspecDependencyPackageVersions">
|
||||||
<MSBuild Targets="_GetPackageVersionInfo"
|
<MSBuild Targets="_GetPackageVersionInfo"
|
||||||
BuildInParallel="$(BuildInParallel)"
|
BuildInParallel="$(BuildInParallel)"
|
||||||
Projects="../../Analyzers/src/Microsoft.AspNetCore.Components.Analyzers.csproj;../../../Security/Authorization/Core/src/Microsoft.AspNetCore.Authorization.csproj">
|
Projects="
|
||||||
|
../../Analyzers/src/Microsoft.AspNetCore.Components.Analyzers.csproj;
|
||||||
|
../../../JSInterop/Microsoft.JSInterop/src/Microsoft.JSInterop.csproj;
|
||||||
|
../../../Security/Authorization/Core/src/Microsoft.AspNetCore.Authorization.csproj">
|
||||||
<Output TaskParameter="TargetOutputs" ItemName="_ProjectPathWithVersion" />
|
<Output TaskParameter="TargetOutputs" ItemName="_ProjectPathWithVersion" />
|
||||||
</MSBuild>
|
</MSBuild>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<NuspecProperty Include="@(_ProjectPathWithVersion->WithMetadataValue('PackageId', 'Microsoft.AspnetCore.Components.Analyzers')->'componentAnalyzerPackageVersion=%(PackageVersion)')" />
|
<NuspecProperty Include="@(_ProjectPathWithVersion->WithMetadataValue('PackageId', 'Microsoft.AspnetCore.Components.Analyzers')->'componentAnalyzerPackageVersion=%(PackageVersion)')" />
|
||||||
<NuspecProperty Include="@(_ProjectPathWithVersion->WithMetadataValue('PackageId', 'Microsoft.AspnetCore.Authorization')->'authorizationPackageVersion=%(PackageVersion)')" />
|
<NuspecProperty Include="@(_ProjectPathWithVersion->WithMetadataValue('PackageId', 'Microsoft.AspnetCore.Authorization')->'authorizationPackageVersion=%(PackageVersion)')" />
|
||||||
|
<NuspecProperty Include="@(_ProjectPathWithVersion->WithMetadataValue('PackageId', 'Microsoft.JSInterop')->'jsInteropPackageVersion=%(PackageVersion)')" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Target>
|
</Target>
|
||||||
|
|
||||||
|
|
@ -50,7 +55,6 @@
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<NuspecProperty Include="jsInteropPackageVersion=$(MicrosoftJSInteropPackageVersion)" />
|
|
||||||
<NuspecProperty Condition="'$(DotNetBuildFromSource)' != 'true'" Include="systemComponentModelAnnotationsPackageVersion=$(SystemComponentModelAnnotationsPackageVersion)" />
|
<NuspecProperty Condition="'$(DotNetBuildFromSource)' != 'true'" Include="systemComponentModelAnnotationsPackageVersion=$(SystemComponentModelAnnotationsPackageVersion)" />
|
||||||
<NuspecProperty Include="AssemblyName=$(AssemblyName)" />
|
<NuspecProperty Include="AssemblyName=$(AssemblyName)" />
|
||||||
<NuspecProperty Include="OutputPath=$(OutputPath)" />
|
<NuspecProperty Include="OutputPath=$(OutputPath)" />
|
||||||
|
|
|
||||||
|
|
@ -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">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>$(DefaultNetCoreTargetFramework)</TargetFramework>
|
<TargetFramework>$(DefaultNetCoreTargetFramework)</TargetFramework>
|
||||||
<Description>Runtime server features for ASP.NET Core Components.</Description>
|
<Description>Runtime server features for ASP.NET Core Components.</Description>
|
||||||
|
|
@ -10,6 +9,7 @@
|
||||||
<NoWarn>CS0436;$(NoWarn)</NoWarn>
|
<NoWarn>CS0436;$(NoWarn)</NoWarn>
|
||||||
<DefineConstants>$(DefineConstants);ENABLE_UNSAFE_MSGPACK;SPAN_BUILTIN;MESSAGEPACK_INTERNAL;COMPONENTS_SERVER</DefineConstants>
|
<DefineConstants>$(DefineConstants);ENABLE_UNSAFE_MSGPACK;SPAN_BUILTIN;MESSAGEPACK_INTERNAL;COMPONENTS_SERVER</DefineConstants>
|
||||||
<IsPackable>false</IsPackable>
|
<IsPackable>false</IsPackable>
|
||||||
|
<EmbeddedFilesManifestFileName>Microsoft.Extensions.FileProviders.Embedded.Manifest.xml</EmbeddedFilesManifestFileName>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
@ -22,7 +22,8 @@
|
||||||
<Reference Include="Microsoft.Extensions.FileProviders.Composite" />
|
<Reference Include="Microsoft.Extensions.FileProviders.Composite" />
|
||||||
<Reference Include="Microsoft.Extensions.FileProviders.Embedded" />
|
<Reference Include="Microsoft.Extensions.FileProviders.Embedded" />
|
||||||
<Reference Include="Microsoft.Extensions.Logging" />
|
<Reference Include="Microsoft.Extensions.Logging" />
|
||||||
<Reference Include="Microsoft.Extensions.ValueStopwatch.Sources" PrivateAssets="All" />
|
|
||||||
|
<Compile Include="$(SharedSourceRoot)ValueStopwatch\*.cs" />
|
||||||
|
|
||||||
<!-- Add a project dependency without reference output assemblies to enforce build order -->
|
<!-- Add a project dependency without reference output assemblies to enforce build order -->
|
||||||
<!-- Applying workaround for https://github.com/microsoft/msbuild/issues/2661 and https://github.com/dotnet/sdk/issues/952 -->
|
<!-- Applying workaround for https://github.com/microsoft/msbuild/issues/2661 and https://github.com/dotnet/sdk/issues/952 -->
|
||||||
|
|
@ -39,6 +40,15 @@
|
||||||
<MessagePackRoot>$(RepoRoot)src\submodules\MessagePack-CSharp\src\MessagePack.UnityClient\Assets\Scripts\MessagePack\</MessagePackRoot>
|
<MessagePackRoot>$(RepoRoot)src\submodules\MessagePack-CSharp\src\MessagePack.UnityClient\Assets\Scripts\MessagePack\</MessagePackRoot>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<!-- This workaround is required when referencing FileProviders.Embedded as a project -->
|
||||||
|
<PropertyGroup>
|
||||||
|
<_FileProviderTaskAssembly>$(ArtifactsDir)bin\Microsoft.Extensions.FileProviders.Embedded.Manifest.Task\$(Configuration)\netstandard2.0\Microsoft.Extensions.FileProviders.Embedded.Manifest.Task.dll</_FileProviderTaskAssembly>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<UsingTask
|
||||||
|
AssemblyFile="$(_FileProviderTaskAssembly)"
|
||||||
|
TaskName="Microsoft.Extensions.FileProviders.Embedded.Manifest.Task.GenerateEmbeddedResourcesManifest" />
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="$(ComponentsSharedSourceRoot)src\CacheHeaderSettings.cs" Link="Shared\CacheHeaderSettings.cs" />
|
<Compile Include="$(ComponentsSharedSourceRoot)src\CacheHeaderSettings.cs" Link="Shared\CacheHeaderSettings.cs" />
|
||||||
<Compile Include="$(ComponentsSharedSourceRoot)src\ArrayBuilder.cs" LinkBase="Circuits" />
|
<Compile Include="$(ComponentsSharedSourceRoot)src\ArrayBuilder.cs" LinkBase="Circuits" />
|
||||||
|
|
@ -84,5 +94,4 @@
|
||||||
<EmbeddedResource Include="$(BlazorServerJSFile)" LogicalName="_framework/%(Filename)%(Extension)" />
|
<EmbeddedResource Include="$(BlazorServerJSFile)" LogicalName="_framework/%(Filename)%(Extension)" />
|
||||||
<EmbeddedResource Include="$(BlazorServerJSFile).map" LogicalName="_framework/%(Filename)%(Extension)" Condition="Exists('$(BlazorServerJSFile).map')" />
|
<EmbeddedResource Include="$(BlazorServerJSFile).map" LogicalName="_framework/%(Filename)%(Extension)" Condition="Exists('$(BlazorServerJSFile).map')" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
@ -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" />
|
<Output TaskParameter="JoinResult" ItemName="_ReferencedExtensionsRefAssemblies" />
|
||||||
</JoinItems>
|
</JoinItems>
|
||||||
|
|
||||||
|
<!-- _DuplicatedExtensionsRefAssemblies represents ref assemblies that are present in Microsoft.Internal.Extensions.Refs package and also are built in this repo. -->
|
||||||
|
<!-- This should be temporary while we migrate sources since there should be no duplication when sources are deleted from extensions. -->
|
||||||
|
<JoinItems Left="@(_ReferencedExtensionsRefAssemblies)" Right="@(_ResolvedProjectReferencePaths)" LeftKey="Filename" RightKey="Filename" ItemSpecToUse="Left">
|
||||||
|
<Output TaskParameter="JoinResult" ItemName="_DuplicatedExtensionsRefAssemblies" />
|
||||||
|
</JoinItems>
|
||||||
|
|
||||||
<JoinItems Left="@(_ReferencedExtensionsRefAssemblies)" Right="@(ExternalAspNetCoreAppReference)" LeftKey="Filename" RightKey="Identity" ItemSpecToUse="Left">
|
<JoinItems Left="@(_ReferencedExtensionsRefAssemblies)" Right="@(ExternalAspNetCoreAppReference)" LeftKey="Filename" RightKey="Identity" ItemSpecToUse="Left">
|
||||||
<Output TaskParameter="JoinResult" ItemName="_SelectedExtensionsRefs" />
|
<Output TaskParameter="JoinResult" ItemName="_SelectedExtensionsRefs" />
|
||||||
</JoinItems>
|
</JoinItems>
|
||||||
|
|
@ -127,12 +133,15 @@ This package is an internal implementation of the .NET Core SDK and is not meant
|
||||||
</JoinItems>
|
</JoinItems>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<!-- Do not exclude the ref assemblies that we built in this repo if it's also included in Microsoft.Internal.Extensions.Refs -->
|
||||||
|
<_ReferencedExtensionsRefAssembliesToExclude Include="@(_ReferencedExtensionsRefAssemblies)" Exclude="@(_DuplicatedExtensionsRefAssemblies)" />
|
||||||
|
|
||||||
<!-- Exclude transitive external dependencies that are not directly referenced by projects in AspNetCore or Extensions -->
|
<!-- Exclude transitive external dependencies that are not directly referenced by projects in AspNetCore or Extensions -->
|
||||||
<AspNetCoreReferenceAssemblyPath
|
<AspNetCoreReferenceAssemblyPath
|
||||||
Include="@(ReferencePathWithRefAssemblies)"
|
Include="@(ReferencePathWithRefAssemblies)"
|
||||||
Condition="'%(ReferencePathWithRefAssemblies.IsReferenceAssembly)' != 'false'"
|
Condition="'%(ReferencePathWithRefAssemblies.IsReferenceAssembly)' != 'false'"
|
||||||
Exclude="
|
Exclude="
|
||||||
@(_ReferencedExtensionsRefAssemblies);
|
@(_ReferencedExtensionsRefAssembliesToExclude);
|
||||||
@(ReferencePathWithRefAssemblies->WithMetadataValue('NuGetPackageId', 'Microsoft.NETCore.App.Ref'));
|
@(ReferencePathWithRefAssemblies->WithMetadataValue('NuGetPackageId', 'Microsoft.NETCore.App.Ref'));
|
||||||
@(ReferencePathWithRefAssemblies->WithMetadataValue('NuGetPackageId', 'System.Security.Cryptography.Pkcs'));
|
@(ReferencePathWithRefAssemblies->WithMetadataValue('NuGetPackageId', 'System.Security.Cryptography.Pkcs'));
|
||||||
@(ReferencePathWithRefAssemblies->WithMetadataValue('NuGetPackageId', 'System.Drawing.Common'));
|
@(ReferencePathWithRefAssemblies->WithMetadataValue('NuGetPackageId', 'System.Drawing.Common'));
|
||||||
|
|
|
||||||
|
|
@ -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