From 5c9701e0b6640b06635e4c3d570bf77e6de79b2b Mon Sep 17 00:00:00 2001 From: Nate McMaster Date: Tue, 30 Oct 2018 17:16:51 -0700 Subject: [PATCH 01/52] Merge branch 'release/2.2' \n\nCommit migrated from https://github.com/dotnet/extensions/commit/34204b6bc41de865f5310f5f237781a57a83976c --- src/Shared/BenchmarkRunner/DefaultCoreConfig.cs | 2 +- src/Shared/CertificateGeneration/CertificateManager.cs | 8 ++++---- .../CertificateGeneration/EnsureCertificateResult.cs | 4 ++-- src/Shared/test/Shared.Tests/CertificateManagerTests.cs | 4 ++-- src/Shared/test/Shared.Tests/DotNetMuxerTests.cs | 2 +- src/Testing/test/ConditionalFactTest.cs | 4 ++-- src/Testing/test/ConditionalTheoryTest.cs | 2 +- 7 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/Shared/BenchmarkRunner/DefaultCoreConfig.cs b/src/Shared/BenchmarkRunner/DefaultCoreConfig.cs index 5e2bafd506..a61833ab26 100644 --- a/src/Shared/BenchmarkRunner/DefaultCoreConfig.cs +++ b/src/Shared/BenchmarkRunner/DefaultCoreConfig.cs @@ -31,7 +31,7 @@ namespace BenchmarkDotNet.Attributes #if NETCOREAPP2_1 .With(CsProjCoreToolchain.From(NetCoreAppSettings.NetCoreApp21)) #else - .With(CsProjCoreToolchain.From(new NetCoreAppSettings("netcoreapp2.2", null, ".NET Core 2.2"))) + .With(CsProjCoreToolchain.From(new NetCoreAppSettings("netcoreapp3.0", null, ".NET Core 3.0"))) #endif .With(new GcMode { Server = true }) .With(RunStrategy.Throughput)); diff --git a/src/Shared/CertificateGeneration/CertificateManager.cs b/src/Shared/CertificateGeneration/CertificateManager.cs index 952cf7c36d..26639572e4 100644 --- a/src/Shared/CertificateGeneration/CertificateManager.cs +++ b/src/Shared/CertificateGeneration/CertificateManager.cs @@ -32,7 +32,7 @@ namespace Microsoft.AspNetCore.Certificates.Generation private const string MacOSSystemKeyChain = "/Library/Keychains/System.keychain"; private static readonly string MacOSUserKeyChain = Environment.GetEnvironmentVariable("HOME") + "/Library/Keychains/login.keychain-db"; private const string MacOSFindCertificateCommandLine = "security"; -#if NETCOREAPP2_0 || NETCOREAPP2_1 || NETCOREAPP2_2 +#if NETCOREAPP2_0 || NETCOREAPP2_1 || NETCOREAPP2_2 || NETCOREAPP3_0 private static readonly string MacOSFindCertificateCommandLineArgumentsFormat = "find-certificate -c {0} -a -Z -p " + MacOSSystemKeyChain; #endif private const string MacOSFindCertificateOutputRegex = "SHA-1 hash: ([0-9A-Z]+)"; @@ -41,7 +41,7 @@ namespace Microsoft.AspNetCore.Certificates.Generation private const string MacOSDeleteCertificateCommandLine = "sudo"; private const string MacOSDeleteCertificateCommandLineArgumentsFormat = "security delete-certificate -Z {0} {1}"; private const string MacOSTrustCertificateCommandLine = "sudo"; -#if NETCOREAPP2_0 || NETCOREAPP2_1 || NETCOREAPP2_2 +#if NETCOREAPP2_0 || NETCOREAPP2_1 || NETCOREAPP2_2 || NETCOREAPP3_0 private static readonly string MacOSTrustCertificateCommandLineArguments = "security add-trusted-cert -d -r trustRoot -k " + MacOSSystemKeyChain + " "; #endif private const int UserCancelledErrorCode = 1223; @@ -153,7 +153,7 @@ namespace Microsoft.AspNetCore.Certificates.Generation } } -#if NETCOREAPP2_0 || NETCOREAPP2_1 || NETCOREAPP2_2 +#if NETCOREAPP2_0 || NETCOREAPP2_1 || NETCOREAPP2_2 || NETCOREAPP3_0 public X509Certificate2 CreateAspNetCoreHttpsDevelopmentCertificate(DateTimeOffset notBefore, DateTimeOffset notAfter, string subjectOverride, DiagnosticInformation diagnostics = null) { @@ -948,4 +948,4 @@ namespace Microsoft.AspNetCore.Certificates.Generation } } } -} \ No newline at end of file +} diff --git a/src/Shared/CertificateGeneration/EnsureCertificateResult.cs b/src/Shared/CertificateGeneration/EnsureCertificateResult.cs index 84c495249d..2106297fae 100644 --- a/src/Shared/CertificateGeneration/EnsureCertificateResult.cs +++ b/src/Shared/CertificateGeneration/EnsureCertificateResult.cs @@ -1,7 +1,7 @@ // 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. -#if NETCOREAPP2_0 || NETCOREAPP2_1 || NETCOREAPP2_2 +#if NETCOREAPP2_0 || NETCOREAPP2_1 || NETCOREAPP2_2 || NETCOREAPP3_0 namespace Microsoft.AspNetCore.Certificates.Generation { @@ -17,4 +17,4 @@ namespace Microsoft.AspNetCore.Certificates.Generation } } -#endif \ No newline at end of file +#endif diff --git a/src/Shared/test/Shared.Tests/CertificateManagerTests.cs b/src/Shared/test/Shared.Tests/CertificateManagerTests.cs index cd314383c9..0979eba21b 100644 --- a/src/Shared/test/Shared.Tests/CertificateManagerTests.cs +++ b/src/Shared/test/Shared.Tests/CertificateManagerTests.cs @@ -1,7 +1,7 @@ // 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. -#if NETCOREAPP2_2 +#if NETCOREAPP3_0 using System; using System.IO; @@ -285,4 +285,4 @@ namespace Microsoft.AspNetCore.Certificates.Generation.Tests } } -#endif \ No newline at end of file +#endif diff --git a/src/Shared/test/Shared.Tests/DotNetMuxerTests.cs b/src/Shared/test/Shared.Tests/DotNetMuxerTests.cs index 92e06a8f70..2f412e292e 100644 --- a/src/Shared/test/Shared.Tests/DotNetMuxerTests.cs +++ b/src/Shared/test/Shared.Tests/DotNetMuxerTests.cs @@ -1,7 +1,7 @@ // 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. -#if NETCOREAPP2_2 +#if NETCOREAPP3_0 using System.IO; using System.Runtime.InteropServices; using Xunit; diff --git a/src/Testing/test/ConditionalFactTest.cs b/src/Testing/test/ConditionalFactTest.cs index a04eb1731d..e136e78247 100644 --- a/src/Testing/test/ConditionalFactTest.cs +++ b/src/Testing/test/ConditionalFactTest.cs @@ -29,7 +29,7 @@ namespace Microsoft.AspNetCore.Testing Assert.True(false, "This test should always be skipped."); } -#if NETCOREAPP2_2 +#if NETCOREAPP3_0 [ConditionalFact] [FrameworkSkipCondition(RuntimeFrameworks.CLR)] public void ThisTestMustRunOnCoreCLR() @@ -57,4 +57,4 @@ namespace Microsoft.AspNetCore.Testing } } } -} \ No newline at end of file +} diff --git a/src/Testing/test/ConditionalTheoryTest.cs b/src/Testing/test/ConditionalTheoryTest.cs index 1181f1365a..6950dd412f 100644 --- a/src/Testing/test/ConditionalTheoryTest.cs +++ b/src/Testing/test/ConditionalTheoryTest.cs @@ -81,7 +81,7 @@ namespace Microsoft.AspNetCore.Testing Assert.True(true); } -#if NETCOREAPP2_2 +#if NETCOREAPP3_0 [ConditionalTheory] [FrameworkSkipCondition(RuntimeFrameworks.CLR)] [MemberData(nameof(GetInts))] From 360b19ca09c7d4cbdc2d6a551ea0b60dc747e9eb Mon Sep 17 00:00:00 2001 From: Nate McMaster Date: Mon, 5 Nov 2018 13:12:14 -0800 Subject: [PATCH 02/52] Merge branch 'release/2.2' \n\nCommit migrated from https://github.com/dotnet/extensions/commit/04bac2f79c39eecba1ea29d035dc9a40e37167d6 --- .../Microsoft.Extensions.FileProviders.Embedded.csproj | 9 ++++++++- .../Microsoft.Extensions.FileProviders.Embedded.nuspec | 4 ++-- .../Microsoft.Extensions.FileProviders.Embedded.props | 2 +- ...ensions.FileProviders.Embedded.Manifest.Task.csproj | 10 ++-------- 4 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.csproj b/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.csproj index ec2c10b569..55284793fb 100644 --- a/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.csproj +++ b/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.csproj @@ -14,7 +14,7 @@ - + @@ -41,10 +41,17 @@ OutputDocumentation=@(DocumentationProjectOutputGroupOutput); +<<<<<<< HEAD:src/FS.Embedded/FS.Embedded.csproj + TaskAssemblyNetStandard=..\FS.Embedded.Manifest.Task\bin\$(Configuration)\netstandard2.0\$(AssemblyName).Manifest.Task.dll; + TaskSymbolNetStandard=..\FS.Embedded.Manifest.Task\bin\$(Configuration)\netstandard2.0\$(AssemblyName).Manifest.Task.pdb; + TaskAssemblyNet461=..\FS.Embedded.Manifest.Task\bin\$(Configuration)\net461\$(AssemblyName).Manifest.Task.dll; + TaskSymbolNet461=..\FS.Embedded.Manifest.Task\bin\$(Configuration)\net461\$(AssemblyName).Manifest.Task.pdb; +======= TaskAssemblyNetStandard=..\..\Manifest.MSBuildTask\src\bin\$(Configuration)\netstandard1.5\$(AssemblyName).Manifest.Task.dll; TaskSymbolNetStandard=..\..\Manifest.MSBuildTask\src\bin\$(Configuration)\netstandard1.5\$(AssemblyName).Manifest.Task.pdb; TaskAssemblyNet461=..\..\Manifest.MSBuildTask\src\bin\$(Configuration)\net461\$(AssemblyName).Manifest.Task.dll; TaskSymbolNet461=..\..\Manifest.MSBuildTask\src\bin\$(Configuration)\net461\$(AssemblyName).Manifest.Task.pdb; +>>>>>>> m22:src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.csproj diff --git a/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.nuspec b/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.nuspec index 0cc5ed823a..af7feca8fd 100644 --- a/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.nuspec +++ b/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.nuspec @@ -25,8 +25,8 @@ - - + + diff --git a/src/FileProviders/Embedded/src/build/netstandard2.0/Microsoft.Extensions.FileProviders.Embedded.props b/src/FileProviders/Embedded/src/build/netstandard2.0/Microsoft.Extensions.FileProviders.Embedded.props index e913e17321..66ea7b5c22 100644 --- a/src/FileProviders/Embedded/src/build/netstandard2.0/Microsoft.Extensions.FileProviders.Embedded.props +++ b/src/FileProviders/Embedded/src/build/netstandard2.0/Microsoft.Extensions.FileProviders.Embedded.props @@ -5,7 +5,7 @@ - <_FileProviderTaskFolder Condition="'$(MSBuildRuntimeType)' == 'Core'">netstandard1.5 + <_FileProviderTaskFolder Condition="'$(MSBuildRuntimeType)' == 'Core'">netstandard2.0 <_FileProviderTaskFolder Condition="'$(MSBuildRuntimeType)' != 'Core'">net461 <_FileProviderTaskAssembly>$(MSBuildThisFileDirectory)..\..\tasks\$(_FileProviderTaskFolder)\Microsoft.Extensions.FileProviders.Embedded.Manifest.Task.dll diff --git a/src/FileProviders/Manifest.MSBuildTask/src/Microsoft.Extensions.FileProviders.Embedded.Manifest.Task.csproj b/src/FileProviders/Manifest.MSBuildTask/src/Microsoft.Extensions.FileProviders.Embedded.Manifest.Task.csproj index 70784650de..018cc98a8e 100644 --- a/src/FileProviders/Manifest.MSBuildTask/src/Microsoft.Extensions.FileProviders.Embedded.Manifest.Task.csproj +++ b/src/FileProviders/Manifest.MSBuildTask/src/Microsoft.Extensions.FileProviders.Embedded.Manifest.Task.csproj @@ -3,23 +3,17 @@ 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. - netstandard1.5;net461 + netstandard2.0 false false false false - + - - - - - - From 83c88bd6bdede632ad38f753bf1e9a30b40ed7dc Mon Sep 17 00:00:00 2001 From: Gert Driesen Date: Tue, 13 Nov 2018 19:03:55 +0100 Subject: [PATCH 03/52] Eliminate use of method groups in PropertyHelper (dotnet/extensions#465) * Eliminate use of method groups, and use for loop to enumerate elements of array. Improves performance and reduces allocations. * PR feedback: Remove extra parentheses, and undo change from foreach to for loop. * PR feedback Revert change from for to foreach. * var-ify TryGetValue out parameter. \n\nCommit migrated from https://github.com/dotnet/extensions/commit/08adb5c2ca594683a43dfc1e6178fffc5e336456 --- src/Shared/PropertyHelper/PropertyHelper.cs | 22 +++++++++------------ 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/src/Shared/PropertyHelper/PropertyHelper.cs b/src/Shared/PropertyHelper/PropertyHelper.cs index f6aad151e5..f3641e03dc 100644 --- a/src/Shared/PropertyHelper/PropertyHelper.cs +++ b/src/Shared/PropertyHelper/PropertyHelper.cs @@ -7,7 +7,6 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; -using System.Runtime.CompilerServices; namespace Microsoft.Extensions.Internal { @@ -145,7 +144,7 @@ namespace Microsoft.Extensions.Internal /// public static PropertyHelper[] GetProperties(Type type) { - return GetProperties(type, CreateInstance, PropertiesCache); + return GetProperties(type, p => CreateInstance(p), PropertiesCache); } /// @@ -164,7 +163,7 @@ namespace Microsoft.Extensions.Internal /// public static PropertyHelper[] GetVisibleProperties(TypeInfo typeInfo) { - return GetVisibleProperties(typeInfo.AsType(), CreateInstance, PropertiesCache, VisiblePropertiesCache); + return GetVisibleProperties(typeInfo.AsType(), p => CreateInstance(p), PropertiesCache, VisiblePropertiesCache); } /// @@ -183,7 +182,7 @@ namespace Microsoft.Extensions.Internal /// public static PropertyHelper[] GetVisibleProperties(Type type) { - return GetVisibleProperties(type, CreateInstance, PropertiesCache, VisiblePropertiesCache); + return GetVisibleProperties(type, p => CreateInstance(p), PropertiesCache, VisiblePropertiesCache); } /// @@ -420,8 +419,7 @@ namespace Microsoft.Extensions.Internal ConcurrentDictionary allPropertiesCache, ConcurrentDictionary visiblePropertiesCache) { - PropertyHelper[] result; - if (visiblePropertiesCache.TryGetValue(type, out result)) + if (visiblePropertiesCache.TryGetValue(type, out var result)) { return result; } @@ -497,18 +495,17 @@ namespace Microsoft.Extensions.Internal // part of the sequence of properties returned by this method. type = Nullable.GetUnderlyingType(type) ?? type; - PropertyHelper[] helpers; - if (!cache.TryGetValue(type, out helpers)) + if (!cache.TryGetValue(type, out var helpers)) { // We avoid loading indexed properties using the Where statement. - var properties = type.GetRuntimeProperties().Where(IsInterestingProperty); + var properties = type.GetRuntimeProperties().Where(p => IsInterestingProperty(p)); var typeInfo = type.GetTypeInfo(); if (typeInfo.IsInterface) { // Reflection does not return information about inherited properties on the interface itself. properties = properties.Concat(typeInfo.ImplementedInterfaces.SelectMany( - interfaceType => interfaceType.GetRuntimeProperties().Where(IsInterestingProperty))); + interfaceType => interfaceType.GetRuntimeProperties().Where(p => IsInterestingProperty(p)))); } helpers = properties.Select(p => createPropertyHelper(p)).ToArray(); @@ -518,17 +515,16 @@ namespace Microsoft.Extensions.Internal return helpers; } - private static bool IsInterestingProperty(PropertyInfo property) { // For improving application startup time, do not use GetIndexParameters() api early in this check as it // creates a copy of parameter array and also we would like to check for the presence of a get method // and short circuit asap. - return + return property.GetMethod != null && property.GetMethod.IsPublic && !property.GetMethod.IsStatic && - + // PropertyHelper can't work with ref structs. !IsRefStructProperty(property) && From ea14c9c86519396d52c51cfb5b8e8aeb7dacb0c1 Mon Sep 17 00:00:00 2001 From: Javier Calvarro Nelson Date: Tue, 13 Nov 2018 10:11:21 -0800 Subject: [PATCH 04/52] Change async void tests to async Task (dotnet/extensions#488) \n\nCommit migrated from https://github.com/dotnet/extensions/commit/d2f4c6a2ed30be5fae6ec166bfb7a3073c9f1d17 --- .../Shared.Tests/ObjectMethodExecutorTest.cs | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/Shared/test/Shared.Tests/ObjectMethodExecutorTest.cs b/src/Shared/test/Shared.Tests/ObjectMethodExecutorTest.cs index 1c26ef1de1..fb9f82aad9 100644 --- a/src/Shared/test/Shared.Tests/ObjectMethodExecutorTest.cs +++ b/src/Shared/test/Shared.Tests/ObjectMethodExecutorTest.cs @@ -155,7 +155,7 @@ namespace Microsoft.Extensions.Internal } [Fact] - public async void TargetMethodReturningCustomAwaitableOfReferenceType_CanInvokeViaExecute() + public async Task TargetMethodReturningCustomAwaitableOfReferenceType_CanInvokeViaExecute() { // Arrange var executor = GetExecutorForMethod("CustomAwaitableOfReferenceTypeAsync"); @@ -171,7 +171,7 @@ namespace Microsoft.Extensions.Internal } [Fact] - public async void TargetMethodReturningCustomAwaitableOfValueType_CanInvokeViaExecute() + public async Task TargetMethodReturningCustomAwaitableOfValueType_CanInvokeViaExecute() { // Arrange var executor = GetExecutorForMethod("CustomAwaitableOfValueTypeAsync"); @@ -186,7 +186,7 @@ namespace Microsoft.Extensions.Internal } [Fact] - public async void TargetMethodReturningCustomAwaitableOfReferenceType_CanInvokeViaExecuteAsync() + public async Task TargetMethodReturningCustomAwaitableOfReferenceType_CanInvokeViaExecuteAsync() { // Arrange var executor = GetExecutorForMethod("CustomAwaitableOfReferenceTypeAsync"); @@ -203,7 +203,7 @@ namespace Microsoft.Extensions.Internal } [Fact] - public async void TargetMethodReturningCustomAwaitableOfValueType_CanInvokeViaExecuteAsync() + public async Task TargetMethodReturningCustomAwaitableOfValueType_CanInvokeViaExecuteAsync() { // Arrange var executor = GetExecutorForMethod("CustomAwaitableOfValueTypeAsync"); @@ -220,7 +220,7 @@ namespace Microsoft.Extensions.Internal } [Fact] - public async void TargetMethodReturningAwaitableOfVoidType_CanInvokeViaExecuteAsync() + public async Task TargetMethodReturningAwaitableOfVoidType_CanInvokeViaExecuteAsync() { // Arrange var executor = GetExecutorForMethod("VoidValueMethodAsync"); @@ -235,7 +235,7 @@ namespace Microsoft.Extensions.Internal } [Fact] - public async void TargetMethodReturningAwaitableWithICriticalNotifyCompletion_UsesUnsafeOnCompleted() + public async Task TargetMethodReturningAwaitableWithICriticalNotifyCompletion_UsesUnsafeOnCompleted() { // Arrange var executor = GetExecutorForMethod("CustomAwaitableWithICriticalNotifyCompletion"); @@ -250,7 +250,7 @@ namespace Microsoft.Extensions.Internal } [Fact] - public async void TargetMethodReturningAwaitableWithoutICriticalNotifyCompletion_UsesOnCompleted() + public async Task TargetMethodReturningAwaitableWithoutICriticalNotifyCompletion_UsesOnCompleted() { // Arrange var executor = GetExecutorForMethod("CustomAwaitableWithoutICriticalNotifyCompletion"); @@ -265,7 +265,7 @@ namespace Microsoft.Extensions.Internal } [Fact] - public async void TargetMethodReturningValueTaskOfValueType_CanBeInvokedViaExecute() + public async Task TargetMethodReturningValueTaskOfValueType_CanBeInvokedViaExecute() { // Arrange var executor = GetExecutorForMethod("ValueTaskOfValueType"); @@ -280,7 +280,7 @@ namespace Microsoft.Extensions.Internal } [Fact] - public async void TargetMethodReturningValueTaskOfReferenceType_CanBeInvokedViaExecute() + public async Task TargetMethodReturningValueTaskOfReferenceType_CanBeInvokedViaExecute() { // Arrange var executor = GetExecutorForMethod("ValueTaskOfReferenceType"); @@ -295,7 +295,7 @@ namespace Microsoft.Extensions.Internal } [Fact] - public async void TargetMethodReturningValueTaskOfValueType_CanBeInvokedViaExecuteAsync() + public async Task TargetMethodReturningValueTaskOfValueType_CanBeInvokedViaExecuteAsync() { // Arrange var executor = GetExecutorForMethod("ValueTaskOfValueType"); @@ -311,7 +311,7 @@ namespace Microsoft.Extensions.Internal } [Fact] - public async void TargetMethodReturningValueTaskOfReferenceType_CanBeInvokedViaExecuteAsync() + public async Task TargetMethodReturningValueTaskOfReferenceType_CanBeInvokedViaExecuteAsync() { // Arrange var executor = GetExecutorForMethod("ValueTaskOfReferenceType"); @@ -326,7 +326,7 @@ namespace Microsoft.Extensions.Internal } [Fact] - public async void TargetMethodReturningFSharpAsync_CanBeInvokedViaExecute() + public async Task TargetMethodReturningFSharpAsync_CanBeInvokedViaExecute() { // Arrange var executor = GetExecutorForMethod("FSharpAsyncMethod"); @@ -344,7 +344,7 @@ namespace Microsoft.Extensions.Internal } [Fact] - public async void TargetMethodReturningFailingFSharpAsync_CanBeInvokedViaExecute() + public async Task TargetMethodReturningFailingFSharpAsync_CanBeInvokedViaExecute() { // Arrange var executor = GetExecutorForMethod("FSharpAsyncFailureMethod"); @@ -365,7 +365,7 @@ namespace Microsoft.Extensions.Internal } [Fact] - public async void TargetMethodReturningFSharpAsync_CanBeInvokedViaExecuteAsync() + public async Task TargetMethodReturningFSharpAsync_CanBeInvokedViaExecuteAsync() { // Arrange var executor = GetExecutorForMethod("FSharpAsyncMethod"); @@ -380,7 +380,7 @@ namespace Microsoft.Extensions.Internal } [Fact] - public async void TargetMethodReturningFailingFSharpAsync_CanBeInvokedViaExecuteAsync() + public async Task TargetMethodReturningFailingFSharpAsync_CanBeInvokedViaExecuteAsync() { // Arrange var executor = GetExecutorForMethod("FSharpAsyncFailureMethod"); From 6c5d291b10806decdd8d70d8648012471cda47ca Mon Sep 17 00:00:00 2001 From: Pavel Krymets Date: Fri, 16 Nov 2018 08:33:57 -0800 Subject: [PATCH 05/52] Remove internal types from Logging and Abstractions (dotnet/extensions#513) \n\nCommit migrated from https://github.com/dotnet/extensions/commit/55518d79834d3319c91f40b449d028338b129ed6 --- src/Shared/BenchmarkRunner/DefaultCoreValidationConfig.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/Shared/BenchmarkRunner/DefaultCoreValidationConfig.cs b/src/Shared/BenchmarkRunner/DefaultCoreValidationConfig.cs index 95fc725564..5a90929cff 100644 --- a/src/Shared/BenchmarkRunner/DefaultCoreValidationConfig.cs +++ b/src/Shared/BenchmarkRunner/DefaultCoreValidationConfig.cs @@ -1,11 +1,6 @@ // 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 BenchmarkDotNet.Attributes; -using BenchmarkDotNet.Running; using BenchmarkDotNet.Configs; using BenchmarkDotNet.Jobs; using BenchmarkDotNet.Loggers; From ebb1165b14b0d8f0a6151d8d323f231529e6f67e Mon Sep 17 00:00:00 2001 From: Pavel Krymets Date: Fri, 16 Nov 2018 13:57:43 -0800 Subject: [PATCH 06/52] Handle nullable enum default values (dotnet/extensions#531) \n\nCommit migrated from https://github.com/dotnet/extensions/commit/33f839d585ae7664195008d8c9c5e317d5897510 --- .../ParameterDefaultValue.cs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/Shared/ParameterDefaultValue/ParameterDefaultValue.cs b/src/Shared/ParameterDefaultValue/ParameterDefaultValue.cs index a71bad37b1..dc635bb789 100644 --- a/src/Shared/ParameterDefaultValue/ParameterDefaultValue.cs +++ b/src/Shared/ParameterDefaultValue/ParameterDefaultValue.cs @@ -8,6 +8,8 @@ namespace Microsoft.Extensions.Internal { internal class ParameterDefaultValue { + private static readonly Type _nullable = typeof(Nullable<>); + public static bool TryGetDefaultValue(ParameterInfo parameter, out object defaultValue) { bool hasDefaultValue; @@ -39,6 +41,19 @@ namespace Microsoft.Extensions.Internal { defaultValue = Activator.CreateInstance(parameter.ParameterType); } + + // Handle nullable enums + if (defaultValue != null && + parameter.ParameterType.IsGenericType && + parameter.ParameterType.GetGenericTypeDefinition() == _nullable + ) + { + var underlyingType = Nullable.GetUnderlyingType(parameter.ParameterType); + if (underlyingType != null && underlyingType.IsEnum) + { + defaultValue = Enum.ToObject(underlyingType, defaultValue); + } + } } return hasDefaultValue; From e7c4b678f99ac20ee46c9fef0fb930d0d2810e81 Mon Sep 17 00:00:00 2001 From: Nate McMaster Date: Tue, 4 Dec 2018 10:40:35 -0800 Subject: [PATCH 07/52] Reorganize source code in preparation to move into aspnet/Extensions Prior to reorganization, this source code was found in https://github.com/aspnet/Localization/tree/dotnet/extensions@60a3d57a3e8b8b6d69232815a9596b5a495a7d0b \n\nCommit migrated from https://github.com/dotnet/extensions/commit/b885414e8a44e777a9dd1170416bb143c24ed524 --- .../test/Microsoft.Extensions.Localization.Tests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Localization/Localization/test/Microsoft.Extensions.Localization.Tests.csproj b/src/Localization/Localization/test/Microsoft.Extensions.Localization.Tests.csproj index 4628ff7c0f..30f071feae 100644 --- a/src/Localization/Localization/test/Microsoft.Extensions.Localization.Tests.csproj +++ b/src/Localization/Localization/test/Microsoft.Extensions.Localization.Tests.csproj @@ -1,7 +1,7 @@  - $(StandardTestTfms) + netcoreapp3.0;net461 From 57cd32129b52caeb329478c67c9c973f5d94efa1 Mon Sep 17 00:00:00 2001 From: Arthur Vickers Date: Fri, 30 Nov 2018 14:43:22 -0800 Subject: [PATCH 08/52] Added tests that can be implemented for any provider to ensure consistency Partly to allow me to get a better understanding of the code, partly so we can have a better idea that all the providers work in a consistent manner. Part of https://github.com/aspnet/Configuration/issues/559 I will file issues for things found. \n\nCommit migrated from https://github.com/dotnet/extensions/commit/1878d16ecd5165849cd6a78ce708e499b71477ca --- .../ConfigurationProviderCommandLineTest.cs | 43 ++ .../test/ConfigurationProviderTestBase.cs | 552 ++++++++++++++++++ .../test/KeyPerFileTests.cs | 4 +- ...ions.Configuration.KeyPerFile.Tests.csproj | 4 + 4 files changed, 602 insertions(+), 1 deletion(-) create mode 100644 src/Configuration.KeyPerFile/test/ConfigurationProviderCommandLineTest.cs create mode 100644 src/Configuration.KeyPerFile/test/ConfigurationProviderTestBase.cs diff --git a/src/Configuration.KeyPerFile/test/ConfigurationProviderCommandLineTest.cs b/src/Configuration.KeyPerFile/test/ConfigurationProviderCommandLineTest.cs new file mode 100644 index 0000000000..066aecf337 --- /dev/null +++ b/src/Configuration.KeyPerFile/test/ConfigurationProviderCommandLineTest.cs @@ -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(); + SectionToTestFiles(testFiles, "", testConfig); + + var provider = new KeyPerFileConfigurationProvider( + new KeyPerFileConfigurationSource + { + Optional = true, + FileProvider = new TestFileProvider(testFiles.ToArray()) + }); + + return (provider, () => { }); + } + + private void SectionToTestFiles(List 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); + } + } + } +} diff --git a/src/Configuration.KeyPerFile/test/ConfigurationProviderTestBase.cs b/src/Configuration.KeyPerFile/test/ConfigurationProviderTestBase.cs new file mode 100644 index 0000000000..c18bea8d28 --- /dev/null +++ b/src/Configuration.KeyPerFile/test/ConfigurationProviderTestBase.cs @@ -0,0 +1,552 @@ +// 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() + { + AssertConfig(BuildConfigRoot(LoadThroughProvider(TestSection.TestConfig))); + } + + [Fact] + public virtual void Combine_after_other_provider() + { + AssertConfig( + BuildConfigRoot( + LoadUsingMemoryProvider(TestSection.MissingSection2Config), + LoadThroughProvider(TestSection.MissingSection4Config))); + } + + [Fact] + public virtual void Combine_before_other_provider() + { + AssertConfig( + BuildConfigRoot( + LoadThroughProvider(TestSection.MissingSection2Config), + LoadUsingMemoryProvider(TestSection.MissingSection4Config))); + } + + [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(); + + 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 AssertConfig(IConfigurationRoot config) + { + 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 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); + } + + 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>(); + SectionToValues(testConfig, "", values); + + return (new MemoryConfigurationProvider( + new MemoryConfigurationSource + { + InitialData = values + }), + () => { }); + } + + protected static void SectionToValues( + TestSection section, + string sectionName, + IList> values) + { + foreach (var tuple in section.Values.SelectMany(e => e.Value.Expand(e.Key))) + { + values.Add(new KeyValuePair(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 (AsString != 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 MissingSection2Config { get; } + = new TestSection + { + Values = new[] { ("Key1", (TestKeyValue)"Value1") }, + Sections = new[] + { + ("Section1", new TestSection + { + Values = new[] {("Key2", (TestKeyValue)"Value12")}, + }), + ("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")}, + }) + } + }), + + } + }; + } + } +} diff --git a/src/Configuration.KeyPerFile/test/KeyPerFileTests.cs b/src/Configuration.KeyPerFile/test/KeyPerFileTests.cs index d409c0eab0..fac0e839e8 100644 --- a/src/Configuration.KeyPerFile/test/KeyPerFileTests.cs +++ b/src/Configuration.KeyPerFile/test/KeyPerFileTests.cs @@ -1,8 +1,10 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + using System; using System.Collections; using System.Collections.Generic; using System.IO; -using System.Linq; using System.Text; using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.Primitives; diff --git a/src/Configuration.KeyPerFile/test/Microsoft.Extensions.Configuration.KeyPerFile.Tests.csproj b/src/Configuration.KeyPerFile/test/Microsoft.Extensions.Configuration.KeyPerFile.Tests.csproj index 634056a345..e78f4fd190 100644 --- a/src/Configuration.KeyPerFile/test/Microsoft.Extensions.Configuration.KeyPerFile.Tests.csproj +++ b/src/Configuration.KeyPerFile/test/Microsoft.Extensions.Configuration.KeyPerFile.Tests.csproj @@ -4,6 +4,10 @@ $(StandardTestTfms) + + + + From 317b4bb4eb3284f9f7051a73bbb7f70997572195 Mon Sep 17 00:00:00 2001 From: Nate McMaster Date: Thu, 13 Dec 2018 09:49:07 -0800 Subject: [PATCH 09/52] Create README.md for the localization folder (dotnet/extensions#683) \n\nCommit migrated from https://github.com/dotnet/extensions/commit/c841b4f9e0ad4b3724136dcfde35a8b1ad71b537 --- src/Localization/README.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 src/Localization/README.md diff --git a/src/Localization/README.md b/src/Localization/README.md new file mode 100644 index 0000000000..dc6894b6e0 --- /dev/null +++ b/src/Localization/README.md @@ -0,0 +1,6 @@ +Localization +============ + +These projects provide abstractions for localizing resources in .NET applications. + +The ASP.NET Core implementation of localization can be found in https://github.com/aspnet/AspNetCore/tree/master/src/Middleware/Localization. From 6d45c068073e7826fafe89e4b6d447763e41d14f Mon Sep 17 00:00:00 2001 From: Arthur Vickers Date: Fri, 7 Dec 2018 11:58:10 -0800 Subject: [PATCH 10/52] Method to get source for each configuration element Fixes dotnet/extensions#609 by allowing the configuration paths to be associated with values to help with debugging. \n\nCommit migrated from https://github.com/dotnet/extensions/commit/d7f8e253d414ce6053ad59b6f974621d5620c0da --- .../src/KeyPerFileConfigurationProvider.cs | 16 +- .../test/ConfigurationProviderTestBase.cs | 325 ++++++++++++++---- .../test/KeyPerFileTests.cs | 81 +---- 3 files changed, 295 insertions(+), 127 deletions(-) diff --git a/src/Configuration.KeyPerFile/src/KeyPerFileConfigurationProvider.cs b/src/Configuration.KeyPerFile/src/KeyPerFileConfigurationProvider.cs index 4748895744..13541110e6 100644 --- a/src/Configuration.KeyPerFile/src/KeyPerFileConfigurationProvider.cs +++ b/src/Configuration.KeyPerFile/src/KeyPerFileConfigurationProvider.cs @@ -39,10 +39,8 @@ namespace Microsoft.Extensions.Configuration.KeyPerFile { return; } - else - { - throw new DirectoryNotFoundException("A non-null file provider for the directory is required when this source is not optional."); - } + + throw new DirectoryNotFoundException("A non-null file provider for the directory is required when this source is not optional."); } var directory = Source.FileProvider.GetDirectoryContents("/"); @@ -68,5 +66,15 @@ namespace Microsoft.Extensions.Configuration.KeyPerFile } } } + + private string GetDirectoryName() + => Source.FileProvider?.GetFileInfo("/")?.PhysicalPath ?? ""; + + /// + /// Generates a string representing this provider name and relevant details. + /// + /// The configuration name. + public override string ToString() + => $"{GetType().Name} for files in '{GetDirectoryName()}' ({(Source.Optional ? "Optional" : "Required")})"; } } diff --git a/src/Configuration.KeyPerFile/test/ConfigurationProviderTestBase.cs b/src/Configuration.KeyPerFile/test/ConfigurationProviderTestBase.cs index c18bea8d28..4609ee2560 100644 --- a/src/Configuration.KeyPerFile/test/ConfigurationProviderTestBase.cs +++ b/src/Configuration.KeyPerFile/test/ConfigurationProviderTestBase.cs @@ -14,7 +14,39 @@ namespace Microsoft.Extensions.Configuration.Test [Fact] public virtual void Load_from_single_provider() { - AssertConfig(BuildConfigRoot(LoadThroughProvider(TestSection.TestConfig))); + 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] @@ -22,8 +54,13 @@ namespace Microsoft.Extensions.Configuration.Test { AssertConfig( BuildConfigRoot( - LoadUsingMemoryProvider(TestSection.MissingSection2Config), + LoadUsingMemoryProvider(TestSection.MissingSection2ValuesConfig), LoadThroughProvider(TestSection.MissingSection4Config))); + + AssertConfig( + BuildConfigRoot( + LoadUsingMemoryProvider(TestSection.MissingSection4Config), + LoadThroughProvider(TestSection.MissingSection2ValuesConfig))); } [Fact] @@ -31,8 +68,13 @@ namespace Microsoft.Extensions.Configuration.Test { AssertConfig( BuildConfigRoot( - LoadThroughProvider(TestSection.MissingSection2Config), + LoadThroughProvider(TestSection.MissingSection2ValuesConfig), LoadUsingMemoryProvider(TestSection.MissingSection4Config))); + + AssertConfig( + BuildConfigRoot( + LoadThroughProvider(TestSection.MissingSection4Config), + LoadUsingMemoryProvider(TestSection.MissingSection2ValuesConfig))); } [Fact] @@ -128,48 +170,83 @@ namespace Microsoft.Extensions.Configuration.Test public string Key4 { get; set; } } - protected virtual void AssertConfig(IConfigurationRoot config) + protected virtual void AssertDebugView( + IConfigurationRoot config, + string expected) { - 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); + 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(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(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(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(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(value344, section4["Key4"], StringComparer.InvariantCultureIgnoreCase); Assert.Equal("Section3:Section4", section4.Path, StringComparer.InvariantCultureIgnoreCase); Assert.Null(section4.Value); @@ -179,7 +256,7 @@ namespace Microsoft.Extensions.Configuration.Test 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(value1, sections[0].Value, StringComparer.InvariantCultureIgnoreCase); Assert.Equal("Section1", sections[1].Key, StringComparer.InvariantCultureIgnoreCase); Assert.Equal("Section1", sections[1].Path, StringComparer.InvariantCultureIgnoreCase); @@ -195,11 +272,55 @@ namespace Microsoft.Extensions.Configuration.Test 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(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); @@ -272,7 +393,7 @@ namespace Microsoft.Extensions.Configuration.Test public IEnumerable<(string Key, string Value)> Expand(string key) { - if (AsString != null) + if (AsArray == null) { yield return (key, AsString); } @@ -310,7 +431,7 @@ namespace Microsoft.Extensions.Configuration.Test Values = new[] { ("Key3", (TestKeyValue)"Value123"), - ("Key3a", (TestKeyValue)new[] {"ArrayValue0", "ArrayValue1", "ArrayValue2"}), + ("Key3a", (TestKeyValue)new[] {"ArrayValue0", "ArrayValue1", "ArrayValue2"}) }, }) } @@ -321,11 +442,10 @@ namespace Microsoft.Extensions.Configuration.Test { ("Section4", new TestSection { - Values = new[] {("Key4", (TestKeyValue)"Value344")}, + Values = new[] {("Key4", (TestKeyValue)"Value344")} }) } - }), - + }) } }; @@ -345,7 +465,7 @@ namespace Microsoft.Extensions.Configuration.Test Values = new[] { ("Key3", (TestKeyValue)"-----"), - ("Key3a", (TestKeyValue)new[] {"-----------", "-----------", "-----------"}), + ("Key3a", (TestKeyValue)new[] {"-----------", "-----------", "-----------"}) }, }) } @@ -356,15 +476,14 @@ namespace Microsoft.Extensions.Configuration.Test { ("Section4", new TestSection { - Values = new[] {("Key4", (TestKeyValue)"--------")}, + Values = new[] {("Key4", (TestKeyValue)"--------")} }) } - }), - + }) } }; - public static TestSection MissingSection2Config { get; } + public static TestSection MissingSection2ValuesConfig { get; } = new TestSection { Values = new[] { ("Key1", (TestKeyValue)"Value1") }, @@ -373,6 +492,16 @@ namespace Microsoft.Extensions.Configuration.Test ("Section1", new TestSection { Values = new[] {("Key2", (TestKeyValue)"Value12")}, + Sections = new[] + { + ("Section2", new TestSection + { + Values = new[] + { + ("Key3a", (TestKeyValue)new[] {"ArrayValue0"}) + }, + }) + } }), ("Section3", new TestSection { @@ -380,11 +509,10 @@ namespace Microsoft.Extensions.Configuration.Test { ("Section4", new TestSection { - Values = new[] {("Key4", (TestKeyValue)"Value344")}, + Values = new[] {("Key4", (TestKeyValue)"Value344")} }) } - }), - + }) } }; @@ -405,15 +533,12 @@ namespace Microsoft.Extensions.Configuration.Test Values = new[] { ("Key3", (TestKeyValue)"Value123"), - ("Key3a", (TestKeyValue)new[] {"ArrayValue0", "ArrayValue1", "ArrayValue2"}), + ("Key3a", (TestKeyValue)new[] {"ArrayValue0", "ArrayValue1", "ArrayValue2"}) }, }) } }), - ("Section3", new TestSection - { - }), - + ("Section3", new TestSection()) } }; @@ -433,7 +558,7 @@ namespace Microsoft.Extensions.Configuration.Test Values = new[] { ("KeY3", (TestKeyValue)"Value123"), - ("KeY3a", (TestKeyValue)new[] {"ArrayValue0", "ArrayValue1", "ArrayValue2"}), + ("KeY3a", (TestKeyValue)new[] {"ArrayValue0", "ArrayValue1", "ArrayValue2"}) }, }) } @@ -444,11 +569,10 @@ namespace Microsoft.Extensions.Configuration.Test { ("SectioN4", new TestSection { - Values = new[] {("KeY4", (TestKeyValue)"Value344")}, + Values = new[] {("KeY4", (TestKeyValue)"Value344")} }) } - }), - + }) } }; @@ -472,7 +596,7 @@ namespace Microsoft.Extensions.Configuration.Test Values = new[] { ("Key3", (TestKeyValue)"Value123"), - ("Key3a", (TestKeyValue)new[] {"ArrayValue0", "ArrayValue1", "ArrayValue2"}), + ("Key3a", (TestKeyValue)new[] {"ArrayValue0", "ArrayValue1", "ArrayValue2"}) }, }), ("Section2", new TestSection @@ -480,7 +604,7 @@ namespace Microsoft.Extensions.Configuration.Test Values = new[] { ("Key3", (TestKeyValue)"Value123"), - ("Key3a", (TestKeyValue)new[] {"ArrayValue0", "ArrayValue1", "ArrayValue2"}), + ("Key3a", (TestKeyValue)new[] {"ArrayValue0", "ArrayValue1", "ArrayValue2"}) }, }) @@ -492,11 +616,10 @@ namespace Microsoft.Extensions.Configuration.Test { ("Section4", new TestSection { - Values = new[] {("Key4", (TestKeyValue)"Value344")}, + Values = new[] {("Key4", (TestKeyValue)"Value344")} }) } - }), - + }) } }; @@ -520,7 +643,7 @@ namespace Microsoft.Extensions.Configuration.Test Values = new[] { ("Key3", (TestKeyValue)"Value123"), - ("Key3a", (TestKeyValue)new[] {"ArrayValue0", "ArrayValue1", "ArrayValue2"}), + ("Key3a", (TestKeyValue)new[] {"ArrayValue0", "ArrayValue1", "ArrayValue2"}) }, }), ("SectioN2", new TestSection @@ -528,7 +651,7 @@ namespace Microsoft.Extensions.Configuration.Test Values = new[] { ("KeY3", (TestKeyValue)"Value123"), - ("KeY3a", (TestKeyValue)new[] {"ArrayValue0", "ArrayValue1", "ArrayValue2"}), + ("KeY3a", (TestKeyValue)new[] {"ArrayValue0", "ArrayValue1", "ArrayValue2"}) }, }) @@ -540,11 +663,97 @@ namespace Microsoft.Extensions.Configuration.Test { ("Section4", new TestSection { - Values = new[] {("Key4", (TestKeyValue)"Value344")}, + 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")} + }) + } + }) } }; } diff --git a/src/Configuration.KeyPerFile/test/KeyPerFileTests.cs b/src/Configuration.KeyPerFile/test/KeyPerFileTests.cs index fac0e839e8..4de528c011 100644 --- a/src/Configuration.KeyPerFile/test/KeyPerFileTests.cs +++ b/src/Configuration.KeyPerFile/test/KeyPerFileTests.cs @@ -190,20 +190,11 @@ namespace Microsoft.Extensions.Configuration.KeyPerFile.Test _contents = new TestDirectoryContents(files); } - public IDirectoryContents GetDirectoryContents(string subpath) - { - return _contents; - } + public IDirectoryContents GetDirectoryContents(string subpath) => _contents; - public IFileInfo GetFileInfo(string subpath) - { - throw new NotImplementedException(); - } + public IFileInfo GetFileInfo(string subpath) => new TestFile("TestDirectory"); - public IChangeToken Watch(string filter) - { - throw new NotImplementedException(); - } + public IChangeToken Watch(string filter) => throw new NotImplementedException(); } class TestDirectoryContents : IDirectoryContents @@ -215,75 +206,33 @@ namespace Microsoft.Extensions.Configuration.KeyPerFile.Test _list = new List(files); } - public bool Exists - { - get - { - return true; - } - } + public bool Exists => true; - public IEnumerator GetEnumerator() - { - return _list.GetEnumerator(); - } + public IEnumerator GetEnumerator() => _list.GetEnumerator(); - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } //TODO: Probably need a directory and file type. class TestFile : IFileInfo { - private string _name; - private string _contents; + private readonly string _name; + private readonly string _contents; - public bool Exists - { - get - { - return true; - } - } + public bool Exists => true; public bool IsDirectory { get; } - public DateTimeOffset LastModified - { - get - { - throw new NotImplementedException(); - } - } + public DateTimeOffset LastModified => throw new NotImplementedException(); - public long Length - { - get - { - throw new NotImplementedException(); - } - } + public long Length => throw new NotImplementedException(); - public string Name - { - get - { - return _name; - } - } + public string Name => _name; - public string PhysicalPath - { - get - { - throw new NotImplementedException(); - } - } + public string PhysicalPath => "Root/" + Name; public TestFile(string name) { @@ -304,7 +253,9 @@ namespace Microsoft.Extensions.Configuration.KeyPerFile.Test throw new InvalidOperationException("Cannot create stream from directory"); } - return new MemoryStream(Encoding.UTF8.GetBytes(_contents)); + return _contents == null + ? new MemoryStream() + : new MemoryStream(Encoding.UTF8.GetBytes(_contents)); } } } From 5f7ab102cfe885af5972449a71aeffb4bfdd9946 Mon Sep 17 00:00:00 2001 From: Nate McMaster Date: Wed, 19 Dec 2018 13:43:43 -0800 Subject: [PATCH 11/52] Fix dotnet/extensions#815 - fix path to embedded manifest task file \n\nCommit migrated from https://github.com/dotnet/extensions/commit/cab86d2e3601db232849e4fab078ff6d5dc4df8d --- .../src/Microsoft.Extensions.FileProviders.Embedded.nuspec | 4 +--- .../Microsoft.Extensions.FileProviders.Embedded.props | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.nuspec b/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.nuspec index af7feca8fd..ff6d385add 100644 --- a/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.nuspec +++ b/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.nuspec @@ -27,7 +27,5 @@ - - - \ No newline at end of file + diff --git a/src/FileProviders/Embedded/src/build/netstandard2.0/Microsoft.Extensions.FileProviders.Embedded.props b/src/FileProviders/Embedded/src/build/netstandard2.0/Microsoft.Extensions.FileProviders.Embedded.props index 66ea7b5c22..aabbabc92f 100644 --- a/src/FileProviders/Embedded/src/build/netstandard2.0/Microsoft.Extensions.FileProviders.Embedded.props +++ b/src/FileProviders/Embedded/src/build/netstandard2.0/Microsoft.Extensions.FileProviders.Embedded.props @@ -5,9 +5,7 @@ - <_FileProviderTaskFolder Condition="'$(MSBuildRuntimeType)' == 'Core'">netstandard2.0 - <_FileProviderTaskFolder Condition="'$(MSBuildRuntimeType)' != 'Core'">net461 - <_FileProviderTaskAssembly>$(MSBuildThisFileDirectory)..\..\tasks\$(_FileProviderTaskFolder)\Microsoft.Extensions.FileProviders.Embedded.Manifest.Task.dll + <_FileProviderTaskAssembly>$(MSBuildThisFileDirectory)..\..\tasks\netstandard2.0\Microsoft.Extensions.FileProviders.Embedded.Manifest.Task.dll Date: Sun, 23 Dec 2018 18:28:10 -0800 Subject: [PATCH 12/52] Fix aspnet/Extensionsdotnet/extensions#639 The issue is that the hosted service would not be registered if other hosted services are present. The fix is to use TryAddEnumble so that we allow multipled `IHostedService`s but not multiple instances of the same type. \n\nCommit migrated from https://github.com/dotnet/extensions/commit/a2f28147c21d5272fb66b75a47325cd9e63cde58 --- .../HealthCheckServiceCollectionExtensions.cs | 2 +- .../ServiceCollectionExtensionsTest.cs | 53 +++++++++++++++++++ 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/src/HealthChecks/HealthChecks/src/DependencyInjection/HealthCheckServiceCollectionExtensions.cs b/src/HealthChecks/HealthChecks/src/DependencyInjection/HealthCheckServiceCollectionExtensions.cs index d6df03d2ae..91ffa59449 100644 --- a/src/HealthChecks/HealthChecks/src/DependencyInjection/HealthCheckServiceCollectionExtensions.cs +++ b/src/HealthChecks/HealthChecks/src/DependencyInjection/HealthCheckServiceCollectionExtensions.cs @@ -26,7 +26,7 @@ namespace Microsoft.Extensions.DependencyInjection public static IHealthChecksBuilder AddHealthChecks(this IServiceCollection services) { services.TryAddSingleton(); - services.TryAddSingleton(); + services.TryAddEnumerable(ServiceDescriptor.Singleton()); return new HealthChecksBuilder(services); } } diff --git a/src/HealthChecks/HealthChecks/test/DependencyInjection/ServiceCollectionExtensionsTest.cs b/src/HealthChecks/HealthChecks/test/DependencyInjection/ServiceCollectionExtensionsTest.cs index 694a97628d..98371e9f13 100644 --- a/src/HealthChecks/HealthChecks/test/DependencyInjection/ServiceCollectionExtensionsTest.cs +++ b/src/HealthChecks/HealthChecks/test/DependencyInjection/ServiceCollectionExtensionsTest.cs @@ -2,6 +2,8 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Linq; +using System.Threading; +using System.Threading.Tasks; using Microsoft.Extensions.Diagnostics.HealthChecks; using Microsoft.Extensions.Hosting; using Xunit; @@ -39,5 +41,56 @@ namespace Microsoft.Extensions.DependencyInjection Assert.Null(actual.ImplementationFactory); }); } + + [Fact] // see: https://github.com/aspnet/Extensions/issues/639 + public void AddHealthChecks_RegistersPublisherService_WhenOtherHostedServicesRegistered() + { + // Arrange + var services = new ServiceCollection(); + + // Act + services.AddSingleton(); + services.AddHealthChecks(); + + // Assert + Assert.Collection(services.OrderBy(s => s.ServiceType.FullName).ThenBy(s => s.ImplementationType.FullName), + actual => + { + Assert.Equal(ServiceLifetime.Singleton, actual.Lifetime); + Assert.Equal(typeof(HealthCheckService), actual.ServiceType); + Assert.Equal(typeof(DefaultHealthCheckService), actual.ImplementationType); + Assert.Null(actual.ImplementationInstance); + Assert.Null(actual.ImplementationFactory); + }, + actual => + { + Assert.Equal(ServiceLifetime.Singleton, actual.Lifetime); + Assert.Equal(typeof(IHostedService), actual.ServiceType); + Assert.Equal(typeof(DummyHostedService), actual.ImplementationType); + Assert.Null(actual.ImplementationInstance); + Assert.Null(actual.ImplementationFactory); + }, + actual => + { + Assert.Equal(ServiceLifetime.Singleton, actual.Lifetime); + Assert.Equal(typeof(IHostedService), actual.ServiceType); + Assert.Equal(typeof(HealthCheckPublisherHostedService), actual.ImplementationType); + Assert.Null(actual.ImplementationInstance); + Assert.Null(actual.ImplementationFactory); + }); + } + + private class DummyHostedService : IHostedService + { + public Task StartAsync(CancellationToken cancellationToken) + { + throw new System.NotImplementedException(); + } + + public Task StopAsync(CancellationToken cancellationToken) + { + throw new System.NotImplementedException(); + } + } } } From bdc2ea81c0da8ca6e0c7633c4b37769de216e995 Mon Sep 17 00:00:00 2001 From: Pavel Krymets Date: Mon, 31 Dec 2018 12:54:54 -0800 Subject: [PATCH 13/52] Unify TypeNameHelpers (dotnet/extensions#876) \n\nCommit migrated from https://github.com/dotnet/extensions/commit/2f53c3619543bb2b7a166c1926ea462b8521d2fc --- src/Shared/TypeNameHelper/TypeNameHelper.cs | 59 ++++++++++++------- .../test/Shared.Tests/TypeNameHelperTest.cs | 41 +++++++++++++ 2 files changed, 80 insertions(+), 20 deletions(-) diff --git a/src/Shared/TypeNameHelper/TypeNameHelper.cs b/src/Shared/TypeNameHelper/TypeNameHelper.cs index 1cc7468646..3994a074b6 100644 --- a/src/Shared/TypeNameHelper/TypeNameHelper.cs +++ b/src/Shared/TypeNameHelper/TypeNameHelper.cs @@ -9,6 +9,8 @@ namespace Microsoft.Extensions.Internal { internal class TypeNameHelper { + private const char DefaultNestedTypeDelimiter = '+'; + private static readonly Dictionary _builtInTypeNames = new Dictionary { { typeof(void), "void" }, @@ -40,15 +42,17 @@ namespace Microsoft.Extensions.Internal /// The . /// true to print a fully qualified name. /// true to include generic parameter names. + /// true to include generic parameters. + /// Character to use as a delimiter in nested type names /// The pretty printed type name. - public static string GetTypeDisplayName(Type type, bool fullName = true, bool includeGenericParameterNames = false) + public static string GetTypeDisplayName(Type type, bool fullName = true, bool includeGenericParameterNames = false, bool includeGenericParameters = true, char nestedTypeDelimiter = DefaultNestedTypeDelimiter) { var builder = new StringBuilder(); - ProcessType(builder, type, new DisplayNameOptions(fullName, includeGenericParameterNames)); + ProcessType(builder, type, new DisplayNameOptions(fullName, includeGenericParameterNames, includeGenericParameters, nestedTypeDelimiter)); return builder.ToString(); } - private static void ProcessType(StringBuilder builder, Type type, DisplayNameOptions options) + private static void ProcessType(StringBuilder builder, Type type, in DisplayNameOptions options) { if (type.IsGenericType) { @@ -72,11 +76,17 @@ namespace Microsoft.Extensions.Internal } else { - builder.Append(options.FullName ? type.FullName : type.Name); + var name = options.FullName ? type.FullName : type.Name; + builder.Append(name); + + if (options.NestedTypeDelimiter != DefaultNestedTypeDelimiter) + { + builder.Replace(DefaultNestedTypeDelimiter, options.NestedTypeDelimiter, builder.Length - name.Length, name.Length); + } } } - private static void ProcessArrayType(StringBuilder builder, Type type, DisplayNameOptions options) + private static void ProcessArrayType(StringBuilder builder, Type type, in DisplayNameOptions options) { var innerType = type; while (innerType.IsArray) @@ -95,7 +105,7 @@ namespace Microsoft.Extensions.Internal } } - private static void ProcessGenericType(StringBuilder builder, Type type, Type[] genericArguments, int length, DisplayNameOptions options) + private static void ProcessGenericType(StringBuilder builder, Type type, Type[] genericArguments, int length, in DisplayNameOptions options) { var offset = 0; if (type.IsNested) @@ -108,7 +118,7 @@ namespace Microsoft.Extensions.Internal if (type.IsNested) { ProcessGenericType(builder, type.DeclaringType, genericArguments, offset, options); - builder.Append('+'); + builder.Append(options.NestedTypeDelimiter); } else if (!string.IsNullOrEmpty(type.Namespace)) { @@ -126,35 +136,44 @@ namespace Microsoft.Extensions.Internal builder.Append(type.Name, 0, genericPartIndex); - builder.Append('<'); - for (var i = offset; i < length; i++) + if (options.IncludeGenericParameters) { - ProcessType(builder, genericArguments[i], options); - if (i + 1 == length) + builder.Append('<'); + for (var i = offset; i < length; i++) { - continue; - } + ProcessType(builder, genericArguments[i], options); + if (i + 1 == length) + { + continue; + } - builder.Append(','); - if (options.IncludeGenericParameterNames || !genericArguments[i + 1].IsGenericParameter) - { - builder.Append(' '); + builder.Append(','); + if (options.IncludeGenericParameterNames || !genericArguments[i + 1].IsGenericParameter) + { + builder.Append(' '); + } } + builder.Append('>'); } - builder.Append('>'); } - private struct DisplayNameOptions + private readonly struct DisplayNameOptions { - public DisplayNameOptions(bool fullName, bool includeGenericParameterNames) + public DisplayNameOptions(bool fullName, bool includeGenericParameterNames, bool includeGenericParameters, char nestedTypeDelimiter) { FullName = fullName; + IncludeGenericParameters = includeGenericParameters; IncludeGenericParameterNames = includeGenericParameterNames; + NestedTypeDelimiter = nestedTypeDelimiter; } public bool FullName { get; } + public bool IncludeGenericParameters { get; } + public bool IncludeGenericParameterNames { get; } + + public char NestedTypeDelimiter { get; } } } } diff --git a/src/Shared/test/Shared.Tests/TypeNameHelperTest.cs b/src/Shared/test/Shared.Tests/TypeNameHelperTest.cs index b7f4285bdc..bd29f647d1 100644 --- a/src/Shared/test/Shared.Tests/TypeNameHelperTest.cs +++ b/src/Shared/test/Shared.Tests/TypeNameHelperTest.cs @@ -216,6 +216,47 @@ namespace Microsoft.Extensions.Internal Assert.Equal(expected, actual); } + public static TheoryData FullTypeNameData + { + get + { + return new TheoryData + { + // Predefined Types + { typeof(int), "int" }, + { typeof(List), "System.Collections.Generic.List" }, + { typeof(Dictionary), "System.Collections.Generic.Dictionary" }, + { typeof(Dictionary>), "System.Collections.Generic.Dictionary" }, + { typeof(List>), "System.Collections.Generic.List" }, + + // Classes inside NonGeneric class + { typeof(A), "Microsoft.Extensions.Internal.TypeNameHelperTest.A" }, + { typeof(B), "Microsoft.Extensions.Internal.TypeNameHelperTest.B" }, + { typeof(C), "Microsoft.Extensions.Internal.TypeNameHelperTest.C" }, + { typeof(C>), "Microsoft.Extensions.Internal.TypeNameHelperTest.C" }, + { typeof(B>), "Microsoft.Extensions.Internal.TypeNameHelperTest.B" }, + + // Classes inside Generic class + { typeof(Outer.D), "Microsoft.Extensions.Internal.TypeNameHelperTest.Outer.D" }, + { typeof(Outer.E), "Microsoft.Extensions.Internal.TypeNameHelperTest.Outer.E" }, + { typeof(Outer.F), "Microsoft.Extensions.Internal.TypeNameHelperTest.Outer.F" }, + { typeof(Outer.F.E>),"Microsoft.Extensions.Internal.TypeNameHelperTest.Outer.F" }, + { typeof(Outer.E.E>), "Microsoft.Extensions.Internal.TypeNameHelperTest.Outer.E" } + }; + } + } + + [Theory] + [MemberData(nameof(FullTypeNameData))] + public void Can_PrettyPrint_FullTypeName_WithoutGenericParametersAndNestedTypeDelimiter(Type type, string expectedTypeName) + { + // Arrange & Act + var displayName = TypeNameHelper.GetTypeDisplayName(type, fullName: true, includeGenericParameters: false, nestedTypeDelimiter: '.'); + + // Assert + Assert.Equal(expectedTypeName, displayName); + } + private class A { } private class B { } From 3287fba28f61d9167bcb342a5447aec0ad45cacd Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Mon, 7 Jan 2019 15:37:53 +1300 Subject: [PATCH 14/52] Don't timeout in testing task extensions when debugger is attached (dotnet/extensions#903) \n\nCommit migrated from https://github.com/dotnet/extensions/commit/24c84a5b1db0fca3cfcc54eed3f4f94c6670a709 --- src/Testing/src/TaskExtensions.cs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/Testing/src/TaskExtensions.cs b/src/Testing/src/TaskExtensions.cs index 83130aeae4..f99bf7361a 100644 --- a/src/Testing/src/TaskExtensions.cs +++ b/src/Testing/src/TaskExtensions.cs @@ -1,7 +1,8 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// 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.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; @@ -12,10 +13,11 @@ namespace Microsoft.AspNetCore.Testing { public static async Task TimeoutAfter(this Task task, TimeSpan timeout, [CallerFilePath] string filePath = null, - [CallerLineNumber] int lineNumber = default(int)) + [CallerLineNumber] int lineNumber = default) { // Don't create a timer if the task is already completed - if (task.IsCompleted) + // or the debugger is attached + if (task.IsCompleted || Debugger.IsAttached) { return await task; } @@ -28,17 +30,17 @@ namespace Microsoft.AspNetCore.Testing } else { - throw new TimeoutException( - CreateMessage(timeout, filePath, lineNumber)); + throw new TimeoutException(CreateMessage(timeout, filePath, lineNumber)); } } public static async Task TimeoutAfter(this Task task, TimeSpan timeout, [CallerFilePath] string filePath = null, - [CallerLineNumber] int lineNumber = default(int)) + [CallerLineNumber] int lineNumber = default) { // Don't create a timer if the task is already completed - if (task.IsCompleted) + // or the debugger is attached + if (task.IsCompleted || Debugger.IsAttached) { await task; return; From 97bb8897cf92da0a371526d72625fcaf93348ae5 Mon Sep 17 00:00:00 2001 From: Pranav K Date: Wed, 9 Jan 2019 11:10:09 -0800 Subject: [PATCH 15/52] Reorganize code \n\nCommit migrated from https://github.com/dotnet/extensions/commit/a24412cc03f496b928da34f8b24d092d3d64d4f5 --- src/JSInterop/.editorconfig | 27 + src/JSInterop/Microsoft.JSInterop.sln | 58 + src/JSInterop/README.md | 8 + .../src/Microsoft.JSInterop.JS/.gitignore | 1 + .../Microsoft.JSInterop.JS.csproj | 13 + .../Microsoft.JSInterop.JS/package-lock.json | 105 + .../src/Microsoft.JSInterop.JS/package.json | 25 + .../src/Microsoft.JSInterop.ts | 287 +++ .../src/Microsoft.JSInterop.JS/tsconfig.json | 19 + .../Microsoft.JSInterop/DotNetDispatcher.cs | 299 +++ .../Microsoft.JSInterop/DotNetObjectRef.cs | 66 + .../ICustomArgSerializer.cs | 22 + .../IJSInProcessRuntime.cs | 20 + .../src/Microsoft.JSInterop/IJSRuntime.cs | 32 + .../InteropArgSerializerStrategy.cs | 121 + .../Microsoft.JSInterop/JSAsyncCallResult.cs | 36 + .../src/Microsoft.JSInterop/JSException.cs | 21 + .../JSInProcessRuntimeBase.cs | 32 + .../JSInvokableAttribute.cs | 48 + .../src/Microsoft.JSInterop/JSRuntime.cs | 34 + .../src/Microsoft.JSInterop/JSRuntimeBase.cs | 116 + .../src/Microsoft.JSInterop/Json/CamelCase.cs | 59 + .../src/Microsoft.JSInterop/Json/Json.cs | 39 + .../Json/SimpleJson/README.txt | 24 + .../Json/SimpleJson/SimpleJson.cs | 2201 +++++++++++++++++ .../Microsoft.JSInterop.csproj | 7 + .../Properties/AssemblyInfo.cs | 3 + .../Microsoft.JSInterop/TaskGenericsUtil.cs | 116 + .../Mono.WebAssembly.Interop/InternalCalls.cs | 25 + .../Mono.WebAssembly.Interop.csproj | 11 + .../MonoWebAssemblyJSRuntime.cs | 114 + .../DotNetDispatcherTest.cs | 443 ++++ .../DotNetObjectRefTest.cs | 68 + .../JSInProcessRuntimeBaseTest.cs | 117 + .../JSRuntimeBaseTest.cs | 191 ++ .../Microsoft.JSInterop.Test/JSRuntimeTest.cs | 37 + .../Microsoft.JSInterop.Test/JsonUtilTest.cs | 349 +++ .../Microsoft.JSInterop.Test.csproj | 20 + 38 files changed, 5214 insertions(+) create mode 100644 src/JSInterop/.editorconfig create mode 100644 src/JSInterop/Microsoft.JSInterop.sln create mode 100644 src/JSInterop/README.md create mode 100644 src/JSInterop/src/Microsoft.JSInterop.JS/.gitignore create mode 100644 src/JSInterop/src/Microsoft.JSInterop.JS/Microsoft.JSInterop.JS.csproj create mode 100644 src/JSInterop/src/Microsoft.JSInterop.JS/package-lock.json create mode 100644 src/JSInterop/src/Microsoft.JSInterop.JS/package.json create mode 100644 src/JSInterop/src/Microsoft.JSInterop.JS/src/Microsoft.JSInterop.ts create mode 100644 src/JSInterop/src/Microsoft.JSInterop.JS/tsconfig.json create mode 100644 src/JSInterop/src/Microsoft.JSInterop/DotNetDispatcher.cs create mode 100644 src/JSInterop/src/Microsoft.JSInterop/DotNetObjectRef.cs create mode 100644 src/JSInterop/src/Microsoft.JSInterop/ICustomArgSerializer.cs create mode 100644 src/JSInterop/src/Microsoft.JSInterop/IJSInProcessRuntime.cs create mode 100644 src/JSInterop/src/Microsoft.JSInterop/IJSRuntime.cs create mode 100644 src/JSInterop/src/Microsoft.JSInterop/InteropArgSerializerStrategy.cs create mode 100644 src/JSInterop/src/Microsoft.JSInterop/JSAsyncCallResult.cs create mode 100644 src/JSInterop/src/Microsoft.JSInterop/JSException.cs create mode 100644 src/JSInterop/src/Microsoft.JSInterop/JSInProcessRuntimeBase.cs create mode 100644 src/JSInterop/src/Microsoft.JSInterop/JSInvokableAttribute.cs create mode 100644 src/JSInterop/src/Microsoft.JSInterop/JSRuntime.cs create mode 100644 src/JSInterop/src/Microsoft.JSInterop/JSRuntimeBase.cs create mode 100644 src/JSInterop/src/Microsoft.JSInterop/Json/CamelCase.cs create mode 100644 src/JSInterop/src/Microsoft.JSInterop/Json/Json.cs create mode 100644 src/JSInterop/src/Microsoft.JSInterop/Json/SimpleJson/README.txt create mode 100644 src/JSInterop/src/Microsoft.JSInterop/Json/SimpleJson/SimpleJson.cs create mode 100644 src/JSInterop/src/Microsoft.JSInterop/Microsoft.JSInterop.csproj create mode 100644 src/JSInterop/src/Microsoft.JSInterop/Properties/AssemblyInfo.cs create mode 100644 src/JSInterop/src/Microsoft.JSInterop/TaskGenericsUtil.cs create mode 100644 src/JSInterop/src/Mono.WebAssembly.Interop/InternalCalls.cs create mode 100644 src/JSInterop/src/Mono.WebAssembly.Interop/Mono.WebAssembly.Interop.csproj create mode 100644 src/JSInterop/src/Mono.WebAssembly.Interop/MonoWebAssemblyJSRuntime.cs create mode 100644 src/JSInterop/test/Microsoft.JSInterop.Test/DotNetDispatcherTest.cs create mode 100644 src/JSInterop/test/Microsoft.JSInterop.Test/DotNetObjectRefTest.cs create mode 100644 src/JSInterop/test/Microsoft.JSInterop.Test/JSInProcessRuntimeBaseTest.cs create mode 100644 src/JSInterop/test/Microsoft.JSInterop.Test/JSRuntimeBaseTest.cs create mode 100644 src/JSInterop/test/Microsoft.JSInterop.Test/JSRuntimeTest.cs create mode 100644 src/JSInterop/test/Microsoft.JSInterop.Test/JsonUtilTest.cs create mode 100644 src/JSInterop/test/Microsoft.JSInterop.Test/Microsoft.JSInterop.Test.csproj diff --git a/src/JSInterop/.editorconfig b/src/JSInterop/.editorconfig new file mode 100644 index 0000000000..0d238f8e41 --- /dev/null +++ b/src/JSInterop/.editorconfig @@ -0,0 +1,27 @@ +# All Files +[*] +charset = utf-8 +end_of_line = crlf +indent_style = space +indent_size = 4 +insert_final_newline = false +trim_trailing_whitespace = true + +# Solution Files +[*.sln] +indent_style = tab + +# Markdown Files +[*.md] +trim_trailing_whitespace = false + +# Web Files +[*.{htm,html,js,ts,css,scss,less}] +insert_final_newline = true +indent_size = 2 + +[*.{yml,json}] +indent_size = 2 + +[*.{xml,csproj,config,*proj,targets,props}] +indent_size = 2 diff --git a/src/JSInterop/Microsoft.JSInterop.sln b/src/JSInterop/Microsoft.JSInterop.sln new file mode 100644 index 0000000000..d646a6b068 --- /dev/null +++ b/src/JSInterop/Microsoft.JSInterop.sln @@ -0,0 +1,58 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.27703.2042 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{1290437E-A890-419E-A317-D0F7FEE185A5}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{B98D4F51-88FB-471C-B56F-752E8EE502E7}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.JSInterop", "src\Microsoft.JSInterop\Microsoft.JSInterop.csproj", "{CB4CD4A6-9BAA-46D1-944F-CE56DEC2663C}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.JSInterop.Test", "test\Microsoft.JSInterop.Test\Microsoft.JSInterop.Test.csproj", "{7FF8B199-52C0-4DFE-A73B-0C9E18220C0E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.JSInterop.JS", "src\Microsoft.JSInterop.JS\Microsoft.JSInterop.JS.csproj", "{60BA5AAD-264A-437E-8319-577841C66CC6}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{BE4CBB33-5C40-4A07-B6FC-1D7C3AE13024}" + ProjectSection(SolutionItems) = preProject + README.md = README.md + EndProjectSection +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Mono.WebAssembly.Interop", "src\Mono.WebAssembly.Interop\Mono.WebAssembly.Interop.csproj", "{10145E99-1B2D-40C5-9595-582BDAF3E024}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {CB4CD4A6-9BAA-46D1-944F-CE56DEC2663C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CB4CD4A6-9BAA-46D1-944F-CE56DEC2663C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CB4CD4A6-9BAA-46D1-944F-CE56DEC2663C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CB4CD4A6-9BAA-46D1-944F-CE56DEC2663C}.Release|Any CPU.Build.0 = Release|Any CPU + {7FF8B199-52C0-4DFE-A73B-0C9E18220C0E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7FF8B199-52C0-4DFE-A73B-0C9E18220C0E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7FF8B199-52C0-4DFE-A73B-0C9E18220C0E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7FF8B199-52C0-4DFE-A73B-0C9E18220C0E}.Release|Any CPU.Build.0 = Release|Any CPU + {60BA5AAD-264A-437E-8319-577841C66CC6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {60BA5AAD-264A-437E-8319-577841C66CC6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {60BA5AAD-264A-437E-8319-577841C66CC6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {60BA5AAD-264A-437E-8319-577841C66CC6}.Release|Any CPU.Build.0 = Release|Any CPU + {10145E99-1B2D-40C5-9595-582BDAF3E024}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {10145E99-1B2D-40C5-9595-582BDAF3E024}.Debug|Any CPU.Build.0 = Debug|Any CPU + {10145E99-1B2D-40C5-9595-582BDAF3E024}.Release|Any CPU.ActiveCfg = Release|Any CPU + {10145E99-1B2D-40C5-9595-582BDAF3E024}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {CB4CD4A6-9BAA-46D1-944F-CE56DEC2663C} = {1290437E-A890-419E-A317-D0F7FEE185A5} + {7FF8B199-52C0-4DFE-A73B-0C9E18220C0E} = {B98D4F51-88FB-471C-B56F-752E8EE502E7} + {60BA5AAD-264A-437E-8319-577841C66CC6} = {1290437E-A890-419E-A317-D0F7FEE185A5} + {10145E99-1B2D-40C5-9595-582BDAF3E024} = {1290437E-A890-419E-A317-D0F7FEE185A5} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {7E07ABF2-427A-43FA-A6A4-82B21B96ACAF} + EndGlobalSection +EndGlobal diff --git a/src/JSInterop/README.md b/src/JSInterop/README.md new file mode 100644 index 0000000000..dcdaf7618e --- /dev/null +++ b/src/JSInterop/README.md @@ -0,0 +1,8 @@ +# jsinterop + +This repo is for `Microsoft.JSInterop`, a package that provides abstractions and features for interop between .NET and JavaScript code. + +## Usage + +The primary use case is for applications built with Mono WebAssembly or Blazor. It's not expected that developers will typically use these libraries separately from Mono WebAssembly, Blazor, or a similar technology. + diff --git a/src/JSInterop/src/Microsoft.JSInterop.JS/.gitignore b/src/JSInterop/src/Microsoft.JSInterop.JS/.gitignore new file mode 100644 index 0000000000..849ddff3b7 --- /dev/null +++ b/src/JSInterop/src/Microsoft.JSInterop.JS/.gitignore @@ -0,0 +1 @@ +dist/ diff --git a/src/JSInterop/src/Microsoft.JSInterop.JS/Microsoft.JSInterop.JS.csproj b/src/JSInterop/src/Microsoft.JSInterop.JS/Microsoft.JSInterop.JS.csproj new file mode 100644 index 0000000000..4a6ba93ebb --- /dev/null +++ b/src/JSInterop/src/Microsoft.JSInterop.JS/Microsoft.JSInterop.JS.csproj @@ -0,0 +1,13 @@ + + + + netstandard2.0 + Latest + false + + + + + + + diff --git a/src/JSInterop/src/Microsoft.JSInterop.JS/package-lock.json b/src/JSInterop/src/Microsoft.JSInterop.JS/package-lock.json new file mode 100644 index 0000000000..a7f31ead4f --- /dev/null +++ b/src/JSInterop/src/Microsoft.JSInterop.JS/package-lock.json @@ -0,0 +1,105 @@ +{ + "name": "@dotnet/jsinterop", + "version": "0.1.1", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "1.0.0", + "concat-map": "0.0.1" + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "glob": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", + "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", + "dev": true, + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "1.4.0", + "wrappy": "1.0.2" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "1.1.11" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1.0.2" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "rimraf": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", + "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", + "dev": true, + "requires": { + "glob": "7.1.3" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + } + } +} diff --git a/src/JSInterop/src/Microsoft.JSInterop.JS/package.json b/src/JSInterop/src/Microsoft.JSInterop.JS/package.json new file mode 100644 index 0000000000..a84734bc46 --- /dev/null +++ b/src/JSInterop/src/Microsoft.JSInterop.JS/package.json @@ -0,0 +1,25 @@ +{ + "name": "@dotnet/jsinterop", + "version": "0.1.1", + "description": "Provides abstractions and features for interop between .NET and JavaScript code.", + "main": "dist/Microsoft.JSInterop.js", + "types": "dist/Microsoft.JSInterop.d.js", + "scripts": { + "prepublish": "rimraf dist && dotnet build && echo 'Finished building NPM package \"@dotnet/jsinterop\"'" + }, + "files": [ + "dist/**" + ], + "author": "Microsoft", + "license": "Apache-2.0", + "bugs": { + "url": "https://github.com/dotnet/jsinterop/issues" + }, + "repository": { + "type": "git", + "url": "https://github.com/dotnet/jsinterop.git" + }, + "devDependencies": { + "rimraf": "^2.5.4" + } +} diff --git a/src/JSInterop/src/Microsoft.JSInterop.JS/src/Microsoft.JSInterop.ts b/src/JSInterop/src/Microsoft.JSInterop.JS/src/Microsoft.JSInterop.ts new file mode 100644 index 0000000000..4b5d409d0f --- /dev/null +++ b/src/JSInterop/src/Microsoft.JSInterop.JS/src/Microsoft.JSInterop.ts @@ -0,0 +1,287 @@ +// This is a single-file self-contained module to avoid the need for a Webpack build + +module DotNet { + (window as any).DotNet = DotNet; // Ensure reachable from anywhere + + export type JsonReviver = ((key: any, value: any) => any); + const jsonRevivers: JsonReviver[] = []; + + const pendingAsyncCalls: { [id: number]: PendingAsyncCall } = {}; + const cachedJSFunctions: { [identifier: string]: Function } = {}; + let nextAsyncCallId = 1; // Start at 1 because zero signals "no response needed" + + let dotNetDispatcher: DotNetCallDispatcher | null = null; + + /** + * Sets the specified .NET call dispatcher as the current instance so that it will be used + * for future invocations. + * + * @param dispatcher An object that can dispatch calls from JavaScript to a .NET runtime. + */ + export function attachDispatcher(dispatcher: DotNetCallDispatcher) { + dotNetDispatcher = dispatcher; + } + + /** + * Adds a JSON reviver callback that will be used when parsing arguments received from .NET. + * @param reviver The reviver to add. + */ + export function attachReviver(reviver: JsonReviver) { + jsonRevivers.push(reviver); + } + + /** + * Invokes the specified .NET public method synchronously. Not all hosting scenarios support + * synchronous invocation, so if possible use invokeMethodAsync instead. + * + * @param assemblyName The short name (without key/version or .dll extension) of the .NET assembly containing the method. + * @param methodIdentifier The identifier of the method to invoke. The method must have a [JSInvokable] attribute specifying this identifier. + * @param args Arguments to pass to the method, each of which must be JSON-serializable. + * @returns The result of the operation. + */ + export function invokeMethod(assemblyName: string, methodIdentifier: string, ...args: any[]): T { + return invokePossibleInstanceMethod(assemblyName, methodIdentifier, null, args); + } + + /** + * Invokes the specified .NET public method asynchronously. + * + * @param assemblyName The short name (without key/version or .dll extension) of the .NET assembly containing the method. + * @param methodIdentifier The identifier of the method to invoke. The method must have a [JSInvokable] attribute specifying this identifier. + * @param args Arguments to pass to the method, each of which must be JSON-serializable. + * @returns A promise representing the result of the operation. + */ + export function invokeMethodAsync(assemblyName: string, methodIdentifier: string, ...args: any[]): Promise { + return invokePossibleInstanceMethodAsync(assemblyName, methodIdentifier, null, args); + } + + function invokePossibleInstanceMethod(assemblyName: string | null, methodIdentifier: string, dotNetObjectId: number | null, args: any[]): T { + const dispatcher = getRequiredDispatcher(); + if (dispatcher.invokeDotNetFromJS) { + const argsJson = JSON.stringify(args, argReplacer); + const resultJson = dispatcher.invokeDotNetFromJS(assemblyName, methodIdentifier, dotNetObjectId, argsJson); + return resultJson ? parseJsonWithRevivers(resultJson) : null; + } else { + throw new Error('The current dispatcher does not support synchronous calls from JS to .NET. Use invokeMethodAsync instead.'); + } + } + + function invokePossibleInstanceMethodAsync(assemblyName: string | null, methodIdentifier: string, dotNetObjectId: number | null, args: any[]): Promise { + const asyncCallId = nextAsyncCallId++; + const resultPromise = new Promise((resolve, reject) => { + pendingAsyncCalls[asyncCallId] = { resolve, reject }; + }); + + try { + const argsJson = JSON.stringify(args, argReplacer); + getRequiredDispatcher().beginInvokeDotNetFromJS(asyncCallId, assemblyName, methodIdentifier, dotNetObjectId, argsJson); + } catch(ex) { + // Synchronous failure + completePendingCall(asyncCallId, false, ex); + } + + return resultPromise; + } + + function getRequiredDispatcher(): DotNetCallDispatcher { + if (dotNetDispatcher !== null) { + return dotNetDispatcher; + } + + throw new Error('No .NET call dispatcher has been set.'); + } + + function completePendingCall(asyncCallId: number, success: boolean, resultOrError: any) { + if (!pendingAsyncCalls.hasOwnProperty(asyncCallId)) { + throw new Error(`There is no pending async call with ID ${asyncCallId}.`); + } + + const asyncCall = pendingAsyncCalls[asyncCallId]; + delete pendingAsyncCalls[asyncCallId]; + if (success) { + asyncCall.resolve(resultOrError); + } else { + asyncCall.reject(resultOrError); + } + } + + interface PendingAsyncCall { + resolve: (value?: T | PromiseLike) => void; + reject: (reason?: any) => void; + } + + /** + * Represents the ability to dispatch calls from JavaScript to a .NET runtime. + */ + export interface DotNetCallDispatcher { + /** + * Optional. If implemented, invoked by the runtime to perform a synchronous call to a .NET method. + * + * @param assemblyName The short name (without key/version or .dll extension) of the .NET assembly holding the method to invoke. The value may be null when invoking instance methods. + * @param methodIdentifier The identifier of the method to invoke. The method must have a [JSInvokable] attribute specifying this identifier. + * @param dotNetObjectId If given, the call will be to an instance method on the specified DotNetObject. Pass null or undefined to call static methods. + * @param argsJson JSON representation of arguments to pass to the method. + * @returns JSON representation of the result of the invocation. + */ + invokeDotNetFromJS?(assemblyName: string | null, methodIdentifier: string, dotNetObjectId: number | null, argsJson: string): string | null; + + /** + * Invoked by the runtime to begin an asynchronous call to a .NET method. + * + * @param callId A value identifying the asynchronous operation. This value should be passed back in a later call from .NET to JS. + * @param assemblyName The short name (without key/version or .dll extension) of the .NET assembly holding the method to invoke. The value may be null when invoking instance methods. + * @param methodIdentifier The identifier of the method to invoke. The method must have a [JSInvokable] attribute specifying this identifier. + * @param dotNetObjectId If given, the call will be to an instance method on the specified DotNetObject. Pass null to call static methods. + * @param argsJson JSON representation of arguments to pass to the method. + */ + beginInvokeDotNetFromJS(callId: number, assemblyName: string | null, methodIdentifier: string, dotNetObjectId: number | null, argsJson: string): void; + } + + /** + * Receives incoming calls from .NET and dispatches them to JavaScript. + */ + export const jsCallDispatcher = { + /** + * Finds the JavaScript function matching the specified identifier. + * + * @param identifier Identifies the globally-reachable function to be returned. + * @returns A Function instance. + */ + findJSFunction, + + /** + * Invokes the specified synchronous JavaScript function. + * + * @param identifier Identifies the globally-reachable function to invoke. + * @param argsJson JSON representation of arguments to be passed to the function. + * @returns JSON representation of the invocation result. + */ + invokeJSFromDotNet: (identifier: string, argsJson: string) => { + const result = findJSFunction(identifier).apply(null, parseJsonWithRevivers(argsJson)); + return result === null || result === undefined + ? null + : JSON.stringify(result, argReplacer); + }, + + /** + * Invokes the specified synchronous or asynchronous JavaScript function. + * + * @param asyncHandle A value identifying the asynchronous operation. This value will be passed back in a later call to endInvokeJSFromDotNet. + * @param identifier Identifies the globally-reachable function to invoke. + * @param argsJson JSON representation of arguments to be passed to the function. + */ + beginInvokeJSFromDotNet: (asyncHandle: number, identifier: string, argsJson: string): void => { + // Coerce synchronous functions into async ones, plus treat + // synchronous exceptions the same as async ones + const promise = new Promise(resolve => { + const synchronousResultOrPromise = findJSFunction(identifier).apply(null, parseJsonWithRevivers(argsJson)); + resolve(synchronousResultOrPromise); + }); + + // We only listen for a result if the caller wants to be notified about it + if (asyncHandle) { + // On completion, dispatch result back to .NET + // Not using "await" because it codegens a lot of boilerplate + promise.then( + result => getRequiredDispatcher().beginInvokeDotNetFromJS(0, 'Microsoft.JSInterop', 'DotNetDispatcher.EndInvoke', null, JSON.stringify([asyncHandle, true, result], argReplacer)), + error => getRequiredDispatcher().beginInvokeDotNetFromJS(0, 'Microsoft.JSInterop', 'DotNetDispatcher.EndInvoke', null, JSON.stringify([asyncHandle, false, formatError(error)])) + ); + } + }, + + /** + * Receives notification that an async call from JS to .NET has completed. + * @param asyncCallId The identifier supplied in an earlier call to beginInvokeDotNetFromJS. + * @param success A flag to indicate whether the operation completed successfully. + * @param resultOrExceptionMessage Either the operation result or an error message. + */ + endInvokeDotNetFromJS: (asyncCallId: string, success: boolean, resultOrExceptionMessage: any): void => { + const resultOrError = success ? resultOrExceptionMessage : new Error(resultOrExceptionMessage); + completePendingCall(parseInt(asyncCallId), success, resultOrError); + } + } + + function parseJsonWithRevivers(json: string): any { + return json ? JSON.parse(json, (key, initialValue) => { + // Invoke each reviver in order, passing the output from the previous reviver, + // so that each one gets a chance to transform the value + return jsonRevivers.reduce( + (latestValue, reviver) => reviver(key, latestValue), + initialValue + ); + }) : null; + } + + function formatError(error: any): string { + if (error instanceof Error) { + return `${error.message}\n${error.stack}`; + } else { + return error ? error.toString() : 'null'; + } + } + + function findJSFunction(identifier: string): Function { + if (cachedJSFunctions.hasOwnProperty(identifier)) { + return cachedJSFunctions[identifier]; + } + + let result: any = window; + let resultIdentifier = 'window'; + identifier.split('.').forEach(segment => { + if (segment in result) { + result = result[segment]; + resultIdentifier += '.' + segment; + } else { + throw new Error(`Could not find '${segment}' in '${resultIdentifier}'.`); + } + }); + + if (result instanceof Function) { + return result; + } else { + throw new Error(`The value '${resultIdentifier}' is not a function.`); + } + } + + class DotNetObject { + constructor(private _id: number) { + } + + public invokeMethod(methodIdentifier: string, ...args: any[]): T { + return invokePossibleInstanceMethod(null, methodIdentifier, this._id, args); + } + + public invokeMethodAsync(methodIdentifier: string, ...args: any[]): Promise { + return invokePossibleInstanceMethodAsync(null, methodIdentifier, this._id, args); + } + + public dispose() { + const promise = invokeMethodAsync( + 'Microsoft.JSInterop', + 'DotNetDispatcher.ReleaseDotNetObject', + this._id); + promise.catch(error => console.error(error)); + } + + public serializeAsArg() { + return `__dotNetObject:${this._id}`; + } + } + + const dotNetObjectValueFormat = /^__dotNetObject\:(\d+)$/; + attachReviver(function reviveDotNetObject(key: any, value: any) { + if (typeof value === 'string') { + const match = value.match(dotNetObjectValueFormat); + if (match) { + return new DotNetObject(parseInt(match[1])); + } + } + + // Unrecognized - let another reviver handle it + return value; + }); + + function argReplacer(key: string, value: any) { + return value instanceof DotNetObject ? value.serializeAsArg() : value; + } +} diff --git a/src/JSInterop/src/Microsoft.JSInterop.JS/tsconfig.json b/src/JSInterop/src/Microsoft.JSInterop.JS/tsconfig.json new file mode 100644 index 0000000000..f5a2b0e31a --- /dev/null +++ b/src/JSInterop/src/Microsoft.JSInterop.JS/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "baseUrl": ".", + "noEmitOnError": true, + "removeComments": false, + "sourceMap": true, + "target": "es5", + "lib": ["es2015", "dom", "es2015.promise"], + "strict": true, + "declaration": true, + "outDir": "dist" + }, + "include": [ + "src/**/*.ts" + ], + "exclude": [ + "dist/**" + ] +} diff --git a/src/JSInterop/src/Microsoft.JSInterop/DotNetDispatcher.cs b/src/JSInterop/src/Microsoft.JSInterop/DotNetDispatcher.cs new file mode 100644 index 0000000000..0f346bbfdd --- /dev/null +++ b/src/JSInterop/src/Microsoft.JSInterop/DotNetDispatcher.cs @@ -0,0 +1,299 @@ +// 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.JSInterop.Internal; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Threading.Tasks; + +namespace Microsoft.JSInterop +{ + /// + /// Provides methods that receive incoming calls from JS to .NET. + /// + public static class DotNetDispatcher + { + private static ConcurrentDictionary> _cachedMethodsByAssembly + = new ConcurrentDictionary>(); + + /// + /// Receives a call from JS to .NET, locating and invoking the specified method. + /// + /// The assembly containing the method to be invoked. + /// The identifier of the method to be invoked. The method must be annotated with a matching this identifier string. + /// For instance method calls, identifies the target object. + /// A JSON representation of the parameters. + /// A JSON representation of the return value, or null. + public static string Invoke(string assemblyName, string methodIdentifier, long dotNetObjectId, string argsJson) + { + // This method doesn't need [JSInvokable] because the platform is responsible for having + // some way to dispatch calls here. The logic inside here is the thing that checks whether + // the targeted method has [JSInvokable]. It is not itself subject to that restriction, + // because there would be nobody to police that. This method *is* the police. + + // DotNetDispatcher only works with JSRuntimeBase instances. + var jsRuntime = (JSRuntimeBase)JSRuntime.Current; + + var targetInstance = (object)null; + if (dotNetObjectId != default) + { + targetInstance = jsRuntime.ArgSerializerStrategy.FindDotNetObject(dotNetObjectId); + } + + var syncResult = InvokeSynchronously(assemblyName, methodIdentifier, targetInstance, argsJson); + return syncResult == null ? null : Json.Serialize(syncResult, jsRuntime.ArgSerializerStrategy); + } + + /// + /// Receives a call from JS to .NET, locating and invoking the specified method asynchronously. + /// + /// A value identifying the asynchronous call that should be passed back with the result, or null if no result notification is required. + /// The assembly containing the method to be invoked. + /// The identifier of the method to be invoked. The method must be annotated with a matching this identifier string. + /// For instance method calls, identifies the target object. + /// A JSON representation of the parameters. + /// A JSON representation of the return value, or null. + public static void BeginInvoke(string callId, string assemblyName, string methodIdentifier, long dotNetObjectId, string argsJson) + { + // This method doesn't need [JSInvokable] because the platform is responsible for having + // some way to dispatch calls here. The logic inside here is the thing that checks whether + // the targeted method has [JSInvokable]. It is not itself subject to that restriction, + // because there would be nobody to police that. This method *is* the police. + + // DotNetDispatcher only works with JSRuntimeBase instances. + // If the developer wants to use a totally custom IJSRuntime, then their JS-side + // code has to implement its own way of returning async results. + var jsRuntimeBaseInstance = (JSRuntimeBase)JSRuntime.Current; + + var targetInstance = dotNetObjectId == default + ? null + : jsRuntimeBaseInstance.ArgSerializerStrategy.FindDotNetObject(dotNetObjectId); + + object syncResult = null; + Exception syncException = null; + + try + { + syncResult = InvokeSynchronously(assemblyName, methodIdentifier, targetInstance, argsJson); + } + catch (Exception ex) + { + syncException = ex; + } + + // If there was no callId, the caller does not want to be notified about the result + if (callId != null) + { + // Invoke and coerce the result to a Task so the caller can use the same async API + // for both synchronous and asynchronous methods + var task = CoerceToTask(syncResult, syncException); + + task.ContinueWith(completedTask => + { + try + { + var result = TaskGenericsUtil.GetTaskResult(completedTask); + jsRuntimeBaseInstance.EndInvokeDotNet(callId, true, result); + } + catch (Exception ex) + { + ex = UnwrapException(ex); + jsRuntimeBaseInstance.EndInvokeDotNet(callId, false, ex); + } + }); + } + } + + private static Task CoerceToTask(object syncResult, Exception syncException) + { + if (syncException != null) + { + return Task.FromException(syncException); + } + else if (syncResult is Task syncResultTask) + { + return syncResultTask; + } + else + { + return Task.FromResult(syncResult); + } + } + + private static object InvokeSynchronously(string assemblyName, string methodIdentifier, object targetInstance, string argsJson) + { + if (targetInstance != null) + { + if (assemblyName != null) + { + throw new ArgumentException($"For instance method calls, '{nameof(assemblyName)}' should be null. Value received: '{assemblyName}'."); + } + + assemblyName = targetInstance.GetType().Assembly.GetName().Name; + } + + var (methodInfo, parameterTypes) = GetCachedMethodInfo(assemblyName, methodIdentifier); + + // There's no direct way to say we want to deserialize as an array with heterogenous + // entry types (e.g., [string, int, bool]), so we need to deserialize in two phases. + // First we deserialize as object[], for which SimpleJson will supply JsonObject + // instances for nonprimitive values. + var suppliedArgs = (object[])null; + var suppliedArgsLength = 0; + if (argsJson != null) + { + suppliedArgs = Json.Deserialize(argsJson).ToArray(); + suppliedArgsLength = suppliedArgs.Length; + } + if (suppliedArgsLength != parameterTypes.Length) + { + throw new ArgumentException($"In call to '{methodIdentifier}', expected {parameterTypes.Length} parameters but received {suppliedArgsLength}."); + } + + // Second, convert each supplied value to the type expected by the method + var runtime = (JSRuntimeBase)JSRuntime.Current; + var serializerStrategy = runtime.ArgSerializerStrategy; + for (var i = 0; i < suppliedArgsLength; i++) + { + if (parameterTypes[i] == typeof(JSAsyncCallResult)) + { + // For JS async call results, we have to defer the deserialization until + // later when we know what type it's meant to be deserialized as + suppliedArgs[i] = new JSAsyncCallResult(suppliedArgs[i]); + } + else + { + suppliedArgs[i] = serializerStrategy.DeserializeObject( + suppliedArgs[i], parameterTypes[i]); + } + } + + try + { + return methodInfo.Invoke(targetInstance, suppliedArgs); + } + catch (Exception ex) + { + throw UnwrapException(ex); + } + } + + /// + /// Receives notification that a call from .NET to JS has finished, marking the + /// associated as completed. + /// + /// The identifier for the function invocation. + /// A flag to indicate whether the invocation succeeded. + /// If is true, specifies the invocation result. If is false, gives the corresponding to the invocation failure. + [JSInvokable(nameof(DotNetDispatcher) + "." + nameof(EndInvoke))] + public static void EndInvoke(long asyncHandle, bool succeeded, JSAsyncCallResult result) + => ((JSRuntimeBase)JSRuntime.Current).EndInvokeJS(asyncHandle, succeeded, result.ResultOrException); + + /// + /// Releases the reference to the specified .NET object. This allows the .NET runtime + /// to garbage collect that object if there are no other references to it. + /// + /// To avoid leaking memory, the JavaScript side code must call this for every .NET + /// object it obtains a reference to. The exception is if that object is used for + /// the entire lifetime of a given user's session, in which case it is released + /// automatically when the JavaScript runtime is disposed. + /// + /// The identifier previously passed to JavaScript code. + [JSInvokable(nameof(DotNetDispatcher) + "." + nameof(ReleaseDotNetObject))] + public static void ReleaseDotNetObject(long dotNetObjectId) + { + // DotNetDispatcher only works with JSRuntimeBase instances. + var jsRuntime = (JSRuntimeBase)JSRuntime.Current; + jsRuntime.ArgSerializerStrategy.ReleaseDotNetObject(dotNetObjectId); + } + + private static (MethodInfo, Type[]) GetCachedMethodInfo(string assemblyName, string methodIdentifier) + { + if (string.IsNullOrWhiteSpace(assemblyName)) + { + throw new ArgumentException("Cannot be null, empty, or whitespace.", nameof(assemblyName)); + } + + if (string.IsNullOrWhiteSpace(methodIdentifier)) + { + throw new ArgumentException("Cannot be null, empty, or whitespace.", nameof(methodIdentifier)); + } + + var assemblyMethods = _cachedMethodsByAssembly.GetOrAdd(assemblyName, ScanAssemblyForCallableMethods); + if (assemblyMethods.TryGetValue(methodIdentifier, out var result)) + { + return result; + } + else + { + throw new ArgumentException($"The assembly '{assemblyName}' does not contain a public method with [{nameof(JSInvokableAttribute)}(\"{methodIdentifier}\")]."); + } + } + + private static IReadOnlyDictionary ScanAssemblyForCallableMethods(string assemblyName) + { + // TODO: Consider looking first for assembly-level attributes (i.e., if there are any, + // only use those) to avoid scanning, especially for framework assemblies. + var result = new Dictionary(); + var invokableMethods = GetRequiredLoadedAssembly(assemblyName) + .GetExportedTypes() + .SelectMany(type => type.GetMethods( + BindingFlags.Public | + BindingFlags.DeclaredOnly | + BindingFlags.Instance | + BindingFlags.Static)) + .Where(method => method.IsDefined(typeof(JSInvokableAttribute), inherit: false)); + foreach (var method in invokableMethods) + { + var identifier = method.GetCustomAttribute(false).Identifier ?? method.Name; + var parameterTypes = method.GetParameters().Select(p => p.ParameterType).ToArray(); + + try + { + result.Add(identifier, (method, parameterTypes)); + } + catch (ArgumentException) + { + if (result.ContainsKey(identifier)) + { + throw new InvalidOperationException($"The assembly '{assemblyName}' contains more than one " + + $"[JSInvokable] method with identifier '{identifier}'. All [JSInvokable] methods within the same " + + $"assembly must have different identifiers. You can pass a custom identifier as a parameter to " + + $"the [JSInvokable] attribute."); + } + else + { + throw; + } + } + } + + return result; + } + + private static Assembly GetRequiredLoadedAssembly(string assemblyName) + { + // We don't want to load assemblies on demand here, because we don't necessarily trust + // "assemblyName" to be something the developer intended to load. So only pick from the + // set of already-loaded assemblies. + // In some edge cases this might force developers to explicitly call something on the + // target assembly (from .NET) before they can invoke its allowed methods from JS. + var loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies(); + return loadedAssemblies.FirstOrDefault(a => a.GetName().Name.Equals(assemblyName, StringComparison.Ordinal)) + ?? throw new ArgumentException($"There is no loaded assembly with the name '{assemblyName}'."); + } + + private static Exception UnwrapException(Exception ex) + { + while ((ex is AggregateException || ex is TargetInvocationException) && ex.InnerException != null) + { + ex = ex.InnerException; + } + + return ex; + } + } +} diff --git a/src/JSInterop/src/Microsoft.JSInterop/DotNetObjectRef.cs b/src/JSInterop/src/Microsoft.JSInterop/DotNetObjectRef.cs new file mode 100644 index 0000000000..aa62bee341 --- /dev/null +++ b/src/JSInterop/src/Microsoft.JSInterop/DotNetObjectRef.cs @@ -0,0 +1,66 @@ +// 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; + +namespace Microsoft.JSInterop +{ + /// + /// Wraps a JS interop argument, indicating that the value should not be serialized as JSON + /// but instead should be passed as a reference. + /// + /// To avoid leaking memory, the reference must later be disposed by JS code or by .NET code. + /// + public class DotNetObjectRef : IDisposable + { + /// + /// Gets the object instance represented by this wrapper. + /// + public object Value { get; } + + // We track an associated IJSRuntime purely so that this class can be IDisposable + // in the normal way. Developers are more likely to use objectRef.Dispose() than + // some less familiar API such as JSRuntime.Current.UntrackObjectRef(objectRef). + private IJSRuntime _attachedToRuntime; + + /// + /// Constructs an instance of . + /// + /// The value being wrapped. + public DotNetObjectRef(object value) + { + Value = value; + } + + /// + /// Ensures the is associated with the specified . + /// Developers do not normally need to invoke this manually, since it is called automatically by + /// framework code. + /// + /// The . + public void EnsureAttachedToJsRuntime(IJSRuntime runtime) + { + // The reason we populate _attachedToRuntime here rather than in the constructor + // is to ensure developers can't accidentally try to reuse DotNetObjectRef across + // different IJSRuntime instances. This method gets called as part of serializing + // the DotNetObjectRef during an interop call. + + var existingRuntime = Interlocked.CompareExchange(ref _attachedToRuntime, runtime, null); + if (existingRuntime != null && existingRuntime != runtime) + { + throw new InvalidOperationException($"The {nameof(DotNetObjectRef)} is already associated with a different {nameof(IJSRuntime)}. Do not attempt to re-use {nameof(DotNetObjectRef)} instances with multiple {nameof(IJSRuntime)} instances."); + } + } + + /// + /// Stops tracking this object reference, allowing it to be garbage collected + /// (if there are no other references to it). Once the instance is disposed, it + /// can no longer be used in interop calls from JavaScript code. + /// + public void Dispose() + { + _attachedToRuntime?.UntrackObjectRef(this); + } + } +} diff --git a/src/JSInterop/src/Microsoft.JSInterop/ICustomArgSerializer.cs b/src/JSInterop/src/Microsoft.JSInterop/ICustomArgSerializer.cs new file mode 100644 index 0000000000..f4012af8e9 --- /dev/null +++ b/src/JSInterop/src/Microsoft.JSInterop/ICustomArgSerializer.cs @@ -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. + +namespace Microsoft.JSInterop.Internal +{ + // This is "soft" internal because we're trying to avoid expanding JsonUtil into a sophisticated + // API. Developers who want that would be better served by using a different JSON package + // instead. Also the perf implications of the ICustomArgSerializer approach aren't ideal + // (it forces structs to be boxed, and returning a dictionary means lots more allocations + // and boxing of any value-typed properties). + + /// + /// Internal. Intended for framework use only. + /// + public interface ICustomArgSerializer + { + /// + /// Internal. Intended for framework use only. + /// + object ToJsonPrimitive(); + } +} diff --git a/src/JSInterop/src/Microsoft.JSInterop/IJSInProcessRuntime.cs b/src/JSInterop/src/Microsoft.JSInterop/IJSInProcessRuntime.cs new file mode 100644 index 0000000000..cae5126db2 --- /dev/null +++ b/src/JSInterop/src/Microsoft.JSInterop/IJSInProcessRuntime.cs @@ -0,0 +1,20 @@ +// 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.JSInterop +{ + /// + /// Represents an instance of a JavaScript runtime to which calls may be dispatched. + /// + public interface IJSInProcessRuntime : IJSRuntime + { + /// + /// Invokes the specified JavaScript function synchronously. + /// + /// The JSON-serializable return type. + /// An identifier for the function to invoke. For example, the value "someScope.someFunction" will invoke the function window.someScope.someFunction. + /// JSON-serializable arguments. + /// An instance of obtained by JSON-deserializing the return value. + T Invoke(string identifier, params object[] args); + } +} diff --git a/src/JSInterop/src/Microsoft.JSInterop/IJSRuntime.cs b/src/JSInterop/src/Microsoft.JSInterop/IJSRuntime.cs new file mode 100644 index 0000000000..b56d1f0089 --- /dev/null +++ b/src/JSInterop/src/Microsoft.JSInterop/IJSRuntime.cs @@ -0,0 +1,32 @@ +// 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.Tasks; + +namespace Microsoft.JSInterop +{ + /// + /// Represents an instance of a JavaScript runtime to which calls may be dispatched. + /// + public interface IJSRuntime + { + /// + /// Invokes the specified JavaScript function asynchronously. + /// + /// The JSON-serializable return type. + /// An identifier for the function to invoke. For example, the value "someScope.someFunction" will invoke the function window.someScope.someFunction. + /// JSON-serializable arguments. + /// An instance of obtained by JSON-deserializing the return value. + Task InvokeAsync(string identifier, params object[] args); + + /// + /// Stops tracking the .NET object represented by the . + /// This allows it to be garbage collected (if nothing else holds a reference to it) + /// and means the JS-side code can no longer invoke methods on the instance or pass + /// it as an argument to subsequent calls. + /// + /// The reference to stop tracking. + /// This method is called automatically by . + void UntrackObjectRef(DotNetObjectRef dotNetObjectRef); + } +} diff --git a/src/JSInterop/src/Microsoft.JSInterop/InteropArgSerializerStrategy.cs b/src/JSInterop/src/Microsoft.JSInterop/InteropArgSerializerStrategy.cs new file mode 100644 index 0000000000..663c1cf85a --- /dev/null +++ b/src/JSInterop/src/Microsoft.JSInterop/InteropArgSerializerStrategy.cs @@ -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 Microsoft.JSInterop.Internal; +using SimpleJson; +using System; +using System.Collections.Generic; + +namespace Microsoft.JSInterop +{ + internal class InteropArgSerializerStrategy : PocoJsonSerializerStrategy + { + private readonly JSRuntimeBase _jsRuntime; + private const string _dotNetObjectPrefix = "__dotNetObject:"; + private object _storageLock = new object(); + private long _nextId = 1; // Start at 1, because 0 signals "no object" + private Dictionary _trackedRefsById = new Dictionary(); + private Dictionary _trackedIdsByRef = new Dictionary(); + + public InteropArgSerializerStrategy(JSRuntimeBase jsRuntime) + { + _jsRuntime = jsRuntime ?? throw new ArgumentNullException(nameof(jsRuntime)); + } + + protected override bool TrySerializeKnownTypes(object input, out object output) + { + switch (input) + { + case DotNetObjectRef marshalByRefValue: + EnsureDotNetObjectTracked(marshalByRefValue, out var id); + + // Special value format recognized by the code in Microsoft.JSInterop.js + // If we have to make it more clash-resistant, we can do + output = _dotNetObjectPrefix + id; + + return true; + + case ICustomArgSerializer customArgSerializer: + output = customArgSerializer.ToJsonPrimitive(); + return true; + + default: + return base.TrySerializeKnownTypes(input, out output); + } + } + + public override object DeserializeObject(object value, Type type) + { + if (value is string valueString) + { + if (valueString.StartsWith(_dotNetObjectPrefix)) + { + var dotNetObjectId = long.Parse(valueString.Substring(_dotNetObjectPrefix.Length)); + return FindDotNetObject(dotNetObjectId); + } + } + + return base.DeserializeObject(value, type); + } + + public object FindDotNetObject(long dotNetObjectId) + { + lock (_storageLock) + { + return _trackedRefsById.TryGetValue(dotNetObjectId, out var dotNetObjectRef) + ? dotNetObjectRef.Value + : throw new ArgumentException($"There is no tracked object with id '{dotNetObjectId}'. Perhaps the reference was already released.", nameof(dotNetObjectId)); + } + } + + /// + /// Stops tracking the specified .NET object reference. + /// This overload is typically invoked from JS code via JS interop. + /// + /// The ID of the . + public void ReleaseDotNetObject(long dotNetObjectId) + { + lock (_storageLock) + { + if (_trackedRefsById.TryGetValue(dotNetObjectId, out var dotNetObjectRef)) + { + _trackedRefsById.Remove(dotNetObjectId); + _trackedIdsByRef.Remove(dotNetObjectRef); + } + } + } + + /// + /// Stops tracking the specified .NET object reference. + /// This overload is typically invoked from .NET code by . + /// + /// The . + public void ReleaseDotNetObject(DotNetObjectRef dotNetObjectRef) + { + lock (_storageLock) + { + if (_trackedIdsByRef.TryGetValue(dotNetObjectRef, out var dotNetObjectId)) + { + _trackedRefsById.Remove(dotNetObjectId); + _trackedIdsByRef.Remove(dotNetObjectRef); + } + } + } + + private void EnsureDotNetObjectTracked(DotNetObjectRef dotNetObjectRef, out long dotNetObjectId) + { + dotNetObjectRef.EnsureAttachedToJsRuntime(_jsRuntime); + + lock (_storageLock) + { + // Assign an ID only if it doesn't already have one + if (!_trackedIdsByRef.TryGetValue(dotNetObjectRef, out dotNetObjectId)) + { + dotNetObjectId = _nextId++; + _trackedRefsById.Add(dotNetObjectId, dotNetObjectRef); + _trackedIdsByRef.Add(dotNetObjectRef, dotNetObjectId); + } + } + } + } +} diff --git a/src/JSInterop/src/Microsoft.JSInterop/JSAsyncCallResult.cs b/src/JSInterop/src/Microsoft.JSInterop/JSAsyncCallResult.cs new file mode 100644 index 0000000000..d46517eddc --- /dev/null +++ b/src/JSInterop/src/Microsoft.JSInterop/JSAsyncCallResult.cs @@ -0,0 +1,36 @@ +// 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.JSInterop.Internal +{ + // This type takes care of a special case in handling the result of an async call from + // .NET to JS. The information about what type the result should be exists only on the + // corresponding TaskCompletionSource. We don't have that information at the time + // that we deserialize the incoming argsJson before calling DotNetDispatcher.EndInvoke. + // Declaring the EndInvoke parameter type as JSAsyncCallResult defers the deserialization + // until later when we have access to the TaskCompletionSource. + // + // There's no reason why developers would need anything similar to this in user code, + // because this is the mechanism by which we resolve the incoming argsJson to the correct + // user types before completing calls. + // + // It's marked as 'public' only because it has to be for use as an argument on a + // [JSInvokable] method. + + /// + /// Intended for framework use only. + /// + public class JSAsyncCallResult + { + internal object ResultOrException { get; } + + /// + /// Constructs an instance of . + /// + /// The result of the call. + internal JSAsyncCallResult(object resultOrException) + { + ResultOrException = resultOrException; + } + } +} diff --git a/src/JSInterop/src/Microsoft.JSInterop/JSException.cs b/src/JSInterop/src/Microsoft.JSInterop/JSException.cs new file mode 100644 index 0000000000..2929f69311 --- /dev/null +++ b/src/JSInterop/src/Microsoft.JSInterop/JSException.cs @@ -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.JSInterop +{ + /// + /// Represents errors that occur during an interop call from .NET to JavaScript. + /// + public class JSException : Exception + { + /// + /// Constructs an instance of . + /// + /// The exception message. + public JSException(string message) : base(message) + { + } + } +} diff --git a/src/JSInterop/src/Microsoft.JSInterop/JSInProcessRuntimeBase.cs b/src/JSInterop/src/Microsoft.JSInterop/JSInProcessRuntimeBase.cs new file mode 100644 index 0000000000..49a47d0595 --- /dev/null +++ b/src/JSInterop/src/Microsoft.JSInterop/JSInProcessRuntimeBase.cs @@ -0,0 +1,32 @@ +// 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.JSInterop +{ + /// + /// Abstract base class for an in-process JavaScript runtime. + /// + public abstract class JSInProcessRuntimeBase : JSRuntimeBase, IJSInProcessRuntime + { + /// + /// Invokes the specified JavaScript function synchronously. + /// + /// The JSON-serializable return type. + /// An identifier for the function to invoke. For example, the value "someScope.someFunction" will invoke the function window.someScope.someFunction. + /// JSON-serializable arguments. + /// An instance of obtained by JSON-deserializing the return value. + public T Invoke(string identifier, params object[] args) + { + var resultJson = InvokeJS(identifier, Json.Serialize(args, ArgSerializerStrategy)); + return Json.Deserialize(resultJson, ArgSerializerStrategy); + } + + /// + /// Performs a synchronous function invocation. + /// + /// The identifier for the function to invoke. + /// A JSON representation of the arguments. + /// A JSON representation of the result. + protected abstract string InvokeJS(string identifier, string argsJson); + } +} diff --git a/src/JSInterop/src/Microsoft.JSInterop/JSInvokableAttribute.cs b/src/JSInterop/src/Microsoft.JSInterop/JSInvokableAttribute.cs new file mode 100644 index 0000000000..e037078cba --- /dev/null +++ b/src/JSInterop/src/Microsoft.JSInterop/JSInvokableAttribute.cs @@ -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; + +namespace Microsoft.JSInterop +{ + /// + /// Identifies a .NET method as allowing invocation from JavaScript code. + /// Any method marked with this attribute may receive arbitrary parameter values + /// from untrusted callers. All inputs should be validated carefully. + /// + [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] + public class JSInvokableAttribute : Attribute + { + /// + /// Gets the identifier for the method. The identifier must be unique within the scope + /// of an assembly. + /// + /// If not set, the identifier is taken from the name of the method. In this case the + /// method name must be unique within the assembly. + /// + public string Identifier { get; } + + /// + /// Constructs an instance of without setting + /// an identifier for the method. + /// + public JSInvokableAttribute() + { + } + + /// + /// Constructs an instance of using the specified + /// identifier. + /// + /// An identifier for the method, which must be unique within the scope of the assembly. + public JSInvokableAttribute(string identifier) + { + if (string.IsNullOrEmpty(identifier)) + { + throw new ArgumentException("Cannot be null or empty", nameof(identifier)); + } + + Identifier = identifier; + } + } +} diff --git a/src/JSInterop/src/Microsoft.JSInterop/JSRuntime.cs b/src/JSInterop/src/Microsoft.JSInterop/JSRuntime.cs new file mode 100644 index 0000000000..5a9830fa35 --- /dev/null +++ b/src/JSInterop/src/Microsoft.JSInterop/JSRuntime.cs @@ -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.Threading; + +namespace Microsoft.JSInterop +{ + /// + /// Provides mechanisms for accessing the current . + /// + public static class JSRuntime + { + private static AsyncLocal _currentJSRuntime + = new AsyncLocal(); + + /// + /// Gets the current , if any. + /// + public static IJSRuntime Current => _currentJSRuntime.Value; + + /// + /// Sets the current JS runtime to the supplied instance. + /// + /// This is intended for framework use. Developers should not normally need to call this method. + /// + /// The new current . + public static void SetCurrentJSRuntime(IJSRuntime instance) + { + _currentJSRuntime.Value = instance + ?? throw new ArgumentNullException(nameof(instance)); + } + } +} diff --git a/src/JSInterop/src/Microsoft.JSInterop/JSRuntimeBase.cs b/src/JSInterop/src/Microsoft.JSInterop/JSRuntimeBase.cs new file mode 100644 index 0000000000..09379396cf --- /dev/null +++ b/src/JSInterop/src/Microsoft.JSInterop/JSRuntimeBase.cs @@ -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 System.Collections.Concurrent; +using System.Threading; +using System.Threading.Tasks; + +namespace Microsoft.JSInterop +{ + /// + /// Abstract base class for a JavaScript runtime. + /// + public abstract class JSRuntimeBase : IJSRuntime + { + private long _nextPendingTaskId = 1; // Start at 1 because zero signals "no response needed" + private readonly ConcurrentDictionary _pendingTasks + = new ConcurrentDictionary(); + + internal InteropArgSerializerStrategy ArgSerializerStrategy { get; } + + /// + /// Constructs an instance of . + /// + public JSRuntimeBase() + { + ArgSerializerStrategy = new InteropArgSerializerStrategy(this); + } + + /// + public void UntrackObjectRef(DotNetObjectRef dotNetObjectRef) + => ArgSerializerStrategy.ReleaseDotNetObject(dotNetObjectRef); + + /// + /// Invokes the specified JavaScript function asynchronously. + /// + /// The JSON-serializable return type. + /// An identifier for the function to invoke. For example, the value "someScope.someFunction" will invoke the function window.someScope.someFunction. + /// JSON-serializable arguments. + /// An instance of obtained by JSON-deserializing the return value. + public Task InvokeAsync(string identifier, params object[] args) + { + // We might consider also adding a default timeout here in case we don't want to + // risk a memory leak in the scenario where the JS-side code is failing to complete + // the operation. + + var taskId = Interlocked.Increment(ref _nextPendingTaskId); + var tcs = new TaskCompletionSource(); + _pendingTasks[taskId] = tcs; + + try + { + var argsJson = args?.Length > 0 + ? Json.Serialize(args, ArgSerializerStrategy) + : null; + BeginInvokeJS(taskId, identifier, argsJson); + return tcs.Task; + } + catch + { + _pendingTasks.TryRemove(taskId, out _); + throw; + } + } + + /// + /// Begins an asynchronous function invocation. + /// + /// The identifier for the function invocation, or zero if no async callback is required. + /// The identifier for the function to invoke. + /// A JSON representation of the arguments. + protected abstract void BeginInvokeJS(long asyncHandle, string identifier, string argsJson); + + internal void EndInvokeDotNet(string callId, bool success, object resultOrException) + { + // For failures, the common case is to call EndInvokeDotNet with the Exception object. + // For these we'll serialize as something that's useful to receive on the JS side. + // If the value is not an Exception, we'll just rely on it being directly JSON-serializable. + if (!success && resultOrException is Exception) + { + resultOrException = resultOrException.ToString(); + } + + // We pass 0 as the async handle because we don't want the JS-side code to + // send back any notification (we're just providing a result for an existing async call) + BeginInvokeJS(0, "DotNet.jsCallDispatcher.endInvokeDotNetFromJS", Json.Serialize(new[] { + callId, + success, + resultOrException + }, ArgSerializerStrategy)); + } + + internal void EndInvokeJS(long asyncHandle, bool succeeded, object resultOrException) + { + if (!_pendingTasks.TryRemove(asyncHandle, out var tcs)) + { + throw new ArgumentException($"There is no pending task with handle '{asyncHandle}'."); + } + + if (succeeded) + { + var resultType = TaskGenericsUtil.GetTaskCompletionSourceResultType(tcs); + if (resultOrException is SimpleJson.JsonObject || resultOrException is SimpleJson.JsonArray) + { + resultOrException = ArgSerializerStrategy.DeserializeObject(resultOrException, resultType); + } + + TaskGenericsUtil.SetTaskCompletionSourceResult(tcs, resultOrException); + } + else + { + TaskGenericsUtil.SetTaskCompletionSourceException(tcs, new JSException(resultOrException.ToString())); + } + } + } +} diff --git a/src/JSInterop/src/Microsoft.JSInterop/Json/CamelCase.cs b/src/JSInterop/src/Microsoft.JSInterop/Json/CamelCase.cs new file mode 100644 index 0000000000..8caae1387b --- /dev/null +++ b/src/JSInterop/src/Microsoft.JSInterop/Json/CamelCase.cs @@ -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. + +using System; + +namespace Microsoft.JSInterop +{ + internal static class CamelCase + { + public static string MemberNameToCamelCase(string value) + { + if (string.IsNullOrEmpty(value)) + { + throw new ArgumentException( + $"The value '{value ?? "null"}' is not a valid member name.", + nameof(value)); + } + + // If we don't need to modify the value, bail out without creating a char array + if (!char.IsUpper(value[0])) + { + return value; + } + + // We have to modify at least one character + var chars = value.ToCharArray(); + + var length = chars.Length; + if (length < 2 || !char.IsUpper(chars[1])) + { + // Only the first character needs to be modified + // Note that this branch is functionally necessary, because the 'else' branch below + // never looks at char[1]. It's always looking at the n+2 character. + chars[0] = char.ToLowerInvariant(chars[0]); + } + else + { + // If chars[0] and chars[1] are both upper, then we'll lowercase the first char plus + // any consecutive uppercase ones, stopping if we find any char that is followed by a + // non-uppercase one + var i = 0; + while (i < length) + { + chars[i] = char.ToLowerInvariant(chars[i]); + + i++; + + // If the next-plus-one char isn't also uppercase, then we're now on the last uppercase, so stop + if (i < length - 1 && !char.IsUpper(chars[i + 1])) + { + break; + } + } + } + + return new string(chars); + } + } +} diff --git a/src/JSInterop/src/Microsoft.JSInterop/Json/Json.cs b/src/JSInterop/src/Microsoft.JSInterop/Json/Json.cs new file mode 100644 index 0000000000..7275dfe427 --- /dev/null +++ b/src/JSInterop/src/Microsoft.JSInterop/Json/Json.cs @@ -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.JSInterop +{ + /// + /// Provides mechanisms for converting between .NET objects and JSON strings for use + /// when making calls to JavaScript functions via . + /// + /// Warning: This is not intended as a general-purpose JSON library. It is only intended + /// for use when making calls via . Eventually its implementation + /// will be replaced by something more general-purpose. + /// + public static class Json + { + /// + /// Serializes the value as a JSON string. + /// + /// The value to serialize. + /// The JSON string. + public static string Serialize(object value) + => SimpleJson.SimpleJson.SerializeObject(value); + + internal static string Serialize(object value, SimpleJson.IJsonSerializerStrategy serializerStrategy) + => SimpleJson.SimpleJson.SerializeObject(value, serializerStrategy); + + /// + /// Deserializes the JSON string, creating an object of the specified generic type. + /// + /// The type of object to create. + /// The JSON string. + /// An object of the specified type. + public static T Deserialize(string json) + => SimpleJson.SimpleJson.DeserializeObject(json); + + internal static T Deserialize(string json, SimpleJson.IJsonSerializerStrategy serializerStrategy) + => SimpleJson.SimpleJson.DeserializeObject(json, serializerStrategy); + } +} diff --git a/src/JSInterop/src/Microsoft.JSInterop/Json/SimpleJson/README.txt b/src/JSInterop/src/Microsoft.JSInterop/Json/SimpleJson/README.txt new file mode 100644 index 0000000000..5e58eb7106 --- /dev/null +++ b/src/JSInterop/src/Microsoft.JSInterop/Json/SimpleJson/README.txt @@ -0,0 +1,24 @@ +SimpleJson is from https://github.com/facebook-csharp-sdk/simple-json + +LICENSE (from https://github.com/facebook-csharp-sdk/simple-json/blob/08b6871e8f63e866810d25e7a03c48502c9a234b/LICENSE.txt): +===== +Copyright (c) 2011, The Outercurve Foundation + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/src/JSInterop/src/Microsoft.JSInterop/Json/SimpleJson/SimpleJson.cs b/src/JSInterop/src/Microsoft.JSInterop/Json/SimpleJson/SimpleJson.cs new file mode 100644 index 0000000000..d12c6fae30 --- /dev/null +++ b/src/JSInterop/src/Microsoft.JSInterop/Json/SimpleJson/SimpleJson.cs @@ -0,0 +1,2201 @@ +//----------------------------------------------------------------------- +// +// Copyright (c) 2011, The Outercurve Foundation. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.opensource.org/licenses/mit-license.php +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Nathan Totten (ntotten.com), Jim Zimmerman (jimzimmerman.com) and Prabir Shrestha (prabir.me) +// https://github.com/facebook-csharp-sdk/simple-json +//----------------------------------------------------------------------- + +// VERSION: + +// NOTE: uncomment the following line to make SimpleJson class internal. +#define SIMPLE_JSON_INTERNAL + +// NOTE: uncomment the following line to make JsonArray and JsonObject class internal. +#define SIMPLE_JSON_OBJARRAYINTERNAL + +// NOTE: uncomment the following line to enable dynamic support. +//#define SIMPLE_JSON_DYNAMIC + +// NOTE: uncomment the following line to enable DataContract support. +//#define SIMPLE_JSON_DATACONTRACT + +// NOTE: uncomment the following line to enable IReadOnlyCollection and IReadOnlyList support. +//#define SIMPLE_JSON_READONLY_COLLECTIONS + +// NOTE: uncomment the following line to disable linq expressions/compiled lambda (better performance) instead of method.invoke(). +// define if you are using .net framework <= 3.0 or < WP7.5 +#define SIMPLE_JSON_NO_LINQ_EXPRESSION + +// NOTE: uncomment the following line if you are compiling under Window Metro style application/library. +// usually already defined in properties +//#define NETFX_CORE; + +// If you are targetting WinStore, WP8 and NET4.5+ PCL make sure to #define SIMPLE_JSON_TYPEINFO; + +// original json parsing code from http://techblog.procurios.nl/k/618/news/view/14605/14863/How-do-I-write-my-own-parser-for-JSON.html + +#if NETFX_CORE +#define SIMPLE_JSON_TYPEINFO +#endif + +using System; +using System.CodeDom.Compiler; +using System.Collections; +using System.Collections.Generic; +#if !SIMPLE_JSON_NO_LINQ_EXPRESSION +using System.Linq.Expressions; +#endif +using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; +#if SIMPLE_JSON_DYNAMIC +using System.Dynamic; +#endif +using System.Globalization; +using System.Reflection; +using System.Runtime.Serialization; +using System.Text; +using Microsoft.JSInterop; +using SimpleJson.Reflection; + +// ReSharper disable LoopCanBeConvertedToQuery +// ReSharper disable RedundantExplicitArrayCreation +// ReSharper disable SuggestUseVarKeywordEvident +namespace SimpleJson +{ + /// + /// Represents the json array. + /// + [GeneratedCode("simple-json", "1.0.0")] + [EditorBrowsable(EditorBrowsableState.Never)] + [SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix")] +#if SIMPLE_JSON_OBJARRAYINTERNAL + internal +#else + public +#endif + class JsonArray : List + { + /// + /// Initializes a new instance of the class. + /// + public JsonArray() { } + + /// + /// Initializes a new instance of the class. + /// + /// The capacity of the json array. + public JsonArray(int capacity) : base(capacity) { } + + /// + /// The json representation of the array. + /// + /// The json representation of the array. + public override string ToString() + { + return SimpleJson.SerializeObject(this) ?? string.Empty; + } + } + + /// + /// Represents the json object. + /// + [GeneratedCode("simple-json", "1.0.0")] + [EditorBrowsable(EditorBrowsableState.Never)] + [SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix")] +#if SIMPLE_JSON_OBJARRAYINTERNAL + internal +#else + public +#endif + class JsonObject : +#if SIMPLE_JSON_DYNAMIC + DynamicObject, +#endif + IDictionary + { + /// + /// The internal member dictionary. + /// + private readonly Dictionary _members; + + /// + /// Initializes a new instance of . + /// + public JsonObject() + { + _members = new Dictionary(); + } + + /// + /// Initializes a new instance of . + /// + /// The implementation to use when comparing keys, or null to use the default for the type of the key. + public JsonObject(IEqualityComparer comparer) + { + _members = new Dictionary(comparer); + } + + /// + /// Gets the at the specified index. + /// + /// + public object this[int index] + { + get { return GetAtIndex(_members, index); } + } + + internal static object GetAtIndex(IDictionary obj, int index) + { + if (obj == null) + throw new ArgumentNullException("obj"); + if (index >= obj.Count) + throw new ArgumentOutOfRangeException("index"); + int i = 0; + foreach (KeyValuePair o in obj) + if (i++ == index) return o.Value; + return null; + } + + /// + /// Adds the specified key. + /// + /// The key. + /// The value. + public void Add(string key, object value) + { + _members.Add(key, value); + } + + /// + /// Determines whether the specified key contains key. + /// + /// The key. + /// + /// true if the specified key contains key; otherwise, false. + /// + public bool ContainsKey(string key) + { + return _members.ContainsKey(key); + } + + /// + /// Gets the keys. + /// + /// The keys. + public ICollection Keys + { + get { return _members.Keys; } + } + + /// + /// Removes the specified key. + /// + /// The key. + /// + public bool Remove(string key) + { + return _members.Remove(key); + } + + /// + /// Tries the get value. + /// + /// The key. + /// The value. + /// + public bool TryGetValue(string key, out object value) + { + return _members.TryGetValue(key, out value); + } + + /// + /// Gets the values. + /// + /// The values. + public ICollection Values + { + get { return _members.Values; } + } + + /// + /// Gets or sets the with the specified key. + /// + /// + public object this[string key] + { + get { return _members[key]; } + set { _members[key] = value; } + } + + /// + /// Adds the specified item. + /// + /// The item. + public void Add(KeyValuePair item) + { + _members.Add(item.Key, item.Value); + } + + /// + /// Clears this instance. + /// + public void Clear() + { + _members.Clear(); + } + + /// + /// Determines whether [contains] [the specified item]. + /// + /// The item. + /// + /// true if [contains] [the specified item]; otherwise, false. + /// + public bool Contains(KeyValuePair item) + { + return _members.ContainsKey(item.Key) && _members[item.Key] == item.Value; + } + + /// + /// Copies to. + /// + /// The array. + /// Index of the array. + public void CopyTo(KeyValuePair[] array, int arrayIndex) + { + if (array == null) throw new ArgumentNullException("array"); + int num = Count; + foreach (KeyValuePair kvp in this) + { + array[arrayIndex++] = kvp; + if (--num <= 0) + return; + } + } + + /// + /// Gets the count. + /// + /// The count. + public int Count + { + get { return _members.Count; } + } + + /// + /// Gets a value indicating whether this instance is read only. + /// + /// + /// true if this instance is read only; otherwise, false. + /// + public bool IsReadOnly + { + get { return false; } + } + + /// + /// Removes the specified item. + /// + /// The item. + /// + public bool Remove(KeyValuePair item) + { + return _members.Remove(item.Key); + } + + /// + /// Gets the enumerator. + /// + /// + public IEnumerator> GetEnumerator() + { + return _members.GetEnumerator(); + } + + /// + /// Returns an enumerator that iterates through a collection. + /// + /// + /// An object that can be used to iterate through the collection. + /// + IEnumerator IEnumerable.GetEnumerator() + { + return _members.GetEnumerator(); + } + + /// + /// Returns a json that represents the current . + /// + /// + /// A json that represents the current . + /// + public override string ToString() + { + return SimpleJson.SerializeObject(this); + } + +#if SIMPLE_JSON_DYNAMIC + /// + /// Provides implementation for type conversion operations. Classes derived from the class can override this method to specify dynamic behavior for operations that convert an object from one type to another. + /// + /// Provides information about the conversion operation. The binder.Type property provides the type to which the object must be converted. For example, for the statement (String)sampleObject in C# (CType(sampleObject, Type) in Visual Basic), where sampleObject is an instance of the class derived from the class, binder.Type returns the type. The binder.Explicit property provides information about the kind of conversion that occurs. It returns true for explicit conversion and false for implicit conversion. + /// The result of the type conversion operation. + /// + /// Alwasy returns true. + /// + public override bool TryConvert(ConvertBinder binder, out object result) + { + // + if (binder == null) + throw new ArgumentNullException("binder"); + // + Type targetType = binder.Type; + + if ((targetType == typeof(IEnumerable)) || + (targetType == typeof(IEnumerable>)) || + (targetType == typeof(IDictionary)) || + (targetType == typeof(IDictionary))) + { + result = this; + return true; + } + + return base.TryConvert(binder, out result); + } + + /// + /// Provides the implementation for operations that delete an object member. This method is not intended for use in C# or Visual Basic. + /// + /// Provides information about the deletion. + /// + /// Alwasy returns true. + /// + public override bool TryDeleteMember(DeleteMemberBinder binder) + { + // + if (binder == null) + throw new ArgumentNullException("binder"); + // + return _members.Remove(binder.Name); + } + + /// + /// Provides the implementation for operations that get a value by index. Classes derived from the class can override this method to specify dynamic behavior for indexing operations. + /// + /// Provides information about the operation. + /// The indexes that are used in the operation. For example, for the sampleObject[3] operation in C# (sampleObject(3) in Visual Basic), where sampleObject is derived from the DynamicObject class, is equal to 3. + /// The result of the index operation. + /// + /// Alwasy returns true. + /// + public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result) + { + if (indexes == null) throw new ArgumentNullException("indexes"); + if (indexes.Length == 1) + { + result = ((IDictionary)this)[(string)indexes[0]]; + return true; + } + result = null; + return true; + } + + /// + /// Provides the implementation for operations that get member values. Classes derived from the class can override this method to specify dynamic behavior for operations such as getting a value for a property. + /// + /// Provides information about the object that called the dynamic operation. The binder.Name property provides the name of the member on which the dynamic operation is performed. For example, for the Console.WriteLine(sampleObject.SampleProperty) statement, where sampleObject is an instance of the class derived from the class, binder.Name returns "SampleProperty". The binder.IgnoreCase property specifies whether the member name is case-sensitive. + /// The result of the get operation. For example, if the method is called for a property, you can assign the property value to . + /// + /// Alwasy returns true. + /// + public override bool TryGetMember(GetMemberBinder binder, out object result) + { + object value; + if (_members.TryGetValue(binder.Name, out value)) + { + result = value; + return true; + } + result = null; + return true; + } + + /// + /// Provides the implementation for operations that set a value by index. Classes derived from the class can override this method to specify dynamic behavior for operations that access objects by a specified index. + /// + /// Provides information about the operation. + /// The indexes that are used in the operation. For example, for the sampleObject[3] = 10 operation in C# (sampleObject(3) = 10 in Visual Basic), where sampleObject is derived from the class, is equal to 3. + /// The value to set to the object that has the specified index. For example, for the sampleObject[3] = 10 operation in C# (sampleObject(3) = 10 in Visual Basic), where sampleObject is derived from the class, is equal to 10. + /// + /// true if the operation is successful; otherwise, false. If this method returns false, the run-time binder of the language determines the behavior. (In most cases, a language-specific run-time exception is thrown. + /// + public override bool TrySetIndex(SetIndexBinder binder, object[] indexes, object value) + { + if (indexes == null) throw new ArgumentNullException("indexes"); + if (indexes.Length == 1) + { + ((IDictionary)this)[(string)indexes[0]] = value; + return true; + } + return base.TrySetIndex(binder, indexes, value); + } + + /// + /// Provides the implementation for operations that set member values. Classes derived from the class can override this method to specify dynamic behavior for operations such as setting a value for a property. + /// + /// Provides information about the object that called the dynamic operation. The binder.Name property provides the name of the member to which the value is being assigned. For example, for the statement sampleObject.SampleProperty = "Test", where sampleObject is an instance of the class derived from the class, binder.Name returns "SampleProperty". The binder.IgnoreCase property specifies whether the member name is case-sensitive. + /// The value to set to the member. For example, for sampleObject.SampleProperty = "Test", where sampleObject is an instance of the class derived from the class, the is "Test". + /// + /// true if the operation is successful; otherwise, false. If this method returns false, the run-time binder of the language determines the behavior. (In most cases, a language-specific run-time exception is thrown.) + /// + public override bool TrySetMember(SetMemberBinder binder, object value) + { + // + if (binder == null) + throw new ArgumentNullException("binder"); + // + _members[binder.Name] = value; + return true; + } + + /// + /// Returns the enumeration of all dynamic member names. + /// + /// + /// A sequence that contains dynamic member names. + /// + public override IEnumerable GetDynamicMemberNames() + { + foreach (var key in Keys) + yield return key; + } +#endif + } +} + +namespace SimpleJson +{ + /// + /// This class encodes and decodes JSON strings. + /// Spec. details, see http://www.json.org/ + /// + /// JSON uses Arrays and Objects. These correspond here to the datatypes JsonArray(IList<object>) and JsonObject(IDictionary<string,object>). + /// All numbers are parsed to doubles. + /// + [GeneratedCode("simple-json", "1.0.0")] +#if SIMPLE_JSON_INTERNAL + internal +#else + public +#endif + static class SimpleJson + { + private const int TOKEN_NONE = 0; + private const int TOKEN_CURLY_OPEN = 1; + private const int TOKEN_CURLY_CLOSE = 2; + private const int TOKEN_SQUARED_OPEN = 3; + private const int TOKEN_SQUARED_CLOSE = 4; + private const int TOKEN_COLON = 5; + private const int TOKEN_COMMA = 6; + private const int TOKEN_STRING = 7; + private const int TOKEN_NUMBER = 8; + private const int TOKEN_TRUE = 9; + private const int TOKEN_FALSE = 10; + private const int TOKEN_NULL = 11; + private const int BUILDER_CAPACITY = 2000; + + private static readonly char[] EscapeTable; + private static readonly char[] EscapeCharacters = new char[] { '"', '\\', '\b', '\f', '\n', '\r', '\t' }; + private static readonly string EscapeCharactersString = new string(EscapeCharacters); + + static SimpleJson() + { + EscapeTable = new char[93]; + EscapeTable['"'] = '"'; + EscapeTable['\\'] = '\\'; + EscapeTable['\b'] = 'b'; + EscapeTable['\f'] = 'f'; + EscapeTable['\n'] = 'n'; + EscapeTable['\r'] = 'r'; + EscapeTable['\t'] = 't'; + } + + /// + /// Parses the string json into a value + /// + /// A JSON string. + /// An IList<object>, a IDictionary<string,object>, a double, a string, null, true, or false + public static object DeserializeObject(string json) + { + object obj; + if (TryDeserializeObject(json, out obj)) + return obj; + throw new SerializationException("Invalid JSON string"); + } + + /// + /// Try parsing the json string into a value. + /// + /// + /// A JSON string. + /// + /// + /// The object. + /// + /// + /// Returns true if successful otherwise false. + /// + [SuppressMessage("Microsoft.Design", "CA1007:UseGenericsWhereAppropriate", Justification="Need to support .NET 2")] + public static bool TryDeserializeObject(string json, out object obj) + { + bool success = true; + if (json != null) + { + char[] charArray = json.ToCharArray(); + int index = 0; + obj = ParseValue(charArray, ref index, ref success); + } + else + obj = null; + + return success; + } + + public static object DeserializeObject(string json, Type type, IJsonSerializerStrategy jsonSerializerStrategy) + { + object jsonObject = DeserializeObject(json); + return type == null || jsonObject != null && ReflectionUtils.IsAssignableFrom(jsonObject.GetType(), type) + ? jsonObject + : (jsonSerializerStrategy ?? CurrentJsonSerializerStrategy).DeserializeObject(jsonObject, type); + } + + public static object DeserializeObject(string json, Type type) + { + return DeserializeObject(json, type, null); + } + + public static T DeserializeObject(string json, IJsonSerializerStrategy jsonSerializerStrategy) + { + return (T)DeserializeObject(json, typeof(T), jsonSerializerStrategy); + } + + public static T DeserializeObject(string json) + { + return (T)DeserializeObject(json, typeof(T), null); + } + + /// + /// Converts a IDictionary<string,object> / IList<object> object into a JSON string + /// + /// A IDictionary<string,object> / IList<object> + /// Serializer strategy to use + /// A JSON encoded string, or null if object 'json' is not serializable + public static string SerializeObject(object json, IJsonSerializerStrategy jsonSerializerStrategy) + { + StringBuilder builder = new StringBuilder(BUILDER_CAPACITY); + bool success = SerializeValue(jsonSerializerStrategy, json, builder); + return (success ? builder.ToString() : null); + } + + public static string SerializeObject(object json) + { + return SerializeObject(json, CurrentJsonSerializerStrategy); + } + + public static string EscapeToJavascriptString(string jsonString) + { + if (string.IsNullOrEmpty(jsonString)) + return jsonString; + + StringBuilder sb = new StringBuilder(); + char c; + + for (int i = 0; i < jsonString.Length; ) + { + c = jsonString[i++]; + + if (c == '\\') + { + int remainingLength = jsonString.Length - i; + if (remainingLength >= 2) + { + char lookahead = jsonString[i]; + if (lookahead == '\\') + { + sb.Append('\\'); + ++i; + } + else if (lookahead == '"') + { + sb.Append("\""); + ++i; + } + else if (lookahead == 't') + { + sb.Append('\t'); + ++i; + } + else if (lookahead == 'b') + { + sb.Append('\b'); + ++i; + } + else if (lookahead == 'n') + { + sb.Append('\n'); + ++i; + } + else if (lookahead == 'r') + { + sb.Append('\r'); + ++i; + } + } + } + else + { + sb.Append(c); + } + } + return sb.ToString(); + } + + static IDictionary ParseObject(char[] json, ref int index, ref bool success) + { + IDictionary table = new JsonObject(); + int token; + + // { + NextToken(json, ref index); + + bool done = false; + while (!done) + { + token = LookAhead(json, index); + if (token == TOKEN_NONE) + { + success = false; + return null; + } + else if (token == TOKEN_COMMA) + NextToken(json, ref index); + else if (token == TOKEN_CURLY_CLOSE) + { + NextToken(json, ref index); + return table; + } + else + { + // name + string name = ParseString(json, ref index, ref success); + if (!success) + { + success = false; + return null; + } + // : + token = NextToken(json, ref index); + if (token != TOKEN_COLON) + { + success = false; + return null; + } + // value + object value = ParseValue(json, ref index, ref success); + if (!success) + { + success = false; + return null; + } + table[name] = value; + } + } + return table; + } + + static JsonArray ParseArray(char[] json, ref int index, ref bool success) + { + JsonArray array = new JsonArray(); + + // [ + NextToken(json, ref index); + + bool done = false; + while (!done) + { + int token = LookAhead(json, index); + if (token == TOKEN_NONE) + { + success = false; + return null; + } + else if (token == TOKEN_COMMA) + NextToken(json, ref index); + else if (token == TOKEN_SQUARED_CLOSE) + { + NextToken(json, ref index); + break; + } + else + { + object value = ParseValue(json, ref index, ref success); + if (!success) + return null; + array.Add(value); + } + } + return array; + } + + static object ParseValue(char[] json, ref int index, ref bool success) + { + switch (LookAhead(json, index)) + { + case TOKEN_STRING: + return ParseString(json, ref index, ref success); + case TOKEN_NUMBER: + return ParseNumber(json, ref index, ref success); + case TOKEN_CURLY_OPEN: + return ParseObject(json, ref index, ref success); + case TOKEN_SQUARED_OPEN: + return ParseArray(json, ref index, ref success); + case TOKEN_TRUE: + NextToken(json, ref index); + return true; + case TOKEN_FALSE: + NextToken(json, ref index); + return false; + case TOKEN_NULL: + NextToken(json, ref index); + return null; + case TOKEN_NONE: + break; + } + success = false; + return null; + } + + static string ParseString(char[] json, ref int index, ref bool success) + { + StringBuilder s = new StringBuilder(BUILDER_CAPACITY); + char c; + + EatWhitespace(json, ref index); + + // " + c = json[index++]; + bool complete = false; + while (!complete) + { + if (index == json.Length) + break; + + c = json[index++]; + if (c == '"') + { + complete = true; + break; + } + else if (c == '\\') + { + if (index == json.Length) + break; + c = json[index++]; + if (c == '"') + s.Append('"'); + else if (c == '\\') + s.Append('\\'); + else if (c == '/') + s.Append('/'); + else if (c == 'b') + s.Append('\b'); + else if (c == 'f') + s.Append('\f'); + else if (c == 'n') + s.Append('\n'); + else if (c == 'r') + s.Append('\r'); + else if (c == 't') + s.Append('\t'); + else if (c == 'u') + { + int remainingLength = json.Length - index; + if (remainingLength >= 4) + { + // parse the 32 bit hex into an integer codepoint + uint codePoint; + if (!(success = UInt32.TryParse(new string(json, index, 4), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out codePoint))) + return ""; + + // convert the integer codepoint to a unicode char and add to string + if (0xD800 <= codePoint && codePoint <= 0xDBFF) // if high surrogate + { + index += 4; // skip 4 chars + remainingLength = json.Length - index; + if (remainingLength >= 6) + { + uint lowCodePoint; + if (new string(json, index, 2) == "\\u" && UInt32.TryParse(new string(json, index + 2, 4), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out lowCodePoint)) + { + if (0xDC00 <= lowCodePoint && lowCodePoint <= 0xDFFF) // if low surrogate + { + s.Append((char)codePoint); + s.Append((char)lowCodePoint); + index += 6; // skip 6 chars + continue; + } + } + } + success = false; // invalid surrogate pair + return ""; + } + s.Append(ConvertFromUtf32((int)codePoint)); + // skip 4 chars + index += 4; + } + else + break; + } + } + else + s.Append(c); + } + if (!complete) + { + success = false; + return null; + } + return s.ToString(); + } + + private static string ConvertFromUtf32(int utf32) + { + // http://www.java2s.com/Open-Source/CSharp/2.6.4-mono-.net-core/System/System/Char.cs.htm + if (utf32 < 0 || utf32 > 0x10FFFF) + throw new ArgumentOutOfRangeException("utf32", "The argument must be from 0 to 0x10FFFF."); + if (0xD800 <= utf32 && utf32 <= 0xDFFF) + throw new ArgumentOutOfRangeException("utf32", "The argument must not be in surrogate pair range."); + if (utf32 < 0x10000) + return new string((char)utf32, 1); + utf32 -= 0x10000; + return new string(new char[] { (char)((utf32 >> 10) + 0xD800), (char)(utf32 % 0x0400 + 0xDC00) }); + } + + static object ParseNumber(char[] json, ref int index, ref bool success) + { + EatWhitespace(json, ref index); + int lastIndex = GetLastIndexOfNumber(json, index); + int charLength = (lastIndex - index) + 1; + object returnNumber; + string str = new string(json, index, charLength); + if (str.IndexOf(".", StringComparison.OrdinalIgnoreCase) != -1 || str.IndexOf("e", StringComparison.OrdinalIgnoreCase) != -1) + { + double number; + success = double.TryParse(new string(json, index, charLength), NumberStyles.Any, CultureInfo.InvariantCulture, out number); + returnNumber = number; + } + else + { + long number; + success = long.TryParse(new string(json, index, charLength), NumberStyles.Any, CultureInfo.InvariantCulture, out number); + returnNumber = number; + } + index = lastIndex + 1; + return returnNumber; + } + + static int GetLastIndexOfNumber(char[] json, int index) + { + int lastIndex; + for (lastIndex = index; lastIndex < json.Length; lastIndex++) + if ("0123456789+-.eE".IndexOf(json[lastIndex]) == -1) break; + return lastIndex - 1; + } + + static void EatWhitespace(char[] json, ref int index) + { + for (; index < json.Length; index++) { + switch (json[index]) { + case ' ': + case '\t': + case '\n': + case '\r': + case '\b': + case '\f': + break; + default: + return; + } + } + } + + static int LookAhead(char[] json, int index) + { + int saveIndex = index; + return NextToken(json, ref saveIndex); + } + + [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] + static int NextToken(char[] json, ref int index) + { + EatWhitespace(json, ref index); + if (index == json.Length) + return TOKEN_NONE; + char c = json[index]; + index++; + switch (c) + { + case '{': + return TOKEN_CURLY_OPEN; + case '}': + return TOKEN_CURLY_CLOSE; + case '[': + return TOKEN_SQUARED_OPEN; + case ']': + return TOKEN_SQUARED_CLOSE; + case ',': + return TOKEN_COMMA; + case '"': + return TOKEN_STRING; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '-': + return TOKEN_NUMBER; + case ':': + return TOKEN_COLON; + } + index--; + int remainingLength = json.Length - index; + // false + if (remainingLength >= 5) + { + if (json[index] == 'f' && json[index + 1] == 'a' && json[index + 2] == 'l' && json[index + 3] == 's' && json[index + 4] == 'e') + { + index += 5; + return TOKEN_FALSE; + } + } + // true + if (remainingLength >= 4) + { + if (json[index] == 't' && json[index + 1] == 'r' && json[index + 2] == 'u' && json[index + 3] == 'e') + { + index += 4; + return TOKEN_TRUE; + } + } + // null + if (remainingLength >= 4) + { + if (json[index] == 'n' && json[index + 1] == 'u' && json[index + 2] == 'l' && json[index + 3] == 'l') + { + index += 4; + return TOKEN_NULL; + } + } + return TOKEN_NONE; + } + + static bool SerializeValue(IJsonSerializerStrategy jsonSerializerStrategy, object value, StringBuilder builder) + { + bool success = true; + string stringValue = value as string; + if (stringValue != null) + success = SerializeString(stringValue, builder); + else + { + IDictionary dict = value as IDictionary; + if (dict != null) + { + success = SerializeObject(jsonSerializerStrategy, dict.Keys, dict.Values, builder); + } + else + { + IDictionary stringDictionary = value as IDictionary; + if (stringDictionary != null) + { + success = SerializeObject(jsonSerializerStrategy, stringDictionary.Keys, stringDictionary.Values, builder); + } + else + { + IEnumerable enumerableValue = value as IEnumerable; + if (enumerableValue != null) + success = SerializeArray(jsonSerializerStrategy, enumerableValue, builder); + else if (IsNumeric(value)) + success = SerializeNumber(value, builder); + else if (value is bool) + builder.Append((bool)value ? "true" : "false"); + else if (value == null) + builder.Append("null"); + else + { + object serializedObject; + success = jsonSerializerStrategy.TrySerializeNonPrimitiveObject(value, out serializedObject); + if (success) + SerializeValue(jsonSerializerStrategy, serializedObject, builder); + } + } + } + } + return success; + } + + static bool SerializeObject(IJsonSerializerStrategy jsonSerializerStrategy, IEnumerable keys, IEnumerable values, StringBuilder builder) + { + builder.Append("{"); + IEnumerator ke = keys.GetEnumerator(); + IEnumerator ve = values.GetEnumerator(); + bool first = true; + while (ke.MoveNext() && ve.MoveNext()) + { + object key = ke.Current; + object value = ve.Current; + if (!first) + builder.Append(","); + string stringKey = key as string; + if (stringKey != null) + SerializeString(stringKey, builder); + else + if (!SerializeValue(jsonSerializerStrategy, value, builder)) return false; + builder.Append(":"); + if (!SerializeValue(jsonSerializerStrategy, value, builder)) + return false; + first = false; + } + builder.Append("}"); + return true; + } + + static bool SerializeArray(IJsonSerializerStrategy jsonSerializerStrategy, IEnumerable anArray, StringBuilder builder) + { + builder.Append("["); + bool first = true; + foreach (object value in anArray) + { + if (!first) + builder.Append(","); + if (!SerializeValue(jsonSerializerStrategy, value, builder)) + return false; + first = false; + } + builder.Append("]"); + return true; + } + + static bool SerializeString(string aString, StringBuilder builder) + { + // Happy path if there's nothing to be escaped. IndexOfAny is highly optimized (and unmanaged) + if (aString.IndexOfAny(EscapeCharacters) == -1) + { + builder.Append('"'); + builder.Append(aString); + builder.Append('"'); + + return true; + } + + builder.Append('"'); + int safeCharacterCount = 0; + char[] charArray = aString.ToCharArray(); + + for (int i = 0; i < charArray.Length; i++) + { + char c = charArray[i]; + + // Non ascii characters are fine, buffer them up and send them to the builder + // in larger chunks if possible. The escape table is a 1:1 translation table + // with \0 [default(char)] denoting a safe character. + if (c >= EscapeTable.Length || EscapeTable[c] == default(char)) + { + safeCharacterCount++; + } + else + { + if (safeCharacterCount > 0) + { + builder.Append(charArray, i - safeCharacterCount, safeCharacterCount); + safeCharacterCount = 0; + } + + builder.Append('\\'); + builder.Append(EscapeTable[c]); + } + } + + if (safeCharacterCount > 0) + { + builder.Append(charArray, charArray.Length - safeCharacterCount, safeCharacterCount); + } + + builder.Append('"'); + return true; + } + + static bool SerializeNumber(object number, StringBuilder builder) + { + if (number is long) + builder.Append(((long)number).ToString(CultureInfo.InvariantCulture)); + else if (number is ulong) + builder.Append(((ulong)number).ToString(CultureInfo.InvariantCulture)); + else if (number is int) + builder.Append(((int)number).ToString(CultureInfo.InvariantCulture)); + else if (number is uint) + builder.Append(((uint)number).ToString(CultureInfo.InvariantCulture)); + else if (number is decimal) + builder.Append(((decimal)number).ToString(CultureInfo.InvariantCulture)); + else if (number is float) + builder.Append(((float)number).ToString(CultureInfo.InvariantCulture)); + else + builder.Append(Convert.ToDouble(number, CultureInfo.InvariantCulture).ToString("r", CultureInfo.InvariantCulture)); + return true; + } + + /// + /// Determines if a given object is numeric in any way + /// (can be integer, double, null, etc). + /// + static bool IsNumeric(object value) + { + if (value is sbyte) return true; + if (value is byte) return true; + if (value is short) return true; + if (value is ushort) return true; + if (value is int) return true; + if (value is uint) return true; + if (value is long) return true; + if (value is ulong) return true; + if (value is float) return true; + if (value is double) return true; + if (value is decimal) return true; + return false; + } + + private static IJsonSerializerStrategy _currentJsonSerializerStrategy; + public static IJsonSerializerStrategy CurrentJsonSerializerStrategy + { + get + { + return _currentJsonSerializerStrategy ?? + (_currentJsonSerializerStrategy = +#if SIMPLE_JSON_DATACONTRACT + DataContractJsonSerializerStrategy +#else + PocoJsonSerializerStrategy +#endif +); + } + set + { + _currentJsonSerializerStrategy = value; + } + } + + private static PocoJsonSerializerStrategy _pocoJsonSerializerStrategy; + [EditorBrowsable(EditorBrowsableState.Advanced)] + public static PocoJsonSerializerStrategy PocoJsonSerializerStrategy + { + get + { + return _pocoJsonSerializerStrategy ?? (_pocoJsonSerializerStrategy = new PocoJsonSerializerStrategy()); + } + } + +#if SIMPLE_JSON_DATACONTRACT + + private static DataContractJsonSerializerStrategy _dataContractJsonSerializerStrategy; + [System.ComponentModel.EditorBrowsable(EditorBrowsableState.Advanced)] + public static DataContractJsonSerializerStrategy DataContractJsonSerializerStrategy + { + get + { + return _dataContractJsonSerializerStrategy ?? (_dataContractJsonSerializerStrategy = new DataContractJsonSerializerStrategy()); + } + } + +#endif + } + + [GeneratedCode("simple-json", "1.0.0")] +#if SIMPLE_JSON_INTERNAL + internal +#else + public +#endif + interface IJsonSerializerStrategy + { + [SuppressMessage("Microsoft.Design", "CA1007:UseGenericsWhereAppropriate", Justification="Need to support .NET 2")] + bool TrySerializeNonPrimitiveObject(object input, out object output); + object DeserializeObject(object value, Type type); + } + + [GeneratedCode("simple-json", "1.0.0")] +#if SIMPLE_JSON_INTERNAL + internal +#else + public +#endif + class PocoJsonSerializerStrategy : IJsonSerializerStrategy + { + internal IDictionary ConstructorCache; + internal IDictionary> GetCache; + internal IDictionary>> SetCache; + + internal static readonly Type[] EmptyTypes = new Type[0]; + internal static readonly Type[] ArrayConstructorParameterTypes = new Type[] { typeof(int) }; + + private static readonly string[] Iso8601Format = new string[] + { + @"yyyy-MM-dd\THH:mm:ss.FFFFFFF\Z", + @"yyyy-MM-dd\THH:mm:ss\Z", + @"yyyy-MM-dd\THH:mm:ssK" + }; + + public PocoJsonSerializerStrategy() + { + ConstructorCache = new ReflectionUtils.ThreadSafeDictionary(ConstructorDelegateFactory); + GetCache = new ReflectionUtils.ThreadSafeDictionary>(GetterValueFactory); + SetCache = new ReflectionUtils.ThreadSafeDictionary>>(SetterValueFactory); + } + + protected virtual string MapClrMemberNameToJsonFieldName(string clrPropertyName) + { + return CamelCase.MemberNameToCamelCase(clrPropertyName); + } + + internal virtual ReflectionUtils.ConstructorDelegate ConstructorDelegateFactory(Type key) + { + // We need List(int) constructor so that DeserializeObject method will work for generating IList-declared values + var needsCapacityArgument = key.IsArray || key.IsConstructedGenericType && key.GetGenericTypeDefinition() == typeof(List<>); + return ReflectionUtils.GetConstructor(key, needsCapacityArgument ? ArrayConstructorParameterTypes : EmptyTypes); + } + + internal virtual IDictionary GetterValueFactory(Type type) + { + IDictionary result = new Dictionary(); + foreach (PropertyInfo propertyInfo in ReflectionUtils.GetProperties(type)) + { + if (propertyInfo.CanRead) + { + MethodInfo getMethod = ReflectionUtils.GetGetterMethodInfo(propertyInfo); + if (getMethod.IsStatic || !getMethod.IsPublic) + continue; + result[MapClrMemberNameToJsonFieldName(propertyInfo.Name)] = ReflectionUtils.GetGetMethod(propertyInfo); + } + } + foreach (FieldInfo fieldInfo in ReflectionUtils.GetFields(type)) + { + if (fieldInfo.IsStatic || !fieldInfo.IsPublic) + continue; + result[MapClrMemberNameToJsonFieldName(fieldInfo.Name)] = ReflectionUtils.GetGetMethod(fieldInfo); + } + return result; + } + + internal virtual IDictionary> SetterValueFactory(Type type) + { + // BLAZOR-SPECIFIC MODIFICATION FROM STOCK SIMPLEJSON: + // + // For incoming keys we match case-insensitively. But if two .NET properties differ only by case, + // it's ambiguous which should be used: the one that matches the incoming JSON exactly, or the + // one that uses 'correct' PascalCase corresponding to the incoming camelCase? What if neither + // meets these descriptions? + // + // To resolve this: + // - If multiple public properties differ only by case, we throw + // - If multiple public fields differ only by case, we throw + // - If there's a public property and a public field that differ only by case, we prefer the property + // This unambiguously selects one member, and that's what we'll use. + + IDictionary> result = new Dictionary>(StringComparer.OrdinalIgnoreCase); + foreach (PropertyInfo propertyInfo in ReflectionUtils.GetProperties(type)) + { + if (propertyInfo.CanWrite) + { + MethodInfo setMethod = ReflectionUtils.GetSetterMethodInfo(propertyInfo); + if (setMethod.IsStatic) + continue; + if (result.ContainsKey(propertyInfo.Name)) + { + throw new InvalidOperationException($"The type '{type.FullName}' contains multiple public properties with names case-insensitively matching '{propertyInfo.Name.ToLowerInvariant()}'. Such types cannot be used for JSON deserialization."); + } + result[propertyInfo.Name] = new KeyValuePair(propertyInfo.PropertyType, ReflectionUtils.GetSetMethod(propertyInfo)); + } + } + + IDictionary> fieldResult = new Dictionary>(StringComparer.OrdinalIgnoreCase); + foreach (FieldInfo fieldInfo in ReflectionUtils.GetFields(type)) + { + if (fieldInfo.IsInitOnly || fieldInfo.IsStatic || !fieldInfo.IsPublic) + continue; + if (fieldResult.ContainsKey(fieldInfo.Name)) + { + throw new InvalidOperationException($"The type '{type.FullName}' contains multiple public fields with names case-insensitively matching '{fieldInfo.Name.ToLowerInvariant()}'. Such types cannot be used for JSON deserialization."); + } + fieldResult[fieldInfo.Name] = new KeyValuePair(fieldInfo.FieldType, ReflectionUtils.GetSetMethod(fieldInfo)); + if (!result.ContainsKey(fieldInfo.Name)) + { + result[fieldInfo.Name] = fieldResult[fieldInfo.Name]; + } + } + + return result; + } + + public virtual bool TrySerializeNonPrimitiveObject(object input, out object output) + { + return TrySerializeKnownTypes(input, out output) || TrySerializeUnknownTypes(input, out output); + } + + [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] + public virtual object DeserializeObject(object value, Type type) + { + if (type == null) throw new ArgumentNullException("type"); + string str = value as string; + + if (type == typeof (Guid) && string.IsNullOrEmpty(str)) + return default(Guid); + + if (type.IsEnum) + { + type = type.GetEnumUnderlyingType(); + } + + if (value == null) + return null; + + object obj = null; + + if (str != null) + { + if (str.Length != 0) // We know it can't be null now. + { + if (type == typeof(TimeSpan) || (ReflectionUtils.IsNullableType(type) && Nullable.GetUnderlyingType(type) == typeof(TimeSpan))) + return TimeSpan.ParseExact(str, "c", CultureInfo.InvariantCulture); + if (type == typeof(DateTime) || (ReflectionUtils.IsNullableType(type) && Nullable.GetUnderlyingType(type) == typeof(DateTime))) + return DateTime.TryParseExact(str, Iso8601Format, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal, out var result) + ? result : DateTime.Parse(str, CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind); + if (type == typeof(DateTimeOffset) || (ReflectionUtils.IsNullableType(type) && Nullable.GetUnderlyingType(type) == typeof(DateTimeOffset))) + return DateTimeOffset.TryParseExact(str, Iso8601Format, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal, out var result) + ? result : DateTimeOffset.Parse(str, CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind); + if (type == typeof(Guid) || (ReflectionUtils.IsNullableType(type) && Nullable.GetUnderlyingType(type) == typeof(Guid))) + return new Guid(str); + if (type == typeof(Uri)) + { + bool isValid = Uri.IsWellFormedUriString(str, UriKind.RelativeOrAbsolute); + + Uri result; + if (isValid && Uri.TryCreate(str, UriKind.RelativeOrAbsolute, out result)) + return result; + + return null; + } + + if (type == typeof(string)) + return str; + + return Convert.ChangeType(str, type, CultureInfo.InvariantCulture); + } + else + { + if (type == typeof(Guid)) + obj = default(Guid); + else if (ReflectionUtils.IsNullableType(type) && Nullable.GetUnderlyingType(type) == typeof(Guid)) + obj = null; + else + obj = str; + } + // Empty string case + if (!ReflectionUtils.IsNullableType(type) && Nullable.GetUnderlyingType(type) == typeof(Guid)) + return str; + } + else if (value is bool) + return value; + + bool valueIsLong = value is long; + bool valueIsDouble = value is double; + if ((valueIsLong && type == typeof(long)) || (valueIsDouble && type == typeof(double))) + return value; + if ((valueIsDouble && type != typeof(double)) || (valueIsLong && type != typeof(long))) + { + obj = type == typeof(int) || type == typeof(long) || type == typeof(double) || type == typeof(float) || type == typeof(bool) || type == typeof(decimal) || type == typeof(byte) || type == typeof(short) + ? Convert.ChangeType(value, type, CultureInfo.InvariantCulture) + : value; + } + else + { + IDictionary objects = value as IDictionary; + if (objects != null) + { + IDictionary jsonObject = objects; + + if (ReflectionUtils.IsTypeDictionary(type)) + { + // if dictionary then + Type[] types = ReflectionUtils.GetGenericTypeArguments(type); + Type keyType = types[0]; + Type valueType = types[1]; + + Type genericType = typeof(Dictionary<,>).MakeGenericType(keyType, valueType); + + IDictionary dict = (IDictionary)ConstructorCache[genericType](); + + foreach (KeyValuePair kvp in jsonObject) + dict.Add(kvp.Key, DeserializeObject(kvp.Value, valueType)); + + obj = dict; + } + else + { + if (type == typeof(object)) + obj = value; + else + { + var constructorDelegate = ConstructorCache[type] + ?? throw new InvalidOperationException($"Cannot deserialize JSON into type '{type.FullName}' because it does not have a public parameterless constructor."); + obj = constructorDelegate(); + + var setterCache = SetCache[type]; + foreach (var jsonKeyValuePair in jsonObject) + { + if (setterCache.TryGetValue(jsonKeyValuePair.Key, out var setter)) + { + var jsonValue = DeserializeObject(jsonKeyValuePair.Value, setter.Key); + setter.Value(obj, jsonValue); + } + } + } + } + } + else + { + IList valueAsList = value as IList; + if (valueAsList != null) + { + IList jsonObject = valueAsList; + IList list = null; + + if (type.IsArray) + { + list = (IList)ConstructorCache[type](jsonObject.Count); + int i = 0; + foreach (object o in jsonObject) + list[i++] = DeserializeObject(o, type.GetElementType()); + } + else if (ReflectionUtils.IsTypeGenericCollectionInterface(type) || ReflectionUtils.IsAssignableFrom(typeof(IList), type)) + { + Type innerType = ReflectionUtils.GetGenericListElementType(type); + list = (IList)(ConstructorCache[type] ?? ConstructorCache[typeof(List<>).MakeGenericType(innerType)])(jsonObject.Count); + foreach (object o in jsonObject) + list.Add(DeserializeObject(o, innerType)); + } + obj = list; + } + } + return obj; + } + if (ReflectionUtils.IsNullableType(type)) + { + // For nullable enums serialized as numbers + if (Nullable.GetUnderlyingType(type).IsEnum) + { + return Enum.ToObject(Nullable.GetUnderlyingType(type), value); + } + + return ReflectionUtils.ToNullableType(obj, type); + } + + return obj; + } + + protected virtual object SerializeEnum(Enum p) + { + return Convert.ToDouble(p, CultureInfo.InvariantCulture); + } + + [SuppressMessage("Microsoft.Design", "CA1007:UseGenericsWhereAppropriate", Justification="Need to support .NET 2")] + protected virtual bool TrySerializeKnownTypes(object input, out object output) + { + bool returnValue = true; + if (input is DateTime) + output = ((DateTime)input).ToUniversalTime().ToString(Iso8601Format[0], CultureInfo.InvariantCulture); + else if (input is DateTimeOffset) + output = ((DateTimeOffset)input).ToString("o"); + else if (input is Guid) + output = ((Guid)input).ToString("D"); + else if (input is Uri) + output = input.ToString(); + else if (input is TimeSpan) + output = ((TimeSpan)input).ToString("c"); + else + { + Enum inputEnum = input as Enum; + if (inputEnum != null) + output = SerializeEnum(inputEnum); + else + { + returnValue = false; + output = null; + } + } + return returnValue; + } + [SuppressMessage("Microsoft.Design", "CA1007:UseGenericsWhereAppropriate", Justification="Need to support .NET 2")] + protected virtual bool TrySerializeUnknownTypes(object input, out object output) + { + if (input == null) throw new ArgumentNullException("input"); + output = null; + Type type = input.GetType(); + if (type.FullName == null) + return false; + IDictionary obj = new JsonObject(); + IDictionary getters = GetCache[type]; + foreach (KeyValuePair getter in getters) + { + if (getter.Value != null) + obj.Add(MapClrMemberNameToJsonFieldName(getter.Key), getter.Value(input)); + } + output = obj; + return true; + } + } + +#if SIMPLE_JSON_DATACONTRACT + [GeneratedCode("simple-json", "1.0.0")] +#if SIMPLE_JSON_INTERNAL + internal +#else + public +#endif + class DataContractJsonSerializerStrategy : PocoJsonSerializerStrategy + { + public DataContractJsonSerializerStrategy() + { + GetCache = new ReflectionUtils.ThreadSafeDictionary>(GetterValueFactory); + SetCache = new ReflectionUtils.ThreadSafeDictionary>>(SetterValueFactory); + } + + internal override IDictionary GetterValueFactory(Type type) + { + bool hasDataContract = ReflectionUtils.GetAttribute(type, typeof(DataContractAttribute)) != null; + if (!hasDataContract) + return base.GetterValueFactory(type); + string jsonKey; + IDictionary result = new Dictionary(); + foreach (PropertyInfo propertyInfo in ReflectionUtils.GetProperties(type)) + { + if (propertyInfo.CanRead) + { + MethodInfo getMethod = ReflectionUtils.GetGetterMethodInfo(propertyInfo); + if (!getMethod.IsStatic && CanAdd(propertyInfo, out jsonKey)) + result[jsonKey] = ReflectionUtils.GetGetMethod(propertyInfo); + } + } + foreach (FieldInfo fieldInfo in ReflectionUtils.GetFields(type)) + { + if (!fieldInfo.IsStatic && CanAdd(fieldInfo, out jsonKey)) + result[jsonKey] = ReflectionUtils.GetGetMethod(fieldInfo); + } + return result; + } + + internal override IDictionary> SetterValueFactory(Type type) + { + bool hasDataContract = ReflectionUtils.GetAttribute(type, typeof(DataContractAttribute)) != null; + if (!hasDataContract) + return base.SetterValueFactory(type); + string jsonKey; + IDictionary> result = new Dictionary>(); + foreach (PropertyInfo propertyInfo in ReflectionUtils.GetProperties(type)) + { + if (propertyInfo.CanWrite) + { + MethodInfo setMethod = ReflectionUtils.GetSetterMethodInfo(propertyInfo); + if (!setMethod.IsStatic && CanAdd(propertyInfo, out jsonKey)) + result[jsonKey] = new KeyValuePair(propertyInfo.PropertyType, ReflectionUtils.GetSetMethod(propertyInfo)); + } + } + foreach (FieldInfo fieldInfo in ReflectionUtils.GetFields(type)) + { + if (!fieldInfo.IsInitOnly && !fieldInfo.IsStatic && CanAdd(fieldInfo, out jsonKey)) + result[jsonKey] = new KeyValuePair(fieldInfo.FieldType, ReflectionUtils.GetSetMethod(fieldInfo)); + } + // todo implement sorting for DATACONTRACT. + return result; + } + + private static bool CanAdd(MemberInfo info, out string jsonKey) + { + jsonKey = null; + if (ReflectionUtils.GetAttribute(info, typeof(IgnoreDataMemberAttribute)) != null) + return false; + DataMemberAttribute dataMemberAttribute = (DataMemberAttribute)ReflectionUtils.GetAttribute(info, typeof(DataMemberAttribute)); + if (dataMemberAttribute == null) + return false; + jsonKey = string.IsNullOrEmpty(dataMemberAttribute.Name) ? info.Name : dataMemberAttribute.Name; + return true; + } + } + +#endif + + namespace Reflection + { + // This class is meant to be copied into other libraries. So we want to exclude it from Code Analysis rules + // that might be in place in the target project. + [GeneratedCode("reflection-utils", "1.0.0")] +#if SIMPLE_JSON_REFLECTION_UTILS_PUBLIC + public +#else + internal +#endif + class ReflectionUtils + { + private static readonly object[] EmptyObjects = new object[] { }; + + public delegate object GetDelegate(object source); + public delegate void SetDelegate(object source, object value); + public delegate object ConstructorDelegate(params object[] args); + + public delegate TValue ThreadSafeDictionaryValueFactory(TKey key); + +#if SIMPLE_JSON_TYPEINFO + public static TypeInfo GetTypeInfo(Type type) + { + return type.GetTypeInfo(); + } +#else + public static Type GetTypeInfo(Type type) + { + return type; + } +#endif + + public static Attribute GetAttribute(MemberInfo info, Type type) + { +#if SIMPLE_JSON_TYPEINFO + if (info == null || type == null || !info.IsDefined(type)) + return null; + return info.GetCustomAttribute(type); +#else + if (info == null || type == null || !Attribute.IsDefined(info, type)) + return null; + return Attribute.GetCustomAttribute(info, type); +#endif + } + + public static Type GetGenericListElementType(Type type) + { + IEnumerable interfaces; +#if SIMPLE_JSON_TYPEINFO + interfaces = type.GetTypeInfo().ImplementedInterfaces; +#else + interfaces = type.GetInterfaces(); +#endif + foreach (Type implementedInterface in interfaces) + { + if (IsTypeGeneric(implementedInterface) && + implementedInterface.GetGenericTypeDefinition() == typeof (IList<>)) + { + return GetGenericTypeArguments(implementedInterface)[0]; + } + } + return GetGenericTypeArguments(type)[0]; + } + + public static Attribute GetAttribute(Type objectType, Type attributeType) + { + +#if SIMPLE_JSON_TYPEINFO + if (objectType == null || attributeType == null || !objectType.GetTypeInfo().IsDefined(attributeType)) + return null; + return objectType.GetTypeInfo().GetCustomAttribute(attributeType); +#else + if (objectType == null || attributeType == null || !Attribute.IsDefined(objectType, attributeType)) + return null; + return Attribute.GetCustomAttribute(objectType, attributeType); +#endif + } + + public static Type[] GetGenericTypeArguments(Type type) + { +#if SIMPLE_JSON_TYPEINFO + return type.GetTypeInfo().GenericTypeArguments; +#else + return type.GetGenericArguments(); +#endif + } + + public static bool IsTypeGeneric(Type type) + { + return GetTypeInfo(type).IsGenericType; + } + + public static bool IsTypeGenericCollectionInterface(Type type) + { + if (!IsTypeGeneric(type)) + return false; + + Type genericDefinition = type.GetGenericTypeDefinition(); + + return (genericDefinition == typeof(IList<>) + || genericDefinition == typeof(ICollection<>) + || genericDefinition == typeof(IEnumerable<>) +#if SIMPLE_JSON_READONLY_COLLECTIONS + || genericDefinition == typeof(IReadOnlyCollection<>) + || genericDefinition == typeof(IReadOnlyList<>) +#endif + ); + } + + public static bool IsAssignableFrom(Type type1, Type type2) + { + return GetTypeInfo(type1).IsAssignableFrom(GetTypeInfo(type2)); + } + + public static bool IsTypeDictionary(Type type) + { +#if SIMPLE_JSON_TYPEINFO + if (typeof(IDictionary<,>).GetTypeInfo().IsAssignableFrom(type.GetTypeInfo())) + return true; +#else + if (typeof(System.Collections.IDictionary).IsAssignableFrom(type)) + return true; +#endif + if (!GetTypeInfo(type).IsGenericType) + return false; + + Type genericDefinition = type.GetGenericTypeDefinition(); + return genericDefinition == typeof(IDictionary<,>); + } + + public static bool IsNullableType(Type type) + { + return GetTypeInfo(type).IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>); + } + + public static object ToNullableType(object obj, Type nullableType) + { + return obj == null ? null : Convert.ChangeType(obj, Nullable.GetUnderlyingType(nullableType), CultureInfo.InvariantCulture); + } + + public static bool IsValueType(Type type) + { + return GetTypeInfo(type).IsValueType; + } + + public static IEnumerable GetConstructors(Type type) + { +#if SIMPLE_JSON_TYPEINFO + return type.GetTypeInfo().DeclaredConstructors; +#else + const BindingFlags flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; + return type.GetConstructors(flags); +#endif + } + + public static ConstructorInfo GetConstructorInfo(Type type, params Type[] argsType) + { + IEnumerable constructorInfos = GetConstructors(type); + int i; + bool matches; + foreach (ConstructorInfo constructorInfo in constructorInfos) + { + ParameterInfo[] parameters = constructorInfo.GetParameters(); + if (argsType.Length != parameters.Length) + continue; + + i = 0; + matches = true; + foreach (ParameterInfo parameterInfo in constructorInfo.GetParameters()) + { + if (parameterInfo.ParameterType != argsType[i]) + { + matches = false; + break; + } + } + + if (matches) + return constructorInfo; + } + + return null; + } + + public static IEnumerable GetProperties(Type type) + { +#if SIMPLE_JSON_TYPEINFO + return type.GetRuntimeProperties(); +#else + return type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); +#endif + } + + public static IEnumerable GetFields(Type type) + { +#if SIMPLE_JSON_TYPEINFO + return type.GetRuntimeFields(); +#else + return type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); +#endif + } + + public static MethodInfo GetGetterMethodInfo(PropertyInfo propertyInfo) + { +#if SIMPLE_JSON_TYPEINFO + return propertyInfo.GetMethod; +#else + return propertyInfo.GetGetMethod(true); +#endif + } + + public static MethodInfo GetSetterMethodInfo(PropertyInfo propertyInfo) + { +#if SIMPLE_JSON_TYPEINFO + return propertyInfo.SetMethod; +#else + return propertyInfo.GetSetMethod(true); +#endif + } + + public static ConstructorDelegate GetConstructor(ConstructorInfo constructorInfo) + { +#if SIMPLE_JSON_NO_LINQ_EXPRESSION + return GetConstructorByReflection(constructorInfo); +#else + return GetConstructorByExpression(constructorInfo); +#endif + } + + public static ConstructorDelegate GetConstructor(Type type, params Type[] argsType) + { +#if SIMPLE_JSON_NO_LINQ_EXPRESSION + return GetConstructorByReflection(type, argsType); +#else + return GetConstructorByExpression(type, argsType); +#endif + } + + public static ConstructorDelegate GetConstructorByReflection(ConstructorInfo constructorInfo) + { + return delegate(object[] args) { return constructorInfo.Invoke(args); }; + } + + public static ConstructorDelegate GetConstructorByReflection(Type type, params Type[] argsType) + { + ConstructorInfo constructorInfo = GetConstructorInfo(type, argsType); + + if (constructorInfo == null && argsType.Length == 0 && type.IsValueType) + { + // If it's a struct, then parameterless constructors are implicit + // We can always call Activator.CreateInstance in lieu of a zero-arg constructor + return args => Activator.CreateInstance(type); + } + + return constructorInfo == null ? null : GetConstructorByReflection(constructorInfo); + } + +#if !SIMPLE_JSON_NO_LINQ_EXPRESSION + + public static ConstructorDelegate GetConstructorByExpression(ConstructorInfo constructorInfo) + { + ParameterInfo[] paramsInfo = constructorInfo.GetParameters(); + ParameterExpression param = Expression.Parameter(typeof(object[]), "args"); + Expression[] argsExp = new Expression[paramsInfo.Length]; + for (int i = 0; i < paramsInfo.Length; i++) + { + Expression index = Expression.Constant(i); + Type paramType = paramsInfo[i].ParameterType; + Expression paramAccessorExp = Expression.ArrayIndex(param, index); + Expression paramCastExp = Expression.Convert(paramAccessorExp, paramType); + argsExp[i] = paramCastExp; + } + NewExpression newExp = Expression.New(constructorInfo, argsExp); + Expression> lambda = Expression.Lambda>(newExp, param); + Func compiledLambda = lambda.Compile(); + return delegate(object[] args) { return compiledLambda(args); }; + } + + public static ConstructorDelegate GetConstructorByExpression(Type type, params Type[] argsType) + { + ConstructorInfo constructorInfo = GetConstructorInfo(type, argsType); + return constructorInfo == null ? null : GetConstructorByExpression(constructorInfo); + } + +#endif + + public static GetDelegate GetGetMethod(PropertyInfo propertyInfo) + { +#if SIMPLE_JSON_NO_LINQ_EXPRESSION + return GetGetMethodByReflection(propertyInfo); +#else + return GetGetMethodByExpression(propertyInfo); +#endif + } + + public static GetDelegate GetGetMethod(FieldInfo fieldInfo) + { +#if SIMPLE_JSON_NO_LINQ_EXPRESSION + return GetGetMethodByReflection(fieldInfo); +#else + return GetGetMethodByExpression(fieldInfo); +#endif + } + + public static GetDelegate GetGetMethodByReflection(PropertyInfo propertyInfo) + { + MethodInfo methodInfo = GetGetterMethodInfo(propertyInfo); + return delegate(object source) { return methodInfo.Invoke(source, EmptyObjects); }; + } + + public static GetDelegate GetGetMethodByReflection(FieldInfo fieldInfo) + { + return delegate(object source) { return fieldInfo.GetValue(source); }; + } + +#if !SIMPLE_JSON_NO_LINQ_EXPRESSION + + public static GetDelegate GetGetMethodByExpression(PropertyInfo propertyInfo) + { + MethodInfo getMethodInfo = GetGetterMethodInfo(propertyInfo); + ParameterExpression instance = Expression.Parameter(typeof(object), "instance"); + UnaryExpression instanceCast = (!IsValueType(propertyInfo.DeclaringType)) ? Expression.TypeAs(instance, propertyInfo.DeclaringType) : Expression.Convert(instance, propertyInfo.DeclaringType); + Func compiled = Expression.Lambda>(Expression.TypeAs(Expression.Call(instanceCast, getMethodInfo), typeof(object)), instance).Compile(); + return delegate(object source) { return compiled(source); }; + } + + public static GetDelegate GetGetMethodByExpression(FieldInfo fieldInfo) + { + ParameterExpression instance = Expression.Parameter(typeof(object), "instance"); + MemberExpression member = Expression.Field(Expression.Convert(instance, fieldInfo.DeclaringType), fieldInfo); + GetDelegate compiled = Expression.Lambda(Expression.Convert(member, typeof(object)), instance).Compile(); + return delegate(object source) { return compiled(source); }; + } + +#endif + + public static SetDelegate GetSetMethod(PropertyInfo propertyInfo) + { +#if SIMPLE_JSON_NO_LINQ_EXPRESSION + return GetSetMethodByReflection(propertyInfo); +#else + return GetSetMethodByExpression(propertyInfo); +#endif + } + + public static SetDelegate GetSetMethod(FieldInfo fieldInfo) + { +#if SIMPLE_JSON_NO_LINQ_EXPRESSION + return GetSetMethodByReflection(fieldInfo); +#else + return GetSetMethodByExpression(fieldInfo); +#endif + } + + public static SetDelegate GetSetMethodByReflection(PropertyInfo propertyInfo) + { + MethodInfo methodInfo = GetSetterMethodInfo(propertyInfo); + return delegate(object source, object value) { methodInfo.Invoke(source, new object[] { value }); }; + } + + public static SetDelegate GetSetMethodByReflection(FieldInfo fieldInfo) + { + return delegate(object source, object value) { fieldInfo.SetValue(source, value); }; + } + +#if !SIMPLE_JSON_NO_LINQ_EXPRESSION + + public static SetDelegate GetSetMethodByExpression(PropertyInfo propertyInfo) + { + MethodInfo setMethodInfo = GetSetterMethodInfo(propertyInfo); + ParameterExpression instance = Expression.Parameter(typeof(object), "instance"); + ParameterExpression value = Expression.Parameter(typeof(object), "value"); + UnaryExpression instanceCast = (!IsValueType(propertyInfo.DeclaringType)) ? Expression.TypeAs(instance, propertyInfo.DeclaringType) : Expression.Convert(instance, propertyInfo.DeclaringType); + UnaryExpression valueCast = (!IsValueType(propertyInfo.PropertyType)) ? Expression.TypeAs(value, propertyInfo.PropertyType) : Expression.Convert(value, propertyInfo.PropertyType); + Action compiled = Expression.Lambda>(Expression.Call(instanceCast, setMethodInfo, valueCast), new ParameterExpression[] { instance, value }).Compile(); + return delegate(object source, object val) { compiled(source, val); }; + } + + public static SetDelegate GetSetMethodByExpression(FieldInfo fieldInfo) + { + ParameterExpression instance = Expression.Parameter(typeof(object), "instance"); + ParameterExpression value = Expression.Parameter(typeof(object), "value"); + Action compiled = Expression.Lambda>( + Assign(Expression.Field(Expression.Convert(instance, fieldInfo.DeclaringType), fieldInfo), Expression.Convert(value, fieldInfo.FieldType)), instance, value).Compile(); + return delegate(object source, object val) { compiled(source, val); }; + } + + public static BinaryExpression Assign(Expression left, Expression right) + { +#if SIMPLE_JSON_TYPEINFO + return Expression.Assign(left, right); +#else + MethodInfo assign = typeof(Assigner<>).MakeGenericType(left.Type).GetMethod("Assign"); + BinaryExpression assignExpr = Expression.Add(left, right, assign); + return assignExpr; +#endif + } + + private static class Assigner + { + public static T Assign(ref T left, T right) + { + return (left = right); + } + } + +#endif + + public sealed class ThreadSafeDictionary : IDictionary + { + private readonly object _lock = new object(); + private readonly ThreadSafeDictionaryValueFactory _valueFactory; + private Dictionary _dictionary; + + public ThreadSafeDictionary(ThreadSafeDictionaryValueFactory valueFactory) + { + _valueFactory = valueFactory; + } + + private TValue Get(TKey key) + { + if (_dictionary == null) + return AddValue(key); + TValue value; + if (!_dictionary.TryGetValue(key, out value)) + return AddValue(key); + return value; + } + + private TValue AddValue(TKey key) + { + TValue value = _valueFactory(key); + lock (_lock) + { + if (_dictionary == null) + { + _dictionary = new Dictionary(); + _dictionary[key] = value; + } + else + { + TValue val; + if (_dictionary.TryGetValue(key, out val)) + return val; + Dictionary dict = new Dictionary(_dictionary); + dict[key] = value; + _dictionary = dict; + } + } + return value; + } + + public void Add(TKey key, TValue value) + { + throw new NotImplementedException(); + } + + public bool ContainsKey(TKey key) + { + return _dictionary.ContainsKey(key); + } + + public ICollection Keys + { + get { return _dictionary.Keys; } + } + + public bool Remove(TKey key) + { + throw new NotImplementedException(); + } + + public bool TryGetValue(TKey key, out TValue value) + { + value = this[key]; + return true; + } + + public ICollection Values + { + get { return _dictionary.Values; } + } + + public TValue this[TKey key] + { + get { return Get(key); } + set { throw new NotImplementedException(); } + } + + public void Add(KeyValuePair item) + { + throw new NotImplementedException(); + } + + public void Clear() + { + throw new NotImplementedException(); + } + + public bool Contains(KeyValuePair item) + { + throw new NotImplementedException(); + } + + public void CopyTo(KeyValuePair[] array, int arrayIndex) + { + throw new NotImplementedException(); + } + + public int Count + { + get { return _dictionary.Count; } + } + + public bool IsReadOnly + { + get { throw new NotImplementedException(); } + } + + public bool Remove(KeyValuePair item) + { + throw new NotImplementedException(); + } + + public IEnumerator> GetEnumerator() + { + return _dictionary.GetEnumerator(); + } + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return _dictionary.GetEnumerator(); + } + } + + } + } +} +// ReSharper restore LoopCanBeConvertedToQuery +// ReSharper restore RedundantExplicitArrayCreation +// ReSharper restore SuggestUseVarKeywordEvident diff --git a/src/JSInterop/src/Microsoft.JSInterop/Microsoft.JSInterop.csproj b/src/JSInterop/src/Microsoft.JSInterop/Microsoft.JSInterop.csproj new file mode 100644 index 0000000000..9f5c4f4abb --- /dev/null +++ b/src/JSInterop/src/Microsoft.JSInterop/Microsoft.JSInterop.csproj @@ -0,0 +1,7 @@ + + + + netstandard2.0 + + + diff --git a/src/JSInterop/src/Microsoft.JSInterop/Properties/AssemblyInfo.cs b/src/JSInterop/src/Microsoft.JSInterop/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..d65c89dc7f --- /dev/null +++ b/src/JSInterop/src/Microsoft.JSInterop/Properties/AssemblyInfo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Microsoft.JSInterop.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] diff --git a/src/JSInterop/src/Microsoft.JSInterop/TaskGenericsUtil.cs b/src/JSInterop/src/Microsoft.JSInterop/TaskGenericsUtil.cs new file mode 100644 index 0000000000..734e9863b8 --- /dev/null +++ b/src/JSInterop/src/Microsoft.JSInterop/TaskGenericsUtil.cs @@ -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 System.Collections.Concurrent; +using System.Linq; +using System.Threading.Tasks; + +namespace Microsoft.JSInterop +{ + internal static class TaskGenericsUtil + { + private static ConcurrentDictionary _cachedResultGetters + = new ConcurrentDictionary(); + + private static ConcurrentDictionary _cachedResultSetters + = new ConcurrentDictionary(); + + public static void SetTaskCompletionSourceResult(object taskCompletionSource, object result) + => CreateResultSetter(taskCompletionSource).SetResult(taskCompletionSource, result); + + public static void SetTaskCompletionSourceException(object taskCompletionSource, Exception exception) + => CreateResultSetter(taskCompletionSource).SetException(taskCompletionSource, exception); + + public static Type GetTaskCompletionSourceResultType(object taskCompletionSource) + => CreateResultSetter(taskCompletionSource).ResultType; + + public static object GetTaskResult(Task task) + { + var getter = _cachedResultGetters.GetOrAdd(task.GetType(), taskInstanceType => + { + var resultType = GetTaskResultType(taskInstanceType); + return resultType == null + ? new VoidTaskResultGetter() + : (ITaskResultGetter)Activator.CreateInstance( + typeof(TaskResultGetter<>).MakeGenericType(resultType)); + }); + return getter.GetResult(task); + } + + private static Type GetTaskResultType(Type taskType) + { + // It might be something derived from Task or Task, so we have to scan + // up the inheritance hierarchy to find the Task or Task + while (taskType != typeof(Task) && + (!taskType.IsGenericType || taskType.GetGenericTypeDefinition() != typeof(Task<>))) + { + taskType = taskType.BaseType + ?? throw new ArgumentException($"The type '{taskType.FullName}' is not inherited from '{typeof(Task).FullName}'."); + } + + return taskType.IsGenericType + ? taskType.GetGenericArguments().Single() + : null; + } + + interface ITcsResultSetter + { + Type ResultType { get; } + void SetResult(object taskCompletionSource, object result); + void SetException(object taskCompletionSource, Exception exception); + } + + private interface ITaskResultGetter + { + object GetResult(Task task); + } + + private class TaskResultGetter : ITaskResultGetter + { + public object GetResult(Task task) => ((Task)task).Result; + } + + private class VoidTaskResultGetter : ITaskResultGetter + { + public object GetResult(Task task) + { + task.Wait(); // Throw if the task failed + return null; + } + } + + private class TcsResultSetter : ITcsResultSetter + { + public Type ResultType => typeof(T); + + public void SetResult(object tcs, object result) + { + var typedTcs = (TaskCompletionSource)tcs; + + // If necessary, attempt a cast + var typedResult = result is T resultT + ? resultT + : (T)Convert.ChangeType(result, typeof(T)); + + typedTcs.SetResult(typedResult); + } + + public void SetException(object tcs, Exception exception) + { + var typedTcs = (TaskCompletionSource)tcs; + typedTcs.SetException(exception); + } + } + + private static ITcsResultSetter CreateResultSetter(object taskCompletionSource) + { + return _cachedResultSetters.GetOrAdd(taskCompletionSource.GetType(), tcsType => + { + var resultType = tcsType.GetGenericArguments().Single(); + return (ITcsResultSetter)Activator.CreateInstance( + typeof(TcsResultSetter<>).MakeGenericType(resultType)); + }); + } + } +} diff --git a/src/JSInterop/src/Mono.WebAssembly.Interop/InternalCalls.cs b/src/JSInterop/src/Mono.WebAssembly.Interop/InternalCalls.cs new file mode 100644 index 0000000000..60c0cdc429 --- /dev/null +++ b/src/JSInterop/src/Mono.WebAssembly.Interop/InternalCalls.cs @@ -0,0 +1,25 @@ +// 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.Runtime.CompilerServices; + +namespace WebAssembly.JSInterop +{ + /// + /// Methods that map to the functions compiled into the Mono WebAssembly runtime, + /// as defined by 'mono_add_internal_call' calls in driver.c + /// + internal class InternalCalls + { + // The exact namespace, type, and method names must match the corresponding entries + // in driver.c in the Mono distribution + + // We're passing asyncHandle by ref not because we want it to be writable, but so it gets + // passed as a pointer (4 bytes). We can pass 4-byte values, but not 8-byte ones. + [MethodImpl(MethodImplOptions.InternalCall)] + public static extern string InvokeJSMarshalled(out string exception, ref long asyncHandle, string functionIdentifier, string argsJson); + + [MethodImpl(MethodImplOptions.InternalCall)] + public static extern TRes InvokeJSUnmarshalled(out string exception, string functionIdentifier, T0 arg0, T1 arg1, T2 arg2); + } +} diff --git a/src/JSInterop/src/Mono.WebAssembly.Interop/Mono.WebAssembly.Interop.csproj b/src/JSInterop/src/Mono.WebAssembly.Interop/Mono.WebAssembly.Interop.csproj new file mode 100644 index 0000000000..81f1173b55 --- /dev/null +++ b/src/JSInterop/src/Mono.WebAssembly.Interop/Mono.WebAssembly.Interop.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + diff --git a/src/JSInterop/src/Mono.WebAssembly.Interop/MonoWebAssemblyJSRuntime.cs b/src/JSInterop/src/Mono.WebAssembly.Interop/MonoWebAssemblyJSRuntime.cs new file mode 100644 index 0000000000..9a502b8bc8 --- /dev/null +++ b/src/JSInterop/src/Mono.WebAssembly.Interop/MonoWebAssemblyJSRuntime.cs @@ -0,0 +1,114 @@ +// 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.JSInterop; +using WebAssembly.JSInterop; + +namespace Mono.WebAssembly.Interop +{ + /// + /// Provides methods for invoking JavaScript functions for applications running + /// on the Mono WebAssembly runtime. + /// + public class MonoWebAssemblyJSRuntime : JSInProcessRuntimeBase + { + /// + protected override string InvokeJS(string identifier, string argsJson) + { + var noAsyncHandle = default(long); + var result = InternalCalls.InvokeJSMarshalled(out var exception, ref noAsyncHandle, identifier, argsJson); + return exception != null + ? throw new JSException(exception) + : result; + } + + /// + protected override void BeginInvokeJS(long asyncHandle, string identifier, string argsJson) + { + InternalCalls.InvokeJSMarshalled(out _, ref asyncHandle, identifier, argsJson); + } + + // Invoked via Mono's JS interop mechanism (invoke_method) + private static string InvokeDotNet(string assemblyName, string methodIdentifier, string dotNetObjectId, string argsJson) + => DotNetDispatcher.Invoke(assemblyName, methodIdentifier, dotNetObjectId == null ? default : long.Parse(dotNetObjectId), argsJson); + + // Invoked via Mono's JS interop mechanism (invoke_method) + private static void BeginInvokeDotNet(string callId, string assemblyNameOrDotNetObjectId, string methodIdentifier, string argsJson) + { + // Figure out whether 'assemblyNameOrDotNetObjectId' is the assembly name or the instance ID + // We only need one for any given call. This helps to work around the limitation that we can + // only pass a maximum of 4 args in a call from JS to Mono WebAssembly. + string assemblyName; + long dotNetObjectId; + if (char.IsDigit(assemblyNameOrDotNetObjectId[0])) + { + dotNetObjectId = long.Parse(assemblyNameOrDotNetObjectId); + assemblyName = null; + } + else + { + dotNetObjectId = default; + assemblyName = assemblyNameOrDotNetObjectId; + } + + DotNetDispatcher.BeginInvoke(callId, assemblyName, methodIdentifier, dotNetObjectId, argsJson); + } + + #region Custom MonoWebAssemblyJSRuntime methods + + /// + /// Invokes the JavaScript function registered with the specified identifier. + /// + /// The .NET type corresponding to the function's return value type. + /// The identifier used when registering the target function. + /// The result of the function invocation. + public TRes InvokeUnmarshalled(string identifier) + => InvokeUnmarshalled(identifier, null, null, null); + + /// + /// Invokes the JavaScript function registered with the specified identifier. + /// + /// The type of the first argument. + /// The .NET type corresponding to the function's return value type. + /// The identifier used when registering the target function. + /// The first argument. + /// The result of the function invocation. + public TRes InvokeUnmarshalled(string identifier, T0 arg0) + => InvokeUnmarshalled(identifier, arg0, null, null); + + /// + /// Invokes the JavaScript function registered with the specified identifier. + /// + /// The type of the first argument. + /// The type of the second argument. + /// The .NET type corresponding to the function's return value type. + /// The identifier used when registering the target function. + /// The first argument. + /// The second argument. + /// The result of the function invocation. + public TRes InvokeUnmarshalled(string identifier, T0 arg0, T1 arg1) + => InvokeUnmarshalled(identifier, arg0, arg1, null); + + /// + /// Invokes the JavaScript function registered with the specified identifier. + /// + /// The type of the first argument. + /// The type of the second argument. + /// The type of the third argument. + /// The .NET type corresponding to the function's return value type. + /// The identifier used when registering the target function. + /// The first argument. + /// The second argument. + /// The third argument. + /// The result of the function invocation. + public TRes InvokeUnmarshalled(string identifier, T0 arg0, T1 arg1, T2 arg2) + { + var result = InternalCalls.InvokeJSUnmarshalled(out var exception, identifier, arg0, arg1, arg2); + return exception != null + ? throw new JSException(exception) + : result; + } + + #endregion + } +} diff --git a/src/JSInterop/test/Microsoft.JSInterop.Test/DotNetDispatcherTest.cs b/src/JSInterop/test/Microsoft.JSInterop.Test/DotNetDispatcherTest.cs new file mode 100644 index 0000000000..eec537f987 --- /dev/null +++ b/src/JSInterop/test/Microsoft.JSInterop.Test/DotNetDispatcherTest.cs @@ -0,0 +1,443 @@ +// 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.Threading.Tasks; +using Xunit; + +namespace Microsoft.JSInterop.Test +{ + public class DotNetDispatcherTest + { + private readonly static string thisAssemblyName + = typeof(DotNetDispatcherTest).Assembly.GetName().Name; + private readonly TestJSRuntime jsRuntime + = new TestJSRuntime(); + + [Fact] + public void CannotInvokeWithEmptyAssemblyName() + { + var ex = Assert.Throws(() => + { + DotNetDispatcher.Invoke(" ", "SomeMethod", default, "[]"); + }); + + Assert.StartsWith("Cannot be null, empty, or whitespace.", ex.Message); + Assert.Equal("assemblyName", ex.ParamName); + } + + [Fact] + public void CannotInvokeWithEmptyMethodIdentifier() + { + var ex = Assert.Throws(() => + { + DotNetDispatcher.Invoke("SomeAssembly", " ", default, "[]"); + }); + + Assert.StartsWith("Cannot be null, empty, or whitespace.", ex.Message); + Assert.Equal("methodIdentifier", ex.ParamName); + } + + [Fact] + public void CannotInvokeMethodsOnUnloadedAssembly() + { + var assemblyName = "Some.Fake.Assembly"; + var ex = Assert.Throws(() => + { + DotNetDispatcher.Invoke(assemblyName, "SomeMethod", default, null); + }); + + Assert.Equal($"There is no loaded assembly with the name '{assemblyName}'.", ex.Message); + } + + // Note: Currently it's also not possible to invoke generic methods. + // That's not something determined by DotNetDispatcher, but rather by the fact that we + // don't close over the generics in the reflection code. + // Not defining this behavior through unit tests because the default outcome is + // fine (an exception stating what info is missing). + + [Theory] + [InlineData("MethodOnInternalType")] + [InlineData("PrivateMethod")] + [InlineData("ProtectedMethod")] + [InlineData("StaticMethodWithoutAttribute")] // That's not really its identifier; just making the point that there's no way to invoke it + [InlineData("InstanceMethodWithoutAttribute")] // That's not really its identifier; just making the point that there's no way to invoke it + public void CannotInvokeUnsuitableMethods(string methodIdentifier) + { + var ex = Assert.Throws(() => + { + DotNetDispatcher.Invoke(thisAssemblyName, methodIdentifier, default, null); + }); + + Assert.Equal($"The assembly '{thisAssemblyName}' does not contain a public method with [JSInvokableAttribute(\"{methodIdentifier}\")].", ex.Message); + } + + [Fact] + public Task CanInvokeStaticVoidMethod() => WithJSRuntime(jsRuntime => + { + // Arrange/Act + SomePublicType.DidInvokeMyInvocableStaticVoid = false; + var resultJson = DotNetDispatcher.Invoke(thisAssemblyName, "InvocableStaticVoid", default, null); + + // Assert + Assert.Null(resultJson); + Assert.True(SomePublicType.DidInvokeMyInvocableStaticVoid); + }); + + [Fact] + public Task CanInvokeStaticNonVoidMethod() => WithJSRuntime(jsRuntime => + { + // Arrange/Act + var resultJson = DotNetDispatcher.Invoke(thisAssemblyName, "InvocableStaticNonVoid", default, null); + var result = Json.Deserialize(resultJson); + + // Assert + Assert.Equal("Test", result.StringVal); + Assert.Equal(123, result.IntVal); + }); + + [Fact] + public Task CanInvokeStaticNonVoidMethodWithoutCustomIdentifier() => WithJSRuntime(jsRuntime => + { + // Arrange/Act + var resultJson = DotNetDispatcher.Invoke(thisAssemblyName, nameof(SomePublicType.InvokableMethodWithoutCustomIdentifier), default, null); + var result = Json.Deserialize(resultJson); + + // Assert + Assert.Equal("InvokableMethodWithoutCustomIdentifier", result.StringVal); + Assert.Equal(456, result.IntVal); + }); + + [Fact] + public Task CanInvokeStaticWithParams() => WithJSRuntime(jsRuntime => + { + // Arrange: Track a .NET object to use as an arg + var arg3 = new TestDTO { IntVal = 999, StringVal = "My string" }; + jsRuntime.Invoke("unimportant", new DotNetObjectRef(arg3)); + + // Arrange: Remaining args + var argsJson = Json.Serialize(new object[] { + new TestDTO { StringVal = "Another string", IntVal = 456 }, + new[] { 100, 200 }, + "__dotNetObject:1" + }); + + // Act + var resultJson = DotNetDispatcher.Invoke(thisAssemblyName, "InvocableStaticWithParams", default, argsJson); + var result = Json.Deserialize(resultJson); + + // Assert: First result value marshalled via JSON + var resultDto1 = (TestDTO)jsRuntime.ArgSerializerStrategy.DeserializeObject(result[0], typeof(TestDTO)); + Assert.Equal("ANOTHER STRING", resultDto1.StringVal); + Assert.Equal(756, resultDto1.IntVal); + + // Assert: Second result value marshalled by ref + var resultDto2Ref = (string)result[1]; + Assert.Equal("__dotNetObject:2", resultDto2Ref); + var resultDto2 = (TestDTO)jsRuntime.ArgSerializerStrategy.FindDotNetObject(2); + Assert.Equal("MY STRING", resultDto2.StringVal); + Assert.Equal(1299, resultDto2.IntVal); + }); + + [Fact] + public Task CanInvokeInstanceVoidMethod() => WithJSRuntime(jsRuntime => + { + // Arrange: Track some instance + var targetInstance = new SomePublicType(); + jsRuntime.Invoke("unimportant", new DotNetObjectRef(targetInstance)); + + // Act + var resultJson = DotNetDispatcher.Invoke(null, "InvokableInstanceVoid", 1, null); + + // Assert + Assert.Null(resultJson); + Assert.True(targetInstance.DidInvokeMyInvocableInstanceVoid); + }); + + [Fact] + public Task CanInvokeBaseInstanceVoidMethod() => WithJSRuntime(jsRuntime => + { + // Arrange: Track some instance + var targetInstance = new DerivedClass(); + jsRuntime.Invoke("unimportant", new DotNetObjectRef(targetInstance)); + + // Act + var resultJson = DotNetDispatcher.Invoke(null, "BaseClassInvokableInstanceVoid", 1, null); + + // Assert + Assert.Null(resultJson); + Assert.True(targetInstance.DidInvokeMyBaseClassInvocableInstanceVoid); + }); + + [Fact] + public Task CannotUseDotNetObjectRefAfterDisposal() => WithJSRuntime(jsRuntime => + { + // This test addresses the case where the developer calls objectRef.Dispose() + // from .NET code, as opposed to .dispose() from JS code + + // Arrange: Track some instance, then dispose it + var targetInstance = new SomePublicType(); + var objectRef = new DotNetObjectRef(targetInstance); + jsRuntime.Invoke("unimportant", objectRef); + objectRef.Dispose(); + + // Act/Assert + var ex = Assert.Throws( + () => DotNetDispatcher.Invoke(null, "InvokableInstanceVoid", 1, null)); + Assert.StartsWith("There is no tracked object with id '1'.", ex.Message); + }); + + [Fact] + public Task CannotUseDotNetObjectRefAfterReleaseDotNetObject() => WithJSRuntime(jsRuntime => + { + // This test addresses the case where the developer calls .dispose() + // from JS code, as opposed to objectRef.Dispose() from .NET code + + // Arrange: Track some instance, then dispose it + var targetInstance = new SomePublicType(); + var objectRef = new DotNetObjectRef(targetInstance); + jsRuntime.Invoke("unimportant", objectRef); + DotNetDispatcher.ReleaseDotNetObject(1); + + // Act/Assert + var ex = Assert.Throws( + () => DotNetDispatcher.Invoke(null, "InvokableInstanceVoid", 1, null)); + Assert.StartsWith("There is no tracked object with id '1'.", ex.Message); + }); + + [Fact] + public Task CanInvokeInstanceMethodWithParams() => WithJSRuntime(jsRuntime => + { + // Arrange: Track some instance plus another object we'll pass as a param + var targetInstance = new SomePublicType(); + var arg2 = new TestDTO { IntVal = 1234, StringVal = "My string" }; + jsRuntime.Invoke("unimportant", + new DotNetObjectRef(targetInstance), + new DotNetObjectRef(arg2)); + var argsJson = "[\"myvalue\",\"__dotNetObject:2\"]"; + + // Act + var resultJson = DotNetDispatcher.Invoke(null, "InvokableInstanceMethod", 1, argsJson); + + // Assert + Assert.Equal("[\"You passed myvalue\",\"__dotNetObject:3\"]", resultJson); + var resultDto = (TestDTO)jsRuntime.ArgSerializerStrategy.FindDotNetObject(3); + Assert.Equal(1235, resultDto.IntVal); + Assert.Equal("MY STRING", resultDto.StringVal); + }); + + [Fact] + public void CannotInvokeWithIncorrectNumberOfParams() + { + // Arrange + var argsJson = Json.Serialize(new object[] { 1, 2, 3, 4 }); + + // Act/Assert + var ex = Assert.Throws(() => + { + DotNetDispatcher.Invoke(thisAssemblyName, "InvocableStaticWithParams", default, argsJson); + }); + + Assert.Equal("In call to 'InvocableStaticWithParams', expected 3 parameters but received 4.", ex.Message); + } + + [Fact] + public Task CanInvokeAsyncMethod() => WithJSRuntime(async jsRuntime => + { + // Arrange: Track some instance plus another object we'll pass as a param + var targetInstance = new SomePublicType(); + var arg2 = new TestDTO { IntVal = 1234, StringVal = "My string" }; + jsRuntime.Invoke("unimportant", new DotNetObjectRef(targetInstance), new DotNetObjectRef(arg2)); + + // Arrange: all args + var argsJson = Json.Serialize(new object[] + { + new TestDTO { IntVal = 1000, StringVal = "String via JSON" }, + "__dotNetObject:2" + }); + + // Act + var callId = "123"; + var resultTask = jsRuntime.NextInvocationTask; + DotNetDispatcher.BeginInvoke(callId, null, "InvokableAsyncMethod", 1, argsJson); + await resultTask; + var result = Json.Deserialize(jsRuntime.LastInvocationArgsJson); + var resultValue = (SimpleJson.JsonArray)result[2]; + + // Assert: Correct info to complete the async call + Assert.Equal(0, jsRuntime.LastInvocationAsyncHandle); // 0 because it doesn't want a further callback from JS to .NET + Assert.Equal("DotNet.jsCallDispatcher.endInvokeDotNetFromJS", jsRuntime.LastInvocationIdentifier); + Assert.Equal(3, result.Count); + Assert.Equal(callId, result[0]); + Assert.True((bool)result[1]); // Success flag + + // Assert: First result value marshalled via JSON + var resultDto1 = (TestDTO)jsRuntime.ArgSerializerStrategy.DeserializeObject(resultValue[0], typeof(TestDTO)); + Assert.Equal("STRING VIA JSON", resultDto1.StringVal); + Assert.Equal(2000, resultDto1.IntVal); + + // Assert: Second result value marshalled by ref + var resultDto2Ref = (string)resultValue[1]; + Assert.Equal("__dotNetObject:3", resultDto2Ref); + var resultDto2 = (TestDTO)jsRuntime.ArgSerializerStrategy.FindDotNetObject(3); + Assert.Equal("MY STRING", resultDto2.StringVal); + Assert.Equal(2468, resultDto2.IntVal); + }); + + Task WithJSRuntime(Action testCode) + { + return WithJSRuntime(jsRuntime => + { + testCode(jsRuntime); + return Task.CompletedTask; + }); + } + + async Task WithJSRuntime(Func testCode) + { + // Since the tests rely on the asynclocal JSRuntime.Current, ensure we + // are on a distinct async context with a non-null JSRuntime.Current + await Task.Yield(); + + var runtime = new TestJSRuntime(); + JSRuntime.SetCurrentJSRuntime(runtime); + await testCode(runtime); + } + + internal class SomeInteralType + { + [JSInvokable("MethodOnInternalType")] public void MyMethod() { } + } + + public class SomePublicType + { + public static bool DidInvokeMyInvocableStaticVoid; + public bool DidInvokeMyInvocableInstanceVoid; + + [JSInvokable("PrivateMethod")] private static void MyPrivateMethod() { } + [JSInvokable("ProtectedMethod")] protected static void MyProtectedMethod() { } + protected static void StaticMethodWithoutAttribute() { } + protected static void InstanceMethodWithoutAttribute() { } + + [JSInvokable("InvocableStaticVoid")] public static void MyInvocableVoid() + { + DidInvokeMyInvocableStaticVoid = true; + } + + [JSInvokable("InvocableStaticNonVoid")] + public static object MyInvocableNonVoid() + => new TestDTO { StringVal = "Test", IntVal = 123 }; + + [JSInvokable("InvocableStaticWithParams")] + public static object[] MyInvocableWithParams(TestDTO dtoViaJson, int[] incrementAmounts, TestDTO dtoByRef) + => new object[] + { + new TestDTO // Return via JSON marshalling + { + StringVal = dtoViaJson.StringVal.ToUpperInvariant(), + IntVal = dtoViaJson.IntVal + incrementAmounts.Sum() + }, + new DotNetObjectRef(new TestDTO // Return by ref + { + StringVal = dtoByRef.StringVal.ToUpperInvariant(), + IntVal = dtoByRef.IntVal + incrementAmounts.Sum() + }) + }; + + [JSInvokable] + public static TestDTO InvokableMethodWithoutCustomIdentifier() + => new TestDTO { StringVal = "InvokableMethodWithoutCustomIdentifier", IntVal = 456 }; + + [JSInvokable] + public void InvokableInstanceVoid() + { + DidInvokeMyInvocableInstanceVoid = true; + } + + [JSInvokable] + public object[] InvokableInstanceMethod(string someString, TestDTO someDTO) + { + // Returning an array to make the point that object references + // can be embedded anywhere in the result + return new object[] + { + $"You passed {someString}", + new DotNetObjectRef(new TestDTO + { + IntVal = someDTO.IntVal + 1, + StringVal = someDTO.StringVal.ToUpperInvariant() + }) + }; + } + + [JSInvokable] + public async Task InvokableAsyncMethod(TestDTO dtoViaJson, TestDTO dtoByRef) + { + await Task.Delay(50); + return new object[] + { + new TestDTO // Return via JSON + { + StringVal = dtoViaJson.StringVal.ToUpperInvariant(), + IntVal = dtoViaJson.IntVal * 2, + }, + new DotNetObjectRef(new TestDTO // Return by ref + { + StringVal = dtoByRef.StringVal.ToUpperInvariant(), + IntVal = dtoByRef.IntVal * 2, + }) + }; + } + } + + public class BaseClass + { + public bool DidInvokeMyBaseClassInvocableInstanceVoid; + + [JSInvokable] + public void BaseClassInvokableInstanceVoid() + { + DidInvokeMyBaseClassInvocableInstanceVoid = true; + } + } + + public class DerivedClass : BaseClass + { + } + + public class TestDTO + { + public string StringVal { get; set; } + public int IntVal { get; set; } + } + + public class TestJSRuntime : JSInProcessRuntimeBase + { + private TaskCompletionSource _nextInvocationTcs = new TaskCompletionSource(); + public Task NextInvocationTask => _nextInvocationTcs.Task; + public long LastInvocationAsyncHandle { get; private set; } + public string LastInvocationIdentifier { get; private set; } + public string LastInvocationArgsJson { get; private set; } + + protected override void BeginInvokeJS(long asyncHandle, string identifier, string argsJson) + { + LastInvocationAsyncHandle = asyncHandle; + LastInvocationIdentifier = identifier; + LastInvocationArgsJson = argsJson; + _nextInvocationTcs.SetResult(null); + _nextInvocationTcs = new TaskCompletionSource(); + } + + protected override string InvokeJS(string identifier, string argsJson) + { + LastInvocationAsyncHandle = default; + LastInvocationIdentifier = identifier; + LastInvocationArgsJson = argsJson; + _nextInvocationTcs.SetResult(null); + _nextInvocationTcs = new TaskCompletionSource(); + return null; + } + } + } +} diff --git a/src/JSInterop/test/Microsoft.JSInterop.Test/DotNetObjectRefTest.cs b/src/JSInterop/test/Microsoft.JSInterop.Test/DotNetObjectRefTest.cs new file mode 100644 index 0000000000..969dcae79d --- /dev/null +++ b/src/JSInterop/test/Microsoft.JSInterop.Test/DotNetObjectRefTest.cs @@ -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; +using System.Threading.Tasks; +using Xunit; + +namespace Microsoft.JSInterop.Test +{ + public class DotNetObjectRefTest + { + [Fact] + public void CanAccessValue() + { + var obj = new object(); + Assert.Same(obj, new DotNetObjectRef(obj).Value); + } + + [Fact] + public void CanAssociateWithSameRuntimeMultipleTimes() + { + var objRef = new DotNetObjectRef(new object()); + var jsRuntime = new TestJsRuntime(); + objRef.EnsureAttachedToJsRuntime(jsRuntime); + objRef.EnsureAttachedToJsRuntime(jsRuntime); + } + + [Fact] + public void CannotAssociateWithDifferentRuntimes() + { + var objRef = new DotNetObjectRef(new object()); + var jsRuntime1 = new TestJsRuntime(); + var jsRuntime2 = new TestJsRuntime(); + objRef.EnsureAttachedToJsRuntime(jsRuntime1); + + var ex = Assert.Throws( + () => objRef.EnsureAttachedToJsRuntime(jsRuntime2)); + Assert.Contains("Do not attempt to re-use", ex.Message); + } + + [Fact] + public void NotifiesAssociatedJsRuntimeOfDisposal() + { + // Arrange + var objRef = new DotNetObjectRef(new object()); + var jsRuntime = new TestJsRuntime(); + objRef.EnsureAttachedToJsRuntime(jsRuntime); + + // Act + objRef.Dispose(); + + // Assert + Assert.Equal(new[] { objRef }, jsRuntime.UntrackedRefs); + } + + class TestJsRuntime : IJSRuntime + { + public List UntrackedRefs = new List(); + + public Task InvokeAsync(string identifier, params object[] args) + => throw new NotImplementedException(); + + public void UntrackObjectRef(DotNetObjectRef dotNetObjectRef) + => UntrackedRefs.Add(dotNetObjectRef); + } + } +} diff --git a/src/JSInterop/test/Microsoft.JSInterop.Test/JSInProcessRuntimeBaseTest.cs b/src/JSInterop/test/Microsoft.JSInterop.Test/JSInProcessRuntimeBaseTest.cs new file mode 100644 index 0000000000..d2e71f6eb2 --- /dev/null +++ b/src/JSInterop/test/Microsoft.JSInterop.Test/JSInProcessRuntimeBaseTest.cs @@ -0,0 +1,117 @@ +// 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 Xunit; + +namespace Microsoft.JSInterop.Test +{ + public class JSInProcessRuntimeBaseTest + { + [Fact] + public void DispatchesSyncCallsAndDeserializesResults() + { + // Arrange + var runtime = new TestJSInProcessRuntime + { + NextResultJson = Json.Serialize( + new TestDTO { IntValue = 123, StringValue = "Hello" }) + }; + + // Act + var syncResult = runtime.Invoke("test identifier 1", "arg1", 123, true ); + var call = runtime.InvokeCalls.Single(); + + // Assert + Assert.Equal(123, syncResult.IntValue); + Assert.Equal("Hello", syncResult.StringValue); + Assert.Equal("test identifier 1", call.Identifier); + Assert.Equal("[\"arg1\",123,true]", call.ArgsJson); + } + + [Fact] + public void SerializesDotNetObjectWrappersInKnownFormat() + { + // Arrange + var runtime = new TestJSInProcessRuntime { NextResultJson = null }; + var obj1 = new object(); + var obj2 = new object(); + var obj3 = new object(); + + // Act + // Showing we can pass the DotNetObject either as top-level args or nested + var syncResult = runtime.Invoke("test identifier", + new DotNetObjectRef(obj1), + new Dictionary + { + { "obj2", new DotNetObjectRef(obj2) }, + { "obj3", new DotNetObjectRef(obj3) } + }); + + // Assert: Handles null result string + Assert.Null(syncResult); + + // Assert: Serialized as expected + var call = runtime.InvokeCalls.Single(); + Assert.Equal("test identifier", call.Identifier); + Assert.Equal("[\"__dotNetObject:1\",{\"obj2\":\"__dotNetObject:2\",\"obj3\":\"__dotNetObject:3\"}]", call.ArgsJson); + + // Assert: Objects were tracked + Assert.Same(obj1, runtime.ArgSerializerStrategy.FindDotNetObject(1)); + Assert.Same(obj2, runtime.ArgSerializerStrategy.FindDotNetObject(2)); + Assert.Same(obj3, runtime.ArgSerializerStrategy.FindDotNetObject(3)); + } + + [Fact] + public void SyncCallResultCanIncludeDotNetObjects() + { + // Arrange + var runtime = new TestJSInProcessRuntime + { + NextResultJson = "[\"__dotNetObject:2\",\"__dotNetObject:1\"]" + }; + var obj1 = new object(); + var obj2 = new object(); + + // Act + var syncResult = runtime.Invoke("test identifier", + new DotNetObjectRef(obj1), + "some other arg", + new DotNetObjectRef(obj2)); + var call = runtime.InvokeCalls.Single(); + + // Assert + Assert.Equal(new[] { obj2, obj1 }, syncResult); + } + + class TestDTO + { + public int IntValue { get; set; } + public string StringValue { get; set; } + } + + class TestJSInProcessRuntime : JSInProcessRuntimeBase + { + public List InvokeCalls { get; set; } = new List(); + + public string NextResultJson { get; set; } + + protected override string InvokeJS(string identifier, string argsJson) + { + InvokeCalls.Add(new InvokeArgs { Identifier = identifier, ArgsJson = argsJson }); + return NextResultJson; + } + + public class InvokeArgs + { + public string Identifier { get; set; } + public string ArgsJson { get; set; } + } + + protected override void BeginInvokeJS(long asyncHandle, string identifier, string argsJson) + => throw new NotImplementedException("This test only covers sync calls"); + } + } +} diff --git a/src/JSInterop/test/Microsoft.JSInterop.Test/JSRuntimeBaseTest.cs b/src/JSInterop/test/Microsoft.JSInterop.Test/JSRuntimeBaseTest.cs new file mode 100644 index 0000000000..9193d6deb8 --- /dev/null +++ b/src/JSInterop/test/Microsoft.JSInterop.Test/JSRuntimeBaseTest.cs @@ -0,0 +1,191 @@ +// 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.JSInterop.Internal; +using System; +using System.Collections.Generic; +using System.Linq; +using Xunit; + +namespace Microsoft.JSInterop.Test +{ + public class JSRuntimeBaseTest + { + [Fact] + public void DispatchesAsyncCallsWithDistinctAsyncHandles() + { + // Arrange + var runtime = new TestJSRuntime(); + + // Act + runtime.InvokeAsync("test identifier 1", "arg1", 123, true ); + runtime.InvokeAsync("test identifier 2", "some other arg"); + + // Assert + Assert.Collection(runtime.BeginInvokeCalls, + call => + { + Assert.Equal("test identifier 1", call.Identifier); + Assert.Equal("[\"arg1\",123,true]", call.ArgsJson); + }, + call => + { + Assert.Equal("test identifier 2", call.Identifier); + Assert.Equal("[\"some other arg\"]", call.ArgsJson); + Assert.NotEqual(runtime.BeginInvokeCalls[0].AsyncHandle, call.AsyncHandle); + }); + } + + [Fact] + public void CanCompleteAsyncCallsAsSuccess() + { + // Arrange + var runtime = new TestJSRuntime(); + + // Act/Assert: Tasks not initially completed + var unrelatedTask = runtime.InvokeAsync("unrelated call", Array.Empty()); + var task = runtime.InvokeAsync("test identifier", Array.Empty()); + Assert.False(unrelatedTask.IsCompleted); + Assert.False(task.IsCompleted); + + // Act/Assert: Task can be completed + runtime.OnEndInvoke( + runtime.BeginInvokeCalls[1].AsyncHandle, + /* succeeded: */ true, + "my result"); + Assert.False(unrelatedTask.IsCompleted); + Assert.True(task.IsCompleted); + Assert.Equal("my result", task.Result); + } + + [Fact] + public void CanCompleteAsyncCallsAsFailure() + { + // Arrange + var runtime = new TestJSRuntime(); + + // Act/Assert: Tasks not initially completed + var unrelatedTask = runtime.InvokeAsync("unrelated call", Array.Empty()); + var task = runtime.InvokeAsync("test identifier", Array.Empty()); + Assert.False(unrelatedTask.IsCompleted); + Assert.False(task.IsCompleted); + + // Act/Assert: Task can be failed + runtime.OnEndInvoke( + runtime.BeginInvokeCalls[1].AsyncHandle, + /* succeeded: */ false, + "This is a test exception"); + Assert.False(unrelatedTask.IsCompleted); + Assert.True(task.IsCompleted); + + Assert.IsType(task.Exception); + Assert.IsType(task.Exception.InnerException); + Assert.Equal("This is a test exception", ((JSException)task.Exception.InnerException).Message); + } + + [Fact] + public void CannotCompleteSameAsyncCallMoreThanOnce() + { + // Arrange + var runtime = new TestJSRuntime(); + + // Act/Assert + runtime.InvokeAsync("test identifier", Array.Empty()); + var asyncHandle = runtime.BeginInvokeCalls[0].AsyncHandle; + runtime.OnEndInvoke(asyncHandle, true, null); + var ex = Assert.Throws(() => + { + // Second "end invoke" will fail + runtime.OnEndInvoke(asyncHandle, true, null); + }); + Assert.Equal($"There is no pending task with handle '{asyncHandle}'.", ex.Message); + } + + [Fact] + public void SerializesDotNetObjectWrappersInKnownFormat() + { + // Arrange + var runtime = new TestJSRuntime(); + var obj1 = new object(); + var obj2 = new object(); + var obj3 = new object(); + + // Act + // Showing we can pass the DotNetObject either as top-level args or nested + var obj1Ref = new DotNetObjectRef(obj1); + var obj1DifferentRef = new DotNetObjectRef(obj1); + runtime.InvokeAsync("test identifier", + obj1Ref, + new Dictionary + { + { "obj2", new DotNetObjectRef(obj2) }, + { "obj3", new DotNetObjectRef(obj3) }, + { "obj1SameRef", obj1Ref }, + { "obj1DifferentRef", obj1DifferentRef }, + }); + + // Assert: Serialized as expected + var call = runtime.BeginInvokeCalls.Single(); + Assert.Equal("test identifier", call.Identifier); + Assert.Equal("[\"__dotNetObject:1\",{\"obj2\":\"__dotNetObject:2\",\"obj3\":\"__dotNetObject:3\",\"obj1SameRef\":\"__dotNetObject:1\",\"obj1DifferentRef\":\"__dotNetObject:4\"}]", call.ArgsJson); + + // Assert: Objects were tracked + Assert.Same(obj1, runtime.ArgSerializerStrategy.FindDotNetObject(1)); + Assert.Same(obj2, runtime.ArgSerializerStrategy.FindDotNetObject(2)); + Assert.Same(obj3, runtime.ArgSerializerStrategy.FindDotNetObject(3)); + Assert.Same(obj1, runtime.ArgSerializerStrategy.FindDotNetObject(4)); + } + + [Fact] + public void SupportsCustomSerializationForArguments() + { + // Arrange + var runtime = new TestJSRuntime(); + + // Arrange/Act + runtime.InvokeAsync("test identifier", + new WithCustomArgSerializer()); + + // Asssert + var call = runtime.BeginInvokeCalls.Single(); + Assert.Equal("[{\"key1\":\"value1\",\"key2\":123}]", call.ArgsJson); + } + + class TestJSRuntime : JSRuntimeBase + { + public List BeginInvokeCalls = new List(); + + public class BeginInvokeAsyncArgs + { + public long AsyncHandle { get; set; } + public string Identifier { get; set; } + public string ArgsJson { get; set; } + } + + protected override void BeginInvokeJS(long asyncHandle, string identifier, string argsJson) + { + BeginInvokeCalls.Add(new BeginInvokeAsyncArgs + { + AsyncHandle = asyncHandle, + Identifier = identifier, + ArgsJson = argsJson, + }); + } + + public void OnEndInvoke(long asyncHandle, bool succeeded, object resultOrException) + => EndInvokeJS(asyncHandle, succeeded, resultOrException); + } + + class WithCustomArgSerializer : ICustomArgSerializer + { + public object ToJsonPrimitive() + { + return new Dictionary + { + { "key1", "value1" }, + { "key2", 123 }, + }; + } + } + } +} diff --git a/src/JSInterop/test/Microsoft.JSInterop.Test/JSRuntimeTest.cs b/src/JSInterop/test/Microsoft.JSInterop.Test/JSRuntimeTest.cs new file mode 100644 index 0000000000..d5fed45ea4 --- /dev/null +++ b/src/JSInterop/test/Microsoft.JSInterop.Test/JSRuntimeTest.cs @@ -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. + +using System; +using System.Linq; +using System.Threading.Tasks; +using Xunit; + +namespace Microsoft.JSInterop.Test +{ + public class JSRuntimeTest + { + [Fact] + public async Task CanHaveDistinctJSRuntimeInstancesInEachAsyncContext() + { + var tasks = Enumerable.Range(0, 20).Select(async _ => + { + var jsRuntime = new FakeJSRuntime(); + JSRuntime.SetCurrentJSRuntime(jsRuntime); + await Task.Delay(50).ConfigureAwait(false); + Assert.Same(jsRuntime, JSRuntime.Current); + }); + + await Task.WhenAll(tasks); + Assert.Null(JSRuntime.Current); + } + + private class FakeJSRuntime : IJSRuntime + { + public Task InvokeAsync(string identifier, params object[] args) + => throw new NotImplementedException(); + + public void UntrackObjectRef(DotNetObjectRef dotNetObjectRef) + => throw new NotImplementedException(); + } + } +} diff --git a/src/JSInterop/test/Microsoft.JSInterop.Test/JsonUtilTest.cs b/src/JSInterop/test/Microsoft.JSInterop.Test/JsonUtilTest.cs new file mode 100644 index 0000000000..1be98b681e --- /dev/null +++ b/src/JSInterop/test/Microsoft.JSInterop.Test/JsonUtilTest.cs @@ -0,0 +1,349 @@ +// 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.JSInterop.Internal; +using System; +using System.Collections.Generic; +using Xunit; + +namespace Microsoft.JSInterop.Test +{ + public class JsonUtilTest + { + // It's not useful to have a complete set of behavior specifications for + // what the JSON serializer/deserializer does in all cases here. We merely + // expose a simple wrapper over a third-party library that maintains its + // own specs and tests. + // + // We should only add tests here to cover behaviors that Blazor itself + // depends on. + + [Theory] + [InlineData(null, "null")] + [InlineData("My string", "\"My string\"")] + [InlineData(123, "123")] + [InlineData(123.456f, "123.456")] + [InlineData(123.456d, "123.456")] + [InlineData(true, "true")] + public void CanSerializePrimitivesToJson(object value, string expectedJson) + { + Assert.Equal(expectedJson, Json.Serialize(value)); + } + + [Theory] + [InlineData("null", null)] + [InlineData("\"My string\"", "My string")] + [InlineData("123", 123L)] // Would also accept 123 as a System.Int32, but Int64 is fine as a default + [InlineData("123.456", 123.456d)] + [InlineData("true", true)] + public void CanDeserializePrimitivesFromJson(string json, object expectedValue) + { + Assert.Equal(expectedValue, Json.Deserialize(json)); + } + + [Fact] + public void CanSerializeClassToJson() + { + // Arrange + var person = new Person + { + Id = 1844, + Name = "Athos", + Pets = new[] { "Aramis", "Porthos", "D'Artagnan" }, + Hobby = Hobbies.Swordfighting, + SecondaryHobby = Hobbies.Reading, + Nicknames = new List { "Comte de la Fère", "Armand" }, + BirthInstant = new DateTimeOffset(1825, 8, 6, 18, 45, 21, TimeSpan.FromHours(-6)), + Age = new TimeSpan(7665, 1, 30, 0), + Allergies = new Dictionary { { "Ducks", true }, { "Geese", false } }, + }; + + // Act/Assert + Assert.Equal( + "{\"id\":1844,\"name\":\"Athos\",\"pets\":[\"Aramis\",\"Porthos\",\"D'Artagnan\"],\"hobby\":2,\"secondaryHobby\":1,\"nullHobby\":null,\"nicknames\":[\"Comte de la Fère\",\"Armand\"],\"birthInstant\":\"1825-08-06T18:45:21.0000000-06:00\",\"age\":\"7665.01:30:00\",\"allergies\":{\"Ducks\":true,\"Geese\":false}}", + Json.Serialize(person)); + } + + [Fact] + public void CanDeserializeClassFromJson() + { + // Arrange + var json = "{\"id\":1844,\"name\":\"Athos\",\"pets\":[\"Aramis\",\"Porthos\",\"D'Artagnan\"],\"hobby\":2,\"secondaryHobby\":1,\"nullHobby\":null,\"nicknames\":[\"Comte de la Fère\",\"Armand\"],\"birthInstant\":\"1825-08-06T18:45:21.0000000-06:00\",\"age\":\"7665.01:30:00\",\"allergies\":{\"Ducks\":true,\"Geese\":false}}"; + + // Act + var person = Json.Deserialize(json); + + // Assert + Assert.Equal(1844, person.Id); + Assert.Equal("Athos", person.Name); + Assert.Equal(new[] { "Aramis", "Porthos", "D'Artagnan" }, person.Pets); + Assert.Equal(Hobbies.Swordfighting, person.Hobby); + Assert.Equal(Hobbies.Reading, person.SecondaryHobby); + Assert.Null(person.NullHobby); + Assert.Equal(new[] { "Comte de la Fère", "Armand" }, person.Nicknames); + Assert.Equal(new DateTimeOffset(1825, 8, 6, 18, 45, 21, TimeSpan.FromHours(-6)), person.BirthInstant); + Assert.Equal(new TimeSpan(7665, 1, 30, 0), person.Age); + Assert.Equal(new Dictionary { { "Ducks", true }, { "Geese", false } }, person.Allergies); + } + + [Fact] + public void CanDeserializeWithCaseInsensitiveKeys() + { + // Arrange + var json = "{\"ID\":1844,\"NamE\":\"Athos\"}"; + + // Act + var person = Json.Deserialize(json); + + // Assert + Assert.Equal(1844, person.Id); + Assert.Equal("Athos", person.Name); + } + + [Fact] + public void DeserializationPrefersPropertiesOverFields() + { + // Arrange + var json = "{\"member1\":\"Hello\"}"; + + // Act + var person = Json.Deserialize(json); + + // Assert + Assert.Equal("Hello", person.Member1); + Assert.Null(person.member1); + } + + [Fact] + public void CanSerializeStructToJson() + { + // Arrange + var commandResult = new SimpleStruct + { + StringProperty = "Test", + BoolProperty = true, + NullableIntProperty = 1 + }; + + // Act + var result = Json.Serialize(commandResult); + + // Assert + Assert.Equal("{\"stringProperty\":\"Test\",\"boolProperty\":true,\"nullableIntProperty\":1}", result); + } + + [Fact] + public void CanDeserializeStructFromJson() + { + // Arrange + var json = "{\"stringProperty\":\"Test\",\"boolProperty\":true,\"nullableIntProperty\":1}"; + + //Act + var simpleError = Json.Deserialize(json); + + // Assert + Assert.Equal("Test", simpleError.StringProperty); + Assert.True(simpleError.BoolProperty); + Assert.Equal(1, simpleError.NullableIntProperty); + } + + [Fact] + public void CanCreateInstanceOfClassWithPrivateConstructor() + { + // Arrange + var expectedName = "NameValue"; + var json = $"{{\"Name\":\"{expectedName}\"}}"; + + // Act + var instance = Json.Deserialize(json); + + // Assert + Assert.Equal(expectedName, instance.Name); + } + + [Fact] + public void CanSetValueOfPublicPropertiesWithNonPublicSetters() + { + // Arrange + var expectedPrivateValue = "PrivateValue"; + var expectedProtectedValue = "ProtectedValue"; + var expectedInternalValue = "InternalValue"; + + var json = "{" + + $"\"PrivateSetter\":\"{expectedPrivateValue}\"," + + $"\"ProtectedSetter\":\"{expectedProtectedValue}\"," + + $"\"InternalSetter\":\"{expectedInternalValue}\"," + + "}"; + + // Act + var instance = Json.Deserialize(json); + + // Assert + Assert.Equal(expectedPrivateValue, instance.PrivateSetter); + Assert.Equal(expectedProtectedValue, instance.ProtectedSetter); + Assert.Equal(expectedInternalValue, instance.InternalSetter); + } + + [Fact] + public void RejectsTypesWithAmbiguouslyNamedProperties() + { + var ex = Assert.Throws(() => + { + Json.Deserialize("{}"); + }); + + Assert.Equal($"The type '{typeof(ClashingProperties).FullName}' contains multiple public properties " + + $"with names case-insensitively matching '{nameof(ClashingProperties.PROP1).ToLowerInvariant()}'. " + + $"Such types cannot be used for JSON deserialization.", + ex.Message); + } + + [Fact] + public void RejectsTypesWithAmbiguouslyNamedFields() + { + var ex = Assert.Throws(() => + { + Json.Deserialize("{}"); + }); + + Assert.Equal($"The type '{typeof(ClashingFields).FullName}' contains multiple public fields " + + $"with names case-insensitively matching '{nameof(ClashingFields.Field1).ToLowerInvariant()}'. " + + $"Such types cannot be used for JSON deserialization.", + ex.Message); + } + + [Fact] + public void NonEmptyConstructorThrowsUsefulException() + { + // Arrange + var json = "{\"Property\":1}"; + var type = typeof(NonEmptyConstructorPoco); + + // Act + var exception = Assert.Throws(() => + { + Json.Deserialize(json); + }); + + // Assert + Assert.Equal( + $"Cannot deserialize JSON into type '{type.FullName}' because it does not have a public parameterless constructor.", + exception.Message); + } + + // Test cases based on https://github.com/JamesNK/Newtonsoft.Json/blob/122afba9908832bd5ac207164ee6c303bfd65cf1/Src/Newtonsoft.Json.Tests/Utilities/StringUtilsTests.cs#L41 + // The only difference is that our logic doesn't have to handle space-separated words, + // because we're only use this for camelcasing .NET member names + // + // Not all of the following cases are really valid .NET member names, but we have no reason + // to implement more logic to detect invalid member names besides the basics (null or empty). + [Theory] + [InlineData("URLValue", "urlValue")] + [InlineData("URL", "url")] + [InlineData("ID", "id")] + [InlineData("I", "i")] + [InlineData("Person", "person")] + [InlineData("xPhone", "xPhone")] + [InlineData("XPhone", "xPhone")] + [InlineData("X_Phone", "x_Phone")] + [InlineData("X__Phone", "x__Phone")] + [InlineData("IsCIA", "isCIA")] + [InlineData("VmQ", "vmQ")] + [InlineData("Xml2Json", "xml2Json")] + [InlineData("SnAkEcAsE", "snAkEcAsE")] + [InlineData("SnA__kEcAsE", "snA__kEcAsE")] + [InlineData("already_snake_case_", "already_snake_case_")] + [InlineData("IsJSONProperty", "isJSONProperty")] + [InlineData("SHOUTING_CASE", "shoutinG_CASE")] + [InlineData("9999-12-31T23:59:59.9999999Z", "9999-12-31T23:59:59.9999999Z")] + [InlineData("Hi!! This is text. Time to test.", "hi!! This is text. Time to test.")] + [InlineData("BUILDING", "building")] + [InlineData("BUILDINGProperty", "buildingProperty")] + public void MemberNameToCamelCase_Valid(string input, string expectedOutput) + { + Assert.Equal(expectedOutput, CamelCase.MemberNameToCamelCase(input)); + } + + [Theory] + [InlineData("")] + [InlineData(null)] + public void MemberNameToCamelCase_Invalid(string input) + { + var ex = Assert.Throws(() => + CamelCase.MemberNameToCamelCase(input)); + Assert.Equal("value", ex.ParamName); + Assert.StartsWith($"The value '{input ?? "null"}' is not a valid member name.", ex.Message); + } + + class NonEmptyConstructorPoco + { + public NonEmptyConstructorPoco(int parameter) {} + + public int Property { get; set; } + } + + struct SimpleStruct + { + public string StringProperty { get; set; } + public bool BoolProperty { get; set; } + public int? NullableIntProperty { get; set; } + } + + class Person + { + public int Id { get; set; } + public string Name { get; set; } + public string[] Pets { get; set; } + public Hobbies Hobby { get; set; } + public Hobbies? SecondaryHobby { get; set; } + public Hobbies? NullHobby { get; set; } + public IList Nicknames { get; set; } + public DateTimeOffset BirthInstant { get; set; } + public TimeSpan Age { get; set; } + public IDictionary Allergies { get; set; } + } + + enum Hobbies { Reading = 1, Swordfighting = 2 } + +#pragma warning disable 0649 + class ClashingProperties + { + public string Prop1 { get; set; } + public int PROP1 { get; set; } + } + + class ClashingFields + { + public string Field1; + public int field1; + } + + class PrefersPropertiesOverFields + { + public string member1; + public string Member1 { get; set; } + } +#pragma warning restore 0649 + + class PrivateConstructor + { + public string Name { get; set; } + + private PrivateConstructor() + { + } + + public PrivateConstructor(string name) + { + Name = name; + } + } + + class NonPublicSetterOnPublicProperty + { + public string PrivateSetter { get; private set; } + public string ProtectedSetter { get; protected set; } + public string InternalSetter { get; internal set; } + } + } +} diff --git a/src/JSInterop/test/Microsoft.JSInterop.Test/Microsoft.JSInterop.Test.csproj b/src/JSInterop/test/Microsoft.JSInterop.Test/Microsoft.JSInterop.Test.csproj new file mode 100644 index 0000000000..893a11d33a --- /dev/null +++ b/src/JSInterop/test/Microsoft.JSInterop.Test/Microsoft.JSInterop.Test.csproj @@ -0,0 +1,20 @@ + + + + netcoreapp2.1 + false + 7.3 + + + + + + + + + + + + + + From 4de3b5e105caf3b9598a7f476d7ca9e244c5b28d Mon Sep 17 00:00:00 2001 From: Chris Ross Date: Wed, 9 Jan 2019 13:38:12 -0800 Subject: [PATCH 16/52] Move the HostFactoryResolver to Extensions (dotnet/extensions#925) \n\nCommit migrated from https://github.com/dotnet/extensions/commit/9cd9cea3983ec3421038c4b540161f0686273754 --- .../HostFactoryResolver.cs | 112 +++++++++++++++++ .../Shared.Tests/HostFactoryResolverTests.cs | 116 ++++++++++++++++++ .../Microsoft.AspNetCore.Shared.Tests.csproj | 10 ++ .../BuildWebHostInvalidSignature.csproj | 12 ++ .../BuildWebHostInvalidSignature/Program.cs | 17 +++ .../BuildWebHostPatternTestSite.csproj | 12 ++ .../BuildWebHostPatternTestSite/Program.cs | 16 +++ .../CreateHostBuilderInvalidSignature.csproj | 12 ++ .../Program.cs | 18 +++ .../CreateHostBuilderPatternTestSite.csproj | 12 ++ .../Program.cs | 19 +++ ...reateWebHostBuilderInvalidSignature.csproj | 12 ++ .../Program.cs | 17 +++ ...CreateWebHostBuilderPatternTestSite.csproj | 12 ++ .../Program.cs | 19 +++ .../test/testassets/MockHostTypes/Host.cs | 12 ++ .../testassets/MockHostTypes/HostBuilder.cs | 10 ++ .../test/testassets/MockHostTypes/IHost.cs | 12 ++ .../testassets/MockHostTypes/IHostBuilder.cs | 10 ++ .../test/testassets/MockHostTypes/IWebHost.cs | 12 ++ .../MockHostTypes/IWebHostBuilder.cs | 10 ++ .../MockHostTypes/MockHostTypes.csproj | 7 ++ .../MockHostTypes/ServiceProvider.cs | 12 ++ .../test/testassets/MockHostTypes/WebHost.cs | 12 ++ .../MockHostTypes/WebHostBuilder.cs | 10 ++ 25 files changed, 523 insertions(+) create mode 100644 src/Shared/HostFactoryResolver/HostFactoryResolver.cs create mode 100644 src/Shared/test/Shared.Tests/HostFactoryResolverTests.cs create mode 100644 src/Shared/test/testassets/BuildWebHostInvalidSignature/BuildWebHostInvalidSignature.csproj create mode 100644 src/Shared/test/testassets/BuildWebHostInvalidSignature/Program.cs create mode 100644 src/Shared/test/testassets/BuildWebHostPatternTestSite/BuildWebHostPatternTestSite.csproj create mode 100644 src/Shared/test/testassets/BuildWebHostPatternTestSite/Program.cs create mode 100644 src/Shared/test/testassets/CreateHostBuilderInvalidSignature/CreateHostBuilderInvalidSignature.csproj create mode 100644 src/Shared/test/testassets/CreateHostBuilderInvalidSignature/Program.cs create mode 100644 src/Shared/test/testassets/CreateHostBuilderPatternTestSite/CreateHostBuilderPatternTestSite.csproj create mode 100644 src/Shared/test/testassets/CreateHostBuilderPatternTestSite/Program.cs create mode 100644 src/Shared/test/testassets/CreateWebHostBuilderInvalidSignature/CreateWebHostBuilderInvalidSignature.csproj create mode 100644 src/Shared/test/testassets/CreateWebHostBuilderInvalidSignature/Program.cs create mode 100644 src/Shared/test/testassets/CreateWebHostBuilderPatternTestSite/CreateWebHostBuilderPatternTestSite.csproj create mode 100644 src/Shared/test/testassets/CreateWebHostBuilderPatternTestSite/Program.cs create mode 100644 src/Shared/test/testassets/MockHostTypes/Host.cs create mode 100644 src/Shared/test/testassets/MockHostTypes/HostBuilder.cs create mode 100644 src/Shared/test/testassets/MockHostTypes/IHost.cs create mode 100644 src/Shared/test/testassets/MockHostTypes/IHostBuilder.cs create mode 100644 src/Shared/test/testassets/MockHostTypes/IWebHost.cs create mode 100644 src/Shared/test/testassets/MockHostTypes/IWebHostBuilder.cs create mode 100644 src/Shared/test/testassets/MockHostTypes/MockHostTypes.csproj create mode 100644 src/Shared/test/testassets/MockHostTypes/ServiceProvider.cs create mode 100644 src/Shared/test/testassets/MockHostTypes/WebHost.cs create mode 100644 src/Shared/test/testassets/MockHostTypes/WebHostBuilder.cs diff --git a/src/Shared/HostFactoryResolver/HostFactoryResolver.cs b/src/Shared/HostFactoryResolver/HostFactoryResolver.cs new file mode 100644 index 0000000000..cb9f811237 --- /dev/null +++ b/src/Shared/HostFactoryResolver/HostFactoryResolver.cs @@ -0,0 +1,112 @@ +// 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.Reflection; + +namespace Microsoft.Extensions.Hosting +{ + internal class HostFactoryResolver + { + public static readonly string BuildWebHost = nameof(BuildWebHost); + public static readonly string CreateWebHostBuilder = nameof(CreateWebHostBuilder); + public static readonly string CreateHostBuilder = nameof(CreateHostBuilder); + + public static Func ResolveWebHostFactory(Assembly assembly) + { + return ResolveFactory(assembly, BuildWebHost); + } + + public static Func ResolveWebHostBuilderFactory(Assembly assembly) + { + return ResolveFactory(assembly, CreateWebHostBuilder); + } + + public static Func ResolveHostBuilderFactory(Assembly assembly) + { + return ResolveFactory(assembly, CreateHostBuilder); + } + + private static Func ResolveFactory(Assembly assembly, string name) + { + var programType = assembly?.EntryPoint?.DeclaringType; + if (programType == null) + { + return null; + } + + var factory = programType.GetTypeInfo().GetDeclaredMethod(name); + if (!IsFactory(factory)) + { + return null; + } + + return args => (T)factory.Invoke(null, new object[] { args }); + } + + // TReturn Factory(string[] args); + private static bool IsFactory(MethodInfo factory) + { + return factory != null + && typeof(TReturn).IsAssignableFrom(factory.ReturnType) + && factory.GetParameters().Length == 1 + && typeof(string[]).Equals(factory.GetParameters()[0].ParameterType); + } + + // Used by EF tooling without any Hosting references. Looses some return type safety checks. + public static Func ResolveServiceProviderFactory(Assembly assembly) + { + // Prefer the older patterns by default for back compat. + var webHostFactory = ResolveWebHostFactory(assembly); + if (webHostFactory != null) + { + return args => + { + var webHost = webHostFactory(args); + return GetServiceProvider(webHost); + }; + } + + var webHostBuilderFactory = ResolveWebHostBuilderFactory(assembly); + if (webHostBuilderFactory != null) + { + return args => + { + var webHostBuilder = webHostBuilderFactory(args); + var webHost = Build(webHostBuilder); + return GetServiceProvider(webHost); + }; + } + + var hostBuilderFactory = ResolveHostBuilderFactory(assembly); + if (hostBuilderFactory != null) + { + return args => + { + var hostBuilder = hostBuilderFactory(args); + var host = Build(hostBuilder); + return GetServiceProvider(host); + }; + } + + return null; + } + + private static object Build(object builder) + { + var buildMethod = builder.GetType().GetMethod("Build"); + return buildMethod?.Invoke(builder, Array.Empty()); + } + + private static IServiceProvider GetServiceProvider(object host) + { + if (host == null) + { + return null; + } + var hostType = host.GetType(); + var servicesProperty = hostType.GetTypeInfo().GetDeclaredProperty("Services"); + return (IServiceProvider)servicesProperty.GetValue(host); + } + } +} diff --git a/src/Shared/test/Shared.Tests/HostFactoryResolverTests.cs b/src/Shared/test/Shared.Tests/HostFactoryResolverTests.cs new file mode 100644 index 0000000000..a26fb7b133 --- /dev/null +++ b/src/Shared/test/Shared.Tests/HostFactoryResolverTests.cs @@ -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 MockHostTypes; +using System; +using Xunit; + +namespace Microsoft.Extensions.Hosting.Tests +{ + public class HostFactoryResolverTests + { + [Fact] + public void BuildWebHostPattern_CanFindWebHost() + { + var factory = HostFactoryResolver.ResolveWebHostFactory(typeof(BuildWebHostPatternTestSite.Program).Assembly); + + Assert.NotNull(factory); + Assert.IsAssignableFrom(factory(Array.Empty())); + } + + [Fact] + public void BuildWebHostPattern_CanFindServiceProvider() + { + var factory = HostFactoryResolver.ResolveServiceProviderFactory(typeof(BuildWebHostPatternTestSite.Program).Assembly); + + Assert.NotNull(factory); + Assert.IsAssignableFrom(factory(Array.Empty())); + } + + [Fact] + public void BuildWebHostPattern__Invalid_CantFindWebHost() + { + var factory = HostFactoryResolver.ResolveWebHostFactory(typeof(BuildWebHostInvalidSignature.Program).Assembly); + + Assert.Null(factory); + } + + [Fact] + public void BuildWebHostPattern__Invalid_CantFindServiceProvider() + { + var factory = HostFactoryResolver.ResolveServiceProviderFactory(typeof(BuildWebHostInvalidSignature.Program).Assembly); + + Assert.Null(factory); + } + + [Fact] + public void CreateWebHostBuilderPattern_CanFindWebHostBuilder() + { + var factory = HostFactoryResolver.ResolveWebHostBuilderFactory(typeof(CreateWebHostBuilderPatternTestSite.Program).Assembly); + + Assert.NotNull(factory); + Assert.IsAssignableFrom(factory(Array.Empty())); + } + + [Fact] + public void CreateWebHostBuilderPattern_CanFindServiceProvider() + { + var factory = HostFactoryResolver.ResolveServiceProviderFactory(typeof(CreateWebHostBuilderPatternTestSite.Program).Assembly); + + Assert.NotNull(factory); + Assert.IsAssignableFrom(factory(Array.Empty())); + } + + [Fact] + public void CreateWebHostBuilderPattern__Invalid_CantFindWebHostBuilder() + { + var factory = HostFactoryResolver.ResolveWebHostBuilderFactory(typeof(CreateWebHostBuilderInvalidSignature.Program).Assembly); + + Assert.Null(factory); + } + + [Fact] + public void CreateWebHostBuilderPattern__InvalidReturnType_CanFindServiceProvider() + { + var factory = HostFactoryResolver.ResolveServiceProviderFactory(typeof(CreateWebHostBuilderInvalidSignature.Program).Assembly); + + Assert.NotNull(factory); + Assert.Null(factory(Array.Empty())); + + } + + [Fact] + public void CreateHostBuilderPattern_CanFindHostBuilder() + { + var factory = HostFactoryResolver.ResolveHostBuilderFactory(typeof(CreateHostBuilderPatternTestSite.Program).Assembly); + + Assert.NotNull(factory); + Assert.IsAssignableFrom(factory(Array.Empty())); + } + + [Fact] + public void CreateHostBuilderPattern_CanFindServiceProvider() + { + var factory = HostFactoryResolver.ResolveServiceProviderFactory(typeof(CreateHostBuilderPatternTestSite.Program).Assembly); + + Assert.NotNull(factory); + Assert.IsAssignableFrom(factory(Array.Empty())); + } + + [Fact] + public void CreateHostBuilderPattern__Invalid_CantFindHostBuilder() + { + var factory = HostFactoryResolver.ResolveHostBuilderFactory(typeof(CreateHostBuilderInvalidSignature.Program).Assembly); + + Assert.Null(factory); + } + + [Fact] + public void CreateHostBuilderPattern__Invalid_CantFindServiceProvider() + { + var factory = HostFactoryResolver.ResolveServiceProviderFactory(typeof(CreateHostBuilderInvalidSignature.Program).Assembly); + + Assert.Null(factory); + } + } +} diff --git a/src/Shared/test/Shared.Tests/Microsoft.AspNetCore.Shared.Tests.csproj b/src/Shared/test/Shared.Tests/Microsoft.AspNetCore.Shared.Tests.csproj index a5950b7fc9..222c88bc67 100644 --- a/src/Shared/test/Shared.Tests/Microsoft.AspNetCore.Shared.Tests.csproj +++ b/src/Shared/test/Shared.Tests/Microsoft.AspNetCore.Shared.Tests.csproj @@ -13,6 +13,16 @@ " /> + + + + + + + + + + diff --git a/src/Shared/test/testassets/BuildWebHostInvalidSignature/BuildWebHostInvalidSignature.csproj b/src/Shared/test/testassets/BuildWebHostInvalidSignature/BuildWebHostInvalidSignature.csproj new file mode 100644 index 0000000000..6368289f65 --- /dev/null +++ b/src/Shared/test/testassets/BuildWebHostInvalidSignature/BuildWebHostInvalidSignature.csproj @@ -0,0 +1,12 @@ + + + + netcoreapp3.0;net472 + Exe + + + + + + + diff --git a/src/Shared/test/testassets/BuildWebHostInvalidSignature/Program.cs b/src/Shared/test/testassets/BuildWebHostInvalidSignature/Program.cs new file mode 100644 index 0000000000..ba9e3dab6a --- /dev/null +++ b/src/Shared/test/testassets/BuildWebHostInvalidSignature/Program.cs @@ -0,0 +1,17 @@ +// 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 MockHostTypes; + +namespace BuildWebHostInvalidSignature +{ + public class Program + { + static void Main(string[] args) + { + } + + // Missing string[] args + public static IWebHost BuildWebHost() => null; + } +} diff --git a/src/Shared/test/testassets/BuildWebHostPatternTestSite/BuildWebHostPatternTestSite.csproj b/src/Shared/test/testassets/BuildWebHostPatternTestSite/BuildWebHostPatternTestSite.csproj new file mode 100644 index 0000000000..6368289f65 --- /dev/null +++ b/src/Shared/test/testassets/BuildWebHostPatternTestSite/BuildWebHostPatternTestSite.csproj @@ -0,0 +1,12 @@ + + + + netcoreapp3.0;net472 + Exe + + + + + + + diff --git a/src/Shared/test/testassets/BuildWebHostPatternTestSite/Program.cs b/src/Shared/test/testassets/BuildWebHostPatternTestSite/Program.cs new file mode 100644 index 0000000000..b1d0655e4d --- /dev/null +++ b/src/Shared/test/testassets/BuildWebHostPatternTestSite/Program.cs @@ -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. + +using MockHostTypes; + +namespace BuildWebHostPatternTestSite +{ + public class Program + { + static void Main(string[] args) + { + } + + public static IWebHost BuildWebHost(string[] args) => new WebHost(); + } +} diff --git a/src/Shared/test/testassets/CreateHostBuilderInvalidSignature/CreateHostBuilderInvalidSignature.csproj b/src/Shared/test/testassets/CreateHostBuilderInvalidSignature/CreateHostBuilderInvalidSignature.csproj new file mode 100644 index 0000000000..6368289f65 --- /dev/null +++ b/src/Shared/test/testassets/CreateHostBuilderInvalidSignature/CreateHostBuilderInvalidSignature.csproj @@ -0,0 +1,12 @@ + + + + netcoreapp3.0;net472 + Exe + + + + + + + diff --git a/src/Shared/test/testassets/CreateHostBuilderInvalidSignature/Program.cs b/src/Shared/test/testassets/CreateHostBuilderInvalidSignature/Program.cs new file mode 100644 index 0000000000..8451301a20 --- /dev/null +++ b/src/Shared/test/testassets/CreateHostBuilderInvalidSignature/Program.cs @@ -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. + +using MockHostTypes; + +namespace CreateHostBuilderInvalidSignature +{ + public class Program + { + public static void Main(string[] args) + { + var webHost = CreateHostBuilder(null, args).Build(); + } + + // Extra parameter + private static IHostBuilder CreateHostBuilder(object extraParam, string[] args) => null; + } +} diff --git a/src/Shared/test/testassets/CreateHostBuilderPatternTestSite/CreateHostBuilderPatternTestSite.csproj b/src/Shared/test/testassets/CreateHostBuilderPatternTestSite/CreateHostBuilderPatternTestSite.csproj new file mode 100644 index 0000000000..6368289f65 --- /dev/null +++ b/src/Shared/test/testassets/CreateHostBuilderPatternTestSite/CreateHostBuilderPatternTestSite.csproj @@ -0,0 +1,12 @@ + + + + netcoreapp3.0;net472 + Exe + + + + + + + diff --git a/src/Shared/test/testassets/CreateHostBuilderPatternTestSite/Program.cs b/src/Shared/test/testassets/CreateHostBuilderPatternTestSite/Program.cs new file mode 100644 index 0000000000..70edf16097 --- /dev/null +++ b/src/Shared/test/testassets/CreateHostBuilderPatternTestSite/Program.cs @@ -0,0 +1,19 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using MockHostTypes; + +namespace CreateHostBuilderPatternTestSite +{ + public class Program + { + public static void Main(string[] args) + { + var webHost = CreateHostBuilder(args).Build(); + } + + // Do not change the signature of this method. It's used for tests. + private static HostBuilder CreateHostBuilder(string[] args) => + new HostBuilder(); + } +} diff --git a/src/Shared/test/testassets/CreateWebHostBuilderInvalidSignature/CreateWebHostBuilderInvalidSignature.csproj b/src/Shared/test/testassets/CreateWebHostBuilderInvalidSignature/CreateWebHostBuilderInvalidSignature.csproj new file mode 100644 index 0000000000..6368289f65 --- /dev/null +++ b/src/Shared/test/testassets/CreateWebHostBuilderInvalidSignature/CreateWebHostBuilderInvalidSignature.csproj @@ -0,0 +1,12 @@ + + + + netcoreapp3.0;net472 + Exe + + + + + + + diff --git a/src/Shared/test/testassets/CreateWebHostBuilderInvalidSignature/Program.cs b/src/Shared/test/testassets/CreateWebHostBuilderInvalidSignature/Program.cs new file mode 100644 index 0000000000..1533acbf57 --- /dev/null +++ b/src/Shared/test/testassets/CreateWebHostBuilderInvalidSignature/Program.cs @@ -0,0 +1,17 @@ +// 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 MockHostTypes; + +namespace CreateWebHostBuilderInvalidSignature +{ + public class Program + { + static void Main(string[] args) + { + } + + // Wrong return type + public static IWebHost CreateWebHostBuilder(string[] args) => new WebHost(); + } +} diff --git a/src/Shared/test/testassets/CreateWebHostBuilderPatternTestSite/CreateWebHostBuilderPatternTestSite.csproj b/src/Shared/test/testassets/CreateWebHostBuilderPatternTestSite/CreateWebHostBuilderPatternTestSite.csproj new file mode 100644 index 0000000000..6368289f65 --- /dev/null +++ b/src/Shared/test/testassets/CreateWebHostBuilderPatternTestSite/CreateWebHostBuilderPatternTestSite.csproj @@ -0,0 +1,12 @@ + + + + netcoreapp3.0;net472 + Exe + + + + + + + diff --git a/src/Shared/test/testassets/CreateWebHostBuilderPatternTestSite/Program.cs b/src/Shared/test/testassets/CreateWebHostBuilderPatternTestSite/Program.cs new file mode 100644 index 0000000000..caab3cb224 --- /dev/null +++ b/src/Shared/test/testassets/CreateWebHostBuilderPatternTestSite/Program.cs @@ -0,0 +1,19 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using MockHostTypes; + +namespace CreateWebHostBuilderPatternTestSite +{ + public class Program + { + public static void Main(string[] args) + { + var webHost = CreateWebHostBuilder(args).Build(); + } + + // Do not change the signature of this method. It's used for tests. + private static IWebHostBuilder CreateWebHostBuilder(string[] args) => + new WebHostBuilder(); + } +} diff --git a/src/Shared/test/testassets/MockHostTypes/Host.cs b/src/Shared/test/testassets/MockHostTypes/Host.cs new file mode 100644 index 0000000000..412ab63ef3 --- /dev/null +++ b/src/Shared/test/testassets/MockHostTypes/Host.cs @@ -0,0 +1,12 @@ +// 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 MockHostTypes +{ + public class Host : IHost + { + public IServiceProvider Services { get; } = new ServiceProvider(); + } +} diff --git a/src/Shared/test/testassets/MockHostTypes/HostBuilder.cs b/src/Shared/test/testassets/MockHostTypes/HostBuilder.cs new file mode 100644 index 0000000000..eb62e9a4b1 --- /dev/null +++ b/src/Shared/test/testassets/MockHostTypes/HostBuilder.cs @@ -0,0 +1,10 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace MockHostTypes +{ + public class HostBuilder : IHostBuilder + { + public IHost Build() => new Host(); + } +} diff --git a/src/Shared/test/testassets/MockHostTypes/IHost.cs b/src/Shared/test/testassets/MockHostTypes/IHost.cs new file mode 100644 index 0000000000..27c6dbaf71 --- /dev/null +++ b/src/Shared/test/testassets/MockHostTypes/IHost.cs @@ -0,0 +1,12 @@ +// 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 MockHostTypes +{ + public interface IHost + { + IServiceProvider Services { get; } + } +} diff --git a/src/Shared/test/testassets/MockHostTypes/IHostBuilder.cs b/src/Shared/test/testassets/MockHostTypes/IHostBuilder.cs new file mode 100644 index 0000000000..2053b52106 --- /dev/null +++ b/src/Shared/test/testassets/MockHostTypes/IHostBuilder.cs @@ -0,0 +1,10 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace MockHostTypes +{ + public interface IHostBuilder + { + IHost Build(); + } +} diff --git a/src/Shared/test/testassets/MockHostTypes/IWebHost.cs b/src/Shared/test/testassets/MockHostTypes/IWebHost.cs new file mode 100644 index 0000000000..f93bba440c --- /dev/null +++ b/src/Shared/test/testassets/MockHostTypes/IWebHost.cs @@ -0,0 +1,12 @@ +// 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 MockHostTypes +{ + public interface IWebHost + { + IServiceProvider Services { get; } + } +} diff --git a/src/Shared/test/testassets/MockHostTypes/IWebHostBuilder.cs b/src/Shared/test/testassets/MockHostTypes/IWebHostBuilder.cs new file mode 100644 index 0000000000..1159ae103e --- /dev/null +++ b/src/Shared/test/testassets/MockHostTypes/IWebHostBuilder.cs @@ -0,0 +1,10 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace MockHostTypes +{ + public interface IWebHostBuilder + { + IWebHost Build(); + } +} diff --git a/src/Shared/test/testassets/MockHostTypes/MockHostTypes.csproj b/src/Shared/test/testassets/MockHostTypes/MockHostTypes.csproj new file mode 100644 index 0000000000..3272f8d93a --- /dev/null +++ b/src/Shared/test/testassets/MockHostTypes/MockHostTypes.csproj @@ -0,0 +1,7 @@ + + + + netcoreapp3.0;net472 + + + diff --git a/src/Shared/test/testassets/MockHostTypes/ServiceProvider.cs b/src/Shared/test/testassets/MockHostTypes/ServiceProvider.cs new file mode 100644 index 0000000000..7b550c9d32 --- /dev/null +++ b/src/Shared/test/testassets/MockHostTypes/ServiceProvider.cs @@ -0,0 +1,12 @@ +// 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 MockHostTypes +{ + public class ServiceProvider : IServiceProvider + { + public object GetService(Type serviceType) => null; + } +} diff --git a/src/Shared/test/testassets/MockHostTypes/WebHost.cs b/src/Shared/test/testassets/MockHostTypes/WebHost.cs new file mode 100644 index 0000000000..77d3d58ca4 --- /dev/null +++ b/src/Shared/test/testassets/MockHostTypes/WebHost.cs @@ -0,0 +1,12 @@ +// 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 MockHostTypes +{ + public class WebHost : IWebHost + { + public IServiceProvider Services { get; } = new ServiceProvider(); + } +} diff --git a/src/Shared/test/testassets/MockHostTypes/WebHostBuilder.cs b/src/Shared/test/testassets/MockHostTypes/WebHostBuilder.cs new file mode 100644 index 0000000000..216fb28d60 --- /dev/null +++ b/src/Shared/test/testassets/MockHostTypes/WebHostBuilder.cs @@ -0,0 +1,10 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace MockHostTypes +{ + public class WebHostBuilder : IWebHostBuilder + { + public IWebHost Build() => new WebHost(); + } +} From 33ee3dc379005a1c465b5ff7c8b8f89e4607e56e Mon Sep 17 00:00:00 2001 From: Pranav K Date: Wed, 9 Jan 2019 11:21:29 -0800 Subject: [PATCH 17/52] Refactoring to do KoreBuild \ mondorepo \n\nCommit migrated from https://github.com/dotnet/extensions/commit/3a884e2e1e6ae3f1fc4381fc8fbb428de5cff063 --- src/JSInterop/.editorconfig | 27 --------- src/JSInterop/Directory.Build.props | 7 +++ .../src}/Microsoft.JSInterop.JS.csproj | 2 +- .../src}/package-lock.json | 0 .../src}/package.json | 0 .../src}/src/Microsoft.JSInterop.ts | 0 .../src}/tsconfig.json | 0 src/JSInterop/Microsoft.JSInterop.sln | 58 ------------------- .../src}/DotNetDispatcher.cs | 0 .../src}/DotNetObjectRef.cs | 0 .../src}/ICustomArgSerializer.cs | 0 .../src}/IJSInProcessRuntime.cs | 0 .../src}/IJSRuntime.cs | 0 .../src}/InteropArgSerializerStrategy.cs | 0 .../src}/JSAsyncCallResult.cs | 0 .../src}/JSException.cs | 0 .../src}/JSInProcessRuntimeBase.cs | 0 .../src}/JSInvokableAttribute.cs | 0 .../src}/JSRuntime.cs | 0 .../src}/JSRuntimeBase.cs | 0 .../src}/Json/CamelCase.cs | 0 .../src}/Json/Json.cs | 0 .../src}/Json/SimpleJson/README.txt | 0 .../src}/Json/SimpleJson/SimpleJson.cs | 0 .../src/Microsoft.JSInterop.csproj | 10 ++++ .../src}/Properties/AssemblyInfo.cs | 0 .../src}/TaskGenericsUtil.cs | 0 .../test}/DotNetDispatcherTest.cs | 0 .../test}/DotNetObjectRefTest.cs | 0 .../test}/JSInProcessRuntimeBaseTest.cs | 0 .../test}/JSRuntimeBaseTest.cs | 0 .../test}/JSRuntimeTest.cs | 0 .../test}/JsonUtilTest.cs | 0 .../test/Microsoft.JSInterop.Test.csproj | 11 ++++ .../src}/InternalCalls.cs | 0 .../src/Mono.WebAssembly.Interop.csproj | 14 +++++ .../src}/MonoWebAssemblyJSRuntime.cs | 0 .../src/Microsoft.JSInterop.JS/.gitignore | 1 - .../Microsoft.JSInterop.csproj | 7 --- .../Mono.WebAssembly.Interop.csproj | 11 ---- .../Microsoft.JSInterop.Test.csproj | 20 ------- 41 files changed, 43 insertions(+), 125 deletions(-) delete mode 100644 src/JSInterop/.editorconfig create mode 100644 src/JSInterop/Directory.Build.props rename src/JSInterop/{src/Microsoft.JSInterop.JS => Microsoft.JSInterop.JS/src}/Microsoft.JSInterop.JS.csproj (76%) rename src/JSInterop/{src/Microsoft.JSInterop.JS => Microsoft.JSInterop.JS/src}/package-lock.json (100%) rename src/JSInterop/{src/Microsoft.JSInterop.JS => Microsoft.JSInterop.JS/src}/package.json (100%) rename src/JSInterop/{src/Microsoft.JSInterop.JS => Microsoft.JSInterop.JS/src}/src/Microsoft.JSInterop.ts (100%) rename src/JSInterop/{src/Microsoft.JSInterop.JS => Microsoft.JSInterop.JS/src}/tsconfig.json (100%) delete mode 100644 src/JSInterop/Microsoft.JSInterop.sln rename src/JSInterop/{src/Microsoft.JSInterop => Microsoft.JSInterop/src}/DotNetDispatcher.cs (100%) rename src/JSInterop/{src/Microsoft.JSInterop => Microsoft.JSInterop/src}/DotNetObjectRef.cs (100%) rename src/JSInterop/{src/Microsoft.JSInterop => Microsoft.JSInterop/src}/ICustomArgSerializer.cs (100%) rename src/JSInterop/{src/Microsoft.JSInterop => Microsoft.JSInterop/src}/IJSInProcessRuntime.cs (100%) rename src/JSInterop/{src/Microsoft.JSInterop => Microsoft.JSInterop/src}/IJSRuntime.cs (100%) rename src/JSInterop/{src/Microsoft.JSInterop => Microsoft.JSInterop/src}/InteropArgSerializerStrategy.cs (100%) rename src/JSInterop/{src/Microsoft.JSInterop => Microsoft.JSInterop/src}/JSAsyncCallResult.cs (100%) rename src/JSInterop/{src/Microsoft.JSInterop => Microsoft.JSInterop/src}/JSException.cs (100%) rename src/JSInterop/{src/Microsoft.JSInterop => Microsoft.JSInterop/src}/JSInProcessRuntimeBase.cs (100%) rename src/JSInterop/{src/Microsoft.JSInterop => Microsoft.JSInterop/src}/JSInvokableAttribute.cs (100%) rename src/JSInterop/{src/Microsoft.JSInterop => Microsoft.JSInterop/src}/JSRuntime.cs (100%) rename src/JSInterop/{src/Microsoft.JSInterop => Microsoft.JSInterop/src}/JSRuntimeBase.cs (100%) rename src/JSInterop/{src/Microsoft.JSInterop => Microsoft.JSInterop/src}/Json/CamelCase.cs (100%) rename src/JSInterop/{src/Microsoft.JSInterop => Microsoft.JSInterop/src}/Json/Json.cs (100%) rename src/JSInterop/{src/Microsoft.JSInterop => Microsoft.JSInterop/src}/Json/SimpleJson/README.txt (100%) rename src/JSInterop/{src/Microsoft.JSInterop => Microsoft.JSInterop/src}/Json/SimpleJson/SimpleJson.cs (100%) create mode 100644 src/JSInterop/Microsoft.JSInterop/src/Microsoft.JSInterop.csproj rename src/JSInterop/{src/Microsoft.JSInterop => Microsoft.JSInterop/src}/Properties/AssemblyInfo.cs (100%) rename src/JSInterop/{src/Microsoft.JSInterop => Microsoft.JSInterop/src}/TaskGenericsUtil.cs (100%) rename src/JSInterop/{test/Microsoft.JSInterop.Test => Microsoft.JSInterop/test}/DotNetDispatcherTest.cs (100%) rename src/JSInterop/{test/Microsoft.JSInterop.Test => Microsoft.JSInterop/test}/DotNetObjectRefTest.cs (100%) rename src/JSInterop/{test/Microsoft.JSInterop.Test => Microsoft.JSInterop/test}/JSInProcessRuntimeBaseTest.cs (100%) rename src/JSInterop/{test/Microsoft.JSInterop.Test => Microsoft.JSInterop/test}/JSRuntimeBaseTest.cs (100%) rename src/JSInterop/{test/Microsoft.JSInterop.Test => Microsoft.JSInterop/test}/JSRuntimeTest.cs (100%) rename src/JSInterop/{test/Microsoft.JSInterop.Test => Microsoft.JSInterop/test}/JsonUtilTest.cs (100%) create mode 100644 src/JSInterop/Microsoft.JSInterop/test/Microsoft.JSInterop.Test.csproj rename src/JSInterop/{src/Mono.WebAssembly.Interop => Mono.WebAssembly.Interop/src}/InternalCalls.cs (100%) create mode 100644 src/JSInterop/Mono.WebAssembly.Interop/src/Mono.WebAssembly.Interop.csproj rename src/JSInterop/{src/Mono.WebAssembly.Interop => Mono.WebAssembly.Interop/src}/MonoWebAssemblyJSRuntime.cs (100%) delete mode 100644 src/JSInterop/src/Microsoft.JSInterop.JS/.gitignore delete mode 100644 src/JSInterop/src/Microsoft.JSInterop/Microsoft.JSInterop.csproj delete mode 100644 src/JSInterop/src/Mono.WebAssembly.Interop/Mono.WebAssembly.Interop.csproj delete mode 100644 src/JSInterop/test/Microsoft.JSInterop.Test/Microsoft.JSInterop.Test.csproj diff --git a/src/JSInterop/.editorconfig b/src/JSInterop/.editorconfig deleted file mode 100644 index 0d238f8e41..0000000000 --- a/src/JSInterop/.editorconfig +++ /dev/null @@ -1,27 +0,0 @@ -# All Files -[*] -charset = utf-8 -end_of_line = crlf -indent_style = space -indent_size = 4 -insert_final_newline = false -trim_trailing_whitespace = true - -# Solution Files -[*.sln] -indent_style = tab - -# Markdown Files -[*.md] -trim_trailing_whitespace = false - -# Web Files -[*.{htm,html,js,ts,css,scss,less}] -insert_final_newline = true -indent_size = 2 - -[*.{yml,json}] -indent_size = 2 - -[*.{xml,csproj,config,*proj,targets,props}] -indent_size = 2 diff --git a/src/JSInterop/Directory.Build.props b/src/JSInterop/Directory.Build.props new file mode 100644 index 0000000000..f25c1d90ce --- /dev/null +++ b/src/JSInterop/Directory.Build.props @@ -0,0 +1,7 @@ + + + + + true + + diff --git a/src/JSInterop/src/Microsoft.JSInterop.JS/Microsoft.JSInterop.JS.csproj b/src/JSInterop/Microsoft.JSInterop.JS/src/Microsoft.JSInterop.JS.csproj similarity index 76% rename from src/JSInterop/src/Microsoft.JSInterop.JS/Microsoft.JSInterop.JS.csproj rename to src/JSInterop/Microsoft.JSInterop.JS/src/Microsoft.JSInterop.JS.csproj index 4a6ba93ebb..7f806d0afa 100644 --- a/src/JSInterop/src/Microsoft.JSInterop.JS/Microsoft.JSInterop.JS.csproj +++ b/src/JSInterop/Microsoft.JSInterop.JS/src/Microsoft.JSInterop.JS.csproj @@ -7,7 +7,7 @@ - + diff --git a/src/JSInterop/src/Microsoft.JSInterop.JS/package-lock.json b/src/JSInterop/Microsoft.JSInterop.JS/src/package-lock.json similarity index 100% rename from src/JSInterop/src/Microsoft.JSInterop.JS/package-lock.json rename to src/JSInterop/Microsoft.JSInterop.JS/src/package-lock.json diff --git a/src/JSInterop/src/Microsoft.JSInterop.JS/package.json b/src/JSInterop/Microsoft.JSInterop.JS/src/package.json similarity index 100% rename from src/JSInterop/src/Microsoft.JSInterop.JS/package.json rename to src/JSInterop/Microsoft.JSInterop.JS/src/package.json diff --git a/src/JSInterop/src/Microsoft.JSInterop.JS/src/Microsoft.JSInterop.ts b/src/JSInterop/Microsoft.JSInterop.JS/src/src/Microsoft.JSInterop.ts similarity index 100% rename from src/JSInterop/src/Microsoft.JSInterop.JS/src/Microsoft.JSInterop.ts rename to src/JSInterop/Microsoft.JSInterop.JS/src/src/Microsoft.JSInterop.ts diff --git a/src/JSInterop/src/Microsoft.JSInterop.JS/tsconfig.json b/src/JSInterop/Microsoft.JSInterop.JS/src/tsconfig.json similarity index 100% rename from src/JSInterop/src/Microsoft.JSInterop.JS/tsconfig.json rename to src/JSInterop/Microsoft.JSInterop.JS/src/tsconfig.json diff --git a/src/JSInterop/Microsoft.JSInterop.sln b/src/JSInterop/Microsoft.JSInterop.sln deleted file mode 100644 index d646a6b068..0000000000 --- a/src/JSInterop/Microsoft.JSInterop.sln +++ /dev/null @@ -1,58 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.27703.2042 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{1290437E-A890-419E-A317-D0F7FEE185A5}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{B98D4F51-88FB-471C-B56F-752E8EE502E7}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.JSInterop", "src\Microsoft.JSInterop\Microsoft.JSInterop.csproj", "{CB4CD4A6-9BAA-46D1-944F-CE56DEC2663C}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.JSInterop.Test", "test\Microsoft.JSInterop.Test\Microsoft.JSInterop.Test.csproj", "{7FF8B199-52C0-4DFE-A73B-0C9E18220C0E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.JSInterop.JS", "src\Microsoft.JSInterop.JS\Microsoft.JSInterop.JS.csproj", "{60BA5AAD-264A-437E-8319-577841C66CC6}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{BE4CBB33-5C40-4A07-B6FC-1D7C3AE13024}" - ProjectSection(SolutionItems) = preProject - README.md = README.md - EndProjectSection -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Mono.WebAssembly.Interop", "src\Mono.WebAssembly.Interop\Mono.WebAssembly.Interop.csproj", "{10145E99-1B2D-40C5-9595-582BDAF3E024}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {CB4CD4A6-9BAA-46D1-944F-CE56DEC2663C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {CB4CD4A6-9BAA-46D1-944F-CE56DEC2663C}.Debug|Any CPU.Build.0 = Debug|Any CPU - {CB4CD4A6-9BAA-46D1-944F-CE56DEC2663C}.Release|Any CPU.ActiveCfg = Release|Any CPU - {CB4CD4A6-9BAA-46D1-944F-CE56DEC2663C}.Release|Any CPU.Build.0 = Release|Any CPU - {7FF8B199-52C0-4DFE-A73B-0C9E18220C0E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7FF8B199-52C0-4DFE-A73B-0C9E18220C0E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7FF8B199-52C0-4DFE-A73B-0C9E18220C0E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7FF8B199-52C0-4DFE-A73B-0C9E18220C0E}.Release|Any CPU.Build.0 = Release|Any CPU - {60BA5AAD-264A-437E-8319-577841C66CC6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {60BA5AAD-264A-437E-8319-577841C66CC6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {60BA5AAD-264A-437E-8319-577841C66CC6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {60BA5AAD-264A-437E-8319-577841C66CC6}.Release|Any CPU.Build.0 = Release|Any CPU - {10145E99-1B2D-40C5-9595-582BDAF3E024}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {10145E99-1B2D-40C5-9595-582BDAF3E024}.Debug|Any CPU.Build.0 = Debug|Any CPU - {10145E99-1B2D-40C5-9595-582BDAF3E024}.Release|Any CPU.ActiveCfg = Release|Any CPU - {10145E99-1B2D-40C5-9595-582BDAF3E024}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {CB4CD4A6-9BAA-46D1-944F-CE56DEC2663C} = {1290437E-A890-419E-A317-D0F7FEE185A5} - {7FF8B199-52C0-4DFE-A73B-0C9E18220C0E} = {B98D4F51-88FB-471C-B56F-752E8EE502E7} - {60BA5AAD-264A-437E-8319-577841C66CC6} = {1290437E-A890-419E-A317-D0F7FEE185A5} - {10145E99-1B2D-40C5-9595-582BDAF3E024} = {1290437E-A890-419E-A317-D0F7FEE185A5} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {7E07ABF2-427A-43FA-A6A4-82B21B96ACAF} - EndGlobalSection -EndGlobal diff --git a/src/JSInterop/src/Microsoft.JSInterop/DotNetDispatcher.cs b/src/JSInterop/Microsoft.JSInterop/src/DotNetDispatcher.cs similarity index 100% rename from src/JSInterop/src/Microsoft.JSInterop/DotNetDispatcher.cs rename to src/JSInterop/Microsoft.JSInterop/src/DotNetDispatcher.cs diff --git a/src/JSInterop/src/Microsoft.JSInterop/DotNetObjectRef.cs b/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectRef.cs similarity index 100% rename from src/JSInterop/src/Microsoft.JSInterop/DotNetObjectRef.cs rename to src/JSInterop/Microsoft.JSInterop/src/DotNetObjectRef.cs diff --git a/src/JSInterop/src/Microsoft.JSInterop/ICustomArgSerializer.cs b/src/JSInterop/Microsoft.JSInterop/src/ICustomArgSerializer.cs similarity index 100% rename from src/JSInterop/src/Microsoft.JSInterop/ICustomArgSerializer.cs rename to src/JSInterop/Microsoft.JSInterop/src/ICustomArgSerializer.cs diff --git a/src/JSInterop/src/Microsoft.JSInterop/IJSInProcessRuntime.cs b/src/JSInterop/Microsoft.JSInterop/src/IJSInProcessRuntime.cs similarity index 100% rename from src/JSInterop/src/Microsoft.JSInterop/IJSInProcessRuntime.cs rename to src/JSInterop/Microsoft.JSInterop/src/IJSInProcessRuntime.cs diff --git a/src/JSInterop/src/Microsoft.JSInterop/IJSRuntime.cs b/src/JSInterop/Microsoft.JSInterop/src/IJSRuntime.cs similarity index 100% rename from src/JSInterop/src/Microsoft.JSInterop/IJSRuntime.cs rename to src/JSInterop/Microsoft.JSInterop/src/IJSRuntime.cs diff --git a/src/JSInterop/src/Microsoft.JSInterop/InteropArgSerializerStrategy.cs b/src/JSInterop/Microsoft.JSInterop/src/InteropArgSerializerStrategy.cs similarity index 100% rename from src/JSInterop/src/Microsoft.JSInterop/InteropArgSerializerStrategy.cs rename to src/JSInterop/Microsoft.JSInterop/src/InteropArgSerializerStrategy.cs diff --git a/src/JSInterop/src/Microsoft.JSInterop/JSAsyncCallResult.cs b/src/JSInterop/Microsoft.JSInterop/src/JSAsyncCallResult.cs similarity index 100% rename from src/JSInterop/src/Microsoft.JSInterop/JSAsyncCallResult.cs rename to src/JSInterop/Microsoft.JSInterop/src/JSAsyncCallResult.cs diff --git a/src/JSInterop/src/Microsoft.JSInterop/JSException.cs b/src/JSInterop/Microsoft.JSInterop/src/JSException.cs similarity index 100% rename from src/JSInterop/src/Microsoft.JSInterop/JSException.cs rename to src/JSInterop/Microsoft.JSInterop/src/JSException.cs diff --git a/src/JSInterop/src/Microsoft.JSInterop/JSInProcessRuntimeBase.cs b/src/JSInterop/Microsoft.JSInterop/src/JSInProcessRuntimeBase.cs similarity index 100% rename from src/JSInterop/src/Microsoft.JSInterop/JSInProcessRuntimeBase.cs rename to src/JSInterop/Microsoft.JSInterop/src/JSInProcessRuntimeBase.cs diff --git a/src/JSInterop/src/Microsoft.JSInterop/JSInvokableAttribute.cs b/src/JSInterop/Microsoft.JSInterop/src/JSInvokableAttribute.cs similarity index 100% rename from src/JSInterop/src/Microsoft.JSInterop/JSInvokableAttribute.cs rename to src/JSInterop/Microsoft.JSInterop/src/JSInvokableAttribute.cs diff --git a/src/JSInterop/src/Microsoft.JSInterop/JSRuntime.cs b/src/JSInterop/Microsoft.JSInterop/src/JSRuntime.cs similarity index 100% rename from src/JSInterop/src/Microsoft.JSInterop/JSRuntime.cs rename to src/JSInterop/Microsoft.JSInterop/src/JSRuntime.cs diff --git a/src/JSInterop/src/Microsoft.JSInterop/JSRuntimeBase.cs b/src/JSInterop/Microsoft.JSInterop/src/JSRuntimeBase.cs similarity index 100% rename from src/JSInterop/src/Microsoft.JSInterop/JSRuntimeBase.cs rename to src/JSInterop/Microsoft.JSInterop/src/JSRuntimeBase.cs diff --git a/src/JSInterop/src/Microsoft.JSInterop/Json/CamelCase.cs b/src/JSInterop/Microsoft.JSInterop/src/Json/CamelCase.cs similarity index 100% rename from src/JSInterop/src/Microsoft.JSInterop/Json/CamelCase.cs rename to src/JSInterop/Microsoft.JSInterop/src/Json/CamelCase.cs diff --git a/src/JSInterop/src/Microsoft.JSInterop/Json/Json.cs b/src/JSInterop/Microsoft.JSInterop/src/Json/Json.cs similarity index 100% rename from src/JSInterop/src/Microsoft.JSInterop/Json/Json.cs rename to src/JSInterop/Microsoft.JSInterop/src/Json/Json.cs diff --git a/src/JSInterop/src/Microsoft.JSInterop/Json/SimpleJson/README.txt b/src/JSInterop/Microsoft.JSInterop/src/Json/SimpleJson/README.txt similarity index 100% rename from src/JSInterop/src/Microsoft.JSInterop/Json/SimpleJson/README.txt rename to src/JSInterop/Microsoft.JSInterop/src/Json/SimpleJson/README.txt diff --git a/src/JSInterop/src/Microsoft.JSInterop/Json/SimpleJson/SimpleJson.cs b/src/JSInterop/Microsoft.JSInterop/src/Json/SimpleJson/SimpleJson.cs similarity index 100% rename from src/JSInterop/src/Microsoft.JSInterop/Json/SimpleJson/SimpleJson.cs rename to src/JSInterop/Microsoft.JSInterop/src/Json/SimpleJson/SimpleJson.cs diff --git a/src/JSInterop/Microsoft.JSInterop/src/Microsoft.JSInterop.csproj b/src/JSInterop/Microsoft.JSInterop/src/Microsoft.JSInterop.csproj new file mode 100644 index 0000000000..bdfea26ad5 --- /dev/null +++ b/src/JSInterop/Microsoft.JSInterop/src/Microsoft.JSInterop.csproj @@ -0,0 +1,10 @@ + + + + netstandard2.0 + Abstractions and features for interop between .NET and JavaScript code. + javascript;interop + true + + + diff --git a/src/JSInterop/src/Microsoft.JSInterop/Properties/AssemblyInfo.cs b/src/JSInterop/Microsoft.JSInterop/src/Properties/AssemblyInfo.cs similarity index 100% rename from src/JSInterop/src/Microsoft.JSInterop/Properties/AssemblyInfo.cs rename to src/JSInterop/Microsoft.JSInterop/src/Properties/AssemblyInfo.cs diff --git a/src/JSInterop/src/Microsoft.JSInterop/TaskGenericsUtil.cs b/src/JSInterop/Microsoft.JSInterop/src/TaskGenericsUtil.cs similarity index 100% rename from src/JSInterop/src/Microsoft.JSInterop/TaskGenericsUtil.cs rename to src/JSInterop/Microsoft.JSInterop/src/TaskGenericsUtil.cs diff --git a/src/JSInterop/test/Microsoft.JSInterop.Test/DotNetDispatcherTest.cs b/src/JSInterop/Microsoft.JSInterop/test/DotNetDispatcherTest.cs similarity index 100% rename from src/JSInterop/test/Microsoft.JSInterop.Test/DotNetDispatcherTest.cs rename to src/JSInterop/Microsoft.JSInterop/test/DotNetDispatcherTest.cs diff --git a/src/JSInterop/test/Microsoft.JSInterop.Test/DotNetObjectRefTest.cs b/src/JSInterop/Microsoft.JSInterop/test/DotNetObjectRefTest.cs similarity index 100% rename from src/JSInterop/test/Microsoft.JSInterop.Test/DotNetObjectRefTest.cs rename to src/JSInterop/Microsoft.JSInterop/test/DotNetObjectRefTest.cs diff --git a/src/JSInterop/test/Microsoft.JSInterop.Test/JSInProcessRuntimeBaseTest.cs b/src/JSInterop/Microsoft.JSInterop/test/JSInProcessRuntimeBaseTest.cs similarity index 100% rename from src/JSInterop/test/Microsoft.JSInterop.Test/JSInProcessRuntimeBaseTest.cs rename to src/JSInterop/Microsoft.JSInterop/test/JSInProcessRuntimeBaseTest.cs diff --git a/src/JSInterop/test/Microsoft.JSInterop.Test/JSRuntimeBaseTest.cs b/src/JSInterop/Microsoft.JSInterop/test/JSRuntimeBaseTest.cs similarity index 100% rename from src/JSInterop/test/Microsoft.JSInterop.Test/JSRuntimeBaseTest.cs rename to src/JSInterop/Microsoft.JSInterop/test/JSRuntimeBaseTest.cs diff --git a/src/JSInterop/test/Microsoft.JSInterop.Test/JSRuntimeTest.cs b/src/JSInterop/Microsoft.JSInterop/test/JSRuntimeTest.cs similarity index 100% rename from src/JSInterop/test/Microsoft.JSInterop.Test/JSRuntimeTest.cs rename to src/JSInterop/Microsoft.JSInterop/test/JSRuntimeTest.cs diff --git a/src/JSInterop/test/Microsoft.JSInterop.Test/JsonUtilTest.cs b/src/JSInterop/Microsoft.JSInterop/test/JsonUtilTest.cs similarity index 100% rename from src/JSInterop/test/Microsoft.JSInterop.Test/JsonUtilTest.cs rename to src/JSInterop/Microsoft.JSInterop/test/JsonUtilTest.cs diff --git a/src/JSInterop/Microsoft.JSInterop/test/Microsoft.JSInterop.Test.csproj b/src/JSInterop/Microsoft.JSInterop/test/Microsoft.JSInterop.Test.csproj new file mode 100644 index 0000000000..5e88f44a3a --- /dev/null +++ b/src/JSInterop/Microsoft.JSInterop/test/Microsoft.JSInterop.Test.csproj @@ -0,0 +1,11 @@ + + + + netcoreapp3.0 + + + + + + + diff --git a/src/JSInterop/src/Mono.WebAssembly.Interop/InternalCalls.cs b/src/JSInterop/Mono.WebAssembly.Interop/src/InternalCalls.cs similarity index 100% rename from src/JSInterop/src/Mono.WebAssembly.Interop/InternalCalls.cs rename to src/JSInterop/Mono.WebAssembly.Interop/src/InternalCalls.cs diff --git a/src/JSInterop/Mono.WebAssembly.Interop/src/Mono.WebAssembly.Interop.csproj b/src/JSInterop/Mono.WebAssembly.Interop/src/Mono.WebAssembly.Interop.csproj new file mode 100644 index 0000000000..75c8272e95 --- /dev/null +++ b/src/JSInterop/Mono.WebAssembly.Interop/src/Mono.WebAssembly.Interop.csproj @@ -0,0 +1,14 @@ + + + + netstandard2.0 + + false + false + + + + + + + diff --git a/src/JSInterop/src/Mono.WebAssembly.Interop/MonoWebAssemblyJSRuntime.cs b/src/JSInterop/Mono.WebAssembly.Interop/src/MonoWebAssemblyJSRuntime.cs similarity index 100% rename from src/JSInterop/src/Mono.WebAssembly.Interop/MonoWebAssemblyJSRuntime.cs rename to src/JSInterop/Mono.WebAssembly.Interop/src/MonoWebAssemblyJSRuntime.cs diff --git a/src/JSInterop/src/Microsoft.JSInterop.JS/.gitignore b/src/JSInterop/src/Microsoft.JSInterop.JS/.gitignore deleted file mode 100644 index 849ddff3b7..0000000000 --- a/src/JSInterop/src/Microsoft.JSInterop.JS/.gitignore +++ /dev/null @@ -1 +0,0 @@ -dist/ diff --git a/src/JSInterop/src/Microsoft.JSInterop/Microsoft.JSInterop.csproj b/src/JSInterop/src/Microsoft.JSInterop/Microsoft.JSInterop.csproj deleted file mode 100644 index 9f5c4f4abb..0000000000 --- a/src/JSInterop/src/Microsoft.JSInterop/Microsoft.JSInterop.csproj +++ /dev/null @@ -1,7 +0,0 @@ - - - - netstandard2.0 - - - diff --git a/src/JSInterop/src/Mono.WebAssembly.Interop/Mono.WebAssembly.Interop.csproj b/src/JSInterop/src/Mono.WebAssembly.Interop/Mono.WebAssembly.Interop.csproj deleted file mode 100644 index 81f1173b55..0000000000 --- a/src/JSInterop/src/Mono.WebAssembly.Interop/Mono.WebAssembly.Interop.csproj +++ /dev/null @@ -1,11 +0,0 @@ - - - - netstandard2.0 - - - - - - - diff --git a/src/JSInterop/test/Microsoft.JSInterop.Test/Microsoft.JSInterop.Test.csproj b/src/JSInterop/test/Microsoft.JSInterop.Test/Microsoft.JSInterop.Test.csproj deleted file mode 100644 index 893a11d33a..0000000000 --- a/src/JSInterop/test/Microsoft.JSInterop.Test/Microsoft.JSInterop.Test.csproj +++ /dev/null @@ -1,20 +0,0 @@ - - - - netcoreapp2.1 - false - 7.3 - - - - - - - - - - - - - - From 66773c57bcbcccdd63c993105b57de978eebeb39 Mon Sep 17 00:00:00 2001 From: Ryan Brandenburg Date: Tue, 29 Jan 2019 13:26:53 -0800 Subject: [PATCH 18/52] Use Arcade (dotnet/extensions#586) Use arcade \n\nCommit migrated from https://github.com/dotnet/extensions/commit/f0458995c625dba132a74733e041b97c0e10e025 --- .../Directory.Build.props | 2 +- ...Extensions.Configuration.KeyPerFile.csproj | 1 + src/FileProviders/Directory.Build.props | 2 +- ...t.Extensions.FileProviders.Embedded.csproj | 11 +++++---- .../Embedded/src/Properties/AssemblyInfo.cs | 4 ---- ...iders.Embedded.Manifest.Task.Tests.csproj} | 0 ...agnostics.HealthChecks.Abstractions.csproj | 1 + ...Extensions.Diagnostics.HealthChecks.csproj | 5 ++++ .../src/Properties/AssemblyInfo.cs | 3 --- .../src/Microsoft.JSInterop.csproj | 4 ++++ .../src/Properties/AssemblyInfo.cs | 3 --- .../test/DotNetDispatcherTest.cs | 17 +++++++------- .../test/DotNetObjectRefTest.cs | 2 +- .../test/JSInProcessRuntimeBaseTest.cs | 4 ++-- .../test/JSRuntimeBaseTest.cs | 4 ++-- .../Microsoft.JSInterop/test/JSRuntimeTest.cs | 2 +- .../Microsoft.JSInterop/test/JsonUtilTest.cs | 12 +++++----- .../test/Microsoft.JSInterop.Test.csproj | 11 --------- .../test/Microsoft.JSInterop.Tests.csproj | 15 ++++++++++++ .../test/xunit.runner.json | 3 +++ ...xtensions.Localization.Abstractions.csproj | 1 + .../Microsoft.Extensions.Localization.csproj | 5 ++++ .../src/Properties/AssemblyInfo.cs | 6 ----- .../Microsoft.Extensions.ObjectPool.csproj | 1 + .../ActivatorUtilities/sharedsources.props | 4 ++++ .../BenchmarkRunner/Directory.Build.props | 8 ------- ...Extensions.CommandLineUtils.Sources.shproj | 13 ----------- .../src/Microsoft.AspNetCore.Testing.csproj | 5 ++++ src/Testing/src/Properties/AssemblyInfo.cs | 6 ----- src/Testing/src/TestPathUtilities.cs | 7 +++++- .../src/xunit/ConditionalFactDiscoverer.cs | 4 ++-- .../src/xunit/ConditionalTheoryAttribute.cs | 2 +- .../src/xunit/ConditionalTheoryDiscoverer.cs | 23 +++++++++++++++++-- src/Testing/src/xunit/SkippedTestCase.cs | 14 +++++++---- .../Microsoft.AspNetCore.Testing.Tests.csproj | 1 - src/Testing/test/TestPathUtilitiesTest.cs | 2 +- .../Microsoft.Extensions.WebEncoders.csproj | 1 + 37 files changed, 117 insertions(+), 92 deletions(-) delete mode 100644 src/FileProviders/Embedded/src/Properties/AssemblyInfo.cs rename src/FileProviders/Manifest.MSBuildTask/test/{Microsoft.Extensions.FileProviders.Embedded.Manifest.Task.Test.csproj => Microsoft.Extensions.FileProviders.Embedded.Manifest.Task.Tests.csproj} (100%) delete mode 100644 src/HealthChecks/HealthChecks/src/Properties/AssemblyInfo.cs delete mode 100644 src/JSInterop/Microsoft.JSInterop/src/Properties/AssemblyInfo.cs delete mode 100644 src/JSInterop/Microsoft.JSInterop/test/Microsoft.JSInterop.Test.csproj create mode 100644 src/JSInterop/Microsoft.JSInterop/test/Microsoft.JSInterop.Tests.csproj create mode 100644 src/JSInterop/Microsoft.JSInterop/test/xunit.runner.json delete mode 100644 src/Localization/Localization/src/Properties/AssemblyInfo.cs delete mode 100644 src/Shared/BenchmarkRunner/Directory.Build.props delete mode 100644 src/Shared/CommandLineUtils/Microsoft.Extensions.CommandLineUtils.Sources.shproj delete mode 100644 src/Testing/src/Properties/AssemblyInfo.cs diff --git a/src/Configuration.KeyPerFile/Directory.Build.props b/src/Configuration.KeyPerFile/Directory.Build.props index 2082380096..fe35a9faec 100644 --- a/src/Configuration.KeyPerFile/Directory.Build.props +++ b/src/Configuration.KeyPerFile/Directory.Build.props @@ -2,7 +2,7 @@ - true + true configuration diff --git a/src/Configuration.KeyPerFile/src/Microsoft.Extensions.Configuration.KeyPerFile.csproj b/src/Configuration.KeyPerFile/src/Microsoft.Extensions.Configuration.KeyPerFile.csproj index 4eb19f3293..82784d0eed 100644 --- a/src/Configuration.KeyPerFile/src/Microsoft.Extensions.Configuration.KeyPerFile.csproj +++ b/src/Configuration.KeyPerFile/src/Microsoft.Extensions.Configuration.KeyPerFile.csproj @@ -4,6 +4,7 @@ Configuration provider that uses files in a directory for Microsoft.Extensions.Configuration. netstandard2.0 false + true diff --git a/src/FileProviders/Directory.Build.props b/src/FileProviders/Directory.Build.props index bf4410dcb7..709c47ddbd 100644 --- a/src/FileProviders/Directory.Build.props +++ b/src/FileProviders/Directory.Build.props @@ -2,7 +2,7 @@ - true + true files;filesystem diff --git a/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.csproj b/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.csproj index e8dd5d85dd..90cb2e4cc3 100644 --- a/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.csproj +++ b/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.csproj @@ -5,8 +5,13 @@ File provider for files in embedded resources for Microsoft.Extensions.FileProviders. netstandard2.0 $(MSBuildProjectName).nuspec + true + + + + @@ -18,7 +23,6 @@ - id=$(PackageId); @@ -40,10 +44,9 @@ OutputDocumentation=@(DocumentationProjectOutputGroupOutput); - TaskAssemblyNetStandard=..\..\Manifest.MSBuildTask\src\bin\$(Configuration)\netstandard2.0\$(AssemblyName).Manifest.Task.dll; - TaskSymbolNetStandard=..\..\Manifest.MSBuildTask\src\bin\$(Configuration)\netstandard2.0\$(AssemblyName).Manifest.Task.pdb; + TaskAssemblyNetStandard=$(ArtifactsDir)bin\$(AssemblyName).Manifest.Task\$(Configuration)\netstandard2.0\$(AssemblyName).Manifest.Task.dll; + TaskSymbolNetStandard=$(ArtifactsDir)bin\$(AssemblyName).Manifest.Task\$(Configuration)\netstandard2.0\$(AssemblyName).Manifest.Task.pdb; - diff --git a/src/FileProviders/Embedded/src/Properties/AssemblyInfo.cs b/src/FileProviders/Embedded/src/Properties/AssemblyInfo.cs deleted file mode 100644 index 610a7fa706..0000000000 --- a/src/FileProviders/Embedded/src/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,4 +0,0 @@ - -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Microsoft.Extensions.FileProviders.Embedded.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] \ No newline at end of file diff --git a/src/FileProviders/Manifest.MSBuildTask/test/Microsoft.Extensions.FileProviders.Embedded.Manifest.Task.Test.csproj b/src/FileProviders/Manifest.MSBuildTask/test/Microsoft.Extensions.FileProviders.Embedded.Manifest.Task.Tests.csproj similarity index 100% rename from src/FileProviders/Manifest.MSBuildTask/test/Microsoft.Extensions.FileProviders.Embedded.Manifest.Task.Test.csproj rename to src/FileProviders/Manifest.MSBuildTask/test/Microsoft.Extensions.FileProviders.Embedded.Manifest.Task.Tests.csproj diff --git a/src/HealthChecks/Abstractions/src/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.csproj b/src/HealthChecks/Abstractions/src/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.csproj index b95d66f7b3..69298be027 100644 --- a/src/HealthChecks/Abstractions/src/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.csproj +++ b/src/HealthChecks/Abstractions/src/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.csproj @@ -11,6 +11,7 @@ Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck $(NoWarn);CS1591 true diagnostics;healthchecks + true diff --git a/src/HealthChecks/HealthChecks/src/Microsoft.Extensions.Diagnostics.HealthChecks.csproj b/src/HealthChecks/HealthChecks/src/Microsoft.Extensions.Diagnostics.HealthChecks.csproj index d0b1c97ef0..b49f194d0c 100644 --- a/src/HealthChecks/HealthChecks/src/Microsoft.Extensions.Diagnostics.HealthChecks.csproj +++ b/src/HealthChecks/HealthChecks/src/Microsoft.Extensions.Diagnostics.HealthChecks.csproj @@ -10,8 +10,13 @@ Microsoft.Extensions.Diagnostics.HealthChecks.IHealthChecksBuilder $(NoWarn);CS1591 true diagnostics;healthchecks + true + + + + diff --git a/src/HealthChecks/HealthChecks/src/Properties/AssemblyInfo.cs b/src/HealthChecks/HealthChecks/src/Properties/AssemblyInfo.cs deleted file mode 100644 index 13e969bfad..0000000000 --- a/src/HealthChecks/HealthChecks/src/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Microsoft.Extensions.Diagnostics.HealthChecks.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] \ No newline at end of file diff --git a/src/JSInterop/Microsoft.JSInterop/src/Microsoft.JSInterop.csproj b/src/JSInterop/Microsoft.JSInterop/src/Microsoft.JSInterop.csproj index bdfea26ad5..d91e8c2524 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/Microsoft.JSInterop.csproj +++ b/src/JSInterop/Microsoft.JSInterop/src/Microsoft.JSInterop.csproj @@ -7,4 +7,8 @@ true + + + + diff --git a/src/JSInterop/Microsoft.JSInterop/src/Properties/AssemblyInfo.cs b/src/JSInterop/Microsoft.JSInterop/src/Properties/AssemblyInfo.cs deleted file mode 100644 index d65c89dc7f..0000000000 --- a/src/JSInterop/Microsoft.JSInterop/src/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Microsoft.JSInterop.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] diff --git a/src/JSInterop/Microsoft.JSInterop/test/DotNetDispatcherTest.cs b/src/JSInterop/Microsoft.JSInterop/test/DotNetDispatcherTest.cs index eec537f987..93ef9a2498 100644 --- a/src/JSInterop/Microsoft.JSInterop/test/DotNetDispatcherTest.cs +++ b/src/JSInterop/Microsoft.JSInterop/test/DotNetDispatcherTest.cs @@ -6,7 +6,7 @@ using System.Linq; using System.Threading.Tasks; using Xunit; -namespace Microsoft.JSInterop.Test +namespace Microsoft.JSInterop.Tests { public class DotNetDispatcherTest { @@ -73,7 +73,7 @@ namespace Microsoft.JSInterop.Test Assert.Equal($"The assembly '{thisAssemblyName}' does not contain a public method with [JSInvokableAttribute(\"{methodIdentifier}\")].", ex.Message); } - [Fact] + [Fact(Skip = "https://github.com/aspnet/AspNetCore-Internal/issues/1733")] public Task CanInvokeStaticVoidMethod() => WithJSRuntime(jsRuntime => { // Arrange/Act @@ -109,7 +109,7 @@ namespace Microsoft.JSInterop.Test Assert.Equal(456, result.IntVal); }); - [Fact] + [Fact(Skip = "https://github.com/aspnet/AspNetCore-Internal/issues/1733")] public Task CanInvokeStaticWithParams() => WithJSRuntime(jsRuntime => { // Arrange: Track a .NET object to use as an arg @@ -140,7 +140,7 @@ namespace Microsoft.JSInterop.Test Assert.Equal(1299, resultDto2.IntVal); }); - [Fact] + [Fact(Skip = "https://github.com/aspnet/AspNetCore-Internal/issues/1733")] public Task CanInvokeInstanceVoidMethod() => WithJSRuntime(jsRuntime => { // Arrange: Track some instance @@ -155,7 +155,7 @@ namespace Microsoft.JSInterop.Test Assert.True(targetInstance.DidInvokeMyInvocableInstanceVoid); }); - [Fact] + [Fact(Skip = "https://github.com/aspnet/AspNetCore-Internal/issues/1733")] public Task CanInvokeBaseInstanceVoidMethod() => WithJSRuntime(jsRuntime => { // Arrange: Track some instance @@ -206,7 +206,7 @@ namespace Microsoft.JSInterop.Test Assert.StartsWith("There is no tracked object with id '1'.", ex.Message); }); - [Fact] + [Fact(Skip = "https://github.com/aspnet/AspNetCore-Internal/issues/1733")] public Task CanInvokeInstanceMethodWithParams() => WithJSRuntime(jsRuntime => { // Arrange: Track some instance plus another object we'll pass as a param @@ -242,7 +242,7 @@ namespace Microsoft.JSInterop.Test Assert.Equal("In call to 'InvocableStaticWithParams', expected 3 parameters but received 4.", ex.Message); } - [Fact] + [Fact(Skip = "https://github.com/aspnet/AspNetCore-Internal/issues/1733")] public Task CanInvokeAsyncMethod() => WithJSRuntime(async jsRuntime => { // Arrange: Track some instance plus another object we'll pass as a param @@ -320,7 +320,8 @@ namespace Microsoft.JSInterop.Test protected static void StaticMethodWithoutAttribute() { } protected static void InstanceMethodWithoutAttribute() { } - [JSInvokable("InvocableStaticVoid")] public static void MyInvocableVoid() + [JSInvokable("InvocableStaticVoid")] + public static void MyInvocableVoid() { DidInvokeMyInvocableStaticVoid = true; } diff --git a/src/JSInterop/Microsoft.JSInterop/test/DotNetObjectRefTest.cs b/src/JSInterop/Microsoft.JSInterop/test/DotNetObjectRefTest.cs index 969dcae79d..1bdec6d465 100644 --- a/src/JSInterop/Microsoft.JSInterop/test/DotNetObjectRefTest.cs +++ b/src/JSInterop/Microsoft.JSInterop/test/DotNetObjectRefTest.cs @@ -6,7 +6,7 @@ using System.Collections.Generic; using System.Threading.Tasks; using Xunit; -namespace Microsoft.JSInterop.Test +namespace Microsoft.JSInterop.Tests { public class DotNetObjectRefTest { diff --git a/src/JSInterop/Microsoft.JSInterop/test/JSInProcessRuntimeBaseTest.cs b/src/JSInterop/Microsoft.JSInterop/test/JSInProcessRuntimeBaseTest.cs index d2e71f6eb2..36474fe407 100644 --- a/src/JSInterop/Microsoft.JSInterop/test/JSInProcessRuntimeBaseTest.cs +++ b/src/JSInterop/Microsoft.JSInterop/test/JSInProcessRuntimeBaseTest.cs @@ -6,7 +6,7 @@ using System.Collections.Generic; using System.Linq; using Xunit; -namespace Microsoft.JSInterop.Test +namespace Microsoft.JSInterop.Tests { public class JSInProcessRuntimeBaseTest { @@ -21,7 +21,7 @@ namespace Microsoft.JSInterop.Test }; // Act - var syncResult = runtime.Invoke("test identifier 1", "arg1", 123, true ); + var syncResult = runtime.Invoke("test identifier 1", "arg1", 123, true); var call = runtime.InvokeCalls.Single(); // Assert diff --git a/src/JSInterop/Microsoft.JSInterop/test/JSRuntimeBaseTest.cs b/src/JSInterop/Microsoft.JSInterop/test/JSRuntimeBaseTest.cs index 9193d6deb8..ab048e812f 100644 --- a/src/JSInterop/Microsoft.JSInterop/test/JSRuntimeBaseTest.cs +++ b/src/JSInterop/Microsoft.JSInterop/test/JSRuntimeBaseTest.cs @@ -7,7 +7,7 @@ using System.Collections.Generic; using System.Linq; using Xunit; -namespace Microsoft.JSInterop.Test +namespace Microsoft.JSInterop.Tests { public class JSRuntimeBaseTest { @@ -18,7 +18,7 @@ namespace Microsoft.JSInterop.Test var runtime = new TestJSRuntime(); // Act - runtime.InvokeAsync("test identifier 1", "arg1", 123, true ); + runtime.InvokeAsync("test identifier 1", "arg1", 123, true); runtime.InvokeAsync("test identifier 2", "some other arg"); // Assert diff --git a/src/JSInterop/Microsoft.JSInterop/test/JSRuntimeTest.cs b/src/JSInterop/Microsoft.JSInterop/test/JSRuntimeTest.cs index d5fed45ea4..b8a1c363dc 100644 --- a/src/JSInterop/Microsoft.JSInterop/test/JSRuntimeTest.cs +++ b/src/JSInterop/Microsoft.JSInterop/test/JSRuntimeTest.cs @@ -6,7 +6,7 @@ using System.Linq; using System.Threading.Tasks; using Xunit; -namespace Microsoft.JSInterop.Test +namespace Microsoft.JSInterop.Tests { public class JSRuntimeTest { diff --git a/src/JSInterop/Microsoft.JSInterop/test/JsonUtilTest.cs b/src/JSInterop/Microsoft.JSInterop/test/JsonUtilTest.cs index 1be98b681e..2b239faab9 100644 --- a/src/JSInterop/Microsoft.JSInterop/test/JsonUtilTest.cs +++ b/src/JSInterop/Microsoft.JSInterop/test/JsonUtilTest.cs @@ -6,7 +6,7 @@ using System; using System.Collections.Generic; using Xunit; -namespace Microsoft.JSInterop.Test +namespace Microsoft.JSInterop.Tests { public class JsonUtilTest { @@ -124,10 +124,10 @@ namespace Microsoft.JSInterop.Test BoolProperty = true, NullableIntProperty = 1 }; - + // Act var result = Json.Serialize(commandResult); - + // Assert Assert.Equal("{\"stringProperty\":\"Test\",\"boolProperty\":true,\"nullableIntProperty\":1}", result); } @@ -222,12 +222,12 @@ namespace Microsoft.JSInterop.Test // Act var exception = Assert.Throws(() => { - Json.Deserialize(json); + Json.Deserialize(json); }); // Assert Assert.Equal( - $"Cannot deserialize JSON into type '{type.FullName}' because it does not have a public parameterless constructor.", + $"Cannot deserialize JSON into type '{type.FullName}' because it does not have a public parameterless constructor.", exception.Message); } @@ -277,7 +277,7 @@ namespace Microsoft.JSInterop.Test class NonEmptyConstructorPoco { - public NonEmptyConstructorPoco(int parameter) {} + public NonEmptyConstructorPoco(int parameter) { } public int Property { get; set; } } diff --git a/src/JSInterop/Microsoft.JSInterop/test/Microsoft.JSInterop.Test.csproj b/src/JSInterop/Microsoft.JSInterop/test/Microsoft.JSInterop.Test.csproj deleted file mode 100644 index 5e88f44a3a..0000000000 --- a/src/JSInterop/Microsoft.JSInterop/test/Microsoft.JSInterop.Test.csproj +++ /dev/null @@ -1,11 +0,0 @@ - - - - netcoreapp3.0 - - - - - - - diff --git a/src/JSInterop/Microsoft.JSInterop/test/Microsoft.JSInterop.Tests.csproj b/src/JSInterop/Microsoft.JSInterop/test/Microsoft.JSInterop.Tests.csproj new file mode 100644 index 0000000000..a39b082180 --- /dev/null +++ b/src/JSInterop/Microsoft.JSInterop/test/Microsoft.JSInterop.Tests.csproj @@ -0,0 +1,15 @@ + + + + netcoreapp3.0 + + + + + + + + + + + diff --git a/src/JSInterop/Microsoft.JSInterop/test/xunit.runner.json b/src/JSInterop/Microsoft.JSInterop/test/xunit.runner.json new file mode 100644 index 0000000000..0f2ad9f769 --- /dev/null +++ b/src/JSInterop/Microsoft.JSInterop/test/xunit.runner.json @@ -0,0 +1,3 @@ +{ + "shadowCopy": true +} diff --git a/src/Localization/Abstractions/src/Microsoft.Extensions.Localization.Abstractions.csproj b/src/Localization/Abstractions/src/Microsoft.Extensions.Localization.Abstractions.csproj index 8508eb071a..636072724f 100644 --- a/src/Localization/Abstractions/src/Microsoft.Extensions.Localization.Abstractions.csproj +++ b/src/Localization/Abstractions/src/Microsoft.Extensions.Localization.Abstractions.csproj @@ -10,6 +10,7 @@ Microsoft.Extensions.Localization.IStringLocalizer<T> $(NoWarn);CS1591 true localization + true diff --git a/src/Localization/Localization/src/Microsoft.Extensions.Localization.csproj b/src/Localization/Localization/src/Microsoft.Extensions.Localization.csproj index 73365a15eb..8a820ac7b9 100644 --- a/src/Localization/Localization/src/Microsoft.Extensions.Localization.csproj +++ b/src/Localization/Localization/src/Microsoft.Extensions.Localization.csproj @@ -7,6 +7,7 @@ $(NoWarn);CS1591 true localization + true @@ -16,4 +17,8 @@ + + + + diff --git a/src/Localization/Localization/src/Properties/AssemblyInfo.cs b/src/Localization/Localization/src/Properties/AssemblyInfo.cs deleted file mode 100644 index 3e297b801e..0000000000 --- a/src/Localization/Localization/src/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Microsoft.Extensions.Localization.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] diff --git a/src/ObjectPool/src/Microsoft.Extensions.ObjectPool.csproj b/src/ObjectPool/src/Microsoft.Extensions.ObjectPool.csproj index cb42c5615a..646d18a8b7 100644 --- a/src/ObjectPool/src/Microsoft.Extensions.ObjectPool.csproj +++ b/src/ObjectPool/src/Microsoft.Extensions.ObjectPool.csproj @@ -6,6 +6,7 @@ $(NoWarn);CS1591 true pooling + true diff --git a/src/Shared/ActivatorUtilities/sharedsources.props b/src/Shared/ActivatorUtilities/sharedsources.props index b35fe34b10..f754677531 100644 --- a/src/Shared/ActivatorUtilities/sharedsources.props +++ b/src/Shared/ActivatorUtilities/sharedsources.props @@ -1,4 +1,8 @@ + + false + + true diff --git a/src/Shared/BenchmarkRunner/Directory.Build.props b/src/Shared/BenchmarkRunner/Directory.Build.props deleted file mode 100644 index d2f65e8d3d..0000000000 --- a/src/Shared/BenchmarkRunner/Directory.Build.props +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - Microsoft.AspNetCore.BenchmarkRunner.Sources - - diff --git a/src/Shared/CommandLineUtils/Microsoft.Extensions.CommandLineUtils.Sources.shproj b/src/Shared/CommandLineUtils/Microsoft.Extensions.CommandLineUtils.Sources.shproj deleted file mode 100644 index c728fe1012..0000000000 --- a/src/Shared/CommandLineUtils/Microsoft.Extensions.CommandLineUtils.Sources.shproj +++ /dev/null @@ -1,13 +0,0 @@ - - - - 00947d4a-c20e-46e3-90c3-6cd6bc87ee72 - 14.0 - - - - - - - - diff --git a/src/Testing/src/Microsoft.AspNetCore.Testing.csproj b/src/Testing/src/Microsoft.AspNetCore.Testing.csproj index 64e0b3c4e1..0e33852b62 100644 --- a/src/Testing/src/Microsoft.AspNetCore.Testing.csproj +++ b/src/Testing/src/Microsoft.AspNetCore.Testing.csproj @@ -8,10 +8,15 @@ aspnetcore false true + false true + + + + diff --git a/src/Testing/src/Properties/AssemblyInfo.cs b/src/Testing/src/Properties/AssemblyInfo.cs deleted file mode 100644 index 0212e111ee..0000000000 --- a/src/Testing/src/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Testing.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] diff --git a/src/Testing/src/TestPathUtilities.cs b/src/Testing/src/TestPathUtilities.cs index ebd10897c3..f982471f39 100644 --- a/src/Testing/src/TestPathUtilities.cs +++ b/src/Testing/src/TestPathUtilities.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// 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; @@ -8,6 +8,11 @@ namespace Microsoft.AspNetCore.Testing { public class TestPathUtilities { + public static string GetRepoRootDirectory() + { + return GetSolutionRootDirectory("Extensions"); + } + public static string GetSolutionRootDirectory(string solution) { var applicationBasePath = AppContext.BaseDirectory; diff --git a/src/Testing/src/xunit/ConditionalFactDiscoverer.cs b/src/Testing/src/xunit/ConditionalFactDiscoverer.cs index 819373fa31..cf49b29e5a 100644 --- a/src/Testing/src/xunit/ConditionalFactDiscoverer.cs +++ b/src/Testing/src/xunit/ConditionalFactDiscoverer.cs @@ -20,8 +20,8 @@ namespace Microsoft.AspNetCore.Testing.xunit { var skipReason = testMethod.EvaluateSkipConditions(); return skipReason != null - ? new SkippedTestCase(skipReason, _diagnosticMessageSink, discoveryOptions.MethodDisplayOrDefault(), testMethod) + ? new SkippedTestCase(skipReason, _diagnosticMessageSink, discoveryOptions.MethodDisplayOrDefault(), TestMethodDisplayOptions.None, testMethod) : base.CreateTestCase(discoveryOptions, testMethod, factAttribute); } } -} \ No newline at end of file +} diff --git a/src/Testing/src/xunit/ConditionalTheoryAttribute.cs b/src/Testing/src/xunit/ConditionalTheoryAttribute.cs index 9249078cc5..fe45f2ffc6 100644 --- a/src/Testing/src/xunit/ConditionalTheoryAttribute.cs +++ b/src/Testing/src/xunit/ConditionalTheoryAttribute.cs @@ -12,4 +12,4 @@ namespace Microsoft.AspNetCore.Testing.xunit public class ConditionalTheoryAttribute : TheoryAttribute { } -} \ No newline at end of file +} diff --git a/src/Testing/src/xunit/ConditionalTheoryDiscoverer.cs b/src/Testing/src/xunit/ConditionalTheoryDiscoverer.cs index d24421f5cd..9e413cd580 100644 --- a/src/Testing/src/xunit/ConditionalTheoryDiscoverer.cs +++ b/src/Testing/src/xunit/ConditionalTheoryDiscoverer.cs @@ -14,11 +14,30 @@ namespace Microsoft.AspNetCore.Testing.xunit { } + private sealed class OptionsWithPreEnumerationEnabled : ITestFrameworkDiscoveryOptions + { + private const string PreEnumerateTheories = "xunit.discovery.PreEnumerateTheories"; + + private readonly ITestFrameworkDiscoveryOptions _original; + + public OptionsWithPreEnumerationEnabled(ITestFrameworkDiscoveryOptions original) + => _original = original; + + public TValue GetValue(string name) + => (name == PreEnumerateTheories) ? (TValue)(object)true : _original.GetValue(name); + + public void SetValue(string name, TValue value) + => _original.SetValue(name, value); + } + + public override IEnumerable Discover(ITestFrameworkDiscoveryOptions discoveryOptions, ITestMethod testMethod, IAttributeInfo theoryAttribute) + => base.Discover(new OptionsWithPreEnumerationEnabled(discoveryOptions), testMethod, theoryAttribute); + protected override IEnumerable CreateTestCasesForTheory(ITestFrameworkDiscoveryOptions discoveryOptions, ITestMethod testMethod, IAttributeInfo theoryAttribute) { var skipReason = testMethod.EvaluateSkipConditions(); return skipReason != null - ? new[] { new SkippedTestCase(skipReason, DiagnosticMessageSink, discoveryOptions.MethodDisplayOrDefault(), testMethod) } + ? new[] { new SkippedTestCase(skipReason, DiagnosticMessageSink, discoveryOptions.MethodDisplayOrDefault(), TestMethodDisplayOptions.None, testMethod) } : base.CreateTestCasesForTheory(discoveryOptions, testMethod, theoryAttribute); } @@ -44,4 +63,4 @@ namespace Microsoft.AspNetCore.Testing.xunit : base.CreateTestCasesForDataRow(discoveryOptions, testMethod, theoryAttribute, dataRow); } } -} \ No newline at end of file +} diff --git a/src/Testing/src/xunit/SkippedTestCase.cs b/src/Testing/src/xunit/SkippedTestCase.cs index c2e15fa640..1c25c507b9 100644 --- a/src/Testing/src/xunit/SkippedTestCase.cs +++ b/src/Testing/src/xunit/SkippedTestCase.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// 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; @@ -16,8 +16,14 @@ namespace Microsoft.AspNetCore.Testing.xunit { } - public SkippedTestCase(string skipReason, IMessageSink diagnosticMessageSink, TestMethodDisplay defaultMethodDisplay, ITestMethod testMethod, object[] testMethodArguments = null) - : base(diagnosticMessageSink, defaultMethodDisplay, testMethod, testMethodArguments) + public SkippedTestCase( + string skipReason, + IMessageSink diagnosticMessageSink, + TestMethodDisplay defaultMethodDisplay, + TestMethodDisplayOptions defaultMethodDisplayOptions, + ITestMethod testMethod, + object[] testMethodArguments = null) + : base(diagnosticMessageSink, defaultMethodDisplay, defaultMethodDisplayOptions, testMethod, testMethodArguments) { _skipReason = skipReason; } @@ -37,4 +43,4 @@ namespace Microsoft.AspNetCore.Testing.xunit data.AddValue(nameof(_skipReason), _skipReason); } } -} \ No newline at end of file +} diff --git a/src/Testing/test/Microsoft.AspNetCore.Testing.Tests.csproj b/src/Testing/test/Microsoft.AspNetCore.Testing.Tests.csproj index 691c9ccd76..acfb34b320 100644 --- a/src/Testing/test/Microsoft.AspNetCore.Testing.Tests.csproj +++ b/src/Testing/test/Microsoft.AspNetCore.Testing.Tests.csproj @@ -24,5 +24,4 @@ - diff --git a/src/Testing/test/TestPathUtilitiesTest.cs b/src/Testing/test/TestPathUtilitiesTest.cs index 0c9a7c5ee4..c77194a548 100644 --- a/src/Testing/test/TestPathUtilitiesTest.cs +++ b/src/Testing/test/TestPathUtilitiesTest.cs @@ -16,7 +16,7 @@ namespace Microsoft.AspNetCore.Testing // Testing\test\Microsoft.AspNetCore.Testing.Tests\bin\Debug\netcoreapp2.0 // Testing\test\Microsoft.AspNetCore.Testing.Tests\bin\Debug\net461 // Testing\test\Microsoft.AspNetCore.Testing.Tests\bin\Debug\net46 - var expectedPath = Path.GetFullPath(Path.Combine(Directory.GetCurrentDirectory(), "..", "..", "..", "..", "..", "..", "..")); + var expectedPath = Path.GetFullPath(Path.Combine(Directory.GetCurrentDirectory(), "..", "..", "..", "..", "..")); Assert.Equal(expectedPath, TestPathUtilities.GetSolutionRootDirectory("Extensions")); } diff --git a/src/WebEncoders/src/Microsoft.Extensions.WebEncoders.csproj b/src/WebEncoders/src/Microsoft.Extensions.WebEncoders.csproj index 18f96d9412..bc73592b3f 100644 --- a/src/WebEncoders/src/Microsoft.Extensions.WebEncoders.csproj +++ b/src/WebEncoders/src/Microsoft.Extensions.WebEncoders.csproj @@ -7,6 +7,7 @@ true true aspnetcore + true From fb64cd68876a1218faf6f13639a4d7610a7a90f3 Mon Sep 17 00:00:00 2001 From: Ryan Brandenburg Date: Tue, 29 Jan 2019 17:05:25 -0800 Subject: [PATCH 19/52] Reduce build times (dotnet/extensions#1016) * Fix package artifacts taking too long to upload * Make sure new packages are packaged \n\nCommit migrated from https://github.com/dotnet/extensions/commit/12613bac075ddd60e2dc8d3eea6510df55ee4e7c --- src/JSInterop/Microsoft.JSInterop/src/Microsoft.JSInterop.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/src/JSInterop/Microsoft.JSInterop/src/Microsoft.JSInterop.csproj b/src/JSInterop/Microsoft.JSInterop/src/Microsoft.JSInterop.csproj index d91e8c2524..096b5d5bdd 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/Microsoft.JSInterop.csproj +++ b/src/JSInterop/Microsoft.JSInterop/src/Microsoft.JSInterop.csproj @@ -5,6 +5,7 @@ Abstractions and features for interop between .NET and JavaScript code. javascript;interop true + true From 154ab28e451898d023e9c3cca97c2c6fb3590a1b Mon Sep 17 00:00:00 2001 From: Nate McMaster Date: Tue, 29 Jan 2019 18:34:54 -0800 Subject: [PATCH 20/52] Cleanup conversion to Arcade (dotnet/extensions#1014) * Remove obsolete targets, properties, and scripts * Replace IsProductComponent with IsShipping * Undo bad merge to version.props * Update documentation, and put workarounds into a common file * Replace usages of RepositoryRoot with RepoRoot * Remove API baselines * Remove unnecessary restore feeds and split workarounds into two files * Enable PR checks on all branches, and disable autocancel\n\nCommit migrated from https://github.com/dotnet/extensions/commit/f41cfded3c12eec0efea89ece1dafe43afa9c6b8 --- ...Extensions.Configuration.KeyPerFile.csproj | 3 +- .../Embedded/Directory.Build.props | 7 - ...t.Extensions.FileProviders.Embedded.csproj | 2 +- .../Embedded/src/baseline.netcore.json | 343 --------- ...ileProviders.Embedded.Manifest.Task.csproj | 2 +- .../Abstractions/Directory.Build.props | 7 - ...agnostics.HealthChecks.Abstractions.csproj | 2 +- .../Abstractions/src/baseline.netcore.json | 5 - .../HealthChecks/Directory.Build.props | 7 - ...Extensions.Diagnostics.HealthChecks.csproj | 2 +- .../HealthChecks/src/baseline.netcore.json | 5 - ...ions.Diagnostics.HealthChecks.Tests.csproj | 2 +- src/JSInterop/Directory.Build.props | 7 - .../src/Microsoft.JSInterop.csproj | 2 +- .../src/Mono.WebAssembly.Interop.csproj | 2 - ...xtensions.Localization.Abstractions.csproj | 2 +- .../Abstractions/src/baseline.netcore.json | 413 ----------- .../Microsoft.Extensions.Localization.csproj | 2 +- .../Localization/src/baseline.netcore.json | 687 ------------------ src/ObjectPool/Directory.Build.props | 7 - .../Microsoft.Extensions.ObjectPool.csproj | 2 +- src/ObjectPool/src/baseline.netcore.json | 612 ---------------- .../src/Microsoft.AspNetCore.Testing.csproj | 1 - src/WebEncoders/Directory.Build.props | 7 - .../Microsoft.Extensions.WebEncoders.csproj | 2 +- src/WebEncoders/src/baseline.netcore.json | 564 -------------- 26 files changed, 11 insertions(+), 2686 deletions(-) delete mode 100644 src/FileProviders/Embedded/Directory.Build.props delete mode 100644 src/FileProviders/Embedded/src/baseline.netcore.json delete mode 100644 src/HealthChecks/Abstractions/Directory.Build.props delete mode 100644 src/HealthChecks/Abstractions/src/baseline.netcore.json delete mode 100644 src/HealthChecks/HealthChecks/Directory.Build.props delete mode 100644 src/HealthChecks/HealthChecks/src/baseline.netcore.json delete mode 100644 src/JSInterop/Directory.Build.props delete mode 100644 src/Localization/Abstractions/src/baseline.netcore.json delete mode 100644 src/Localization/Localization/src/baseline.netcore.json delete mode 100644 src/ObjectPool/Directory.Build.props delete mode 100644 src/ObjectPool/src/baseline.netcore.json delete mode 100644 src/WebEncoders/Directory.Build.props delete mode 100644 src/WebEncoders/src/baseline.netcore.json diff --git a/src/Configuration.KeyPerFile/src/Microsoft.Extensions.Configuration.KeyPerFile.csproj b/src/Configuration.KeyPerFile/src/Microsoft.Extensions.Configuration.KeyPerFile.csproj index 82784d0eed..5bd7b2c7ef 100644 --- a/src/Configuration.KeyPerFile/src/Microsoft.Extensions.Configuration.KeyPerFile.csproj +++ b/src/Configuration.KeyPerFile/src/Microsoft.Extensions.Configuration.KeyPerFile.csproj @@ -3,8 +3,7 @@ Configuration provider that uses files in a directory for Microsoft.Extensions.Configuration. netstandard2.0 - false - true + true diff --git a/src/FileProviders/Embedded/Directory.Build.props b/src/FileProviders/Embedded/Directory.Build.props deleted file mode 100644 index f25c1d90ce..0000000000 --- a/src/FileProviders/Embedded/Directory.Build.props +++ /dev/null @@ -1,7 +0,0 @@ - - - - - true - - diff --git a/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.csproj b/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.csproj index 90cb2e4cc3..792b9ff5b3 100644 --- a/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.csproj +++ b/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.csproj @@ -5,7 +5,7 @@ File provider for files in embedded resources for Microsoft.Extensions.FileProviders. netstandard2.0 $(MSBuildProjectName).nuspec - true + true diff --git a/src/FileProviders/Embedded/src/baseline.netcore.json b/src/FileProviders/Embedded/src/baseline.netcore.json deleted file mode 100644 index 821969ea0b..0000000000 --- a/src/FileProviders/Embedded/src/baseline.netcore.json +++ /dev/null @@ -1,343 +0,0 @@ -{ - "AssemblyIdentity": "Microsoft.Extensions.FileProviders.Embedded, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", - "Types": [ - { - "Name": "Microsoft.Extensions.FileProviders.EmbeddedFileProvider", - "Visibility": "Public", - "Kind": "Class", - "ImplementedInterfaces": [ - "Microsoft.Extensions.FileProviders.IFileProvider" - ], - "Members": [ - { - "Kind": "Method", - "Name": "GetFileInfo", - "Parameters": [ - { - "Name": "subpath", - "Type": "System.String" - } - ], - "ReturnType": "Microsoft.Extensions.FileProviders.IFileInfo", - "Sealed": true, - "Virtual": true, - "ImplementedInterface": "Microsoft.Extensions.FileProviders.IFileProvider", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "GetDirectoryContents", - "Parameters": [ - { - "Name": "subpath", - "Type": "System.String" - } - ], - "ReturnType": "Microsoft.Extensions.FileProviders.IDirectoryContents", - "Sealed": true, - "Virtual": true, - "ImplementedInterface": "Microsoft.Extensions.FileProviders.IFileProvider", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "Watch", - "Parameters": [ - { - "Name": "pattern", - "Type": "System.String" - } - ], - "ReturnType": "Microsoft.Extensions.Primitives.IChangeToken", - "Sealed": true, - "Virtual": true, - "ImplementedInterface": "Microsoft.Extensions.FileProviders.IFileProvider", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Constructor", - "Name": ".ctor", - "Parameters": [ - { - "Name": "assembly", - "Type": "System.Reflection.Assembly" - } - ], - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Constructor", - "Name": ".ctor", - "Parameters": [ - { - "Name": "assembly", - "Type": "System.Reflection.Assembly" - }, - { - "Name": "baseNamespace", - "Type": "System.String" - } - ], - "Visibility": "Public", - "GenericParameter": [] - } - ], - "GenericParameters": [] - }, - { - "Name": "Microsoft.Extensions.FileProviders.ManifestEmbeddedFileProvider", - "Visibility": "Public", - "Kind": "Class", - "ImplementedInterfaces": [ - "Microsoft.Extensions.FileProviders.IFileProvider" - ], - "Members": [ - { - "Kind": "Method", - "Name": "get_Assembly", - "Parameters": [], - "ReturnType": "System.Reflection.Assembly", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "GetDirectoryContents", - "Parameters": [ - { - "Name": "subpath", - "Type": "System.String" - } - ], - "ReturnType": "Microsoft.Extensions.FileProviders.IDirectoryContents", - "Sealed": true, - "Virtual": true, - "ImplementedInterface": "Microsoft.Extensions.FileProviders.IFileProvider", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "GetFileInfo", - "Parameters": [ - { - "Name": "subpath", - "Type": "System.String" - } - ], - "ReturnType": "Microsoft.Extensions.FileProviders.IFileInfo", - "Sealed": true, - "Virtual": true, - "ImplementedInterface": "Microsoft.Extensions.FileProviders.IFileProvider", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "Watch", - "Parameters": [ - { - "Name": "filter", - "Type": "System.String" - } - ], - "ReturnType": "Microsoft.Extensions.Primitives.IChangeToken", - "Sealed": true, - "Virtual": true, - "ImplementedInterface": "Microsoft.Extensions.FileProviders.IFileProvider", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Constructor", - "Name": ".ctor", - "Parameters": [ - { - "Name": "assembly", - "Type": "System.Reflection.Assembly" - } - ], - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Constructor", - "Name": ".ctor", - "Parameters": [ - { - "Name": "assembly", - "Type": "System.Reflection.Assembly" - }, - { - "Name": "root", - "Type": "System.String" - } - ], - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Constructor", - "Name": ".ctor", - "Parameters": [ - { - "Name": "assembly", - "Type": "System.Reflection.Assembly" - }, - { - "Name": "root", - "Type": "System.String" - }, - { - "Name": "lastModified", - "Type": "System.DateTimeOffset" - } - ], - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Constructor", - "Name": ".ctor", - "Parameters": [ - { - "Name": "assembly", - "Type": "System.Reflection.Assembly" - }, - { - "Name": "root", - "Type": "System.String" - }, - { - "Name": "manifestName", - "Type": "System.String" - }, - { - "Name": "lastModified", - "Type": "System.DateTimeOffset" - } - ], - "Visibility": "Public", - "GenericParameter": [] - } - ], - "GenericParameters": [] - }, - { - "Name": "Microsoft.Extensions.FileProviders.Embedded.EmbeddedResourceFileInfo", - "Visibility": "Public", - "Kind": "Class", - "ImplementedInterfaces": [ - "Microsoft.Extensions.FileProviders.IFileInfo" - ], - "Members": [ - { - "Kind": "Method", - "Name": "get_Exists", - "Parameters": [], - "ReturnType": "System.Boolean", - "Sealed": true, - "Virtual": true, - "ImplementedInterface": "Microsoft.Extensions.FileProviders.IFileInfo", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "get_Length", - "Parameters": [], - "ReturnType": "System.Int64", - "Sealed": true, - "Virtual": true, - "ImplementedInterface": "Microsoft.Extensions.FileProviders.IFileInfo", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "get_PhysicalPath", - "Parameters": [], - "ReturnType": "System.String", - "Sealed": true, - "Virtual": true, - "ImplementedInterface": "Microsoft.Extensions.FileProviders.IFileInfo", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "get_Name", - "Parameters": [], - "ReturnType": "System.String", - "Sealed": true, - "Virtual": true, - "ImplementedInterface": "Microsoft.Extensions.FileProviders.IFileInfo", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "get_LastModified", - "Parameters": [], - "ReturnType": "System.DateTimeOffset", - "Sealed": true, - "Virtual": true, - "ImplementedInterface": "Microsoft.Extensions.FileProviders.IFileInfo", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "get_IsDirectory", - "Parameters": [], - "ReturnType": "System.Boolean", - "Sealed": true, - "Virtual": true, - "ImplementedInterface": "Microsoft.Extensions.FileProviders.IFileInfo", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "CreateReadStream", - "Parameters": [], - "ReturnType": "System.IO.Stream", - "Sealed": true, - "Virtual": true, - "ImplementedInterface": "Microsoft.Extensions.FileProviders.IFileInfo", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Constructor", - "Name": ".ctor", - "Parameters": [ - { - "Name": "assembly", - "Type": "System.Reflection.Assembly" - }, - { - "Name": "resourcePath", - "Type": "System.String" - }, - { - "Name": "name", - "Type": "System.String" - }, - { - "Name": "lastModified", - "Type": "System.DateTimeOffset" - } - ], - "Visibility": "Public", - "GenericParameter": [] - } - ], - "GenericParameters": [] - } - ] -} \ No newline at end of file diff --git a/src/FileProviders/Manifest.MSBuildTask/src/Microsoft.Extensions.FileProviders.Embedded.Manifest.Task.csproj b/src/FileProviders/Manifest.MSBuildTask/src/Microsoft.Extensions.FileProviders.Embedded.Manifest.Task.csproj index 018cc98a8e..f8ae098eab 100644 --- a/src/FileProviders/Manifest.MSBuildTask/src/Microsoft.Extensions.FileProviders.Embedded.Manifest.Task.csproj +++ b/src/FileProviders/Manifest.MSBuildTask/src/Microsoft.Extensions.FileProviders.Embedded.Manifest.Task.csproj @@ -5,7 +5,7 @@ metadata of the files embedded in the assembly at compilation time. netstandard2.0 false - false + true false false diff --git a/src/HealthChecks/Abstractions/Directory.Build.props b/src/HealthChecks/Abstractions/Directory.Build.props deleted file mode 100644 index f25c1d90ce..0000000000 --- a/src/HealthChecks/Abstractions/Directory.Build.props +++ /dev/null @@ -1,7 +0,0 @@ - - - - - true - - diff --git a/src/HealthChecks/Abstractions/src/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.csproj b/src/HealthChecks/Abstractions/src/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.csproj index 69298be027..2bba5959a3 100644 --- a/src/HealthChecks/Abstractions/src/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.csproj +++ b/src/HealthChecks/Abstractions/src/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.csproj @@ -11,7 +11,7 @@ Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck $(NoWarn);CS1591 true diagnostics;healthchecks - true + true diff --git a/src/HealthChecks/Abstractions/src/baseline.netcore.json b/src/HealthChecks/Abstractions/src/baseline.netcore.json deleted file mode 100644 index 871db4c089..0000000000 --- a/src/HealthChecks/Abstractions/src/baseline.netcore.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "AssemblyIdentity": "Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", - "Types": [ - ] -} \ No newline at end of file diff --git a/src/HealthChecks/HealthChecks/Directory.Build.props b/src/HealthChecks/HealthChecks/Directory.Build.props deleted file mode 100644 index f25c1d90ce..0000000000 --- a/src/HealthChecks/HealthChecks/Directory.Build.props +++ /dev/null @@ -1,7 +0,0 @@ - - - - - true - - diff --git a/src/HealthChecks/HealthChecks/src/Microsoft.Extensions.Diagnostics.HealthChecks.csproj b/src/HealthChecks/HealthChecks/src/Microsoft.Extensions.Diagnostics.HealthChecks.csproj index b49f194d0c..463e5b3632 100644 --- a/src/HealthChecks/HealthChecks/src/Microsoft.Extensions.Diagnostics.HealthChecks.csproj +++ b/src/HealthChecks/HealthChecks/src/Microsoft.Extensions.Diagnostics.HealthChecks.csproj @@ -10,7 +10,7 @@ Microsoft.Extensions.Diagnostics.HealthChecks.IHealthChecksBuilder $(NoWarn);CS1591 true diagnostics;healthchecks - true + true diff --git a/src/HealthChecks/HealthChecks/src/baseline.netcore.json b/src/HealthChecks/HealthChecks/src/baseline.netcore.json deleted file mode 100644 index cb2fe053f1..0000000000 --- a/src/HealthChecks/HealthChecks/src/baseline.netcore.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "AssemblyIdentity": "Microsoft.Extensions.Diagnostics.HealthChecks, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", - "Types": [ - ] -} \ No newline at end of file diff --git a/src/HealthChecks/HealthChecks/test/Microsoft.Extensions.Diagnostics.HealthChecks.Tests.csproj b/src/HealthChecks/HealthChecks/test/Microsoft.Extensions.Diagnostics.HealthChecks.Tests.csproj index 56b7e7e81b..163b618900 100644 --- a/src/HealthChecks/HealthChecks/test/Microsoft.Extensions.Diagnostics.HealthChecks.Tests.csproj +++ b/src/HealthChecks/HealthChecks/test/Microsoft.Extensions.Diagnostics.HealthChecks.Tests.csproj @@ -1,6 +1,6 @@  - + netcoreapp3.0;net472 diff --git a/src/JSInterop/Directory.Build.props b/src/JSInterop/Directory.Build.props deleted file mode 100644 index f25c1d90ce..0000000000 --- a/src/JSInterop/Directory.Build.props +++ /dev/null @@ -1,7 +0,0 @@ - - - - - true - - diff --git a/src/JSInterop/Microsoft.JSInterop/src/Microsoft.JSInterop.csproj b/src/JSInterop/Microsoft.JSInterop/src/Microsoft.JSInterop.csproj index 096b5d5bdd..f92b8d457d 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/Microsoft.JSInterop.csproj +++ b/src/JSInterop/Microsoft.JSInterop/src/Microsoft.JSInterop.csproj @@ -5,7 +5,7 @@ Abstractions and features for interop between .NET and JavaScript code. javascript;interop true - true + true diff --git a/src/JSInterop/Mono.WebAssembly.Interop/src/Mono.WebAssembly.Interop.csproj b/src/JSInterop/Mono.WebAssembly.Interop/src/Mono.WebAssembly.Interop.csproj index 75c8272e95..0ad8effd80 100644 --- a/src/JSInterop/Mono.WebAssembly.Interop/src/Mono.WebAssembly.Interop.csproj +++ b/src/JSInterop/Mono.WebAssembly.Interop/src/Mono.WebAssembly.Interop.csproj @@ -2,8 +2,6 @@ netstandard2.0 - - false false diff --git a/src/Localization/Abstractions/src/Microsoft.Extensions.Localization.Abstractions.csproj b/src/Localization/Abstractions/src/Microsoft.Extensions.Localization.Abstractions.csproj index 636072724f..33f58b6358 100644 --- a/src/Localization/Abstractions/src/Microsoft.Extensions.Localization.Abstractions.csproj +++ b/src/Localization/Abstractions/src/Microsoft.Extensions.Localization.Abstractions.csproj @@ -10,7 +10,7 @@ Microsoft.Extensions.Localization.IStringLocalizer<T> $(NoWarn);CS1591 true localization - true + true diff --git a/src/Localization/Abstractions/src/baseline.netcore.json b/src/Localization/Abstractions/src/baseline.netcore.json deleted file mode 100644 index 02ba71db8e..0000000000 --- a/src/Localization/Abstractions/src/baseline.netcore.json +++ /dev/null @@ -1,413 +0,0 @@ -{ - "AssemblyIdentity": "Microsoft.Extensions.Localization.Abstractions, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", - "Types": [ - { - "Name": "Microsoft.Extensions.Localization.IStringLocalizer", - "Visibility": "Public", - "Kind": "Interface", - "Abstract": true, - "ImplementedInterfaces": [], - "Members": [ - { - "Kind": "Method", - "Name": "get_Item", - "Parameters": [ - { - "Name": "name", - "Type": "System.String" - } - ], - "ReturnType": "Microsoft.Extensions.Localization.LocalizedString", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "get_Item", - "Parameters": [ - { - "Name": "name", - "Type": "System.String" - }, - { - "Name": "arguments", - "Type": "System.Object[]", - "IsParams": true - } - ], - "ReturnType": "Microsoft.Extensions.Localization.LocalizedString", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "GetAllStrings", - "Parameters": [ - { - "Name": "includeParentCultures", - "Type": "System.Boolean" - } - ], - "ReturnType": "System.Collections.Generic.IEnumerable", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "WithCulture", - "Parameters": [ - { - "Name": "culture", - "Type": "System.Globalization.CultureInfo" - } - ], - "ReturnType": "Microsoft.Extensions.Localization.IStringLocalizer", - "GenericParameter": [] - } - ], - "GenericParameters": [] - }, - { - "Name": "Microsoft.Extensions.Localization.IStringLocalizerFactory", - "Visibility": "Public", - "Kind": "Interface", - "Abstract": true, - "ImplementedInterfaces": [], - "Members": [ - { - "Kind": "Method", - "Name": "Create", - "Parameters": [ - { - "Name": "resourceSource", - "Type": "System.Type" - } - ], - "ReturnType": "Microsoft.Extensions.Localization.IStringLocalizer", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "Create", - "Parameters": [ - { - "Name": "baseName", - "Type": "System.String" - }, - { - "Name": "location", - "Type": "System.String" - } - ], - "ReturnType": "Microsoft.Extensions.Localization.IStringLocalizer", - "GenericParameter": [] - } - ], - "GenericParameters": [] - }, - { - "Name": "Microsoft.Extensions.Localization.IStringLocalizer", - "Visibility": "Public", - "Kind": "Interface", - "Abstract": true, - "ImplementedInterfaces": [ - "Microsoft.Extensions.Localization.IStringLocalizer" - ], - "Members": [], - "GenericParameters": [ - { - "ParameterName": "T", - "ParameterPosition": 0, - "BaseTypeOrInterfaces": [] - } - ] - }, - { - "Name": "Microsoft.Extensions.Localization.LocalizedString", - "Visibility": "Public", - "Kind": "Class", - "ImplementedInterfaces": [], - "Members": [ - { - "Kind": "Method", - "Name": "op_Implicit", - "Parameters": [ - { - "Name": "localizedString", - "Type": "Microsoft.Extensions.Localization.LocalizedString" - } - ], - "ReturnType": "System.String", - "Static": true, - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "get_Name", - "Parameters": [], - "ReturnType": "System.String", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "get_Value", - "Parameters": [], - "ReturnType": "System.String", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "get_ResourceNotFound", - "Parameters": [], - "ReturnType": "System.Boolean", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "get_SearchedLocation", - "Parameters": [], - "ReturnType": "System.String", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "ToString", - "Parameters": [], - "ReturnType": "System.String", - "Virtual": true, - "Override": true, - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Constructor", - "Name": ".ctor", - "Parameters": [ - { - "Name": "name", - "Type": "System.String" - }, - { - "Name": "value", - "Type": "System.String" - } - ], - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Constructor", - "Name": ".ctor", - "Parameters": [ - { - "Name": "name", - "Type": "System.String" - }, - { - "Name": "value", - "Type": "System.String" - }, - { - "Name": "resourceNotFound", - "Type": "System.Boolean" - } - ], - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Constructor", - "Name": ".ctor", - "Parameters": [ - { - "Name": "name", - "Type": "System.String" - }, - { - "Name": "value", - "Type": "System.String" - }, - { - "Name": "resourceNotFound", - "Type": "System.Boolean" - }, - { - "Name": "searchedLocation", - "Type": "System.String" - } - ], - "Visibility": "Public", - "GenericParameter": [] - } - ], - "GenericParameters": [] - }, - { - "Name": "Microsoft.Extensions.Localization.StringLocalizerExtensions", - "Visibility": "Public", - "Kind": "Class", - "Abstract": true, - "Static": true, - "Sealed": true, - "ImplementedInterfaces": [], - "Members": [ - { - "Kind": "Method", - "Name": "GetString", - "Parameters": [ - { - "Name": "stringLocalizer", - "Type": "Microsoft.Extensions.Localization.IStringLocalizer" - }, - { - "Name": "name", - "Type": "System.String" - } - ], - "ReturnType": "Microsoft.Extensions.Localization.LocalizedString", - "Static": true, - "Extension": true, - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "GetString", - "Parameters": [ - { - "Name": "stringLocalizer", - "Type": "Microsoft.Extensions.Localization.IStringLocalizer" - }, - { - "Name": "name", - "Type": "System.String" - }, - { - "Name": "arguments", - "Type": "System.Object[]", - "IsParams": true - } - ], - "ReturnType": "Microsoft.Extensions.Localization.LocalizedString", - "Static": true, - "Extension": true, - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "GetAllStrings", - "Parameters": [ - { - "Name": "stringLocalizer", - "Type": "Microsoft.Extensions.Localization.IStringLocalizer" - } - ], - "ReturnType": "System.Collections.Generic.IEnumerable", - "Static": true, - "Extension": true, - "Visibility": "Public", - "GenericParameter": [] - } - ], - "GenericParameters": [] - }, - { - "Name": "Microsoft.Extensions.Localization.StringLocalizer", - "Visibility": "Public", - "Kind": "Class", - "ImplementedInterfaces": [ - "Microsoft.Extensions.Localization.IStringLocalizer" - ], - "Members": [ - { - "Kind": "Method", - "Name": "get_Item", - "Parameters": [ - { - "Name": "name", - "Type": "System.String" - } - ], - "ReturnType": "Microsoft.Extensions.Localization.LocalizedString", - "Virtual": true, - "ImplementedInterface": "Microsoft.Extensions.Localization.IStringLocalizer", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "get_Item", - "Parameters": [ - { - "Name": "name", - "Type": "System.String" - }, - { - "Name": "arguments", - "Type": "System.Object[]", - "IsParams": true - } - ], - "ReturnType": "Microsoft.Extensions.Localization.LocalizedString", - "Virtual": true, - "ImplementedInterface": "Microsoft.Extensions.Localization.IStringLocalizer", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "GetAllStrings", - "Parameters": [ - { - "Name": "includeParentCultures", - "Type": "System.Boolean" - } - ], - "ReturnType": "System.Collections.Generic.IEnumerable", - "Sealed": true, - "Virtual": true, - "ImplementedInterface": "Microsoft.Extensions.Localization.IStringLocalizer", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "WithCulture", - "Parameters": [ - { - "Name": "culture", - "Type": "System.Globalization.CultureInfo" - } - ], - "ReturnType": "Microsoft.Extensions.Localization.IStringLocalizer", - "Virtual": true, - "ImplementedInterface": "Microsoft.Extensions.Localization.IStringLocalizer", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Constructor", - "Name": ".ctor", - "Parameters": [ - { - "Name": "factory", - "Type": "Microsoft.Extensions.Localization.IStringLocalizerFactory" - } - ], - "Visibility": "Public", - "GenericParameter": [] - } - ], - "GenericParameters": [ - { - "ParameterName": "TResourceSource", - "ParameterPosition": 0, - "BaseTypeOrInterfaces": [] - } - ] - } - ] -} \ No newline at end of file diff --git a/src/Localization/Localization/src/Microsoft.Extensions.Localization.csproj b/src/Localization/Localization/src/Microsoft.Extensions.Localization.csproj index 8a820ac7b9..f16cbd9dab 100644 --- a/src/Localization/Localization/src/Microsoft.Extensions.Localization.csproj +++ b/src/Localization/Localization/src/Microsoft.Extensions.Localization.csproj @@ -7,7 +7,7 @@ $(NoWarn);CS1591 true localization - true + true diff --git a/src/Localization/Localization/src/baseline.netcore.json b/src/Localization/Localization/src/baseline.netcore.json deleted file mode 100644 index 860db76899..0000000000 --- a/src/Localization/Localization/src/baseline.netcore.json +++ /dev/null @@ -1,687 +0,0 @@ -{ - "AssemblyIdentity": "Microsoft.Extensions.Localization, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", - "Types": [ - { - "Name": "Microsoft.Extensions.DependencyInjection.LocalizationServiceCollectionExtensions", - "Visibility": "Public", - "Kind": "Class", - "Abstract": true, - "Static": true, - "Sealed": true, - "ImplementedInterfaces": [], - "Members": [ - { - "Kind": "Method", - "Name": "AddLocalization", - "Parameters": [ - { - "Name": "services", - "Type": "Microsoft.Extensions.DependencyInjection.IServiceCollection" - } - ], - "ReturnType": "Microsoft.Extensions.DependencyInjection.IServiceCollection", - "Static": true, - "Extension": true, - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "AddLocalization", - "Parameters": [ - { - "Name": "services", - "Type": "Microsoft.Extensions.DependencyInjection.IServiceCollection" - }, - { - "Name": "setupAction", - "Type": "System.Action" - } - ], - "ReturnType": "Microsoft.Extensions.DependencyInjection.IServiceCollection", - "Static": true, - "Extension": true, - "Visibility": "Public", - "GenericParameter": [] - } - ], - "GenericParameters": [] - }, - { - "Name": "Microsoft.Extensions.Localization.IResourceNamesCache", - "Visibility": "Public", - "Kind": "Interface", - "Abstract": true, - "ImplementedInterfaces": [], - "Members": [ - { - "Kind": "Method", - "Name": "GetOrAdd", - "Parameters": [ - { - "Name": "name", - "Type": "System.String" - }, - { - "Name": "valueFactory", - "Type": "System.Func>" - } - ], - "ReturnType": "System.Collections.Generic.IList", - "GenericParameter": [] - } - ], - "GenericParameters": [] - }, - { - "Name": "Microsoft.Extensions.Localization.LocalizationOptions", - "Visibility": "Public", - "Kind": "Class", - "ImplementedInterfaces": [], - "Members": [ - { - "Kind": "Method", - "Name": "get_ResourcesPath", - "Parameters": [], - "ReturnType": "System.String", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "set_ResourcesPath", - "Parameters": [ - { - "Name": "value", - "Type": "System.String" - } - ], - "ReturnType": "System.Void", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Constructor", - "Name": ".ctor", - "Parameters": [], - "Visibility": "Public", - "GenericParameter": [] - } - ], - "GenericParameters": [] - }, - { - "Name": "Microsoft.Extensions.Localization.ResourceLocationAttribute", - "Visibility": "Public", - "Kind": "Class", - "BaseType": "System.Attribute", - "ImplementedInterfaces": [], - "Members": [ - { - "Kind": "Method", - "Name": "get_ResourceLocation", - "Parameters": [], - "ReturnType": "System.String", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Constructor", - "Name": ".ctor", - "Parameters": [ - { - "Name": "resourceLocation", - "Type": "System.String" - } - ], - "Visibility": "Public", - "GenericParameter": [] - } - ], - "GenericParameters": [] - }, - { - "Name": "Microsoft.Extensions.Localization.ResourceManagerStringLocalizer", - "Visibility": "Public", - "Kind": "Class", - "ImplementedInterfaces": [ - "Microsoft.Extensions.Localization.IStringLocalizer" - ], - "Members": [ - { - "Kind": "Method", - "Name": "get_Item", - "Parameters": [ - { - "Name": "name", - "Type": "System.String" - } - ], - "ReturnType": "Microsoft.Extensions.Localization.LocalizedString", - "Virtual": true, - "ImplementedInterface": "Microsoft.Extensions.Localization.IStringLocalizer", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "get_Item", - "Parameters": [ - { - "Name": "name", - "Type": "System.String" - }, - { - "Name": "arguments", - "Type": "System.Object[]", - "IsParams": true - } - ], - "ReturnType": "Microsoft.Extensions.Localization.LocalizedString", - "Virtual": true, - "ImplementedInterface": "Microsoft.Extensions.Localization.IStringLocalizer", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "WithCulture", - "Parameters": [ - { - "Name": "culture", - "Type": "System.Globalization.CultureInfo" - } - ], - "ReturnType": "Microsoft.Extensions.Localization.IStringLocalizer", - "Sealed": true, - "Virtual": true, - "ImplementedInterface": "Microsoft.Extensions.Localization.IStringLocalizer", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "GetAllStrings", - "Parameters": [ - { - "Name": "includeParentCultures", - "Type": "System.Boolean" - } - ], - "ReturnType": "System.Collections.Generic.IEnumerable", - "Virtual": true, - "ImplementedInterface": "Microsoft.Extensions.Localization.IStringLocalizer", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "GetAllStrings", - "Parameters": [ - { - "Name": "includeParentCultures", - "Type": "System.Boolean" - }, - { - "Name": "culture", - "Type": "System.Globalization.CultureInfo" - } - ], - "ReturnType": "System.Collections.Generic.IEnumerable", - "Visibility": "Protected", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "GetStringSafely", - "Parameters": [ - { - "Name": "name", - "Type": "System.String" - }, - { - "Name": "culture", - "Type": "System.Globalization.CultureInfo" - } - ], - "ReturnType": "System.String", - "Visibility": "Protected", - "GenericParameter": [] - }, - { - "Kind": "Constructor", - "Name": ".ctor", - "Parameters": [ - { - "Name": "resourceManager", - "Type": "System.Resources.ResourceManager" - }, - { - "Name": "resourceAssembly", - "Type": "System.Reflection.Assembly" - }, - { - "Name": "baseName", - "Type": "System.String" - }, - { - "Name": "resourceNamesCache", - "Type": "Microsoft.Extensions.Localization.IResourceNamesCache" - }, - { - "Name": "logger", - "Type": "Microsoft.Extensions.Logging.ILogger" - } - ], - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Constructor", - "Name": ".ctor", - "Parameters": [ - { - "Name": "resourceManager", - "Type": "System.Resources.ResourceManager" - }, - { - "Name": "resourceAssemblyWrapper", - "Type": "Microsoft.Extensions.Localization.Internal.AssemblyWrapper" - }, - { - "Name": "baseName", - "Type": "System.String" - }, - { - "Name": "resourceNamesCache", - "Type": "Microsoft.Extensions.Localization.IResourceNamesCache" - }, - { - "Name": "logger", - "Type": "Microsoft.Extensions.Logging.ILogger" - } - ], - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Constructor", - "Name": ".ctor", - "Parameters": [ - { - "Name": "resourceManager", - "Type": "System.Resources.ResourceManager" - }, - { - "Name": "resourceStringProvider", - "Type": "Microsoft.Extensions.Localization.Internal.IResourceStringProvider" - }, - { - "Name": "baseName", - "Type": "System.String" - }, - { - "Name": "resourceNamesCache", - "Type": "Microsoft.Extensions.Localization.IResourceNamesCache" - }, - { - "Name": "logger", - "Type": "Microsoft.Extensions.Logging.ILogger" - } - ], - "Visibility": "Public", - "GenericParameter": [] - } - ], - "GenericParameters": [] - }, - { - "Name": "Microsoft.Extensions.Localization.ResourceManagerStringLocalizerFactory", - "Visibility": "Public", - "Kind": "Class", - "ImplementedInterfaces": [ - "Microsoft.Extensions.Localization.IStringLocalizerFactory" - ], - "Members": [ - { - "Kind": "Method", - "Name": "GetResourcePrefix", - "Parameters": [ - { - "Name": "typeInfo", - "Type": "System.Reflection.TypeInfo" - } - ], - "ReturnType": "System.String", - "Virtual": true, - "Visibility": "Protected", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "GetResourcePrefix", - "Parameters": [ - { - "Name": "typeInfo", - "Type": "System.Reflection.TypeInfo" - }, - { - "Name": "baseNamespace", - "Type": "System.String" - }, - { - "Name": "resourcesRelativePath", - "Type": "System.String" - } - ], - "ReturnType": "System.String", - "Virtual": true, - "Visibility": "Protected", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "GetResourcePrefix", - "Parameters": [ - { - "Name": "baseResourceName", - "Type": "System.String" - }, - { - "Name": "baseNamespace", - "Type": "System.String" - } - ], - "ReturnType": "System.String", - "Virtual": true, - "Visibility": "Protected", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "Create", - "Parameters": [ - { - "Name": "resourceSource", - "Type": "System.Type" - } - ], - "ReturnType": "Microsoft.Extensions.Localization.IStringLocalizer", - "Sealed": true, - "Virtual": true, - "ImplementedInterface": "Microsoft.Extensions.Localization.IStringLocalizerFactory", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "Create", - "Parameters": [ - { - "Name": "baseName", - "Type": "System.String" - }, - { - "Name": "location", - "Type": "System.String" - } - ], - "ReturnType": "Microsoft.Extensions.Localization.IStringLocalizer", - "Sealed": true, - "Virtual": true, - "ImplementedInterface": "Microsoft.Extensions.Localization.IStringLocalizerFactory", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "CreateResourceManagerStringLocalizer", - "Parameters": [ - { - "Name": "assembly", - "Type": "System.Reflection.Assembly" - }, - { - "Name": "baseName", - "Type": "System.String" - } - ], - "ReturnType": "Microsoft.Extensions.Localization.ResourceManagerStringLocalizer", - "Virtual": true, - "Visibility": "Protected", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "GetResourcePrefix", - "Parameters": [ - { - "Name": "location", - "Type": "System.String" - }, - { - "Name": "baseName", - "Type": "System.String" - }, - { - "Name": "resourceLocation", - "Type": "System.String" - } - ], - "ReturnType": "System.String", - "Virtual": true, - "Visibility": "Protected", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "GetResourceLocationAttribute", - "Parameters": [ - { - "Name": "assembly", - "Type": "System.Reflection.Assembly" - } - ], - "ReturnType": "Microsoft.Extensions.Localization.ResourceLocationAttribute", - "Virtual": true, - "Visibility": "Protected", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "GetRootNamespaceAttribute", - "Parameters": [ - { - "Name": "assembly", - "Type": "System.Reflection.Assembly" - } - ], - "ReturnType": "Microsoft.Extensions.Localization.RootNamespaceAttribute", - "Virtual": true, - "Visibility": "Protected", - "GenericParameter": [] - }, - { - "Kind": "Constructor", - "Name": ".ctor", - "Parameters": [ - { - "Name": "localizationOptions", - "Type": "Microsoft.Extensions.Options.IOptions" - }, - { - "Name": "loggerFactory", - "Type": "Microsoft.Extensions.Logging.ILoggerFactory" - } - ], - "Visibility": "Public", - "GenericParameter": [] - } - ], - "GenericParameters": [] - }, - { - "Name": "Microsoft.Extensions.Localization.ResourceManagerWithCultureStringLocalizer", - "Visibility": "Public", - "Kind": "Class", - "BaseType": "Microsoft.Extensions.Localization.ResourceManagerStringLocalizer", - "ImplementedInterfaces": [], - "Members": [ - { - "Kind": "Method", - "Name": "get_Item", - "Parameters": [ - { - "Name": "name", - "Type": "System.String" - } - ], - "ReturnType": "Microsoft.Extensions.Localization.LocalizedString", - "Virtual": true, - "Override": true, - "ImplementedInterface": "Microsoft.Extensions.Localization.IStringLocalizer", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "get_Item", - "Parameters": [ - { - "Name": "name", - "Type": "System.String" - }, - { - "Name": "arguments", - "Type": "System.Object[]", - "IsParams": true - } - ], - "ReturnType": "Microsoft.Extensions.Localization.LocalizedString", - "Virtual": true, - "Override": true, - "ImplementedInterface": "Microsoft.Extensions.Localization.IStringLocalizer", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "GetAllStrings", - "Parameters": [ - { - "Name": "includeParentCultures", - "Type": "System.Boolean" - } - ], - "ReturnType": "System.Collections.Generic.IEnumerable", - "Virtual": true, - "Override": true, - "ImplementedInterface": "Microsoft.Extensions.Localization.IStringLocalizer", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Constructor", - "Name": ".ctor", - "Parameters": [ - { - "Name": "resourceManager", - "Type": "System.Resources.ResourceManager" - }, - { - "Name": "resourceAssembly", - "Type": "System.Reflection.Assembly" - }, - { - "Name": "baseName", - "Type": "System.String" - }, - { - "Name": "resourceNamesCache", - "Type": "Microsoft.Extensions.Localization.IResourceNamesCache" - }, - { - "Name": "culture", - "Type": "System.Globalization.CultureInfo" - }, - { - "Name": "logger", - "Type": "Microsoft.Extensions.Logging.ILogger" - } - ], - "Visibility": "Public", - "GenericParameter": [] - } - ], - "GenericParameters": [] - }, - { - "Name": "Microsoft.Extensions.Localization.ResourceNamesCache", - "Visibility": "Public", - "Kind": "Class", - "ImplementedInterfaces": [ - "Microsoft.Extensions.Localization.IResourceNamesCache" - ], - "Members": [ - { - "Kind": "Method", - "Name": "GetOrAdd", - "Parameters": [ - { - "Name": "name", - "Type": "System.String" - }, - { - "Name": "valueFactory", - "Type": "System.Func>" - } - ], - "ReturnType": "System.Collections.Generic.IList", - "Sealed": true, - "Virtual": true, - "ImplementedInterface": "Microsoft.Extensions.Localization.IResourceNamesCache", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Constructor", - "Name": ".ctor", - "Parameters": [], - "Visibility": "Public", - "GenericParameter": [] - } - ], - "GenericParameters": [] - }, - { - "Name": "Microsoft.Extensions.Localization.RootNamespaceAttribute", - "Visibility": "Public", - "Kind": "Class", - "BaseType": "System.Attribute", - "ImplementedInterfaces": [], - "Members": [ - { - "Kind": "Method", - "Name": "get_RootNamespace", - "Parameters": [], - "ReturnType": "System.String", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Constructor", - "Name": ".ctor", - "Parameters": [ - { - "Name": "rootNamespace", - "Type": "System.String" - } - ], - "Visibility": "Public", - "GenericParameter": [] - } - ], - "GenericParameters": [] - } - ] -} \ No newline at end of file diff --git a/src/ObjectPool/Directory.Build.props b/src/ObjectPool/Directory.Build.props deleted file mode 100644 index f25c1d90ce..0000000000 --- a/src/ObjectPool/Directory.Build.props +++ /dev/null @@ -1,7 +0,0 @@ - - - - - true - - diff --git a/src/ObjectPool/src/Microsoft.Extensions.ObjectPool.csproj b/src/ObjectPool/src/Microsoft.Extensions.ObjectPool.csproj index 646d18a8b7..71e9abed79 100644 --- a/src/ObjectPool/src/Microsoft.Extensions.ObjectPool.csproj +++ b/src/ObjectPool/src/Microsoft.Extensions.ObjectPool.csproj @@ -6,7 +6,7 @@ $(NoWarn);CS1591 true pooling - true + true diff --git a/src/ObjectPool/src/baseline.netcore.json b/src/ObjectPool/src/baseline.netcore.json deleted file mode 100644 index 253c1f6b66..0000000000 --- a/src/ObjectPool/src/baseline.netcore.json +++ /dev/null @@ -1,612 +0,0 @@ -{ - "AssemblyIdentity": "Microsoft.Extensions.ObjectPool, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", - "Types": [ - { - "Name": "Microsoft.Extensions.ObjectPool.DefaultObjectPool", - "Visibility": "Public", - "Kind": "Class", - "BaseType": "Microsoft.Extensions.ObjectPool.ObjectPool", - "ImplementedInterfaces": [], - "Members": [ - { - "Kind": "Method", - "Name": "Get", - "Parameters": [], - "ReturnType": "T0", - "Virtual": true, - "Override": true, - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "Return", - "Parameters": [ - { - "Name": "obj", - "Type": "T0" - } - ], - "ReturnType": "System.Void", - "Virtual": true, - "Override": true, - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Constructor", - "Name": ".ctor", - "Parameters": [ - { - "Name": "policy", - "Type": "Microsoft.Extensions.ObjectPool.IPooledObjectPolicy" - } - ], - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Constructor", - "Name": ".ctor", - "Parameters": [ - { - "Name": "policy", - "Type": "Microsoft.Extensions.ObjectPool.IPooledObjectPolicy" - }, - { - "Name": "maximumRetained", - "Type": "System.Int32" - } - ], - "Visibility": "Public", - "GenericParameter": [] - } - ], - "GenericParameters": [ - { - "ParameterName": "T", - "ParameterPosition": 0, - "Class": true, - "BaseTypeOrInterfaces": [] - } - ] - }, - { - "Name": "Microsoft.Extensions.ObjectPool.DefaultObjectPoolProvider", - "Visibility": "Public", - "Kind": "Class", - "BaseType": "Microsoft.Extensions.ObjectPool.ObjectPoolProvider", - "ImplementedInterfaces": [], - "Members": [ - { - "Kind": "Method", - "Name": "get_MaximumRetained", - "Parameters": [], - "ReturnType": "System.Int32", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "set_MaximumRetained", - "Parameters": [ - { - "Name": "value", - "Type": "System.Int32" - } - ], - "ReturnType": "System.Void", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "Create", - "Parameters": [ - { - "Name": "policy", - "Type": "Microsoft.Extensions.ObjectPool.IPooledObjectPolicy" - } - ], - "ReturnType": "Microsoft.Extensions.ObjectPool.ObjectPool", - "Virtual": true, - "Override": true, - "Visibility": "Public", - "GenericParameter": [ - { - "ParameterName": "T", - "ParameterPosition": 0, - "Class": true, - "BaseTypeOrInterfaces": [] - } - ] - }, - { - "Kind": "Constructor", - "Name": ".ctor", - "Parameters": [], - "Visibility": "Public", - "GenericParameter": [] - } - ], - "GenericParameters": [] - }, - { - "Name": "Microsoft.Extensions.ObjectPool.DefaultPooledObjectPolicy", - "Visibility": "Public", - "Kind": "Class", - "BaseType": "Microsoft.Extensions.ObjectPool.PooledObjectPolicy", - "ImplementedInterfaces": [], - "Members": [ - { - "Kind": "Method", - "Name": "Create", - "Parameters": [], - "ReturnType": "T0", - "Virtual": true, - "Override": true, - "ImplementedInterface": "Microsoft.Extensions.ObjectPool.IPooledObjectPolicy", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "Return", - "Parameters": [ - { - "Name": "obj", - "Type": "T0" - } - ], - "ReturnType": "System.Boolean", - "Virtual": true, - "Override": true, - "ImplementedInterface": "Microsoft.Extensions.ObjectPool.IPooledObjectPolicy", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Constructor", - "Name": ".ctor", - "Parameters": [], - "Visibility": "Public", - "GenericParameter": [] - } - ], - "GenericParameters": [ - { - "ParameterName": "T", - "ParameterPosition": 0, - "New": true, - "Class": true, - "BaseTypeOrInterfaces": [] - } - ] - }, - { - "Name": "Microsoft.Extensions.ObjectPool.IPooledObjectPolicy", - "Visibility": "Public", - "Kind": "Interface", - "Abstract": true, - "ImplementedInterfaces": [], - "Members": [ - { - "Kind": "Method", - "Name": "Create", - "Parameters": [], - "ReturnType": "T0", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "Return", - "Parameters": [ - { - "Name": "obj", - "Type": "T0" - } - ], - "ReturnType": "System.Boolean", - "GenericParameter": [] - } - ], - "GenericParameters": [ - { - "ParameterName": "T", - "ParameterPosition": 0, - "BaseTypeOrInterfaces": [] - } - ] - }, - { - "Name": "Microsoft.Extensions.ObjectPool.LeakTrackingObjectPool", - "Visibility": "Public", - "Kind": "Class", - "BaseType": "Microsoft.Extensions.ObjectPool.ObjectPool", - "ImplementedInterfaces": [], - "Members": [ - { - "Kind": "Method", - "Name": "Get", - "Parameters": [], - "ReturnType": "T0", - "Virtual": true, - "Override": true, - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "Return", - "Parameters": [ - { - "Name": "obj", - "Type": "T0" - } - ], - "ReturnType": "System.Void", - "Virtual": true, - "Override": true, - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Constructor", - "Name": ".ctor", - "Parameters": [ - { - "Name": "inner", - "Type": "Microsoft.Extensions.ObjectPool.ObjectPool" - } - ], - "Visibility": "Public", - "GenericParameter": [] - } - ], - "GenericParameters": [ - { - "ParameterName": "T", - "ParameterPosition": 0, - "Class": true, - "BaseTypeOrInterfaces": [] - } - ] - }, - { - "Name": "Microsoft.Extensions.ObjectPool.LeakTrackingObjectPoolProvider", - "Visibility": "Public", - "Kind": "Class", - "BaseType": "Microsoft.Extensions.ObjectPool.ObjectPoolProvider", - "ImplementedInterfaces": [], - "Members": [ - { - "Kind": "Method", - "Name": "Create", - "Parameters": [ - { - "Name": "policy", - "Type": "Microsoft.Extensions.ObjectPool.IPooledObjectPolicy" - } - ], - "ReturnType": "Microsoft.Extensions.ObjectPool.ObjectPool", - "Virtual": true, - "Override": true, - "Visibility": "Public", - "GenericParameter": [ - { - "ParameterName": "T", - "ParameterPosition": 0, - "Class": true, - "BaseTypeOrInterfaces": [] - } - ] - }, - { - "Kind": "Constructor", - "Name": ".ctor", - "Parameters": [ - { - "Name": "inner", - "Type": "Microsoft.Extensions.ObjectPool.ObjectPoolProvider" - } - ], - "Visibility": "Public", - "GenericParameter": [] - } - ], - "GenericParameters": [] - }, - { - "Name": "Microsoft.Extensions.ObjectPool.ObjectPool", - "Visibility": "Public", - "Kind": "Class", - "Abstract": true, - "ImplementedInterfaces": [], - "Members": [ - { - "Kind": "Method", - "Name": "Get", - "Parameters": [], - "ReturnType": "T0", - "Virtual": true, - "Abstract": true, - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "Return", - "Parameters": [ - { - "Name": "obj", - "Type": "T0" - } - ], - "ReturnType": "System.Void", - "Virtual": true, - "Abstract": true, - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Constructor", - "Name": ".ctor", - "Parameters": [], - "Visibility": "Protected", - "GenericParameter": [] - } - ], - "GenericParameters": [ - { - "ParameterName": "T", - "ParameterPosition": 0, - "Class": true, - "BaseTypeOrInterfaces": [] - } - ] - }, - { - "Name": "Microsoft.Extensions.ObjectPool.ObjectPoolProvider", - "Visibility": "Public", - "Kind": "Class", - "Abstract": true, - "ImplementedInterfaces": [], - "Members": [ - { - "Kind": "Method", - "Name": "Create", - "Parameters": [ - { - "Name": "policy", - "Type": "Microsoft.Extensions.ObjectPool.IPooledObjectPolicy" - } - ], - "ReturnType": "Microsoft.Extensions.ObjectPool.ObjectPool", - "Virtual": true, - "Abstract": true, - "Visibility": "Public", - "GenericParameter": [ - { - "ParameterName": "T", - "ParameterPosition": 0, - "Class": true, - "BaseTypeOrInterfaces": [] - } - ] - }, - { - "Kind": "Method", - "Name": "Create", - "Parameters": [], - "ReturnType": "Microsoft.Extensions.ObjectPool.ObjectPool", - "Visibility": "Public", - "GenericParameter": [ - { - "ParameterName": "T", - "ParameterPosition": 0, - "New": true, - "Class": true, - "BaseTypeOrInterfaces": [] - } - ] - }, - { - "Kind": "Constructor", - "Name": ".ctor", - "Parameters": [], - "Visibility": "Protected", - "GenericParameter": [] - } - ], - "GenericParameters": [] - }, - { - "Name": "Microsoft.Extensions.ObjectPool.ObjectPoolProviderExtensions", - "Visibility": "Public", - "Kind": "Class", - "Abstract": true, - "Static": true, - "Sealed": true, - "ImplementedInterfaces": [], - "Members": [ - { - "Kind": "Method", - "Name": "CreateStringBuilderPool", - "Parameters": [ - { - "Name": "provider", - "Type": "Microsoft.Extensions.ObjectPool.ObjectPoolProvider" - } - ], - "ReturnType": "Microsoft.Extensions.ObjectPool.ObjectPool", - "Static": true, - "Extension": true, - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "CreateStringBuilderPool", - "Parameters": [ - { - "Name": "provider", - "Type": "Microsoft.Extensions.ObjectPool.ObjectPoolProvider" - }, - { - "Name": "initialCapacity", - "Type": "System.Int32" - }, - { - "Name": "maximumRetainedCapacity", - "Type": "System.Int32" - } - ], - "ReturnType": "Microsoft.Extensions.ObjectPool.ObjectPool", - "Static": true, - "Extension": true, - "Visibility": "Public", - "GenericParameter": [] - } - ], - "GenericParameters": [] - }, - { - "Name": "Microsoft.Extensions.ObjectPool.PooledObjectPolicy", - "Visibility": "Public", - "Kind": "Class", - "Abstract": true, - "ImplementedInterfaces": [ - "Microsoft.Extensions.ObjectPool.IPooledObjectPolicy" - ], - "Members": [ - { - "Kind": "Method", - "Name": "Create", - "Parameters": [], - "ReturnType": "T0", - "Virtual": true, - "Abstract": true, - "ImplementedInterface": "Microsoft.Extensions.ObjectPool.IPooledObjectPolicy", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "Return", - "Parameters": [ - { - "Name": "obj", - "Type": "T0" - } - ], - "ReturnType": "System.Boolean", - "Virtual": true, - "Abstract": true, - "ImplementedInterface": "Microsoft.Extensions.ObjectPool.IPooledObjectPolicy", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Constructor", - "Name": ".ctor", - "Parameters": [], - "Visibility": "Protected", - "GenericParameter": [] - } - ], - "GenericParameters": [ - { - "ParameterName": "T", - "ParameterPosition": 0, - "BaseTypeOrInterfaces": [] - } - ] - }, - { - "Name": "Microsoft.Extensions.ObjectPool.StringBuilderPooledObjectPolicy", - "Visibility": "Public", - "Kind": "Class", - "BaseType": "Microsoft.Extensions.ObjectPool.PooledObjectPolicy", - "ImplementedInterfaces": [], - "Members": [ - { - "Kind": "Method", - "Name": "Create", - "Parameters": [], - "ReturnType": "System.Text.StringBuilder", - "Virtual": true, - "Override": true, - "ImplementedInterface": "Microsoft.Extensions.ObjectPool.IPooledObjectPolicy", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "Return", - "Parameters": [ - { - "Name": "obj", - "Type": "System.Text.StringBuilder" - } - ], - "ReturnType": "System.Boolean", - "Virtual": true, - "Override": true, - "ImplementedInterface": "Microsoft.Extensions.ObjectPool.IPooledObjectPolicy", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "get_InitialCapacity", - "Parameters": [], - "ReturnType": "System.Int32", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "set_InitialCapacity", - "Parameters": [ - { - "Name": "value", - "Type": "System.Int32" - } - ], - "ReturnType": "System.Void", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "get_MaximumRetainedCapacity", - "Parameters": [], - "ReturnType": "System.Int32", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "set_MaximumRetainedCapacity", - "Parameters": [ - { - "Name": "value", - "Type": "System.Int32" - } - ], - "ReturnType": "System.Void", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Constructor", - "Name": ".ctor", - "Parameters": [], - "Visibility": "Public", - "GenericParameter": [] - } - ], - "GenericParameters": [] - } - ] -} \ No newline at end of file diff --git a/src/Testing/src/Microsoft.AspNetCore.Testing.csproj b/src/Testing/src/Microsoft.AspNetCore.Testing.csproj index 0e33852b62..b3b8046ccb 100644 --- a/src/Testing/src/Microsoft.AspNetCore.Testing.csproj +++ b/src/Testing/src/Microsoft.AspNetCore.Testing.csproj @@ -6,7 +6,6 @@ $(NoWarn);CS1591 true aspnetcore - false true false diff --git a/src/WebEncoders/Directory.Build.props b/src/WebEncoders/Directory.Build.props deleted file mode 100644 index f25c1d90ce..0000000000 --- a/src/WebEncoders/Directory.Build.props +++ /dev/null @@ -1,7 +0,0 @@ - - - - - true - - diff --git a/src/WebEncoders/src/Microsoft.Extensions.WebEncoders.csproj b/src/WebEncoders/src/Microsoft.Extensions.WebEncoders.csproj index bc73592b3f..dfc575c73f 100644 --- a/src/WebEncoders/src/Microsoft.Extensions.WebEncoders.csproj +++ b/src/WebEncoders/src/Microsoft.Extensions.WebEncoders.csproj @@ -7,7 +7,7 @@ true true aspnetcore - true + true diff --git a/src/WebEncoders/src/baseline.netcore.json b/src/WebEncoders/src/baseline.netcore.json deleted file mode 100644 index 6da0ae0754..0000000000 --- a/src/WebEncoders/src/baseline.netcore.json +++ /dev/null @@ -1,564 +0,0 @@ -{ - "AssemblyIdentity": "Microsoft.Extensions.WebEncoders, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", - "Types": [ - { - "Name": "Microsoft.Extensions.WebEncoders.WebEncoderOptions", - "Visibility": "Public", - "Kind": "Class", - "Sealed": true, - "ImplementedInterfaces": [], - "Members": [ - { - "Kind": "Method", - "Name": "get_TextEncoderSettings", - "Parameters": [], - "ReturnType": "System.Text.Encodings.Web.TextEncoderSettings", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "set_TextEncoderSettings", - "Parameters": [ - { - "Name": "value", - "Type": "System.Text.Encodings.Web.TextEncoderSettings" - } - ], - "ReturnType": "System.Void", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Constructor", - "Name": ".ctor", - "Parameters": [], - "Visibility": "Public", - "GenericParameter": [] - } - ], - "GenericParameters": [] - }, - { - "Name": "Microsoft.Extensions.WebEncoders.Testing.HtmlTestEncoder", - "Visibility": "Public", - "Kind": "Class", - "Sealed": true, - "BaseType": "System.Text.Encodings.Web.HtmlEncoder", - "ImplementedInterfaces": [], - "Members": [ - { - "Kind": "Method", - "Name": "get_MaxOutputCharactersPerInputCharacter", - "Parameters": [], - "ReturnType": "System.Int32", - "Virtual": true, - "Override": true, - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "Encode", - "Parameters": [ - { - "Name": "value", - "Type": "System.String" - } - ], - "ReturnType": "System.String", - "Virtual": true, - "Override": true, - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "Encode", - "Parameters": [ - { - "Name": "output", - "Type": "System.IO.TextWriter" - }, - { - "Name": "value", - "Type": "System.Char[]" - }, - { - "Name": "startIndex", - "Type": "System.Int32" - }, - { - "Name": "characterCount", - "Type": "System.Int32" - } - ], - "ReturnType": "System.Void", - "Virtual": true, - "Override": true, - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "Encode", - "Parameters": [ - { - "Name": "output", - "Type": "System.IO.TextWriter" - }, - { - "Name": "value", - "Type": "System.String" - }, - { - "Name": "startIndex", - "Type": "System.Int32" - }, - { - "Name": "characterCount", - "Type": "System.Int32" - } - ], - "ReturnType": "System.Void", - "Virtual": true, - "Override": true, - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "WillEncode", - "Parameters": [ - { - "Name": "unicodeScalar", - "Type": "System.Int32" - } - ], - "ReturnType": "System.Boolean", - "Virtual": true, - "Override": true, - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "FindFirstCharacterToEncode", - "Parameters": [ - { - "Name": "text", - "Type": "System.Char*" - }, - { - "Name": "textLength", - "Type": "System.Int32" - } - ], - "ReturnType": "System.Int32", - "Virtual": true, - "Override": true, - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "TryEncodeUnicodeScalar", - "Parameters": [ - { - "Name": "unicodeScalar", - "Type": "System.Int32" - }, - { - "Name": "buffer", - "Type": "System.Char*" - }, - { - "Name": "bufferLength", - "Type": "System.Int32" - }, - { - "Name": "numberOfCharactersWritten", - "Type": "System.Int32", - "Direction": "Out" - } - ], - "ReturnType": "System.Boolean", - "Virtual": true, - "Override": true, - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Constructor", - "Name": ".ctor", - "Parameters": [], - "Visibility": "Public", - "GenericParameter": [] - } - ], - "GenericParameters": [] - }, - { - "Name": "Microsoft.Extensions.WebEncoders.Testing.JavaScriptTestEncoder", - "Visibility": "Public", - "Kind": "Class", - "BaseType": "System.Text.Encodings.Web.JavaScriptEncoder", - "ImplementedInterfaces": [], - "Members": [ - { - "Kind": "Method", - "Name": "get_MaxOutputCharactersPerInputCharacter", - "Parameters": [], - "ReturnType": "System.Int32", - "Virtual": true, - "Override": true, - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "Encode", - "Parameters": [ - { - "Name": "value", - "Type": "System.String" - } - ], - "ReturnType": "System.String", - "Virtual": true, - "Override": true, - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "Encode", - "Parameters": [ - { - "Name": "output", - "Type": "System.IO.TextWriter" - }, - { - "Name": "value", - "Type": "System.Char[]" - }, - { - "Name": "startIndex", - "Type": "System.Int32" - }, - { - "Name": "characterCount", - "Type": "System.Int32" - } - ], - "ReturnType": "System.Void", - "Virtual": true, - "Override": true, - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "Encode", - "Parameters": [ - { - "Name": "output", - "Type": "System.IO.TextWriter" - }, - { - "Name": "value", - "Type": "System.String" - }, - { - "Name": "startIndex", - "Type": "System.Int32" - }, - { - "Name": "characterCount", - "Type": "System.Int32" - } - ], - "ReturnType": "System.Void", - "Virtual": true, - "Override": true, - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "WillEncode", - "Parameters": [ - { - "Name": "unicodeScalar", - "Type": "System.Int32" - } - ], - "ReturnType": "System.Boolean", - "Virtual": true, - "Override": true, - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "FindFirstCharacterToEncode", - "Parameters": [ - { - "Name": "text", - "Type": "System.Char*" - }, - { - "Name": "textLength", - "Type": "System.Int32" - } - ], - "ReturnType": "System.Int32", - "Virtual": true, - "Override": true, - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "TryEncodeUnicodeScalar", - "Parameters": [ - { - "Name": "unicodeScalar", - "Type": "System.Int32" - }, - { - "Name": "buffer", - "Type": "System.Char*" - }, - { - "Name": "bufferLength", - "Type": "System.Int32" - }, - { - "Name": "numberOfCharactersWritten", - "Type": "System.Int32", - "Direction": "Out" - } - ], - "ReturnType": "System.Boolean", - "Virtual": true, - "Override": true, - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Constructor", - "Name": ".ctor", - "Parameters": [], - "Visibility": "Public", - "GenericParameter": [] - } - ], - "GenericParameters": [] - }, - { - "Name": "Microsoft.Extensions.WebEncoders.Testing.UrlTestEncoder", - "Visibility": "Public", - "Kind": "Class", - "BaseType": "System.Text.Encodings.Web.UrlEncoder", - "ImplementedInterfaces": [], - "Members": [ - { - "Kind": "Method", - "Name": "get_MaxOutputCharactersPerInputCharacter", - "Parameters": [], - "ReturnType": "System.Int32", - "Virtual": true, - "Override": true, - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "Encode", - "Parameters": [ - { - "Name": "value", - "Type": "System.String" - } - ], - "ReturnType": "System.String", - "Virtual": true, - "Override": true, - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "Encode", - "Parameters": [ - { - "Name": "output", - "Type": "System.IO.TextWriter" - }, - { - "Name": "value", - "Type": "System.Char[]" - }, - { - "Name": "startIndex", - "Type": "System.Int32" - }, - { - "Name": "characterCount", - "Type": "System.Int32" - } - ], - "ReturnType": "System.Void", - "Virtual": true, - "Override": true, - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "Encode", - "Parameters": [ - { - "Name": "output", - "Type": "System.IO.TextWriter" - }, - { - "Name": "value", - "Type": "System.String" - }, - { - "Name": "startIndex", - "Type": "System.Int32" - }, - { - "Name": "characterCount", - "Type": "System.Int32" - } - ], - "ReturnType": "System.Void", - "Virtual": true, - "Override": true, - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "WillEncode", - "Parameters": [ - { - "Name": "unicodeScalar", - "Type": "System.Int32" - } - ], - "ReturnType": "System.Boolean", - "Virtual": true, - "Override": true, - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "FindFirstCharacterToEncode", - "Parameters": [ - { - "Name": "text", - "Type": "System.Char*" - }, - { - "Name": "textLength", - "Type": "System.Int32" - } - ], - "ReturnType": "System.Int32", - "Virtual": true, - "Override": true, - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "TryEncodeUnicodeScalar", - "Parameters": [ - { - "Name": "unicodeScalar", - "Type": "System.Int32" - }, - { - "Name": "buffer", - "Type": "System.Char*" - }, - { - "Name": "bufferLength", - "Type": "System.Int32" - }, - { - "Name": "numberOfCharactersWritten", - "Type": "System.Int32", - "Direction": "Out" - } - ], - "ReturnType": "System.Boolean", - "Virtual": true, - "Override": true, - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Constructor", - "Name": ".ctor", - "Parameters": [], - "Visibility": "Public", - "GenericParameter": [] - } - ], - "GenericParameters": [] - }, - { - "Name": "Microsoft.Extensions.DependencyInjection.EncoderServiceCollectionExtensions", - "Visibility": "Public", - "Kind": "Class", - "Abstract": true, - "Static": true, - "Sealed": true, - "ImplementedInterfaces": [], - "Members": [ - { - "Kind": "Method", - "Name": "AddWebEncoders", - "Parameters": [ - { - "Name": "services", - "Type": "Microsoft.Extensions.DependencyInjection.IServiceCollection" - } - ], - "ReturnType": "Microsoft.Extensions.DependencyInjection.IServiceCollection", - "Static": true, - "Extension": true, - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "AddWebEncoders", - "Parameters": [ - { - "Name": "services", - "Type": "Microsoft.Extensions.DependencyInjection.IServiceCollection" - }, - { - "Name": "setupAction", - "Type": "System.Action" - } - ], - "ReturnType": "Microsoft.Extensions.DependencyInjection.IServiceCollection", - "Static": true, - "Extension": true, - "Visibility": "Public", - "GenericParameter": [] - } - ], - "GenericParameters": [] - } - ] -} \ No newline at end of file From 070bba8ad4dfae6dc0a0e1df438592960365c35e Mon Sep 17 00:00:00 2001 From: Kahbazi Date: Wed, 9 Jan 2019 08:16:28 +0330 Subject: [PATCH 21/52] Add event name in localization\n\nCommit migrated from https://github.com/dotnet/extensions/commit/d2b1ad3d233a0750647ed2c73343a293f08c5709 --- .../Internal/ResourceManagerStringLocalizerLoggerExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Localization/Localization/src/Internal/ResourceManagerStringLocalizerLoggerExtensions.cs b/src/Localization/Localization/src/Internal/ResourceManagerStringLocalizerLoggerExtensions.cs index 456e07009e..63f40536ca 100644 --- a/src/Localization/Localization/src/Internal/ResourceManagerStringLocalizerLoggerExtensions.cs +++ b/src/Localization/Localization/src/Internal/ResourceManagerStringLocalizerLoggerExtensions.cs @@ -15,7 +15,7 @@ namespace Microsoft.Extensions.Localization.Internal { _searchedLocation = LoggerMessage.Define( LogLevel.Debug, - 1, + new EventId(1, "SearchedLocation"), $"{nameof(ResourceManagerStringLocalizer)} searched for '{{Key}}' in '{{LocationSearched}}' with culture '{{Culture}}'."); } From bbca5f30e729c957e3a89fd717a014c18fd5cc1f Mon Sep 17 00:00:00 2001 From: Nate McMaster Date: Thu, 31 Jan 2019 13:20:34 -0800 Subject: [PATCH 22/52] Remove implicit references for non-test projects (dotnet/extensions#1037) \n\nCommit migrated from https://github.com/dotnet/extensions/commit/e504b4ee74bba3a7df2be5612e8d509ad61b0c24 --- src/Testing/src/Microsoft.AspNetCore.Testing.csproj | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/Testing/src/Microsoft.AspNetCore.Testing.csproj b/src/Testing/src/Microsoft.AspNetCore.Testing.csproj index b3b8046ccb..9da267c419 100644 --- a/src/Testing/src/Microsoft.AspNetCore.Testing.csproj +++ b/src/Testing/src/Microsoft.AspNetCore.Testing.csproj @@ -6,6 +6,8 @@ $(NoWarn);CS1591 true aspnetcore + + false true false @@ -19,6 +21,15 @@ + + From a663bcf605510df7c7114fccaf9943e4595a2b00 Mon Sep 17 00:00:00 2001 From: Nate McMaster Date: Fri, 1 Feb 2019 10:33:04 -0800 Subject: [PATCH 23/52] Add code check CI tests, add docs about ReferenceResolution, and other cleanups (dotnet/extensions#1044) Changes: * Add a step which checks that generated code is up to date * Copy over the documentation on how to work with `` * Fixup a broken reference in the .sln * Fix the casing on Microsoft.Extensions.ValueStopwatch.Sources * Remove some unused references and variables\n\nCommit migrated from https://github.com/dotnet/extensions/commit/92acd8b95901f1e8f47d3d128cb5f7c38b684644 --- ...rosoft.Extensions.FileProviders.Embedded.Manifest.Task.csproj | 1 - ....Extensions.FileProviders.Embedded.Manifest.Task.Tests.csproj | 1 - 2 files changed, 2 deletions(-) diff --git a/src/FileProviders/Manifest.MSBuildTask/src/Microsoft.Extensions.FileProviders.Embedded.Manifest.Task.csproj b/src/FileProviders/Manifest.MSBuildTask/src/Microsoft.Extensions.FileProviders.Embedded.Manifest.Task.csproj index f8ae098eab..cdc4ffdcb0 100644 --- a/src/FileProviders/Manifest.MSBuildTask/src/Microsoft.Extensions.FileProviders.Embedded.Manifest.Task.csproj +++ b/src/FileProviders/Manifest.MSBuildTask/src/Microsoft.Extensions.FileProviders.Embedded.Manifest.Task.csproj @@ -11,7 +11,6 @@ - diff --git a/src/FileProviders/Manifest.MSBuildTask/test/Microsoft.Extensions.FileProviders.Embedded.Manifest.Task.Tests.csproj b/src/FileProviders/Manifest.MSBuildTask/test/Microsoft.Extensions.FileProviders.Embedded.Manifest.Task.Tests.csproj index 3f7647a4f1..ed68958fe8 100644 --- a/src/FileProviders/Manifest.MSBuildTask/test/Microsoft.Extensions.FileProviders.Embedded.Manifest.Task.Tests.csproj +++ b/src/FileProviders/Manifest.MSBuildTask/test/Microsoft.Extensions.FileProviders.Embedded.Manifest.Task.Tests.csproj @@ -9,7 +9,6 @@ - From b47bd1789403016d7a778a78402ae4e330e7da60 Mon Sep 17 00:00:00 2001 From: Pranav K Date: Fri, 1 Feb 2019 14:12:49 -0800 Subject: [PATCH 24/52] Publish Microsoft.Interop.Js and Mono.WebAssembly.Interop (dotnet/extensions#1048) * Publish Microsoft.Interop.Js and Mono.WebAssembly.Interop \n\nCommit migrated from https://github.com/dotnet/extensions/commit/bc2e7150f00198a160cb5b0452a294f30b78af76 --- .../src/Microsoft.JSInterop.JS.csproj | 13 - .../src/Microsoft.JSInterop.JS.npmproj | 12 + .../src/package-lock.json | 269 +++++++++++++++++- .../Microsoft.JSInterop.JS/src/package.json | 30 +- .../Microsoft.JSInterop.JS/src/tslint.json | 14 + .../src/Mono.WebAssembly.Interop.csproj | 5 +- 6 files changed, 304 insertions(+), 39 deletions(-) delete mode 100644 src/JSInterop/Microsoft.JSInterop.JS/src/Microsoft.JSInterop.JS.csproj create mode 100644 src/JSInterop/Microsoft.JSInterop.JS/src/Microsoft.JSInterop.JS.npmproj create mode 100644 src/JSInterop/Microsoft.JSInterop.JS/src/tslint.json diff --git a/src/JSInterop/Microsoft.JSInterop.JS/src/Microsoft.JSInterop.JS.csproj b/src/JSInterop/Microsoft.JSInterop.JS/src/Microsoft.JSInterop.JS.csproj deleted file mode 100644 index 7f806d0afa..0000000000 --- a/src/JSInterop/Microsoft.JSInterop.JS/src/Microsoft.JSInterop.JS.csproj +++ /dev/null @@ -1,13 +0,0 @@ - - - - netstandard2.0 - Latest - false - - - - - - - diff --git a/src/JSInterop/Microsoft.JSInterop.JS/src/Microsoft.JSInterop.JS.npmproj b/src/JSInterop/Microsoft.JSInterop.JS/src/Microsoft.JSInterop.JS.npmproj new file mode 100644 index 0000000000..e8d0554ff7 --- /dev/null +++ b/src/JSInterop/Microsoft.JSInterop.JS/src/Microsoft.JSInterop.JS.npmproj @@ -0,0 +1,12 @@ + + + + + @dotnet/jsinterop + true + false + true + + + + diff --git a/src/JSInterop/Microsoft.JSInterop.JS/src/package-lock.json b/src/JSInterop/Microsoft.JSInterop.JS/src/package-lock.json index a7f31ead4f..4c82321255 100644 --- a/src/JSInterop/Microsoft.JSInterop.JS/src/package-lock.json +++ b/src/JSInterop/Microsoft.JSInterop.JS/src/package-lock.json @@ -1,9 +1,56 @@ { "name": "@dotnet/jsinterop", - "version": "0.1.1", + "version": "3.0.0-dev", "lockfileVersion": 1, "requires": true, "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "babel-code-frame": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", + "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", + "dev": true, + "requires": { + "chalk": "^1.1.3", + "esutils": "^2.0.2", + "js-tokens": "^3.0.2" + }, + "dependencies": { + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + } + } + }, "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", @@ -16,16 +63,98 @@ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "requires": { - "balanced-match": "1.0.0", + "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, + "builtin-modules": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", + "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", + "dev": true + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "commander": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.19.0.tgz", + "integrity": "sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==", + "dev": true + }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true }, + "diff": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, + "esutils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", + "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", + "dev": true + }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -38,22 +167,37 @@ "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", "dev": true, "requires": { - "fs.realpath": "1.0.0", - "inflight": "1.0.6", - "inherits": "2.0.3", - "minimatch": "3.0.4", - "once": "1.4.0", - "path-is-absolute": "1.0.1" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" } }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", "dev": true, "requires": { - "once": "1.4.0", - "wrappy": "1.0.2" + "once": "^1.3.0", + "wrappy": "1" } }, "inherits": { @@ -62,13 +206,29 @@ "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", "dev": true }, + "js-tokens": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", + "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", + "dev": true + }, + "js-yaml": { + "version": "3.12.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.1.tgz", + "integrity": "sha512-um46hB9wNOKlwkHgiuyEVAybXBjwFUV0Z/RaHJblRd9DXltue9FTYvzCr9ErQrK9Adz5MU4gHWVaNUfdmrC8qA==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, "requires": { - "brace-expansion": "1.1.11" + "brace-expansion": "^1.1.7" } }, "once": { @@ -77,7 +237,7 @@ "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "dev": true, "requires": { - "wrappy": "1.0.2" + "wrappy": "1" } }, "path-is-absolute": { @@ -86,15 +246,98 @@ "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "dev": true }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "dev": true + }, + "resolve": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.10.0.tgz", + "integrity": "sha512-3sUr9aq5OfSg2S9pNtPA9hL1FVEAjvfOC4leW0SNf/mpnaakz2a9femSd6LqAww2RaFctwyf1lCqnTHuF1rxDg==", + "dev": true, + "requires": { + "path-parse": "^1.0.6" + } + }, "rimraf": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", "dev": true, "requires": { - "glob": "7.1.3" + "glob": "^7.0.5" } }, + "semver": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", + "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==", + "dev": true + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + }, + "tslib": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", + "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==", + "dev": true + }, + "tslint": { + "version": "5.12.1", + "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.12.1.tgz", + "integrity": "sha512-sfodBHOucFg6egff8d1BvuofoOQ/nOeYNfbp7LDlKBcLNrL3lmS5zoiDGyOMdT7YsEXAwWpTdAHwOGOc8eRZAw==", + "dev": true, + "requires": { + "babel-code-frame": "^6.22.0", + "builtin-modules": "^1.1.1", + "chalk": "^2.3.0", + "commander": "^2.12.1", + "diff": "^3.2.0", + "glob": "^7.1.1", + "js-yaml": "^3.7.0", + "minimatch": "^3.0.4", + "resolve": "^1.3.2", + "semver": "^5.3.0", + "tslib": "^1.8.0", + "tsutils": "^2.27.2" + } + }, + "tsutils": { + "version": "2.29.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz", + "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + } + }, + "typescript": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.9.2.tgz", + "integrity": "sha512-Gr4p6nFNaoufRIY4NMdpQRNmgxVIGMs4Fcu/ujdYk3nAZqk7supzBE9idmvfZIlH/Cuj//dvi+019qEue9lV0w==", + "dev": true + }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", diff --git a/src/JSInterop/Microsoft.JSInterop.JS/src/package.json b/src/JSInterop/Microsoft.JSInterop.JS/src/package.json index a84734bc46..8be950e16f 100644 --- a/src/JSInterop/Microsoft.JSInterop.JS/src/package.json +++ b/src/JSInterop/Microsoft.JSInterop.JS/src/package.json @@ -1,25 +1,31 @@ { "name": "@dotnet/jsinterop", - "version": "0.1.1", + "version": "3.0.0-dev", "description": "Provides abstractions and features for interop between .NET and JavaScript code.", "main": "dist/Microsoft.JSInterop.js", "types": "dist/Microsoft.JSInterop.d.js", "scripts": { - "prepublish": "rimraf dist && dotnet build && echo 'Finished building NPM package \"@dotnet/jsinterop\"'" - }, - "files": [ - "dist/**" - ], - "author": "Microsoft", - "license": "Apache-2.0", - "bugs": { - "url": "https://github.com/dotnet/jsinterop/issues" + "clean": "node node_modules/rimraf/bin.js ./dist", + "build": "npm run clean && npm run build:esm", + "build:lint": "node node_modules/tslint/bin/tslint -p ./tsconfig.json", + "build:esm": "node node_modules/typescript/bin/tsc --project ./tsconfig.json" }, "repository": { "type": "git", - "url": "https://github.com/dotnet/jsinterop.git" + "url": "git+https://github.com/aspnet/AspNetCore.git" }, + "author": "Microsoft", + "license": "Apache-2.0", + "bugs": { + "url": "https://github.com/aspnet/AspNetCore/issues" + }, + "homepage": "https://github.com/aspnet/Extensions/tree/master/src/JSInterop#readme", + "files": [ + "dist/**" + ], "devDependencies": { - "rimraf": "^2.5.4" + "rimraf": "^2.5.4", + "tslint": "^5.9.1", + "typescript": "^2.7.1" } } diff --git a/src/JSInterop/Microsoft.JSInterop.JS/src/tslint.json b/src/JSInterop/Microsoft.JSInterop.JS/src/tslint.json new file mode 100644 index 0000000000..5c38bef990 --- /dev/null +++ b/src/JSInterop/Microsoft.JSInterop.JS/src/tslint.json @@ -0,0 +1,14 @@ +{ + "extends": "tslint:recommended", + "rules": { + "max-line-length": { "options": [300] }, + "member-ordering": false, + "interface-name": false, + "unified-signatures": false, + "max-classes-per-file": false, + "no-floating-promises": true, + "no-empty": false, + "no-bitwise": false, + "no-console": false + } +} diff --git a/src/JSInterop/Mono.WebAssembly.Interop/src/Mono.WebAssembly.Interop.csproj b/src/JSInterop/Mono.WebAssembly.Interop/src/Mono.WebAssembly.Interop.csproj index 0ad8effd80..413d084e48 100644 --- a/src/JSInterop/Mono.WebAssembly.Interop/src/Mono.WebAssembly.Interop.csproj +++ b/src/JSInterop/Mono.WebAssembly.Interop/src/Mono.WebAssembly.Interop.csproj @@ -2,7 +2,10 @@ netstandard2.0 - false + Abstractions and features for interop between Mono WebAssembly and JavaScript code. + wasm;javascript;interop + true + true From 2c0287d686dc7becb6aa20d097d78505e0280f8d Mon Sep 17 00:00:00 2001 From: marciomyst Date: Fri, 8 Feb 2019 15:35:13 -0200 Subject: [PATCH 25/52] Fix ignored exception parameter on Degraded method The 'exception' parameter was ignored and a hardcoded null value was used on HealthCheckResult.Degraded\n\nCommit migrated from https://github.com/dotnet/extensions/commit/dbfc2dee3a582b581f91386cff9a356fb111ca37 --- src/HealthChecks/Abstractions/src/HealthCheckResult.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/HealthChecks/Abstractions/src/HealthCheckResult.cs b/src/HealthChecks/Abstractions/src/HealthCheckResult.cs index e01cb5aceb..7f4522da19 100644 --- a/src/HealthChecks/Abstractions/src/HealthCheckResult.cs +++ b/src/HealthChecks/Abstractions/src/HealthCheckResult.cs @@ -70,7 +70,7 @@ namespace Microsoft.Extensions.Diagnostics.HealthChecks /// A representing a degraged component. public static HealthCheckResult Degraded(string description = null, Exception exception = null, IReadOnlyDictionary data = null) { - return new HealthCheckResult(status: HealthStatus.Degraded, description, exception: null, data); + return new HealthCheckResult(status: HealthStatus.Degraded, description, exception: exception, data); } /// From 39b6a19f90b13581717a662654dc5d5ded7bfa06 Mon Sep 17 00:00:00 2001 From: Pavel Krymets Date: Mon, 11 Feb 2019 12:10:15 -0800 Subject: [PATCH 26/52] Add reference assembly generations support (dotnet/extensions#1093) \n\nCommit migrated from https://github.com/dotnet/extensions/commit/8ad1395c9181c0f370ac8012760984541f557eee --- ...Extensions.Configuration.KeyPerFile.csproj | 12 +++ ...Configuration.KeyPerFile.netstandard2.0.cs | 29 ++++++ ...t.Extensions.FileProviders.Embedded.csproj | 11 +++ ...s.FileProviders.Embedded.netstandard2.0.cs | 39 ++++++++ ...agnostics.HealthChecks.Abstractions.csproj | 11 +++ ...ealthChecks.Abstractions.netstandard2.0.cs | 67 ++++++++++++++ ...Extensions.Diagnostics.HealthChecks.csproj | 13 +++ ...Diagnostics.HealthChecks.netstandard2.0.cs | 52 +++++++++++ .../ref/Microsoft.JSInterop.csproj | 11 +++ .../ref/Microsoft.JSInterop.netstandard2.0.cs | 76 ++++++++++++++++ .../ref/Mono.WebAssembly.Interop.csproj | 11 +++ ...Mono.WebAssembly.Interop.netstandard2.0.cs | 16 ++++ ...xtensions.Localization.Abstractions.csproj | 11 +++ ...ocalization.Abstractions.netstandard2.0.cs | 47 ++++++++++ .../Microsoft.Extensions.Localization.csproj | 14 +++ ....Extensions.Localization.netstandard2.0.cs | 91 +++++++++++++++++++ .../Microsoft.Extensions.ObjectPool.csproj | 11 +++ ...ft.Extensions.ObjectPool.netstandard2.0.cs | 72 +++++++++++++++ src/WebEncoders/Directory.Build.props | 8 ++ .../Microsoft.Extensions.WebEncoders.csproj | 13 +++ ...t.Extensions.WebEncoders.netstandard2.0.cs | 55 +++++++++++ .../Microsoft.Extensions.WebEncoders.csproj | 1 - 22 files changed, 670 insertions(+), 1 deletion(-) create mode 100644 src/Configuration.KeyPerFile/ref/Microsoft.Extensions.Configuration.KeyPerFile.csproj create mode 100644 src/Configuration.KeyPerFile/ref/Microsoft.Extensions.Configuration.KeyPerFile.netstandard2.0.cs create mode 100644 src/FileProviders/Embedded/ref/Microsoft.Extensions.FileProviders.Embedded.csproj create mode 100644 src/FileProviders/Embedded/ref/Microsoft.Extensions.FileProviders.Embedded.netstandard2.0.cs create mode 100644 src/HealthChecks/Abstractions/ref/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.csproj create mode 100644 src/HealthChecks/Abstractions/ref/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.netstandard2.0.cs create mode 100644 src/HealthChecks/HealthChecks/ref/Microsoft.Extensions.Diagnostics.HealthChecks.csproj create mode 100644 src/HealthChecks/HealthChecks/ref/Microsoft.Extensions.Diagnostics.HealthChecks.netstandard2.0.cs create mode 100644 src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.csproj create mode 100644 src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netstandard2.0.cs create mode 100644 src/JSInterop/Mono.WebAssembly.Interop/ref/Mono.WebAssembly.Interop.csproj create mode 100644 src/JSInterop/Mono.WebAssembly.Interop/ref/Mono.WebAssembly.Interop.netstandard2.0.cs create mode 100644 src/Localization/Abstractions/ref/Microsoft.Extensions.Localization.Abstractions.csproj create mode 100644 src/Localization/Abstractions/ref/Microsoft.Extensions.Localization.Abstractions.netstandard2.0.cs create mode 100644 src/Localization/Localization/ref/Microsoft.Extensions.Localization.csproj create mode 100644 src/Localization/Localization/ref/Microsoft.Extensions.Localization.netstandard2.0.cs create mode 100644 src/ObjectPool/ref/Microsoft.Extensions.ObjectPool.csproj create mode 100644 src/ObjectPool/ref/Microsoft.Extensions.ObjectPool.netstandard2.0.cs create mode 100644 src/WebEncoders/Directory.Build.props create mode 100644 src/WebEncoders/ref/Microsoft.Extensions.WebEncoders.csproj create mode 100644 src/WebEncoders/ref/Microsoft.Extensions.WebEncoders.netstandard2.0.cs diff --git a/src/Configuration.KeyPerFile/ref/Microsoft.Extensions.Configuration.KeyPerFile.csproj b/src/Configuration.KeyPerFile/ref/Microsoft.Extensions.Configuration.KeyPerFile.csproj new file mode 100644 index 0000000000..3df73aba0c --- /dev/null +++ b/src/Configuration.KeyPerFile/ref/Microsoft.Extensions.Configuration.KeyPerFile.csproj @@ -0,0 +1,12 @@ + + + + netstandard2.0 + false + + + + + + + diff --git a/src/Configuration.KeyPerFile/ref/Microsoft.Extensions.Configuration.KeyPerFile.netstandard2.0.cs b/src/Configuration.KeyPerFile/ref/Microsoft.Extensions.Configuration.KeyPerFile.netstandard2.0.cs new file mode 100644 index 0000000000..e26ca1909d --- /dev/null +++ b/src/Configuration.KeyPerFile/ref/Microsoft.Extensions.Configuration.KeyPerFile.netstandard2.0.cs @@ -0,0 +1,29 @@ +// 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 configureSource) { throw null; } + public static Microsoft.Extensions.Configuration.IConfigurationBuilder AddKeyPerFile(this Microsoft.Extensions.Configuration.IConfigurationBuilder builder, string directoryPath, bool optional) { throw null; } + } +} +namespace Microsoft.Extensions.Configuration.KeyPerFile +{ + public partial class KeyPerFileConfigurationProvider : Microsoft.Extensions.Configuration.ConfigurationProvider + { + public KeyPerFileConfigurationProvider(Microsoft.Extensions.Configuration.KeyPerFile.KeyPerFileConfigurationSource source) { } + 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 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 Microsoft.Extensions.Configuration.IConfigurationProvider Build(Microsoft.Extensions.Configuration.IConfigurationBuilder builder) { throw null; } + } +} diff --git a/src/FileProviders/Embedded/ref/Microsoft.Extensions.FileProviders.Embedded.csproj b/src/FileProviders/Embedded/ref/Microsoft.Extensions.FileProviders.Embedded.csproj new file mode 100644 index 0000000000..ba6fddca2b --- /dev/null +++ b/src/FileProviders/Embedded/ref/Microsoft.Extensions.FileProviders.Embedded.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + false + + + + + + diff --git a/src/FileProviders/Embedded/ref/Microsoft.Extensions.FileProviders.Embedded.netstandard2.0.cs b/src/FileProviders/Embedded/ref/Microsoft.Extensions.FileProviders.Embedded.netstandard2.0.cs new file mode 100644 index 0000000000..1596f191fd --- /dev/null +++ b/src/FileProviders/Embedded/ref/Microsoft.Extensions.FileProviders.Embedded.netstandard2.0.cs @@ -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; } + } +} diff --git a/src/HealthChecks/Abstractions/ref/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.csproj b/src/HealthChecks/Abstractions/ref/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.csproj new file mode 100644 index 0000000000..201f8d515b --- /dev/null +++ b/src/HealthChecks/Abstractions/ref/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + false + + + + + + diff --git a/src/HealthChecks/Abstractions/ref/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.netstandard2.0.cs b/src/HealthChecks/Abstractions/ref/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.netstandard2.0.cs new file mode 100644 index 0000000000..c0117ef473 --- /dev/null +++ b/src/HealthChecks/Abstractions/ref/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.netstandard2.0.cs @@ -0,0 +1,67 @@ +// 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, System.Nullable failureStatus, System.Collections.Generic.IEnumerable tags) { } + public HealthCheckRegistration(string name, System.Func factory, System.Nullable failureStatus, System.Collections.Generic.IEnumerable tags) { } + public System.Func 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 Tags { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + } + [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 data = null) { throw null; } + public System.Collections.Generic.IReadOnlyDictionary 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 data = null) { throw null; } + public static Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckResult Healthy(string description = null, System.Collections.Generic.IReadOnlyDictionary data = null) { throw null; } + public static Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckResult Unhealthy(string description = null, System.Exception exception = null, System.Collections.Generic.IReadOnlyDictionary data = null) { throw null; } + } + public sealed partial class HealthReport + { + public HealthReport(System.Collections.Generic.IReadOnlyDictionary entries, System.TimeSpan totalDuration) { } + public System.Collections.Generic.IReadOnlyDictionary 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 data) { throw null; } + public System.Collections.Generic.IReadOnlyDictionary 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 enum HealthStatus + { + Degraded = 1, + Healthy = 2, + Unhealthy = 0, + } + public partial interface IHealthCheck + { + System.Threading.Tasks.Task 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); + } +} diff --git a/src/HealthChecks/HealthChecks/ref/Microsoft.Extensions.Diagnostics.HealthChecks.csproj b/src/HealthChecks/HealthChecks/ref/Microsoft.Extensions.Diagnostics.HealthChecks.csproj new file mode 100644 index 0000000000..925e89cfa0 --- /dev/null +++ b/src/HealthChecks/HealthChecks/ref/Microsoft.Extensions.Diagnostics.HealthChecks.csproj @@ -0,0 +1,13 @@ + + + + netstandard2.0 + false + + + + + + + + diff --git a/src/HealthChecks/HealthChecks/ref/Microsoft.Extensions.Diagnostics.HealthChecks.netstandard2.0.cs b/src/HealthChecks/HealthChecks/ref/Microsoft.Extensions.Diagnostics.HealthChecks.netstandard2.0.cs new file mode 100644 index 0000000000..89281584e2 --- /dev/null +++ b/src/HealthChecks/HealthChecks/ref/Microsoft.Extensions.Diagnostics.HealthChecks.netstandard2.0.cs @@ -0,0 +1,52 @@ +// 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, System.Nullable failureStatus = default(System.Nullable), System.Collections.Generic.IEnumerable tags = null) { throw null; } + public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, System.Nullable failureStatus = default(System.Nullable), System.Collections.Generic.IEnumerable tags = null) where T : class, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck { throw null; } + public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddTypeActivatedCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, System.Nullable failureStatus, System.Collections.Generic.IEnumerable tags, params object[] args) where T : class, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck { throw null; } + public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddTypeActivatedCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, System.Nullable failureStatus, params object[] args) where T : class, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck { throw null; } + public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddTypeActivatedCheck(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> check, System.Collections.Generic.IEnumerable tags = null) { throw null; } + public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddAsyncCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, System.Func> check, System.Collections.Generic.IEnumerable tags = null) { throw null; } + public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, System.Func check, System.Collections.Generic.IEnumerable tags = null) { throw null; } + public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, System.Func check, System.Collections.Generic.IEnumerable tags = null) { 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 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 CheckHealthAsync(System.Func predicate, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); + public System.Threading.Tasks.Task CheckHealthAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + } + public sealed partial class HealthCheckServiceOptions + { + public HealthCheckServiceOptions() { } + public System.Collections.Generic.ICollection Registrations { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + } +} diff --git a/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.csproj b/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.csproj new file mode 100644 index 0000000000..a59b30a32e --- /dev/null +++ b/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + false + + + + + + diff --git a/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netstandard2.0.cs b/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netstandard2.0.cs new file mode 100644 index 0000000000..02146c7bab --- /dev/null +++ b/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netstandard2.0.cs @@ -0,0 +1,76 @@ +// 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.JSInterop +{ + public static partial class DotNetDispatcher + { + public static void BeginInvoke(string callId, string assemblyName, string methodIdentifier, long dotNetObjectId, string argsJson) { } + [Microsoft.JSInterop.JSInvokableAttribute("DotNetDispatcher.EndInvoke")] + public static void EndInvoke(long asyncHandle, bool succeeded, Microsoft.JSInterop.Internal.JSAsyncCallResult result) { } + public static string Invoke(string assemblyName, string methodIdentifier, long dotNetObjectId, string argsJson) { throw null; } + [Microsoft.JSInterop.JSInvokableAttribute("DotNetDispatcher.ReleaseDotNetObject")] + public static void ReleaseDotNetObject(long dotNetObjectId) { } + } + public partial class DotNetObjectRef : System.IDisposable + { + public DotNetObjectRef(object value) { } + public object Value { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public void Dispose() { } + public void EnsureAttachedToJsRuntime(Microsoft.JSInterop.IJSRuntime runtime) { } + } + public partial interface IJSInProcessRuntime : Microsoft.JSInterop.IJSRuntime + { + T Invoke(string identifier, params object[] args); + } + public partial interface IJSRuntime + { + System.Threading.Tasks.Task InvokeAsync(string identifier, params object[] args); + void UntrackObjectRef(Microsoft.JSInterop.DotNetObjectRef dotNetObjectRef); + } + public partial class JSException : System.Exception + { + public JSException(string message) { } + } + public abstract partial class JSInProcessRuntimeBase : Microsoft.JSInterop.JSRuntimeBase, Microsoft.JSInterop.IJSInProcessRuntime, Microsoft.JSInterop.IJSRuntime + { + protected JSInProcessRuntimeBase() { } + protected abstract string InvokeJS(string identifier, string argsJson); + public T Invoke(string identifier, params object[] args) { throw null; } + } + [System.AttributeUsageAttribute(System.AttributeTargets.Method, AllowMultiple=true)] + public partial class JSInvokableAttribute : System.Attribute + { + public JSInvokableAttribute() { } + public JSInvokableAttribute(string identifier) { } + public string Identifier { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + } + public static partial class Json + { + public static T Deserialize(string json) { throw null; } + public static string Serialize(object value) { throw null; } + } + public static partial class JSRuntime + { + public static Microsoft.JSInterop.IJSRuntime Current { get { throw null; } } + public static void SetCurrentJSRuntime(Microsoft.JSInterop.IJSRuntime instance) { } + } + public abstract partial class JSRuntimeBase : Microsoft.JSInterop.IJSRuntime + { + public JSRuntimeBase() { } + protected abstract void BeginInvokeJS(long asyncHandle, string identifier, string argsJson); + public System.Threading.Tasks.Task InvokeAsync(string identifier, params object[] args) { throw null; } + public void UntrackObjectRef(Microsoft.JSInterop.DotNetObjectRef dotNetObjectRef) { } + } +} +namespace Microsoft.JSInterop.Internal +{ + public partial interface ICustomArgSerializer + { + object ToJsonPrimitive(); + } + public partial class JSAsyncCallResult + { + internal JSAsyncCallResult() { } + } +} diff --git a/src/JSInterop/Mono.WebAssembly.Interop/ref/Mono.WebAssembly.Interop.csproj b/src/JSInterop/Mono.WebAssembly.Interop/ref/Mono.WebAssembly.Interop.csproj new file mode 100644 index 0000000000..49f8622c83 --- /dev/null +++ b/src/JSInterop/Mono.WebAssembly.Interop/ref/Mono.WebAssembly.Interop.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + false + + + + + + diff --git a/src/JSInterop/Mono.WebAssembly.Interop/ref/Mono.WebAssembly.Interop.netstandard2.0.cs b/src/JSInterop/Mono.WebAssembly.Interop/ref/Mono.WebAssembly.Interop.netstandard2.0.cs new file mode 100644 index 0000000000..033e97237a --- /dev/null +++ b/src/JSInterop/Mono.WebAssembly.Interop/ref/Mono.WebAssembly.Interop.netstandard2.0.cs @@ -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 Mono.WebAssembly.Interop +{ + public partial class MonoWebAssemblyJSRuntime : Microsoft.JSInterop.JSInProcessRuntimeBase + { + public MonoWebAssemblyJSRuntime() { } + protected override void BeginInvokeJS(long asyncHandle, string identifier, string argsJson) { } + protected override string InvokeJS(string identifier, string argsJson) { throw null; } + public TRes InvokeUnmarshalled(string identifier) { throw null; } + public TRes InvokeUnmarshalled(string identifier, T0 arg0) { throw null; } + public TRes InvokeUnmarshalled(string identifier, T0 arg0, T1 arg1) { throw null; } + public TRes InvokeUnmarshalled(string identifier, T0 arg0, T1 arg1, T2 arg2) { throw null; } + } +} diff --git a/src/Localization/Abstractions/ref/Microsoft.Extensions.Localization.Abstractions.csproj b/src/Localization/Abstractions/ref/Microsoft.Extensions.Localization.Abstractions.csproj new file mode 100644 index 0000000000..e526fd4fb8 --- /dev/null +++ b/src/Localization/Abstractions/ref/Microsoft.Extensions.Localization.Abstractions.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + false + + + + + + diff --git a/src/Localization/Abstractions/ref/Microsoft.Extensions.Localization.Abstractions.netstandard2.0.cs b/src/Localization/Abstractions/ref/Microsoft.Extensions.Localization.Abstractions.netstandard2.0.cs new file mode 100644 index 0000000000..b18cec35f8 --- /dev/null +++ b/src/Localization/Abstractions/ref/Microsoft.Extensions.Localization.Abstractions.netstandard2.0.cs @@ -0,0 +1,47 @@ +// 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.Localization +{ + public partial interface IStringLocalizer + { + Microsoft.Extensions.Localization.LocalizedString this[string name] { get; } + Microsoft.Extensions.Localization.LocalizedString this[string name, params object[] arguments] { get; } + System.Collections.Generic.IEnumerable GetAllStrings(bool includeParentCultures); + Microsoft.Extensions.Localization.IStringLocalizer WithCulture(System.Globalization.CultureInfo culture); + } + public partial interface IStringLocalizerFactory + { + Microsoft.Extensions.Localization.IStringLocalizer Create(string baseName, string location); + Microsoft.Extensions.Localization.IStringLocalizer Create(System.Type resourceSource); + } + public partial interface IStringLocalizer : Microsoft.Extensions.Localization.IStringLocalizer + { + } + public partial class LocalizedString + { + public LocalizedString(string name, string value) { } + public LocalizedString(string name, string value, bool resourceNotFound) { } + public LocalizedString(string name, string value, bool resourceNotFound, string searchedLocation) { } + public string Name { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public bool ResourceNotFound { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public string SearchedLocation { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public string Value { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public static implicit operator string (Microsoft.Extensions.Localization.LocalizedString localizedString) { throw null; } + public override string ToString() { throw null; } + } + public static partial class StringLocalizerExtensions + { + public static System.Collections.Generic.IEnumerable GetAllStrings(this Microsoft.Extensions.Localization.IStringLocalizer stringLocalizer) { throw null; } + public static Microsoft.Extensions.Localization.LocalizedString GetString(this Microsoft.Extensions.Localization.IStringLocalizer stringLocalizer, string name) { throw null; } + public static Microsoft.Extensions.Localization.LocalizedString GetString(this Microsoft.Extensions.Localization.IStringLocalizer stringLocalizer, string name, params object[] arguments) { throw null; } + } + public partial class StringLocalizer : Microsoft.Extensions.Localization.IStringLocalizer, Microsoft.Extensions.Localization.IStringLocalizer + { + public StringLocalizer(Microsoft.Extensions.Localization.IStringLocalizerFactory factory) { } + public virtual Microsoft.Extensions.Localization.LocalizedString this[string name] { get { throw null; } } + public virtual Microsoft.Extensions.Localization.LocalizedString this[string name, params object[] arguments] { get { throw null; } } + public System.Collections.Generic.IEnumerable GetAllStrings(bool includeParentCultures) { throw null; } + public virtual Microsoft.Extensions.Localization.IStringLocalizer WithCulture(System.Globalization.CultureInfo culture) { throw null; } + } +} diff --git a/src/Localization/Localization/ref/Microsoft.Extensions.Localization.csproj b/src/Localization/Localization/ref/Microsoft.Extensions.Localization.csproj new file mode 100644 index 0000000000..d48c510f7d --- /dev/null +++ b/src/Localization/Localization/ref/Microsoft.Extensions.Localization.csproj @@ -0,0 +1,14 @@ + + + + netstandard2.0 + false + + + + + + + + + diff --git a/src/Localization/Localization/ref/Microsoft.Extensions.Localization.netstandard2.0.cs b/src/Localization/Localization/ref/Microsoft.Extensions.Localization.netstandard2.0.cs new file mode 100644 index 0000000000..7bbe1797a5 --- /dev/null +++ b/src/Localization/Localization/ref/Microsoft.Extensions.Localization.netstandard2.0.cs @@ -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. + +namespace Microsoft.Extensions.DependencyInjection +{ + public static partial class LocalizationServiceCollectionExtensions + { + public static Microsoft.Extensions.DependencyInjection.IServiceCollection AddLocalization(this Microsoft.Extensions.DependencyInjection.IServiceCollection services) { throw null; } + public static Microsoft.Extensions.DependencyInjection.IServiceCollection AddLocalization(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, System.Action setupAction) { throw null; } + } +} +namespace Microsoft.Extensions.Localization +{ + public partial interface IResourceNamesCache + { + System.Collections.Generic.IList GetOrAdd(string name, System.Func> valueFactory); + } + public partial class LocalizationOptions + { + public LocalizationOptions() { } + public string ResourcesPath { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + } + [System.AttributeUsageAttribute(System.AttributeTargets.Assembly, AllowMultiple=false, Inherited=false)] + public partial class ResourceLocationAttribute : System.Attribute + { + public ResourceLocationAttribute(string resourceLocation) { } + public string ResourceLocation { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + } + public partial class ResourceManagerStringLocalizer : Microsoft.Extensions.Localization.IStringLocalizer + { + public ResourceManagerStringLocalizer(System.Resources.ResourceManager resourceManager, Microsoft.Extensions.Localization.Internal.AssemblyWrapper resourceAssemblyWrapper, string baseName, Microsoft.Extensions.Localization.IResourceNamesCache resourceNamesCache, Microsoft.Extensions.Logging.ILogger logger) { } + public ResourceManagerStringLocalizer(System.Resources.ResourceManager resourceManager, Microsoft.Extensions.Localization.Internal.IResourceStringProvider resourceStringProvider, string baseName, Microsoft.Extensions.Localization.IResourceNamesCache resourceNamesCache, Microsoft.Extensions.Logging.ILogger logger) { } + public ResourceManagerStringLocalizer(System.Resources.ResourceManager resourceManager, System.Reflection.Assembly resourceAssembly, string baseName, Microsoft.Extensions.Localization.IResourceNamesCache resourceNamesCache, Microsoft.Extensions.Logging.ILogger logger) { } + public virtual Microsoft.Extensions.Localization.LocalizedString this[string name] { get { throw null; } } + public virtual Microsoft.Extensions.Localization.LocalizedString this[string name, params object[] arguments] { get { throw null; } } + public virtual System.Collections.Generic.IEnumerable GetAllStrings(bool includeParentCultures) { throw null; } + protected System.Collections.Generic.IEnumerable GetAllStrings(bool includeParentCultures, System.Globalization.CultureInfo culture) { throw null; } + protected string GetStringSafely(string name, System.Globalization.CultureInfo culture) { throw null; } + public Microsoft.Extensions.Localization.IStringLocalizer WithCulture(System.Globalization.CultureInfo culture) { throw null; } + } + public partial class ResourceManagerStringLocalizerFactory : Microsoft.Extensions.Localization.IStringLocalizerFactory + { + public ResourceManagerStringLocalizerFactory(Microsoft.Extensions.Options.IOptions localizationOptions, Microsoft.Extensions.Logging.ILoggerFactory loggerFactory) { } + public Microsoft.Extensions.Localization.IStringLocalizer Create(string baseName, string location) { throw null; } + public Microsoft.Extensions.Localization.IStringLocalizer Create(System.Type resourceSource) { throw null; } + protected virtual Microsoft.Extensions.Localization.ResourceManagerStringLocalizer CreateResourceManagerStringLocalizer(System.Reflection.Assembly assembly, string baseName) { throw null; } + protected virtual Microsoft.Extensions.Localization.ResourceLocationAttribute GetResourceLocationAttribute(System.Reflection.Assembly assembly) { throw null; } + protected virtual string GetResourcePrefix(System.Reflection.TypeInfo typeInfo) { throw null; } + protected virtual string GetResourcePrefix(System.Reflection.TypeInfo typeInfo, string baseNamespace, string resourcesRelativePath) { throw null; } + protected virtual string GetResourcePrefix(string baseResourceName, string baseNamespace) { throw null; } + protected virtual string GetResourcePrefix(string location, string baseName, string resourceLocation) { throw null; } + protected virtual Microsoft.Extensions.Localization.RootNamespaceAttribute GetRootNamespaceAttribute(System.Reflection.Assembly assembly) { throw null; } + } + public partial class ResourceManagerWithCultureStringLocalizer : Microsoft.Extensions.Localization.ResourceManagerStringLocalizer + { + public ResourceManagerWithCultureStringLocalizer(System.Resources.ResourceManager resourceManager, System.Reflection.Assembly resourceAssembly, string baseName, Microsoft.Extensions.Localization.IResourceNamesCache resourceNamesCache, System.Globalization.CultureInfo culture, Microsoft.Extensions.Logging.ILogger logger) : base (default(System.Resources.ResourceManager), default(System.Reflection.Assembly), default(string), default(Microsoft.Extensions.Localization.IResourceNamesCache), default(Microsoft.Extensions.Logging.ILogger)) { } + public override Microsoft.Extensions.Localization.LocalizedString this[string name] { get { throw null; } } + public override Microsoft.Extensions.Localization.LocalizedString this[string name, params object[] arguments] { get { throw null; } } + public override System.Collections.Generic.IEnumerable GetAllStrings(bool includeParentCultures) { throw null; } + } + public partial class ResourceNamesCache : Microsoft.Extensions.Localization.IResourceNamesCache + { + public ResourceNamesCache() { } + public System.Collections.Generic.IList GetOrAdd(string name, System.Func> valueFactory) { throw null; } + } + [System.AttributeUsageAttribute(System.AttributeTargets.Assembly, AllowMultiple=false, Inherited=false)] + public partial class RootNamespaceAttribute : System.Attribute + { + public RootNamespaceAttribute(string rootNamespace) { } + public string RootNamespace { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + } +} +namespace Microsoft.Extensions.Localization.Internal +{ + public partial class AssemblyWrapper + { + public AssemblyWrapper(System.Reflection.Assembly assembly) { } + public System.Reflection.Assembly Assembly { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public virtual string FullName { get { throw null; } } + public virtual System.IO.Stream GetManifestResourceStream(string name) { throw null; } + } + public partial interface IResourceStringProvider + { + System.Collections.Generic.IList GetAllResourceStrings(System.Globalization.CultureInfo culture, bool throwOnMissing); + } + public partial class ResourceManagerStringProvider : Microsoft.Extensions.Localization.Internal.IResourceStringProvider + { + public ResourceManagerStringProvider(Microsoft.Extensions.Localization.IResourceNamesCache resourceCache, System.Resources.ResourceManager resourceManager, System.Reflection.Assembly assembly, string baseName) { } + public System.Collections.Generic.IList GetAllResourceStrings(System.Globalization.CultureInfo culture, bool throwOnMissing) { throw null; } + } +} diff --git a/src/ObjectPool/ref/Microsoft.Extensions.ObjectPool.csproj b/src/ObjectPool/ref/Microsoft.Extensions.ObjectPool.csproj new file mode 100644 index 0000000000..8ae73841b1 --- /dev/null +++ b/src/ObjectPool/ref/Microsoft.Extensions.ObjectPool.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + false + + + + + + diff --git a/src/ObjectPool/ref/Microsoft.Extensions.ObjectPool.netstandard2.0.cs b/src/ObjectPool/ref/Microsoft.Extensions.ObjectPool.netstandard2.0.cs new file mode 100644 index 0000000000..16fd8379a6 --- /dev/null +++ b/src/ObjectPool/ref/Microsoft.Extensions.ObjectPool.netstandard2.0.cs @@ -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.ObjectPool +{ + public partial class DefaultObjectPoolProvider : Microsoft.Extensions.ObjectPool.ObjectPoolProvider + { + public DefaultObjectPoolProvider() { } + public int MaximumRetained { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + public override Microsoft.Extensions.ObjectPool.ObjectPool Create(Microsoft.Extensions.ObjectPool.IPooledObjectPolicy policy) { throw null; } + } + public partial class DefaultObjectPool : Microsoft.Extensions.ObjectPool.ObjectPool where T : class + { + public DefaultObjectPool(Microsoft.Extensions.ObjectPool.IPooledObjectPolicy policy) { } + public DefaultObjectPool(Microsoft.Extensions.ObjectPool.IPooledObjectPolicy policy, int maximumRetained) { } + public override T Get() { throw null; } + public override void Return(T obj) { } + } + public partial class DefaultPooledObjectPolicy : Microsoft.Extensions.ObjectPool.PooledObjectPolicy where T : class, new() + { + public DefaultPooledObjectPolicy() { } + public override T Create() { throw null; } + public override bool Return(T obj) { throw null; } + } + public partial interface IPooledObjectPolicy + { + T Create(); + bool Return(T obj); + } + public partial class LeakTrackingObjectPoolProvider : Microsoft.Extensions.ObjectPool.ObjectPoolProvider + { + public LeakTrackingObjectPoolProvider(Microsoft.Extensions.ObjectPool.ObjectPoolProvider inner) { } + public override Microsoft.Extensions.ObjectPool.ObjectPool Create(Microsoft.Extensions.ObjectPool.IPooledObjectPolicy policy) { throw null; } + } + public partial class LeakTrackingObjectPool : Microsoft.Extensions.ObjectPool.ObjectPool where T : class + { + public LeakTrackingObjectPool(Microsoft.Extensions.ObjectPool.ObjectPool inner) { } + public override T Get() { throw null; } + public override void Return(T obj) { } + } + public abstract partial class ObjectPoolProvider + { + protected ObjectPoolProvider() { } + public Microsoft.Extensions.ObjectPool.ObjectPool Create() where T : class, new() { throw null; } + public abstract Microsoft.Extensions.ObjectPool.ObjectPool Create(Microsoft.Extensions.ObjectPool.IPooledObjectPolicy policy) where T : class; + } + public static partial class ObjectPoolProviderExtensions + { + public static Microsoft.Extensions.ObjectPool.ObjectPool CreateStringBuilderPool(this Microsoft.Extensions.ObjectPool.ObjectPoolProvider provider) { throw null; } + public static Microsoft.Extensions.ObjectPool.ObjectPool CreateStringBuilderPool(this Microsoft.Extensions.ObjectPool.ObjectPoolProvider provider, int initialCapacity, int maximumRetainedCapacity) { throw null; } + } + public abstract partial class ObjectPool where T : class + { + protected ObjectPool() { } + public abstract T Get(); + public abstract void Return(T obj); + } + public abstract partial class PooledObjectPolicy : Microsoft.Extensions.ObjectPool.IPooledObjectPolicy + { + protected PooledObjectPolicy() { } + public abstract T Create(); + public abstract bool Return(T obj); + } + public partial class StringBuilderPooledObjectPolicy : Microsoft.Extensions.ObjectPool.PooledObjectPolicy + { + public StringBuilderPooledObjectPolicy() { } + public int InitialCapacity { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + public int MaximumRetainedCapacity { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + public override System.Text.StringBuilder Create() { throw null; } + public override bool Return(System.Text.StringBuilder obj) { throw null; } + } +} diff --git a/src/WebEncoders/Directory.Build.props b/src/WebEncoders/Directory.Build.props new file mode 100644 index 0000000000..81557e1bca --- /dev/null +++ b/src/WebEncoders/Directory.Build.props @@ -0,0 +1,8 @@ + + + + + true + + + diff --git a/src/WebEncoders/ref/Microsoft.Extensions.WebEncoders.csproj b/src/WebEncoders/ref/Microsoft.Extensions.WebEncoders.csproj new file mode 100644 index 0000000000..d102a7d31b --- /dev/null +++ b/src/WebEncoders/ref/Microsoft.Extensions.WebEncoders.csproj @@ -0,0 +1,13 @@ + + + + netstandard2.0 + false + + + + + + + + diff --git a/src/WebEncoders/ref/Microsoft.Extensions.WebEncoders.netstandard2.0.cs b/src/WebEncoders/ref/Microsoft.Extensions.WebEncoders.netstandard2.0.cs new file mode 100644 index 0000000000..18cdcbdfa3 --- /dev/null +++ b/src/WebEncoders/ref/Microsoft.Extensions.WebEncoders.netstandard2.0.cs @@ -0,0 +1,55 @@ +// 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 EncoderServiceCollectionExtensions + { + public static Microsoft.Extensions.DependencyInjection.IServiceCollection AddWebEncoders(this Microsoft.Extensions.DependencyInjection.IServiceCollection services) { throw null; } + public static Microsoft.Extensions.DependencyInjection.IServiceCollection AddWebEncoders(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, System.Action setupAction) { throw null; } + } +} +namespace Microsoft.Extensions.WebEncoders +{ + public sealed partial class WebEncoderOptions + { + public WebEncoderOptions() { } + public System.Text.Encodings.Web.TextEncoderSettings TextEncoderSettings { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + } +} +namespace Microsoft.Extensions.WebEncoders.Testing +{ + public sealed partial class HtmlTestEncoder : System.Text.Encodings.Web.HtmlEncoder + { + public HtmlTestEncoder() { } + public override int MaxOutputCharactersPerInputCharacter { get { throw null; } } + public override void Encode(System.IO.TextWriter output, char[] value, int startIndex, int characterCount) { } + public override void Encode(System.IO.TextWriter output, string value, int startIndex, int characterCount) { } + public override string Encode(string value) { throw null; } + public unsafe override int FindFirstCharacterToEncode(char* text, int textLength) { throw null; } + public unsafe override bool TryEncodeUnicodeScalar(int unicodeScalar, char* buffer, int bufferLength, out int numberOfCharactersWritten) { throw null; } + public override bool WillEncode(int unicodeScalar) { throw null; } + } + public partial class JavaScriptTestEncoder : System.Text.Encodings.Web.JavaScriptEncoder + { + public JavaScriptTestEncoder() { } + public override int MaxOutputCharactersPerInputCharacter { get { throw null; } } + public override void Encode(System.IO.TextWriter output, char[] value, int startIndex, int characterCount) { } + public override void Encode(System.IO.TextWriter output, string value, int startIndex, int characterCount) { } + public override string Encode(string value) { throw null; } + public unsafe override int FindFirstCharacterToEncode(char* text, int textLength) { throw null; } + public unsafe override bool TryEncodeUnicodeScalar(int unicodeScalar, char* buffer, int bufferLength, out int numberOfCharactersWritten) { throw null; } + public override bool WillEncode(int unicodeScalar) { throw null; } + } + public partial class UrlTestEncoder : System.Text.Encodings.Web.UrlEncoder + { + public UrlTestEncoder() { } + public override int MaxOutputCharactersPerInputCharacter { get { throw null; } } + public override void Encode(System.IO.TextWriter output, char[] value, int startIndex, int characterCount) { } + public override void Encode(System.IO.TextWriter output, string value, int startIndex, int characterCount) { } + public override string Encode(string value) { throw null; } + public unsafe override int FindFirstCharacterToEncode(char* text, int textLength) { throw null; } + public unsafe override bool TryEncodeUnicodeScalar(int unicodeScalar, char* buffer, int bufferLength, out int numberOfCharactersWritten) { throw null; } + public override bool WillEncode(int unicodeScalar) { throw null; } + } +} diff --git a/src/WebEncoders/src/Microsoft.Extensions.WebEncoders.csproj b/src/WebEncoders/src/Microsoft.Extensions.WebEncoders.csproj index dfc575c73f..8f60f8f983 100644 --- a/src/WebEncoders/src/Microsoft.Extensions.WebEncoders.csproj +++ b/src/WebEncoders/src/Microsoft.Extensions.WebEncoders.csproj @@ -4,7 +4,6 @@ Contains registration and configuration APIs to add the core framework encoders to a dependency injection container. netstandard2.0 $(NoWarn);CS1591 - true true aspnetcore true From dfb597732491b3d941b47a23dbeaf41c4eb07009 Mon Sep 17 00:00:00 2001 From: Glenn Condron Date: Mon, 4 Feb 2019 15:14:37 -0800 Subject: [PATCH 27/52] Update HealthCheckPublisherOptions.cs This seems wrong. Presumably fixes: https://github.com/aspnet/Extensions/issues/1041\n\nCommit migrated from https://github.com/dotnet/extensions/commit/b3c88d78fe112bc3b2e272299156857a383edf5d --- .../HealthChecks/src/HealthCheckPublisherOptions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/HealthChecks/HealthChecks/src/HealthCheckPublisherOptions.cs b/src/HealthChecks/HealthChecks/src/HealthCheckPublisherOptions.cs index 1313718af8..6b7c8c3365 100644 --- a/src/HealthChecks/HealthChecks/src/HealthCheckPublisherOptions.cs +++ b/src/HealthChecks/HealthChecks/src/HealthCheckPublisherOptions.cs @@ -60,7 +60,7 @@ namespace Microsoft.Extensions.Diagnostics.HealthChecks throw new ArgumentException($"The {nameof(Period)} must not be infinite.", nameof(value)); } - _delay = value; + _period = value; } } From c960040b533ae026b66adceda72dd5203d2bc5dc Mon Sep 17 00:00:00 2001 From: Chris Ross Date: Fri, 15 Feb 2019 13:40:33 -0800 Subject: [PATCH 28/52] Deprecate and replace IHostingEnvronment & IApplicationLifetime (dotnet/extensions#1100) * Deprecate and replace IHostingEnvronment & IApplicationLifetime dotnet/extensions#966 * Fix startvs * Fix ref generation for obosolete\n\nCommit migrated from https://github.com/dotnet/extensions/commit/6991e2b0e8f7a133208d015dc7d0ca4192523ba0 --- .../ref/Microsoft.Extensions.Configuration.KeyPerFile.csproj | 1 - .../ref/Microsoft.Extensions.FileProviders.Embedded.csproj | 1 - ...osoft.Extensions.Diagnostics.HealthChecks.Abstractions.csproj | 1 - .../ref/Microsoft.Extensions.Diagnostics.HealthChecks.csproj | 1 - src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.csproj | 1 - .../Mono.WebAssembly.Interop/ref/Mono.WebAssembly.Interop.csproj | 1 - .../ref/Microsoft.Extensions.Localization.Abstractions.csproj | 1 - .../Localization/ref/Microsoft.Extensions.Localization.csproj | 1 - src/ObjectPool/ref/Microsoft.Extensions.ObjectPool.csproj | 1 - src/WebEncoders/ref/Microsoft.Extensions.WebEncoders.csproj | 1 - 10 files changed, 10 deletions(-) diff --git a/src/Configuration.KeyPerFile/ref/Microsoft.Extensions.Configuration.KeyPerFile.csproj b/src/Configuration.KeyPerFile/ref/Microsoft.Extensions.Configuration.KeyPerFile.csproj index 3df73aba0c..21f0053e59 100644 --- a/src/Configuration.KeyPerFile/ref/Microsoft.Extensions.Configuration.KeyPerFile.csproj +++ b/src/Configuration.KeyPerFile/ref/Microsoft.Extensions.Configuration.KeyPerFile.csproj @@ -2,7 +2,6 @@ netstandard2.0 - false diff --git a/src/FileProviders/Embedded/ref/Microsoft.Extensions.FileProviders.Embedded.csproj b/src/FileProviders/Embedded/ref/Microsoft.Extensions.FileProviders.Embedded.csproj index ba6fddca2b..b8f2f33387 100644 --- a/src/FileProviders/Embedded/ref/Microsoft.Extensions.FileProviders.Embedded.csproj +++ b/src/FileProviders/Embedded/ref/Microsoft.Extensions.FileProviders.Embedded.csproj @@ -2,7 +2,6 @@ netstandard2.0 - false diff --git a/src/HealthChecks/Abstractions/ref/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.csproj b/src/HealthChecks/Abstractions/ref/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.csproj index 201f8d515b..be23858955 100644 --- a/src/HealthChecks/Abstractions/ref/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.csproj +++ b/src/HealthChecks/Abstractions/ref/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.csproj @@ -2,7 +2,6 @@ netstandard2.0 - false diff --git a/src/HealthChecks/HealthChecks/ref/Microsoft.Extensions.Diagnostics.HealthChecks.csproj b/src/HealthChecks/HealthChecks/ref/Microsoft.Extensions.Diagnostics.HealthChecks.csproj index 925e89cfa0..277e60910f 100644 --- a/src/HealthChecks/HealthChecks/ref/Microsoft.Extensions.Diagnostics.HealthChecks.csproj +++ b/src/HealthChecks/HealthChecks/ref/Microsoft.Extensions.Diagnostics.HealthChecks.csproj @@ -2,7 +2,6 @@ netstandard2.0 - false diff --git a/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.csproj b/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.csproj index a59b30a32e..87fd913427 100644 --- a/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.csproj +++ b/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.csproj @@ -2,7 +2,6 @@ netstandard2.0 - false diff --git a/src/JSInterop/Mono.WebAssembly.Interop/ref/Mono.WebAssembly.Interop.csproj b/src/JSInterop/Mono.WebAssembly.Interop/ref/Mono.WebAssembly.Interop.csproj index 49f8622c83..a1ba605705 100644 --- a/src/JSInterop/Mono.WebAssembly.Interop/ref/Mono.WebAssembly.Interop.csproj +++ b/src/JSInterop/Mono.WebAssembly.Interop/ref/Mono.WebAssembly.Interop.csproj @@ -2,7 +2,6 @@ netstandard2.0 - false diff --git a/src/Localization/Abstractions/ref/Microsoft.Extensions.Localization.Abstractions.csproj b/src/Localization/Abstractions/ref/Microsoft.Extensions.Localization.Abstractions.csproj index e526fd4fb8..0608c06147 100644 --- a/src/Localization/Abstractions/ref/Microsoft.Extensions.Localization.Abstractions.csproj +++ b/src/Localization/Abstractions/ref/Microsoft.Extensions.Localization.Abstractions.csproj @@ -2,7 +2,6 @@ netstandard2.0 - false diff --git a/src/Localization/Localization/ref/Microsoft.Extensions.Localization.csproj b/src/Localization/Localization/ref/Microsoft.Extensions.Localization.csproj index d48c510f7d..d628c33a54 100644 --- a/src/Localization/Localization/ref/Microsoft.Extensions.Localization.csproj +++ b/src/Localization/Localization/ref/Microsoft.Extensions.Localization.csproj @@ -2,7 +2,6 @@ netstandard2.0 - false diff --git a/src/ObjectPool/ref/Microsoft.Extensions.ObjectPool.csproj b/src/ObjectPool/ref/Microsoft.Extensions.ObjectPool.csproj index 8ae73841b1..1fbb81a9ca 100644 --- a/src/ObjectPool/ref/Microsoft.Extensions.ObjectPool.csproj +++ b/src/ObjectPool/ref/Microsoft.Extensions.ObjectPool.csproj @@ -2,7 +2,6 @@ netstandard2.0 - false diff --git a/src/WebEncoders/ref/Microsoft.Extensions.WebEncoders.csproj b/src/WebEncoders/ref/Microsoft.Extensions.WebEncoders.csproj index d102a7d31b..9dea660cb0 100644 --- a/src/WebEncoders/ref/Microsoft.Extensions.WebEncoders.csproj +++ b/src/WebEncoders/ref/Microsoft.Extensions.WebEncoders.csproj @@ -2,7 +2,6 @@ netstandard2.0 - false From b9912fee6b8082459fc33788c50916316944a2f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnther=20Foidl?= Date: Sat, 19 Jan 2019 20:00:52 +0100 Subject: [PATCH 29/52] DisposableObjectPool added \n\nCommit migrated from https://github.com/dotnet/extensions/commit/6bcb1aee83d64225b24416b7c5e8d78f204ae2f0 --- src/ObjectPool/src/DefaultObjectPool.cs | 30 +++++-- src/ObjectPool/src/DisposableObjectPool.cs | 70 ++++++++++++++++ .../test/DisposableObjectPoolTest.cs | 83 +++++++++++++++++++ 3 files changed, 175 insertions(+), 8 deletions(-) create mode 100644 src/ObjectPool/src/DisposableObjectPool.cs create mode 100644 src/ObjectPool/test/DisposableObjectPoolTest.cs diff --git a/src/ObjectPool/src/DefaultObjectPool.cs b/src/ObjectPool/src/DefaultObjectPool.cs index dcd7f1c715..6ecc923140 100644 --- a/src/ObjectPool/src/DefaultObjectPool.cs +++ b/src/ObjectPool/src/DefaultObjectPool.cs @@ -10,10 +10,10 @@ namespace Microsoft.Extensions.ObjectPool { public class DefaultObjectPool : ObjectPool where T : class { - private readonly ObjectWrapper[] _items; + protected readonly ObjectWrapper[] _items; private readonly IPooledObjectPolicy _policy; private readonly bool _isDefaultPolicy; - private T _firstItem; + protected T _firstItem; // This class was introduced in 2.1 to avoid the interface call where possible private readonly PooledObjectPolicy _fastPolicy; @@ -71,29 +71,43 @@ namespace Microsoft.Extensions.ObjectPool [MethodImpl(MethodImplOptions.NoInlining)] private T Create() => _fastPolicy?.Create() ?? _policy.Create(); - public override void Return(T obj) + public override void Return(T obj) => ReturnCore(obj); + + protected bool ReturnCore(T obj) { if (_isDefaultPolicy || (_fastPolicy?.Return(obj) ?? _policy.Return(obj))) { - if (_firstItem != null || Interlocked.CompareExchange(ref _firstItem, obj, null) != null) + if (_firstItem == null && Interlocked.CompareExchange(ref _firstItem, obj, null) == null) { - ReturnViaScan(obj); + return true; // returned to pool + } + else + { + return ReturnViaScan(obj); } } + + return false; // not returned to pool } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void ReturnViaScan(T obj) + private bool ReturnViaScan(T obj) { var items = _items; - for (var i = 0; i < items.Length && Interlocked.CompareExchange(ref items[i].Element, obj, null) != null; ++i) + for (var i = 0; i < items.Length; i++) { + if (Interlocked.CompareExchange(ref items[i].Element, obj, null) == null) + { + return true; + } } + + return false; } // PERF: the struct wrapper avoids array-covariance-checks from the runtime when assigning to elements of the array. [DebuggerDisplay("{Element}")] - private struct ObjectWrapper + protected struct ObjectWrapper { public T Element; } diff --git a/src/ObjectPool/src/DisposableObjectPool.cs b/src/ObjectPool/src/DisposableObjectPool.cs new file mode 100644 index 0000000000..ca9288a6d6 --- /dev/null +++ b/src/ObjectPool/src/DisposableObjectPool.cs @@ -0,0 +1,70 @@ +// 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; + +namespace Microsoft.Extensions.ObjectPool +{ + public class DisposableObjectPool : DefaultObjectPool, IDisposable where T : class + { + private volatile bool _isDisposed; + + public DisposableObjectPool(IPooledObjectPolicy policy) + : base(policy) + { + } + + public DisposableObjectPool(IPooledObjectPolicy policy, int maximumRetained) + : base(policy, maximumRetained) + { + } + + public override T Get() + { + if (_isDisposed) + { + ThrowObjectDisposedException(); + } + + return base.Get(); + + void ThrowObjectDisposedException() + { + throw new ObjectDisposedException(this.GetType().Name); + } + } + + public override void Return(T obj) + { + // When the pool is disposed or the obj is not returned to the pool, dispose it + if (_isDisposed || !ReturnCore(obj)) + { + DisposeItem(obj); + } + } + + public void Dispose() + { + DisposeItem(_firstItem); + _firstItem = null; + + ObjectWrapper[] items = _items; + for (var i = 0; i < items.Length; i++) + { + DisposeItem(items[i].Element); + items[i].Element = null; + } + + _isDisposed = true; + } + + private void DisposeItem(T item) + { + if (item is IDisposable disposable) + { + disposable.Dispose(); + } + } + } +} diff --git a/src/ObjectPool/test/DisposableObjectPoolTest.cs b/src/ObjectPool/test/DisposableObjectPoolTest.cs new file mode 100644 index 0000000000..4bc9142efb --- /dev/null +++ b/src/ObjectPool/test/DisposableObjectPoolTest.cs @@ -0,0 +1,83 @@ +// 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.ObjectPool +{ + public class DisposableObjectPoolTest + { + [Fact] + public void DisposableObjectPoolWithOneElement_Dispose_ObjectDisposed() + { + // Arrange + var pool = new DisposableObjectPool(new DefaultPooledObjectPolicy()); + var obj = pool.Get(); + pool.Return(obj); + + // Act + pool.Dispose(); + + // Assert + Assert.True(obj.IsDisposed); + } + + [Fact] + public void DisposableObjectPoolWithTwoElements_Dispose_ObjectsDisposed() + { + // Arrange + var pool = new DisposableObjectPool(new DefaultPooledObjectPolicy()); + var obj1 = pool.Get(); + var obj2 = pool.Get(); + pool.Return(obj1); + pool.Return(obj2); + + // Act + pool.Dispose(); + + // Assert + Assert.True(obj1.IsDisposed); + Assert.True(obj2.IsDisposed); + } + + [Fact] + public void DisposableObjectPool_DisposeAndGet_ThrowsObjectDisposed() + { + // Arrange + var pool = new DisposableObjectPool(new DefaultPooledObjectPolicy()); + var obj1 = pool.Get(); + var obj2 = pool.Get(); + pool.Return(obj1); + pool.Return(obj2); + + // Act + pool.Dispose(); + + // Assert + Assert.Throws(() => pool.Get()); + } + + [Fact] + public void DisposableObjectPool_DisposeAndReturn_DisposesObject() + { + // Arrange + var pool = new DisposableObjectPool(new DefaultPooledObjectPolicy()); + var obj = pool.Get(); + + // Act + pool.Dispose(); + pool.Return(obj); + + // Assert + Assert.True(obj.IsDisposed); + } + + private class DisposableObject : IDisposable + { + public bool IsDisposed { get; private set; } + + public void Dispose() => IsDisposed = true; + } + } +} From 033b118a9623873fddb3b8a6beac7ef03462d695 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnther=20Foidl?= Date: Sat, 19 Jan 2019 20:16:41 +0100 Subject: [PATCH 30/52] DefaultObjectPoolProvider chooses on T and added factory method to ObjectPool \n\nCommit migrated from https://github.com/dotnet/extensions/commit/17883d97a9fc1d8bd5e42a126f70338458dd95b8 --- .../src/DefaultObjectPoolProvider.cs | 5 +++ src/ObjectPool/src/ObjectPool.cs | 9 ++++ .../test/DefaultObjectPoolProviderTest.cs | 44 +++++++++++++++++++ 3 files changed, 58 insertions(+) create mode 100644 src/ObjectPool/test/DefaultObjectPoolProviderTest.cs diff --git a/src/ObjectPool/src/DefaultObjectPoolProvider.cs b/src/ObjectPool/src/DefaultObjectPoolProvider.cs index fb3c4bfa7e..cedacaedbd 100644 --- a/src/ObjectPool/src/DefaultObjectPoolProvider.cs +++ b/src/ObjectPool/src/DefaultObjectPoolProvider.cs @@ -11,6 +11,11 @@ namespace Microsoft.Extensions.ObjectPool public override ObjectPool Create(IPooledObjectPolicy policy) { + if (typeof(IDisposable).IsAssignableFrom(typeof(T))) + { + return new DisposableObjectPool(policy, MaximumRetained); + } + return new DefaultObjectPool(policy, MaximumRetained); } } diff --git a/src/ObjectPool/src/ObjectPool.cs b/src/ObjectPool/src/ObjectPool.cs index 8cf52c9195..691beae60c 100644 --- a/src/ObjectPool/src/ObjectPool.cs +++ b/src/ObjectPool/src/ObjectPool.cs @@ -9,4 +9,13 @@ namespace Microsoft.Extensions.ObjectPool public abstract void Return(T obj); } + + public static class ObjectPool + { + public static ObjectPool Create(IPooledObjectPolicy policy = null) where T : class, new() + { + var provider = new DefaultObjectPoolProvider(); + return provider.Create(policy ?? new DefaultPooledObjectPolicy()); + } + } } diff --git a/src/ObjectPool/test/DefaultObjectPoolProviderTest.cs b/src/ObjectPool/test/DefaultObjectPoolProviderTest.cs new file mode 100644 index 0000000000..7096b60b34 --- /dev/null +++ b/src/ObjectPool/test/DefaultObjectPoolProviderTest.cs @@ -0,0 +1,44 @@ +// 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.ObjectPool +{ + public class DefaultObjectPoolProviderTest + { + [Fact] + public void DefaultObjectPoolProvider_CreateForObject_DefaultObjectPoolReturned() + { + // Arrange + var provider = new DefaultObjectPoolProvider(); + + // Act + var pool = provider.Create(); + + // Assert + Assert.IsType>(pool); + } + + [Fact] + public void DefaultObjectPoolProvider_CreateForIDisposable_DisposableObjectPoolReturned() + { + // Arrange + var provider = new DefaultObjectPoolProvider(); + + // Act + var pool = provider.Create(); + + // Assert + Assert.IsType>(pool); + } + + private class DisposableObject : IDisposable + { + public bool IsDisposed { get; private set; } + + public void Dispose() => IsDisposed = true; + } + } +} From 5e19936a9f1a1e6d46878753bc7a8ca14001e125 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnther=20Foidl?= Date: Sun, 20 Jan 2019 21:01:59 +0100 Subject: [PATCH 31/52] PR Feedback * DefaultObjectPool left unchanged (expect access modifiers) + manual inlining ob scan-methods * DisposableObjectPool made internal and it handles Return self, thus keeping the DefaultObjectPool fast \n\nCommit migrated from https://github.com/dotnet/extensions/commit/a7dc496507e10fbf56e4b76a156b4d0a0f22bcba --- src/ObjectPool/src/DefaultObjectPool.cs | 72 ++++++------------- .../src/DefaultObjectPoolProvider.cs | 5 ++ src/ObjectPool/src/DisposableObjectPool.cs | 31 ++++++-- src/ObjectPool/src/Properties/AssemblyInfo.cs | 3 + .../test/DisposableObjectPoolTest.cs | 62 ++++++++++++++++ src/ObjectPool/test/ThreadingTest.cs | 18 ++++- 6 files changed, 135 insertions(+), 56 deletions(-) create mode 100644 src/ObjectPool/src/Properties/AssemblyInfo.cs diff --git a/src/ObjectPool/src/DefaultObjectPool.cs b/src/ObjectPool/src/DefaultObjectPool.cs index 6ecc923140..070508a014 100644 --- a/src/ObjectPool/src/DefaultObjectPool.cs +++ b/src/ObjectPool/src/DefaultObjectPool.cs @@ -10,13 +10,13 @@ namespace Microsoft.Extensions.ObjectPool { public class DefaultObjectPool : ObjectPool where T : class { - protected readonly ObjectWrapper[] _items; - private readonly IPooledObjectPolicy _policy; - private readonly bool _isDefaultPolicy; - protected T _firstItem; + private protected readonly ObjectWrapper[] _items; + private protected readonly IPooledObjectPolicy _policy; + private protected readonly bool _isDefaultPolicy; + private protected T _firstItem; // This class was introduced in 2.1 to avoid the interface call where possible - private readonly PooledObjectPolicy _fastPolicy; + private protected readonly PooledObjectPolicy _fastPolicy; public DefaultObjectPool(IPooledObjectPolicy policy) : this(policy, Environment.ProcessorCount * 2) @@ -45,69 +45,43 @@ namespace Microsoft.Extensions.ObjectPool var item = _firstItem; if (item == null || Interlocked.CompareExchange(ref _firstItem, null, item) != item) { - item = GetViaScan(); + var items = _items; + for (var i = 0; i < items.Length; i++) + { + item = items[i].Element; + if (item != null && Interlocked.CompareExchange(ref items[i].Element, null, item) == item) + { + return item; + } + } + + item = Create(); } return item; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private T GetViaScan() - { - var items = _items; - for (var i = 0; i < items.Length; i++) - { - var item = items[i].Element; - if (item != null && Interlocked.CompareExchange(ref items[i].Element, null, item) == item) - { - return item; - } - } - - return Create(); - } - // Non-inline to improve its code quality as uncommon path [MethodImpl(MethodImplOptions.NoInlining)] private T Create() => _fastPolicy?.Create() ?? _policy.Create(); - public override void Return(T obj) => ReturnCore(obj); - - protected bool ReturnCore(T obj) + public override void Return(T obj) { if (_isDefaultPolicy || (_fastPolicy?.Return(obj) ?? _policy.Return(obj))) { - if (_firstItem == null && Interlocked.CompareExchange(ref _firstItem, obj, null) == null) + if (_firstItem != null || Interlocked.CompareExchange(ref _firstItem, obj, null) != null) { - return true; // returned to pool - } - else - { - return ReturnViaScan(obj); + var items = _items; + for (var i = 0; i < items.Length && Interlocked.CompareExchange(ref items[i].Element, obj, null) != null; ++i) + { + } } } - - return false; // not returned to pool - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private bool ReturnViaScan(T obj) - { - var items = _items; - for (var i = 0; i < items.Length; i++) - { - if (Interlocked.CompareExchange(ref items[i].Element, obj, null) == null) - { - return true; - } - } - - return false; } // PERF: the struct wrapper avoids array-covariance-checks from the runtime when assigning to elements of the array. [DebuggerDisplay("{Element}")] - protected struct ObjectWrapper + private protected struct ObjectWrapper { public T Element; } diff --git a/src/ObjectPool/src/DefaultObjectPoolProvider.cs b/src/ObjectPool/src/DefaultObjectPoolProvider.cs index cedacaedbd..2e7767ab35 100644 --- a/src/ObjectPool/src/DefaultObjectPoolProvider.cs +++ b/src/ObjectPool/src/DefaultObjectPoolProvider.cs @@ -11,6 +11,11 @@ namespace Microsoft.Extensions.ObjectPool public override ObjectPool Create(IPooledObjectPolicy policy) { + if (policy == null) + { + throw new ArgumentNullException(nameof(policy)); + } + if (typeof(IDisposable).IsAssignableFrom(typeof(T))) { return new DisposableObjectPool(policy, MaximumRetained); diff --git a/src/ObjectPool/src/DisposableObjectPool.cs b/src/ObjectPool/src/DisposableObjectPool.cs index ca9288a6d6..17ada443e5 100644 --- a/src/ObjectPool/src/DisposableObjectPool.cs +++ b/src/ObjectPool/src/DisposableObjectPool.cs @@ -2,11 +2,12 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Runtime.CompilerServices; using System.Threading; namespace Microsoft.Extensions.ObjectPool { - public class DisposableObjectPool : DefaultObjectPool, IDisposable where T : class + internal sealed class DisposableObjectPool : DefaultObjectPool, IDisposable where T : class { private volatile bool _isDisposed; @@ -31,7 +32,7 @@ namespace Microsoft.Extensions.ObjectPool void ThrowObjectDisposedException() { - throw new ObjectDisposedException(this.GetType().Name); + throw new ObjectDisposedException(GetType().Name); } } @@ -44,8 +45,32 @@ namespace Microsoft.Extensions.ObjectPool } } + private bool ReturnCore(T obj) + { + bool returnedTooPool = false; + + if (_isDefaultPolicy || (_fastPolicy?.Return(obj) ?? _policy.Return(obj))) + { + if (_firstItem == null && Interlocked.CompareExchange(ref _firstItem, obj, null) == null) + { + returnedTooPool = true; + } + else + { + var items = _items; + for (var i = 0; i < items.Length && !(returnedTooPool = Interlocked.CompareExchange(ref items[i].Element, obj, null) == null); i++) + { + } + } + } + + return returnedTooPool; + } + public void Dispose() { + _isDisposed = true; + DisposeItem(_firstItem); _firstItem = null; @@ -55,8 +80,6 @@ namespace Microsoft.Extensions.ObjectPool DisposeItem(items[i].Element); items[i].Element = null; } - - _isDisposed = true; } private void DisposeItem(T item) diff --git a/src/ObjectPool/src/Properties/AssemblyInfo.cs b/src/ObjectPool/src/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..121f8990b1 --- /dev/null +++ b/src/ObjectPool/src/Properties/AssemblyInfo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Microsoft.Extensions.ObjectPool.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] diff --git a/src/ObjectPool/test/DisposableObjectPoolTest.cs b/src/ObjectPool/test/DisposableObjectPoolTest.cs index 4bc9142efb..3bcefbaf66 100644 --- a/src/ObjectPool/test/DisposableObjectPoolTest.cs +++ b/src/ObjectPool/test/DisposableObjectPoolTest.cs @@ -2,12 +2,61 @@ // 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 Xunit; namespace Microsoft.Extensions.ObjectPool { public class DisposableObjectPoolTest { + [Fact] + public void DisposableObjectPoolWithDefaultPolicy_GetAnd_ReturnObject_SameInstance() + { + // Arrange + var pool = new DisposableObjectPool(new DefaultPooledObjectPolicy()); + + var obj1 = pool.Get(); + pool.Return(obj1); + + // Act + var obj2 = pool.Get(); + + // Assert + Assert.Same(obj1, obj2); + } + + [Fact] + public void DisposableObjectPool_GetAndReturnObject_SameInstance() + { + // Arrange + var pool = new DisposableObjectPool>(new ListPolicy()); + + var list1 = pool.Get(); + pool.Return(list1); + + // Act + var list2 = pool.Get(); + + // Assert + Assert.Same(list1, list2); + } + + [Fact] + public void DisposableObjectPool_Return_RejectedByPolicy() + { + // Arrange + var pool = new DisposableObjectPool>(new ListPolicy()); + var list1 = pool.Get(); + list1.Capacity = 20; + + // Act + pool.Return(list1); + var list2 = pool.Get(); + + // Assert + Assert.NotSame(list1, list2); + } + [Fact] public void DisposableObjectPoolWithOneElement_Dispose_ObjectDisposed() { @@ -73,6 +122,19 @@ namespace Microsoft.Extensions.ObjectPool Assert.True(obj.IsDisposed); } + private class ListPolicy : IPooledObjectPolicy> + { + public List Create() + { + return new List(17); + } + + public bool Return(List obj) + { + return obj.Capacity == 17; + } + } + private class DisposableObject : IDisposable { public bool IsDisposed { get; private set; } diff --git a/src/ObjectPool/test/ThreadingTest.cs b/src/ObjectPool/test/ThreadingTest.cs index 541bc5ffd4..dbab7a5301 100644 --- a/src/ObjectPool/test/ThreadingTest.cs +++ b/src/ObjectPool/test/ThreadingTest.cs @@ -13,10 +13,22 @@ namespace Microsoft.Extensions.ObjectPool private bool _foundError; [Fact] - public void RunThreadingTest() + public void DefaultObjectPool_RunThreadingTest() + { + _pool = new DefaultObjectPool(new DefaultPooledObjectPolicy(), 10); + RunThreadingTest(); + } + + [Fact] + public void DisposableObjectPool_RunThreadingTest() + { + _pool = new DisposableObjectPool(new DefaultPooledObjectPolicy(), 10); + RunThreadingTest(); + } + + private void RunThreadingTest() { _cts = new CancellationTokenSource(); - _pool = new DefaultObjectPool(new DefaultPooledObjectPolicy(), 10); var threads = new Thread[8]; for (var i = 0; i < threads.Length; i++) @@ -66,7 +78,7 @@ namespace Microsoft.Extensions.ObjectPool _pool.Return(obj2); } } - + private class Item { public int i = 0; From 491cbdd1194b8006a611272f1e9df7f56461e9d8 Mon Sep 17 00:00:00 2001 From: Ryan Nowak Date: Sun, 17 Feb 2019 12:44:35 -0800 Subject: [PATCH 32/52] Update ObjectPool reference assemblies \n\nCommit migrated from https://github.com/dotnet/extensions/commit/f13113c06bea25f2d9ab6daae6f5b12ebaf74cdf --- .../ref/Microsoft.Extensions.ObjectPool.netstandard2.0.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/ObjectPool/ref/Microsoft.Extensions.ObjectPool.netstandard2.0.cs b/src/ObjectPool/ref/Microsoft.Extensions.ObjectPool.netstandard2.0.cs index 16fd8379a6..083aaf14ef 100644 --- a/src/ObjectPool/ref/Microsoft.Extensions.ObjectPool.netstandard2.0.cs +++ b/src/ObjectPool/ref/Microsoft.Extensions.ObjectPool.netstandard2.0.cs @@ -38,6 +38,10 @@ namespace Microsoft.Extensions.ObjectPool public override T Get() { throw null; } public override void Return(T obj) { } } + public static partial class ObjectPool + { + public static Microsoft.Extensions.ObjectPool.ObjectPool Create(Microsoft.Extensions.ObjectPool.IPooledObjectPolicy policy = null) where T : class, new() { throw null; } + } public abstract partial class ObjectPoolProvider { protected ObjectPoolProvider() { } From a4cd6152b674aabcfecf392ff3e1bcb580b7163b Mon Sep 17 00:00:00 2001 From: Pranav K Date: Tue, 19 Feb 2019 13:01:13 -0800 Subject: [PATCH 33/52] Make JSRuntime.Current non-public (dotnet/extensions#1118) Fixes https://github.com/aspnet/AspNetCore/issues/6828\n\nCommit migrated from https://github.com/dotnet/extensions/commit/c1d8be8b980de62a299958e1816f06a81fea07bb --- .../ref/Microsoft.JSInterop.netstandard2.0.cs | 1 - src/JSInterop/Microsoft.JSInterop/src/JSRuntime.cs | 8 ++------ 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netstandard2.0.cs b/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netstandard2.0.cs index 02146c7bab..95b9b7956c 100644 --- a/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netstandard2.0.cs +++ b/src/JSInterop/Microsoft.JSInterop/ref/Microsoft.JSInterop.netstandard2.0.cs @@ -52,7 +52,6 @@ namespace Microsoft.JSInterop } public static partial class JSRuntime { - public static Microsoft.JSInterop.IJSRuntime Current { get { throw null; } } public static void SetCurrentJSRuntime(Microsoft.JSInterop.IJSRuntime instance) { } } public abstract partial class JSRuntimeBase : Microsoft.JSInterop.IJSRuntime diff --git a/src/JSInterop/Microsoft.JSInterop/src/JSRuntime.cs b/src/JSInterop/Microsoft.JSInterop/src/JSRuntime.cs index 5a9830fa35..ae097ca68e 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/JSRuntime.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/JSRuntime.cs @@ -11,13 +11,9 @@ namespace Microsoft.JSInterop /// public static class JSRuntime { - private static AsyncLocal _currentJSRuntime - = new AsyncLocal(); + private static readonly AsyncLocal _currentJSRuntime = new AsyncLocal(); - /// - /// Gets the current , if any. - /// - public static IJSRuntime Current => _currentJSRuntime.Value; + internal static IJSRuntime Current => _currentJSRuntime.Value; /// /// Sets the current JS runtime to the supplied instance. From ffd47e1bd84cc405de8a0d4eacc398840a07678c Mon Sep 17 00:00:00 2001 From: Pavel Krymets Date: Tue, 19 Feb 2019 17:25:53 -0800 Subject: [PATCH 34/52] Manualy update tooling dependencies to normalize names (dotnet/extensions#1135) * Manualy update tooling dependencies to normalize names * Nullable \n\nCommit migrated from https://github.com/dotnet/extensions/commit/77db8d2b6bcf2d3b655a1d4852febf0fb47cca82 --- ...iagnostics.HealthChecks.Abstractions.netstandard2.0.cs | 4 ++-- ....Extensions.Diagnostics.HealthChecks.netstandard2.0.cs | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/HealthChecks/Abstractions/ref/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.netstandard2.0.cs b/src/HealthChecks/Abstractions/ref/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.netstandard2.0.cs index c0117ef473..7fb5beb0e9 100644 --- a/src/HealthChecks/Abstractions/ref/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.netstandard2.0.cs +++ b/src/HealthChecks/Abstractions/ref/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.netstandard2.0.cs @@ -10,8 +10,8 @@ namespace Microsoft.Extensions.Diagnostics.HealthChecks } public sealed partial class HealthCheckRegistration { - public HealthCheckRegistration(string name, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck instance, System.Nullable failureStatus, System.Collections.Generic.IEnumerable tags) { } - public HealthCheckRegistration(string name, System.Func factory, System.Nullable failureStatus, System.Collections.Generic.IEnumerable tags) { } + public HealthCheckRegistration(string name, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck instance, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus, System.Collections.Generic.IEnumerable tags) { } + public HealthCheckRegistration(string name, System.Func factory, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus, System.Collections.Generic.IEnumerable tags) { } public System.Func 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 { } } diff --git a/src/HealthChecks/HealthChecks/ref/Microsoft.Extensions.Diagnostics.HealthChecks.netstandard2.0.cs b/src/HealthChecks/HealthChecks/ref/Microsoft.Extensions.Diagnostics.HealthChecks.netstandard2.0.cs index 89281584e2..58bdc80602 100644 --- a/src/HealthChecks/HealthChecks/ref/Microsoft.Extensions.Diagnostics.HealthChecks.netstandard2.0.cs +++ b/src/HealthChecks/HealthChecks/ref/Microsoft.Extensions.Diagnostics.HealthChecks.netstandard2.0.cs @@ -5,10 +5,10 @@ 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, System.Nullable failureStatus = default(System.Nullable), System.Collections.Generic.IEnumerable tags = null) { throw null; } - public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, System.Nullable failureStatus = default(System.Nullable), System.Collections.Generic.IEnumerable tags = null) where T : class, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck { throw null; } - public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddTypeActivatedCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, System.Nullable failureStatus, System.Collections.Generic.IEnumerable tags, params object[] args) where T : class, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck { throw null; } - public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddTypeActivatedCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, System.Nullable failureStatus, params object[] args) where T : class, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck { 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 tags = null) { throw null; } + public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus = default(Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus?), System.Collections.Generic.IEnumerable tags = null) where T : class, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck { throw null; } + public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddTypeActivatedCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus, System.Collections.Generic.IEnumerable tags, params object[] args) where T : class, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck { throw null; } + public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddTypeActivatedCheck(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(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 From 58ea57e63f33e15049676b62beb595d951b004d1 Mon Sep 17 00:00:00 2001 From: Ryan Brandenburg Date: Wed, 20 Feb 2019 11:43:55 -0800 Subject: [PATCH 35/52] Obsolete ResourceManagerWithCultureStringLocalizer and WithCulture (dotnet/extensions#1133) Obsolete ResourceManagerWithCultureStringLocalizer and WithCulture \n\nCommit migrated from https://github.com/dotnet/extensions/commit/924015e98bc443023a6b0eea2c0016b876e4051f --- ...Extensions.Localization.Abstractions.netstandard2.0.cs | 2 ++ src/Localization/Abstractions/src/IStringLocalizer.cs | 8 +++++--- src/Localization/Abstractions/src/StringLocalizerOfT.cs | 7 ++++--- .../Microsoft.Extensions.Localization.netstandard2.0.cs | 2 ++ .../Localization/src/ResourceManagerStringLocalizer.cs | 7 ++++--- .../src/ResourceManagerWithCultureStringLocalizer.cs | 7 ++++--- .../test/ResourceManagerStringLocalizerTest.cs | 5 ++--- 7 files changed, 23 insertions(+), 15 deletions(-) diff --git a/src/Localization/Abstractions/ref/Microsoft.Extensions.Localization.Abstractions.netstandard2.0.cs b/src/Localization/Abstractions/ref/Microsoft.Extensions.Localization.Abstractions.netstandard2.0.cs index b18cec35f8..3f9f422021 100644 --- a/src/Localization/Abstractions/ref/Microsoft.Extensions.Localization.Abstractions.netstandard2.0.cs +++ b/src/Localization/Abstractions/ref/Microsoft.Extensions.Localization.Abstractions.netstandard2.0.cs @@ -8,6 +8,7 @@ namespace Microsoft.Extensions.Localization Microsoft.Extensions.Localization.LocalizedString this[string name] { get; } Microsoft.Extensions.Localization.LocalizedString this[string name, params object[] arguments] { get; } System.Collections.Generic.IEnumerable GetAllStrings(bool includeParentCultures); + [System.ObsoleteAttribute("This method is obsolete. Use `CurrentCulture` and `CurrentUICulture` instead.")] Microsoft.Extensions.Localization.IStringLocalizer WithCulture(System.Globalization.CultureInfo culture); } public partial interface IStringLocalizerFactory @@ -42,6 +43,7 @@ namespace Microsoft.Extensions.Localization public virtual Microsoft.Extensions.Localization.LocalizedString this[string name] { get { throw null; } } public virtual Microsoft.Extensions.Localization.LocalizedString this[string name, params object[] arguments] { get { throw null; } } public System.Collections.Generic.IEnumerable GetAllStrings(bool includeParentCultures) { throw null; } + [System.ObsoleteAttribute("This method is obsolete. Use `CurrentCulture` and `CurrentUICulture` instead.")] public virtual Microsoft.Extensions.Localization.IStringLocalizer WithCulture(System.Globalization.CultureInfo culture) { throw null; } } } diff --git a/src/Localization/Abstractions/src/IStringLocalizer.cs b/src/Localization/Abstractions/src/IStringLocalizer.cs index 0e1145bbca..cabdf434ac 100644 --- a/src/Localization/Abstractions/src/IStringLocalizer.cs +++ b/src/Localization/Abstractions/src/IStringLocalizer.cs @@ -1,6 +1,7 @@ -// 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. +// 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.Globalization; @@ -40,6 +41,7 @@ namespace Microsoft.Extensions.Localization /// /// The to use. /// A culture-specific . + [Obsolete("This method is obsolete. Use `CurrentCulture` and `CurrentUICulture` instead.")] IStringLocalizer WithCulture(CultureInfo culture); } -} \ No newline at end of file +} diff --git a/src/Localization/Abstractions/src/StringLocalizerOfT.cs b/src/Localization/Abstractions/src/StringLocalizerOfT.cs index 131c1126ec..4190ca14ff 100644 --- a/src/Localization/Abstractions/src/StringLocalizerOfT.cs +++ b/src/Localization/Abstractions/src/StringLocalizerOfT.cs @@ -1,5 +1,5 @@ -// 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. +// 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; @@ -30,6 +30,7 @@ namespace Microsoft.Extensions.Localization } /// + [Obsolete("This method is obsolete. Use `CurrentCulture` and `CurrentUICulture` instead.")] public virtual IStringLocalizer WithCulture(CultureInfo culture) => _localizer.WithCulture(culture); /// @@ -64,4 +65,4 @@ namespace Microsoft.Extensions.Localization public IEnumerable GetAllStrings(bool includeParentCultures) => _localizer.GetAllStrings(includeParentCultures); } -} \ No newline at end of file +} diff --git a/src/Localization/Localization/ref/Microsoft.Extensions.Localization.netstandard2.0.cs b/src/Localization/Localization/ref/Microsoft.Extensions.Localization.netstandard2.0.cs index 7bbe1797a5..80175da718 100644 --- a/src/Localization/Localization/ref/Microsoft.Extensions.Localization.netstandard2.0.cs +++ b/src/Localization/Localization/ref/Microsoft.Extensions.Localization.netstandard2.0.cs @@ -36,6 +36,7 @@ namespace Microsoft.Extensions.Localization public virtual System.Collections.Generic.IEnumerable GetAllStrings(bool includeParentCultures) { throw null; } protected System.Collections.Generic.IEnumerable GetAllStrings(bool includeParentCultures, System.Globalization.CultureInfo culture) { throw null; } protected string GetStringSafely(string name, System.Globalization.CultureInfo culture) { throw null; } + [System.ObsoleteAttribute("This method is obsolete. Use `CurrentCulture` and `CurrentUICulture` instead.")] public Microsoft.Extensions.Localization.IStringLocalizer WithCulture(System.Globalization.CultureInfo culture) { throw null; } } public partial class ResourceManagerStringLocalizerFactory : Microsoft.Extensions.Localization.IStringLocalizerFactory @@ -51,6 +52,7 @@ namespace Microsoft.Extensions.Localization protected virtual string GetResourcePrefix(string location, string baseName, string resourceLocation) { throw null; } protected virtual Microsoft.Extensions.Localization.RootNamespaceAttribute GetRootNamespaceAttribute(System.Reflection.Assembly assembly) { throw null; } } + [System.ObsoleteAttribute("This method is obsolete. Use `CurrentCulture` and `CurrentUICulture` instead.")] public partial class ResourceManagerWithCultureStringLocalizer : Microsoft.Extensions.Localization.ResourceManagerStringLocalizer { public ResourceManagerWithCultureStringLocalizer(System.Resources.ResourceManager resourceManager, System.Reflection.Assembly resourceAssembly, string baseName, Microsoft.Extensions.Localization.IResourceNamesCache resourceNamesCache, System.Globalization.CultureInfo culture, Microsoft.Extensions.Logging.ILogger logger) : base (default(System.Resources.ResourceManager), default(System.Reflection.Assembly), default(string), default(Microsoft.Extensions.Localization.IResourceNamesCache), default(Microsoft.Extensions.Logging.ILogger)) { } diff --git a/src/Localization/Localization/src/ResourceManagerStringLocalizer.cs b/src/Localization/Localization/src/ResourceManagerStringLocalizer.cs index e2e1a3f234..90f8e077b4 100644 --- a/src/Localization/Localization/src/ResourceManagerStringLocalizer.cs +++ b/src/Localization/Localization/src/ResourceManagerStringLocalizer.cs @@ -1,5 +1,5 @@ -// 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. +// 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.Concurrent; @@ -151,6 +151,7 @@ namespace Microsoft.Extensions.Localization /// /// The to use. /// A culture-specific . + [Obsolete("This method is obsolete. Use `CurrentCulture` and `CurrentUICulture` instead.")] public IStringLocalizer WithCulture(CultureInfo culture) { return culture == null @@ -271,4 +272,4 @@ namespace Microsoft.Extensions.Localization return resourceNames; } } -} \ No newline at end of file +} diff --git a/src/Localization/Localization/src/ResourceManagerWithCultureStringLocalizer.cs b/src/Localization/Localization/src/ResourceManagerWithCultureStringLocalizer.cs index 65b6ae242c..2bc51289da 100644 --- a/src/Localization/Localization/src/ResourceManagerWithCultureStringLocalizer.cs +++ b/src/Localization/Localization/src/ResourceManagerWithCultureStringLocalizer.cs @@ -1,5 +1,5 @@ -// 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. +// 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; @@ -15,6 +15,7 @@ namespace Microsoft.Extensions.Localization /// An that uses the and /// to provide localized strings for a specific . /// + [Obsolete("This method is obsolete. Use `CurrentCulture` and `CurrentUICulture` instead.")] public class ResourceManagerWithCultureStringLocalizer : ResourceManagerStringLocalizer { private readonly string _resourceBaseName; @@ -161,4 +162,4 @@ namespace Microsoft.Extensions.Localization public override IEnumerable GetAllStrings(bool includeParentCultures) => GetAllStrings(includeParentCultures, _culture); } -} \ No newline at end of file +} diff --git a/src/Localization/Localization/test/ResourceManagerStringLocalizerTest.cs b/src/Localization/Localization/test/ResourceManagerStringLocalizerTest.cs index ff7bfa9933..a82ce9d1d3 100644 --- a/src/Localization/Localization/test/ResourceManagerStringLocalizerTest.cs +++ b/src/Localization/Localization/test/ResourceManagerStringLocalizerTest.cs @@ -182,12 +182,11 @@ namespace Microsoft.Extensions.Localization var resourceManager = new TestResourceManager(baseName, resourceAssembly); var logger = Logger; - var localizer = new ResourceManagerWithCultureStringLocalizer( + var localizer = new ResourceManagerStringLocalizer( resourceManager, resourceAssembly.Assembly, baseName, resourceNamesCache, - CultureInfo.CurrentCulture, logger); // Act & Assert @@ -291,7 +290,7 @@ namespace Microsoft.Extensions.Localization public override Stream GetManifestResourceStream(string name) { ManifestResourceStreamCallCount++; - + return HasResources ? MakeResourceStream() : null; } } From d805cf3b2e5507940152361af106b74eb8043f3b Mon Sep 17 00:00:00 2001 From: Nathanael Marchand Date: Mon, 18 Feb 2019 15:48:45 +0100 Subject: [PATCH 36/52] Implement parallel health checks \n\nCommit migrated from https://github.com/dotnet/extensions/commit/f14a45e09670dc998be5750e66f363ca45e6931c --- .../src/DefaultHealthCheckService.cs | 137 +++++++++--------- 1 file changed, 71 insertions(+), 66 deletions(-) diff --git a/src/HealthChecks/HealthChecks/src/DefaultHealthCheckService.cs b/src/HealthChecks/HealthChecks/src/DefaultHealthCheckService.cs index d5d71d9cb4..b2b99c99b3 100644 --- a/src/HealthChecks/HealthChecks/src/DefaultHealthCheckService.cs +++ b/src/HealthChecks/HealthChecks/src/DefaultHealthCheckService.cs @@ -39,76 +39,81 @@ namespace Microsoft.Extensions.Diagnostics.HealthChecks Func predicate, CancellationToken cancellationToken = default) { - var registrations = _options.Value.Registrations; + async Task<(string registrationName, HealthReportEntry result)> RunCheckAsync(IServiceScope scope, HealthCheckRegistration registration) + { + 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; + try + { + var result = await healthCheck.CheckHealthAsync(context, cancellationToken); + var duration = stopwatch.GetElapsedTime(); + + entry = new HealthReportEntry( + status: result.Status, + description: result.Description, + duration: duration, + exception: result.Exception, + data: result.Data); + + Log.HealthCheckEnd(_logger, registration, entry, duration); + Log.HealthCheckData(_logger, registration, entry); + } + + // Allow cancellation to propagate. + 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); + } + + return (registration.Name, entry); + } + } + + IEnumerable registrations = _options.Value.Registrations; + if (predicate != null) + { + registrations = registrations.Where(predicate); + } + + var totalTime = ValueStopwatch.StartNew(); + Log.HealthCheckProcessingBegin(_logger); + + (string registrationName, HealthReportEntry result)[] results; using (var scope = _scopeFactory.CreateScope()) { - var context = new HealthCheckContext(); - var entries = new Dictionary(StringComparer.OrdinalIgnoreCase); - - var totalTime = ValueStopwatch.StartNew(); - Log.HealthCheckProcessingBegin(_logger); - - foreach (var registration in registrations) - { - if (predicate != null && !predicate(registration)) - { - continue; - } - - 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(); - context.Registration = registration; - - Log.HealthCheckBegin(_logger, registration); - - HealthReportEntry entry; - try - { - var result = await healthCheck.CheckHealthAsync(context, cancellationToken); - var duration = stopwatch.GetElapsedTime(); - - entry = new HealthReportEntry( - status: result.Status, - description: result.Description, - duration: duration, - exception: result.Exception, - data: result.Data); - - Log.HealthCheckEnd(_logger, registration, entry, duration); - Log.HealthCheckData(_logger, registration, entry); - } - - // Allow cancellation to propagate. - 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); - } - - entries[registration.Name] = entry; - } - } - - var totalElapsedTime = totalTime.GetElapsedTime(); - var report = new HealthReport(entries, totalElapsedTime); - Log.HealthCheckProcessingEnd(_logger, report.Status, totalElapsedTime); - return report; + results = await Task.WhenAll(registrations.Select(r => RunCheckAsync(scope, r))); } + + var entries = new Dictionary(StringComparer.OrdinalIgnoreCase); + foreach (var (registrationName, result) in results) + { + entries[registrationName] = result; + } + + var totalElapsedTime = totalTime.GetElapsedTime(); + var report = new HealthReport(entries, totalElapsedTime); + Log.HealthCheckProcessingEnd(_logger, report.Status, totalElapsedTime); + return report; } private static void ValidateRegistrations(IEnumerable registrations) From 30c6051d3c1bc558f696d917816994200e2eee60 Mon Sep 17 00:00:00 2001 From: Nathanael Marchand Date: Wed, 20 Feb 2019 12:10:04 +0100 Subject: [PATCH 37/52] Take in account code review \n\nCommit migrated from https://github.com/dotnet/extensions/commit/ecea8c802e8dccee184c8cdaf251a5c8b9b80d57 --- .../src/DefaultHealthCheckService.cs | 121 ++++++++++-------- .../test/DefaultHealthCheckServiceTest.cs | 40 +++++- .../HealthCheckPublisherHostedServiceTest.cs | 12 +- 3 files changed, 110 insertions(+), 63 deletions(-) diff --git a/src/HealthChecks/HealthChecks/src/DefaultHealthCheckService.cs b/src/HealthChecks/HealthChecks/src/DefaultHealthCheckService.cs index b2b99c99b3..da8c00a5aa 100644 --- a/src/HealthChecks/HealthChecks/src/DefaultHealthCheckService.cs +++ b/src/HealthChecks/HealthChecks/src/DefaultHealthCheckService.cs @@ -39,75 +39,32 @@ namespace Microsoft.Extensions.Diagnostics.HealthChecks Func predicate, CancellationToken cancellationToken = default) { - async Task<(string registrationName, HealthReportEntry result)> RunCheckAsync(IServiceScope scope, HealthCheckRegistration registration) - { - 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; - try - { - var result = await healthCheck.CheckHealthAsync(context, cancellationToken); - var duration = stopwatch.GetElapsedTime(); - - entry = new HealthReportEntry( - status: result.Status, - description: result.Description, - duration: duration, - exception: result.Exception, - data: result.Data); - - Log.HealthCheckEnd(_logger, registration, entry, duration); - Log.HealthCheckData(_logger, registration, entry); - } - - // Allow cancellation to propagate. - 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); - } - - return (registration.Name, entry); - } - } - - IEnumerable registrations = _options.Value.Registrations; + var registrations = _options.Value.Registrations; if (predicate != null) { - registrations = registrations.Where(predicate); + registrations = registrations.Where(predicate).ToArray(); } var totalTime = ValueStopwatch.StartNew(); Log.HealthCheckProcessingBegin(_logger); - (string registrationName, HealthReportEntry result)[] results; + var tasks = new Task[registrations.Count]; + var index = 0; using (var scope = _scopeFactory.CreateScope()) { - results = await Task.WhenAll(registrations.Select(r => RunCheckAsync(scope, r))); + foreach (var registration in registrations) + { + tasks[index++] = RunCheckAsync(scope, registration, cancellationToken); + } + + await Task.WhenAll(tasks).ConfigureAwait(false); } + index = 0; var entries = new Dictionary(StringComparer.OrdinalIgnoreCase); - foreach (var (registrationName, result) in results) + foreach (var registration in registrations) { - entries[registrationName] = result; + entries[registration.Name] = tasks[index++].Result; } var totalElapsedTime = totalTime.GetElapsedTime(); @@ -116,6 +73,58 @@ namespace Microsoft.Extensions.Diagnostics.HealthChecks return report; } + private async Task RunCheckAsync(IServiceScope scope, HealthCheckRegistration registration, CancellationToken cancellationToken) + { + await Task.Yield(); + + 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; + try + { + var result = await healthCheck.CheckHealthAsync(context, cancellationToken); + var duration = stopwatch.GetElapsedTime(); + + entry = new HealthReportEntry( + status: result.Status, + description: result.Description, + duration: duration, + exception: result.Exception, + data: result.Data); + + Log.HealthCheckEnd(_logger, registration, entry, duration); + Log.HealthCheckData(_logger, registration, entry); + } + + // Allow cancellation to propagate. + 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); + } + + return entry; + } + } + private static void ValidateRegistrations(IEnumerable registrations) { // Scan the list for duplicate names to provide a better error if there are duplicates. diff --git a/src/HealthChecks/HealthChecks/test/DefaultHealthCheckServiceTest.cs b/src/HealthChecks/HealthChecks/test/DefaultHealthCheckServiceTest.cs index 9ab991204e..cc6d9936a2 100644 --- a/src/HealthChecks/HealthChecks/test/DefaultHealthCheckServiceTest.cs +++ b/src/HealthChecks/HealthChecks/test/DefaultHealthCheckServiceTest.cs @@ -375,12 +375,50 @@ namespace Microsoft.Extensions.Diagnostics.HealthChecks }); } - private static DefaultHealthCheckService CreateHealthChecksService(Action configure) + [Fact] + public async Task CheckHealthAsync_ChecksAreRunInParallel() + { + // Arrange + var sink = new TestSink(); + async Task CheckMethod() + { + await Task.Delay(100); + return HealthCheckResult.Healthy(); + } + var service = CreateHealthChecksService(b => + { + b.AddAsyncCheck("test1", CheckMethod); + b.AddAsyncCheck("test2", CheckMethod); + b.AddAsyncCheck("test3", CheckMethod); + }, sink); + + // Act + _ = await service.CheckHealthAsync(); + + // Assert + Assert.Collection( + sink.Writes, + entry => { Assert.Equal(DefaultHealthCheckService.EventIds.HealthCheckProcessingBegin, entry.EventId); }, + entry => { Assert.Equal(DefaultHealthCheckService.EventIds.HealthCheckBegin, entry.EventId); }, + entry => { Assert.Equal(DefaultHealthCheckService.EventIds.HealthCheckBegin, entry.EventId); }, + entry => { Assert.Equal(DefaultHealthCheckService.EventIds.HealthCheckBegin, entry.EventId); }, + entry => { Assert.Equal(DefaultHealthCheckService.EventIds.HealthCheckEnd, entry.EventId); }, + entry => { Assert.Equal(DefaultHealthCheckService.EventIds.HealthCheckEnd, entry.EventId); }, + entry => { Assert.Equal(DefaultHealthCheckService.EventIds.HealthCheckEnd, entry.EventId); }, + entry => { Assert.Equal(DefaultHealthCheckService.EventIds.HealthCheckProcessingEnd, entry.EventId); }); + } + + private static DefaultHealthCheckService CreateHealthChecksService(Action configure, ITestSink sink = null) { var services = new ServiceCollection(); services.AddLogging(); services.AddOptions(); + if (sink != null) + { + services.AddSingleton(new TestLoggerFactory(sink, enabled: true)); + } + var builder = services.AddHealthChecks(); if (configure != null) { diff --git a/src/HealthChecks/HealthChecks/test/HealthCheckPublisherHostedServiceTest.cs b/src/HealthChecks/HealthChecks/test/HealthCheckPublisherHostedServiceTest.cs index 94687efcb8..099944a473 100644 --- a/src/HealthChecks/HealthChecks/test/HealthCheckPublisherHostedServiceTest.cs +++ b/src/HealthChecks/HealthChecks/test/HealthCheckPublisherHostedServiceTest.cs @@ -211,8 +211,8 @@ namespace Microsoft.Extensions.Diagnostics.HealthChecks entry => { Assert.Equal(HealthCheckPublisherHostedService.EventIds.HealthCheckPublisherProcessingBegin, entry.EventId); }, entry => { Assert.Equal(DefaultHealthCheckService.EventIds.HealthCheckProcessingBegin, entry.EventId); }, entry => { Assert.Equal(DefaultHealthCheckService.EventIds.HealthCheckBegin, entry.EventId); }, - entry => { Assert.Equal(DefaultHealthCheckService.EventIds.HealthCheckEnd, entry.EventId); }, - entry => { Assert.Equal(DefaultHealthCheckService.EventIds.HealthCheckBegin, entry.EventId); }, + entry => { Assert.Contains(entry.EventId, new[] { DefaultHealthCheckService.EventIds.HealthCheckBegin, DefaultHealthCheckService.EventIds.HealthCheckEnd }); }, + entry => { Assert.Contains(entry.EventId, new[] { DefaultHealthCheckService.EventIds.HealthCheckBegin, DefaultHealthCheckService.EventIds.HealthCheckEnd }); }, entry => { Assert.Equal(DefaultHealthCheckService.EventIds.HealthCheckEnd, entry.EventId); }, entry => { Assert.Equal(DefaultHealthCheckService.EventIds.HealthCheckProcessingEnd, entry.EventId); }, entry => { Assert.Equal(HealthCheckPublisherHostedService.EventIds.HealthCheckPublisherBegin, entry.EventId); }, @@ -321,8 +321,8 @@ namespace Microsoft.Extensions.Diagnostics.HealthChecks entry => { Assert.Equal(HealthCheckPublisherHostedService.EventIds.HealthCheckPublisherProcessingBegin, entry.EventId); }, entry => { Assert.Equal(DefaultHealthCheckService.EventIds.HealthCheckProcessingBegin, entry.EventId); }, entry => { Assert.Equal(DefaultHealthCheckService.EventIds.HealthCheckBegin, entry.EventId); }, - entry => { Assert.Equal(DefaultHealthCheckService.EventIds.HealthCheckEnd, entry.EventId); }, - entry => { Assert.Equal(DefaultHealthCheckService.EventIds.HealthCheckBegin, entry.EventId); }, + entry => { Assert.Contains(entry.EventId, new[] { DefaultHealthCheckService.EventIds.HealthCheckBegin, DefaultHealthCheckService.EventIds.HealthCheckEnd }); }, + entry => { Assert.Contains(entry.EventId, new[] { DefaultHealthCheckService.EventIds.HealthCheckBegin, DefaultHealthCheckService.EventIds.HealthCheckEnd }); }, entry => { Assert.Equal(DefaultHealthCheckService.EventIds.HealthCheckEnd, entry.EventId); }, entry => { Assert.Equal(DefaultHealthCheckService.EventIds.HealthCheckProcessingEnd, entry.EventId); }, entry => { Assert.Equal(HealthCheckPublisherHostedService.EventIds.HealthCheckPublisherBegin, entry.EventId); }, @@ -399,8 +399,8 @@ namespace Microsoft.Extensions.Diagnostics.HealthChecks entry => { Assert.Equal(HealthCheckPublisherHostedService.EventIds.HealthCheckPublisherProcessingBegin, entry.EventId); }, entry => { Assert.Equal(DefaultHealthCheckService.EventIds.HealthCheckProcessingBegin, entry.EventId); }, entry => { Assert.Equal(DefaultHealthCheckService.EventIds.HealthCheckBegin, entry.EventId); }, - entry => { Assert.Equal(DefaultHealthCheckService.EventIds.HealthCheckEnd, entry.EventId); }, - entry => { Assert.Equal(DefaultHealthCheckService.EventIds.HealthCheckBegin, entry.EventId); }, + entry => { Assert.Contains(entry.EventId, new[] { DefaultHealthCheckService.EventIds.HealthCheckBegin, DefaultHealthCheckService.EventIds.HealthCheckEnd }); }, + entry => { Assert.Contains(entry.EventId, new[] { DefaultHealthCheckService.EventIds.HealthCheckBegin, DefaultHealthCheckService.EventIds.HealthCheckEnd }); }, entry => { Assert.Equal(DefaultHealthCheckService.EventIds.HealthCheckEnd, entry.EventId); }, entry => { Assert.Equal(DefaultHealthCheckService.EventIds.HealthCheckProcessingEnd, entry.EventId); }, entry => { Assert.Equal(HealthCheckPublisherHostedService.EventIds.HealthCheckPublisherBegin, entry.EventId); }, From 1f05a6666142453397c78dc72021a98f6f7f6a93 Mon Sep 17 00:00:00 2001 From: Nathanael Marchand Date: Thu, 21 Feb 2019 11:12:32 +0100 Subject: [PATCH 38/52] Take in account code review for test \n\nCommit migrated from https://github.com/dotnet/extensions/commit/49bf9920e4e9079b02e2f775b2bc923abf2be4d1 --- .../test/DefaultHealthCheckServiceTest.cs | 65 +++++++++++-------- 1 file changed, 38 insertions(+), 27 deletions(-) diff --git a/src/HealthChecks/HealthChecks/test/DefaultHealthCheckServiceTest.cs b/src/HealthChecks/HealthChecks/test/DefaultHealthCheckServiceTest.cs index cc6d9936a2..fa08cccd20 100644 --- a/src/HealthChecks/HealthChecks/test/DefaultHealthCheckServiceTest.cs +++ b/src/HealthChecks/HealthChecks/test/DefaultHealthCheckServiceTest.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; +using Microsoft.AspNetCore.Testing; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Testing; @@ -379,46 +380,56 @@ namespace Microsoft.Extensions.Diagnostics.HealthChecks public async Task CheckHealthAsync_ChecksAreRunInParallel() { // Arrange - var sink = new TestSink(); - async Task CheckMethod() - { - await Task.Delay(100); - return HealthCheckResult.Healthy(); - } + var input1 = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + var input2 = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + var output1 = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + var output2 = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + var service = CreateHealthChecksService(b => { - b.AddAsyncCheck("test1", CheckMethod); - b.AddAsyncCheck("test2", CheckMethod); - b.AddAsyncCheck("test3", CheckMethod); - }, sink); + b.AddAsyncCheck("test1", + async () => + { + output1.SetResult(null); + await input1.Task; + return HealthCheckResult.Healthy(); + }); + b.AddAsyncCheck("test2", + async () => + { + output2.SetResult(null); + await input2.Task; + return HealthCheckResult.Healthy(); + }); + }); // Act - _ = await service.CheckHealthAsync(); + var checkHealthTask = service.CheckHealthAsync(); + await Task.WhenAll(output1.Task, output2.Task).TimeoutAfter(TimeSpan.FromSeconds(10)); + input1.SetResult(null); + input2.SetResult(null); + await checkHealthTask; // Assert - Assert.Collection( - sink.Writes, - entry => { Assert.Equal(DefaultHealthCheckService.EventIds.HealthCheckProcessingBegin, entry.EventId); }, - entry => { Assert.Equal(DefaultHealthCheckService.EventIds.HealthCheckBegin, entry.EventId); }, - entry => { Assert.Equal(DefaultHealthCheckService.EventIds.HealthCheckBegin, entry.EventId); }, - entry => { Assert.Equal(DefaultHealthCheckService.EventIds.HealthCheckBegin, entry.EventId); }, - entry => { Assert.Equal(DefaultHealthCheckService.EventIds.HealthCheckEnd, entry.EventId); }, - entry => { Assert.Equal(DefaultHealthCheckService.EventIds.HealthCheckEnd, entry.EventId); }, - entry => { Assert.Equal(DefaultHealthCheckService.EventIds.HealthCheckEnd, entry.EventId); }, - entry => { Assert.Equal(DefaultHealthCheckService.EventIds.HealthCheckProcessingEnd, entry.EventId); }); + Assert.Collection(checkHealthTask.Result.Entries, + entry => + { + Assert.Equal("test1", entry.Key); + Assert.Equal(HealthStatus.Healthy, entry.Value.Status); + }, + entry => + { + Assert.Equal("test2", entry.Key); + Assert.Equal(HealthStatus.Healthy, entry.Value.Status); + }); } - private static DefaultHealthCheckService CreateHealthChecksService(Action configure, ITestSink sink = null) + private static DefaultHealthCheckService CreateHealthChecksService(Action configure) { var services = new ServiceCollection(); services.AddLogging(); services.AddOptions(); - if (sink != null) - { - services.AddSingleton(new TestLoggerFactory(sink, enabled: true)); - } - var builder = services.AddHealthChecks(); if (configure != null) { From 7960bde7dada91a3c8bd51c971dcd6973d5aadde Mon Sep 17 00:00:00 2001 From: Nathanael Marchand Date: Tue, 19 Feb 2019 10:23:49 +0100 Subject: [PATCH 39/52] Implement Timeout \n\nCommit migrated from https://github.com/dotnet/extensions/commit/50593c9336a6746bcafa6325fc5ac772bb07fc65 --- ...ealthChecks.Abstractions.netstandard2.0.cs | 3 + .../src/HealthCheckRegistration.cs | 71 +++++++++++- ...Diagnostics.HealthChecks.netstandard2.0.cs | 19 +++- .../src/DefaultHealthCheckService.cs | 34 +++++- .../HealthChecksBuilderAddCheckExtensions.cs | 106 +++++++++++++++++- .../HealthChecksBuilderDelegateExtensions.cs | 98 ++++++++++++++-- .../test/DefaultHealthCheckServiceTest.cs | 26 +++++ 7 files changed, 332 insertions(+), 25 deletions(-) diff --git a/src/HealthChecks/Abstractions/ref/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.netstandard2.0.cs b/src/HealthChecks/Abstractions/ref/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.netstandard2.0.cs index 7fb5beb0e9..9ab497257e 100644 --- a/src/HealthChecks/Abstractions/ref/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.netstandard2.0.cs +++ b/src/HealthChecks/Abstractions/ref/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.netstandard2.0.cs @@ -11,11 +11,14 @@ namespace Microsoft.Extensions.Diagnostics.HealthChecks 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 tags) { } + public HealthCheckRegistration(string name, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck instance, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus, System.Collections.Generic.IEnumerable tags, System.TimeSpan? timeout) { } public HealthCheckRegistration(string name, System.Func factory, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus, System.Collections.Generic.IEnumerable tags) { } + public HealthCheckRegistration(string name, System.Func factory, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus, System.Collections.Generic.IEnumerable tags, System.TimeSpan? timeout) { } public System.Func 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 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 diff --git a/src/HealthChecks/Abstractions/src/HealthCheckRegistration.cs b/src/HealthChecks/Abstractions/src/HealthCheckRegistration.cs index 9291c38846..8ee11e3195 100644 --- a/src/HealthChecks/Abstractions/src/HealthCheckRegistration.cs +++ b/src/HealthChecks/Abstractions/src/HealthCheckRegistration.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// 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; @@ -24,6 +24,22 @@ namespace Microsoft.Extensions.Diagnostics.HealthChecks { private Func _factory; private string _name; + private TimeSpan _timeout; + + /// + /// Creates a new for an existing instance. + /// + /// The health check name. + /// The instance. + /// + /// The that should be reported upon failure of the health check. If the provided value + /// is null, then will be reported. + /// + /// A list of tags that can be used for filtering health checks. + public HealthCheckRegistration(string name, IHealthCheck instance, HealthStatus? failureStatus, IEnumerable tags) + : this(name, instance, failureStatus, tags, default) + { + } /// /// Creates a new for an existing instance. @@ -35,7 +51,8 @@ namespace Microsoft.Extensions.Diagnostics.HealthChecks /// is null, then will be reported. /// /// A list of tags that can be used for filtering health checks. - public HealthCheckRegistration(string name, IHealthCheck instance, HealthStatus? failureStatus, IEnumerable tags) + /// An optional representing the timeout of the check. + public HealthCheckRegistration(string name, IHealthCheck instance, HealthStatus? failureStatus, IEnumerable tags, TimeSpan? timeout) { if (name == null) { @@ -47,10 +64,16 @@ namespace Microsoft.Extensions.Diagnostics.HealthChecks 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(tags ?? Array.Empty(), StringComparer.OrdinalIgnoreCase); Factory = (_) => instance; + Timeout = timeout ?? System.Threading.Timeout.InfiniteTimeSpan; } /// @@ -68,6 +91,27 @@ namespace Microsoft.Extensions.Diagnostics.HealthChecks Func factory, HealthStatus? failureStatus, IEnumerable tags) + : this(name, factory, failureStatus, tags, default) + { + } + + /// + /// Creates a new for an existing instance. + /// + /// The health check name. + /// A delegate used to create the instance. + /// + /// The that should be reported when the health check reports a failure. If the provided value + /// is null, then will be reported. + /// + /// A list of tags that can be used for filtering health checks. + /// An optional representing the timeout of the check. + public HealthCheckRegistration( + string name, + Func factory, + HealthStatus? failureStatus, + IEnumerable tags, + TimeSpan? timeout) { if (name == null) { @@ -79,10 +123,16 @@ namespace Microsoft.Extensions.Diagnostics.HealthChecks 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(tags ?? Array.Empty(), StringComparer.OrdinalIgnoreCase); Factory = factory; + Timeout = timeout ?? System.Threading.Timeout.InfiniteTimeSpan; } /// @@ -107,6 +157,23 @@ namespace Microsoft.Extensions.Diagnostics.HealthChecks /// public HealthStatus FailureStatus { get; set; } + /// + /// Gets or sets the timeout used for the test. + /// + public TimeSpan Timeout + { + get => _timeout; + set + { + if (value <= TimeSpan.Zero && value != System.Threading.Timeout.InfiniteTimeSpan) + { + throw new ArgumentOutOfRangeException(nameof(value)); + } + + _timeout = value; + } + } + /// /// Gets or sets the health check name. /// diff --git a/src/HealthChecks/HealthChecks/ref/Microsoft.Extensions.Diagnostics.HealthChecks.netstandard2.0.cs b/src/HealthChecks/HealthChecks/ref/Microsoft.Extensions.Diagnostics.HealthChecks.netstandard2.0.cs index 58bdc80602..a23961efdd 100644 --- a/src/HealthChecks/HealthChecks/ref/Microsoft.Extensions.Diagnostics.HealthChecks.netstandard2.0.cs +++ b/src/HealthChecks/HealthChecks/ref/Microsoft.Extensions.Diagnostics.HealthChecks.netstandard2.0.cs @@ -5,18 +5,25 @@ 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 = default(Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus?), System.Collections.Generic.IEnumerable tags = null) { throw null; } - public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus = default(Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus?), System.Collections.Generic.IEnumerable tags = null) where T : class, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck { 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, System.Collections.Generic.IEnumerable 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 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, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus, System.Collections.Generic.IEnumerable tags) where T : class, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck { throw null; } + public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus = default(Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus?), System.Collections.Generic.IEnumerable 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(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus, System.Collections.Generic.IEnumerable tags, params object[] args) where T : class, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck { throw null; } + public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddTypeActivatedCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus, System.Collections.Generic.IEnumerable tags, System.TimeSpan timeout, params object[] args) where T : class, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck { throw null; } public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddTypeActivatedCheck(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(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> check, System.Collections.Generic.IEnumerable tags = null) { throw null; } - public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddAsyncCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, System.Func> check, System.Collections.Generic.IEnumerable tags = null) { throw null; } - public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, System.Func check, System.Collections.Generic.IEnumerable tags = null) { throw null; } - public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, System.Func check, System.Collections.Generic.IEnumerable tags = null) { throw null; } + public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddAsyncCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, System.Func> check, System.Collections.Generic.IEnumerable tags) { throw null; } + public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddAsyncCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, System.Func> check, System.Collections.Generic.IEnumerable 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> check, System.Collections.Generic.IEnumerable tags) { throw null; } + public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddAsyncCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, System.Func> check, System.Collections.Generic.IEnumerable 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 check, System.Collections.Generic.IEnumerable tags) { throw null; } + public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, System.Func check, System.Collections.Generic.IEnumerable 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 check, System.Collections.Generic.IEnumerable tags) { throw null; } + public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, System.Func check, System.Collections.Generic.IEnumerable tags = null, System.TimeSpan? timeout = default(System.TimeSpan?)) { throw null; } } public static partial class HealthCheckServiceCollectionExtensions { diff --git a/src/HealthChecks/HealthChecks/src/DefaultHealthCheckService.cs b/src/HealthChecks/HealthChecks/src/DefaultHealthCheckService.cs index da8c00a5aa..56ce966d18 100644 --- a/src/HealthChecks/HealthChecks/src/DefaultHealthCheckService.cs +++ b/src/HealthChecks/HealthChecks/src/DefaultHealthCheckService.cs @@ -91,9 +91,21 @@ namespace Microsoft.Extensions.Diagnostics.HealthChecks Log.HealthCheckBegin(_logger, registration); HealthReportEntry entry; + CancellationTokenSource timeoutCancellationTokenSource = null; try { - var result = await healthCheck.CheckHealthAsync(context, cancellationToken); + 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); + var duration = stopwatch.GetElapsedTime(); entry = new HealthReportEntry( @@ -107,7 +119,20 @@ namespace Microsoft.Extensions.Diagnostics.HealthChecks Log.HealthCheckData(_logger, registration, entry); } - // Allow cancellation to propagate. + catch (OperationCanceledException ex) when (!cancellationToken.IsCancellationRequested) + { + var duration = stopwatch.GetElapsedTime(); + entry = new HealthReportEntry( + status: HealthStatus.Unhealthy, + description: "A timeout occured 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(); @@ -121,6 +146,11 @@ namespace Microsoft.Extensions.Diagnostics.HealthChecks Log.HealthCheckError(_logger, registration, ex, duration); } + finally + { + timeoutCancellationTokenSource?.Dispose(); + } + return entry; } } diff --git a/src/HealthChecks/HealthChecks/src/DependencyInjection/HealthChecksBuilderAddCheckExtensions.cs b/src/HealthChecks/HealthChecks/src/DependencyInjection/HealthChecksBuilderAddCheckExtensions.cs index 9508889054..74ee30290c 100644 --- a/src/HealthChecks/HealthChecks/src/DependencyInjection/HealthChecksBuilderAddCheckExtensions.cs +++ b/src/HealthChecks/HealthChecks/src/DependencyInjection/HealthChecksBuilderAddCheckExtensions.cs @@ -24,12 +24,37 @@ namespace Microsoft.Extensions.DependencyInjection /// /// A list of tags that can be used to filter health checks. /// The . + // 2.0 BACKCOMPAT OVERLOAD -- DO NOT TOUCH + public static IHealthChecksBuilder AddCheck( + this IHealthChecksBuilder builder, + string name, + IHealthCheck instance, + HealthStatus? failureStatus, + IEnumerable tags) + { + return AddCheck(builder, name, instance, failureStatus, tags, default); + } + + /// + /// Adds a new health check with the specified name and implementation. + /// + /// The . + /// The name of the health check. + /// An instance. + /// + /// The that should be reported when the health check reports a failure. If the provided value + /// is null, then will be reported. + /// + /// A list of tags that can be used to filter health checks. + /// An optional representing the timeout of the check. + /// The . public static IHealthChecksBuilder AddCheck( this IHealthChecksBuilder builder, string name, IHealthCheck instance, HealthStatus? failureStatus = null, - IEnumerable tags = null) + IEnumerable tags = null, + TimeSpan? timeout = null) { if (builder == null) { @@ -46,7 +71,7 @@ namespace Microsoft.Extensions.DependencyInjection throw new ArgumentNullException(nameof(instance)); } - return builder.Add(new HealthCheckRegistration(name, instance, failureStatus, tags)); + return builder.Add(new HealthCheckRegistration(name, instance, failureStatus, tags, timeout)); } /// @@ -63,15 +88,45 @@ namespace Microsoft.Extensions.DependencyInjection /// The . /// /// This method will use to create the health check - /// instance when needed. If a service of type is registred in the dependency injection container - /// with any liftime it will be used. Otherwise an instance of type will be constructed with + /// instance when needed. If a service of type is registered in the dependency injection container + /// with any lifetime it will be used. Otherwise an instance of type will be constructed with + /// access to services from the dependency injection container. + /// + // 2.0 BACKCOMPAT OVERLOAD -- DO NOT TOUCH + public static IHealthChecksBuilder AddCheck( + this IHealthChecksBuilder builder, + string name, + HealthStatus? failureStatus, + IEnumerable tags) where T : class, IHealthCheck + { + return AddCheck(builder, name, failureStatus, tags, default); + } + + /// + /// Adds a new health check with the specified name and implementation. + /// + /// The health check implementation type. + /// The . + /// The name of the health check. + /// + /// The that should be reported when the health check reports a failure. If the provided value + /// is null, then will be reported. + /// + /// A list of tags that can be used to filter health checks. + /// An optional representing the timeout of the check. + /// The . + /// + /// This method will use to create the health check + /// instance when needed. If a service of type is registered in the dependency injection container + /// with any lifetime it will be used. Otherwise an instance of type will be constructed with /// access to services from the dependency injection container. /// public static IHealthChecksBuilder AddCheck( this IHealthChecksBuilder builder, string name, HealthStatus? failureStatus = null, - IEnumerable tags = null) where T : class, IHealthCheck + IEnumerable tags = null, + TimeSpan? timeout = null) where T : class, IHealthCheck { if (builder == null) { @@ -83,7 +138,7 @@ namespace Microsoft.Extensions.DependencyInjection throw new ArgumentNullException(nameof(name)); } - return builder.Add(new HealthCheckRegistration(name, s => ActivatorUtilities.GetServiceOrCreateInstance(s), failureStatus, tags)); + return builder.Add(new HealthCheckRegistration(name, s => ActivatorUtilities.GetServiceOrCreateInstance(s), failureStatus, tags, timeout)); } // NOTE: AddTypeActivatedCheck has overloads rather than default parameters values, because default parameter values don't @@ -187,5 +242,44 @@ namespace Microsoft.Extensions.DependencyInjection return builder.Add(new HealthCheckRegistration(name, s => ActivatorUtilities.CreateInstance(s, args), failureStatus, tags)); } + + /// + /// Adds a new type activated health check with the specified name and implementation. + /// + /// The health check implementation type. + /// The . + /// The name of the health check. + /// + /// The that should be reported when the health check reports a failure. If the provided value + /// is null, then will be reported. + /// + /// A list of tags that can be used to filter health checks. + /// Additional arguments to provide to the constructor. + /// A representing the timeout of the check. + /// The . + /// + /// This method will use to create the health check + /// instance when needed. Additional arguments can be provided to the constructor via . + /// + public static IHealthChecksBuilder AddTypeActivatedCheck( + this IHealthChecksBuilder builder, + string name, + HealthStatus? failureStatus, + IEnumerable 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(s, args), failureStatus, tags, timeout)); + } } } diff --git a/src/HealthChecks/HealthChecks/src/DependencyInjection/HealthChecksBuilderDelegateExtensions.cs b/src/HealthChecks/HealthChecks/src/DependencyInjection/HealthChecksBuilderDelegateExtensions.cs index d7dfdd90ae..ba27ab5554 100644 --- a/src/HealthChecks/HealthChecks/src/DependencyInjection/HealthChecksBuilderDelegateExtensions.cs +++ b/src/HealthChecks/HealthChecks/src/DependencyInjection/HealthChecksBuilderDelegateExtensions.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// 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; @@ -22,11 +22,31 @@ namespace Microsoft.Extensions.DependencyInjection /// A list of tags that can be used to filter health checks. /// A delegate that provides the health check implementation. /// The . + // 2.0 BACKCOMPAT OVERLOAD -- DO NOT TOUCH public static IHealthChecksBuilder AddCheck( this IHealthChecksBuilder builder, string name, Func check, - IEnumerable tags = null) + IEnumerable tags) + { + return AddCheck(builder, name, check, tags, default); + } + + /// + /// Adds a new health check with the specified name and implementation. + /// + /// The . + /// The name of the health check. + /// A list of tags that can be used to filter health checks. + /// A delegate that provides the health check implementation. + /// An optional representing the timeout of the check. + /// The . + public static IHealthChecksBuilder AddCheck( + this IHealthChecksBuilder builder, + string name, + Func check, + IEnumerable tags = null, + TimeSpan? timeout = default) { if (builder == null) { @@ -44,7 +64,7 @@ namespace Microsoft.Extensions.DependencyInjection } var instance = new DelegateHealthCheck((ct) => Task.FromResult(check())); - return builder.Add(new HealthCheckRegistration(name, instance, failureStatus: null, tags)); + return builder.Add(new HealthCheckRegistration(name, instance, failureStatus: null, tags, timeout)); } /// @@ -55,11 +75,31 @@ namespace Microsoft.Extensions.DependencyInjection /// A list of tags that can be used to filter health checks. /// A delegate that provides the health check implementation. /// The . + // 2.0 BACKCOMPAT OVERLOAD -- DO NOT TOUCH public static IHealthChecksBuilder AddCheck( this IHealthChecksBuilder builder, string name, Func check, - IEnumerable tags = null) + IEnumerable tags) + { + return AddCheck(builder, name, check, tags, default); + } + + /// + /// Adds a new health check with the specified name and implementation. + /// + /// The . + /// The name of the health check. + /// A list of tags that can be used to filter health checks. + /// A delegate that provides the health check implementation. + /// An optional representing the timeout of the check. + /// The . + public static IHealthChecksBuilder AddCheck( + this IHealthChecksBuilder builder, + string name, + Func check, + IEnumerable tags = null, + TimeSpan? timeout = default) { if (builder == null) { @@ -77,7 +117,7 @@ namespace Microsoft.Extensions.DependencyInjection } var instance = new DelegateHealthCheck((ct) => Task.FromResult(check(ct))); - return builder.Add(new HealthCheckRegistration(name, instance, failureStatus: null, tags)); + return builder.Add(new HealthCheckRegistration(name, instance, failureStatus: null, tags, timeout)); } /// @@ -88,11 +128,31 @@ namespace Microsoft.Extensions.DependencyInjection /// A list of tags that can be used to filter health checks. /// A delegate that provides the health check implementation. /// The . + // 2.0 BACKCOMPAT OVERLOAD -- DO NOT TOUCH public static IHealthChecksBuilder AddAsyncCheck( this IHealthChecksBuilder builder, string name, Func> check, - IEnumerable tags = null) + IEnumerable tags) + { + return AddAsyncCheck(builder, name, check, tags, default); + } + + /// + /// Adds a new health check with the specified name and implementation. + /// + /// The . + /// The name of the health check. + /// A list of tags that can be used to filter health checks. + /// A delegate that provides the health check implementation. + /// An optional representing the timeout of the check. + /// The . + public static IHealthChecksBuilder AddAsyncCheck( + this IHealthChecksBuilder builder, + string name, + Func> check, + IEnumerable tags = null, + TimeSpan? timeout = default) { if (builder == null) { @@ -110,7 +170,7 @@ namespace Microsoft.Extensions.DependencyInjection } var instance = new DelegateHealthCheck((ct) => check()); - return builder.Add(new HealthCheckRegistration(name, instance, failureStatus: null, tags)); + return builder.Add(new HealthCheckRegistration(name, instance, failureStatus: null, tags, timeout)); } /// @@ -121,11 +181,31 @@ namespace Microsoft.Extensions.DependencyInjection /// A list of tags that can be used to filter health checks. /// A delegate that provides the health check implementation. /// The . + // 2.0 BACKCOMPAT OVERLOAD -- DO NOT TOUCH public static IHealthChecksBuilder AddAsyncCheck( this IHealthChecksBuilder builder, string name, Func> check, - IEnumerable tags = null) + IEnumerable tags) + { + return AddAsyncCheck(builder, name, check, tags, default); + } + + /// + /// Adds a new health check with the specified name and implementation. + /// + /// The . + /// The name of the health check. + /// A list of tags that can be used to filter health checks. + /// A delegate that provides the health check implementation. + /// An optional representing the timeout of the check. + /// The . + public static IHealthChecksBuilder AddAsyncCheck( + this IHealthChecksBuilder builder, + string name, + Func> check, + IEnumerable tags = null, + TimeSpan? timeout = default) { if (builder == null) { @@ -143,7 +223,7 @@ namespace Microsoft.Extensions.DependencyInjection } var instance = new DelegateHealthCheck((ct) => check(ct)); - return builder.Add(new HealthCheckRegistration(name, instance, failureStatus: null, tags)); + return builder.Add(new HealthCheckRegistration(name, instance, failureStatus: null, tags, timeout)); } } } diff --git a/src/HealthChecks/HealthChecks/test/DefaultHealthCheckServiceTest.cs b/src/HealthChecks/HealthChecks/test/DefaultHealthCheckServiceTest.cs index fa08cccd20..ba0a2f32d5 100644 --- a/src/HealthChecks/HealthChecks/test/DefaultHealthCheckServiceTest.cs +++ b/src/HealthChecks/HealthChecks/test/DefaultHealthCheckServiceTest.cs @@ -424,6 +424,32 @@ namespace Microsoft.Extensions.Diagnostics.HealthChecks }); } + [Fact] + public async Task CheckHealthAsync_TimeoutReturnsUnhealthy() + { + // Arrange + var service = CreateHealthChecksService(b => + { + b.AddAsyncCheck("timeout", async (ct) => + { + await Task.Delay(2000, ct); + return HealthCheckResult.Healthy(); + }, timeout: TimeSpan.FromMilliseconds(100)); + }); + + // Act + var results = await service.CheckHealthAsync(); + + // Assert + Assert.Collection( + results.Entries, + actual => + { + Assert.Equal("timeout", actual.Key); + Assert.Equal(HealthStatus.Unhealthy, actual.Value.Status); + }); + } + private static DefaultHealthCheckService CreateHealthChecksService(Action configure) { var services = new ServiceCollection(); From a623a5c20fa46d4ea5e3b460af8f9934fd93fbd3 Mon Sep 17 00:00:00 2001 From: Ryan Nowak Date: Fri, 22 Feb 2019 17:49:48 -0800 Subject: [PATCH 40/52] Fix dotnet/extensions#598 and dotnet/extensions#684 Pass args into type activator \n\nCommit migrated from https://github.com/dotnet/extensions/commit/a787ee2870d3cfc416334f37195f0e6f52570474 --- .../HealthChecksBuilderAddCheckExtensions.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/HealthChecks/HealthChecks/src/DependencyInjection/HealthChecksBuilderAddCheckExtensions.cs b/src/HealthChecks/HealthChecks/src/DependencyInjection/HealthChecksBuilderAddCheckExtensions.cs index 74ee30290c..51b7815438 100644 --- a/src/HealthChecks/HealthChecks/src/DependencyInjection/HealthChecksBuilderAddCheckExtensions.cs +++ b/src/HealthChecks/HealthChecks/src/DependencyInjection/HealthChecksBuilderAddCheckExtensions.cs @@ -168,7 +168,7 @@ namespace Microsoft.Extensions.DependencyInjection throw new ArgumentNullException(nameof(name)); } - return AddTypeActivatedCheck(builder, name, failureStatus: null, tags: null); + return AddTypeActivatedCheck(builder, name, failureStatus: null, tags: null, args); } /// @@ -203,7 +203,7 @@ namespace Microsoft.Extensions.DependencyInjection throw new ArgumentNullException(nameof(name)); } - return AddTypeActivatedCheck(builder, name, failureStatus, tags: null); + return AddTypeActivatedCheck(builder, name, failureStatus, tags: null, args); } /// From 09b9a49da6d0e9b06a4fc3a05ae11ecb43149e33 Mon Sep 17 00:00:00 2001 From: Martin Costello Date: Mon, 4 Mar 2019 18:14:16 +0000 Subject: [PATCH 41/52] Atomically swap config data (dotnet/extensions#1202) Swap the configuration providers' data atomically, rather than directly changing the property, so that any enumeration of the dictionary running during the reload operation does not throw an InvalidOperationException due to the collection being modified. Relates to dotnet/extensions#1189.\n\nCommit migrated from https://github.com/dotnet/extensions/commit/192abfdf3e73106e40d7651eecfb621e4f78c344 --- .../src/KeyPerFileConfigurationProvider.cs | 7 ++- .../test/KeyPerFileTests.cs | 43 +++++++++++++++++++ 2 files changed, 48 insertions(+), 2 deletions(-) diff --git a/src/Configuration.KeyPerFile/src/KeyPerFileConfigurationProvider.cs b/src/Configuration.KeyPerFile/src/KeyPerFileConfigurationProvider.cs index 13541110e6..2e33b9dfcd 100644 --- a/src/Configuration.KeyPerFile/src/KeyPerFileConfigurationProvider.cs +++ b/src/Configuration.KeyPerFile/src/KeyPerFileConfigurationProvider.cs @@ -31,12 +31,13 @@ namespace Microsoft.Extensions.Configuration.KeyPerFile /// public override void Load() { - Data = new Dictionary(StringComparer.OrdinalIgnoreCase); + var data = new Dictionary(StringComparer.OrdinalIgnoreCase); if (Source.FileProvider == null) { if (Source.Optional) { + Data = data; return; } @@ -61,10 +62,12 @@ namespace Microsoft.Extensions.Configuration.KeyPerFile { if (Source.IgnoreCondition == null || !Source.IgnoreCondition(file.Name)) { - Data.Add(NormalizeKey(file.Name), TrimNewLine(streamReader.ReadToEnd())); + data.Add(NormalizeKey(file.Name), TrimNewLine(streamReader.ReadToEnd())); } } } + + Data = data; } private string GetDirectoryName() diff --git a/src/Configuration.KeyPerFile/test/KeyPerFileTests.cs b/src/Configuration.KeyPerFile/test/KeyPerFileTests.cs index 4de528c011..838e62222d 100644 --- a/src/Configuration.KeyPerFile/test/KeyPerFileTests.cs +++ b/src/Configuration.KeyPerFile/test/KeyPerFileTests.cs @@ -6,6 +6,8 @@ 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; @@ -179,6 +181,47 @@ namespace Microsoft.Extensions.Configuration.KeyPerFile.Test 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(); + } + } + + Assert.Equal(-2, options.Number); + Assert.Equal("Foo", options.Text); + } + + private sealed class MyOptions + { + public int Number { get; set; } + public string Text { get; set; } + } } class TestFileProvider : IFileProvider From b13ea4cd548e560a5cfe32fedb9fef9a7a32f7bd Mon Sep 17 00:00:00 2001 From: Martin Costello Date: Tue, 5 Mar 2019 17:43:59 +0000 Subject: [PATCH 42/52] Use localhost for HttpListener (dotnet/extensions#1206) Use localhost instead of 127.0.0.1 to fix dotnet/extensions#1205.\n\nCommit migrated from https://github.com/dotnet/extensions/commit/99b4ddbdbc86bffdafa76d7cac568b6b5ca46f1c --- src/Testing/test/HttpClientSlimTest.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Testing/test/HttpClientSlimTest.cs b/src/Testing/test/HttpClientSlimTest.cs index 42b19ece08..ede48243e5 100644 --- a/src/Testing/test/HttpClientSlimTest.cs +++ b/src/Testing/test/HttpClientSlimTest.cs @@ -4,7 +4,6 @@ using System; using System.Net; using System.Net.Http; -using System.Net.Sockets; using System.Text; using System.Threading.Tasks; using Xunit; @@ -13,7 +12,7 @@ namespace Microsoft.AspNetCore.Testing { public class HttpClientSlimTest { - private static byte[] _defaultResponse = Encoding.ASCII.GetBytes("test"); + private static readonly byte[] _defaultResponse = Encoding.ASCII.GetBytes("test"); [Fact] public async Task GetStringAsyncHttp() @@ -79,7 +78,7 @@ namespace Microsoft.AspNetCore.Testing // HttpListener doesn't support requesting port 0 (dynamic). // Requesting port 0 from Sockets and then passing that to HttpListener is racy. // Just keep trying until we find a free one. - address = $"http://127.0.0.1:{random.Next(1024, ushort.MaxValue)}/"; + address = $"http://localhost:{random.Next(1024, ushort.MaxValue)}/"; listener.Prefixes.Add(address); listener.Start(); break; From d9627c80efda3434cf4608e1af70bbff142fb50e Mon Sep 17 00:00:00 2001 From: Andrew Stanton-Nurse Date: Wed, 6 Mar 2019 15:19:11 -0800 Subject: [PATCH 43/52] add FlakyAttribute to mark flaky tests (dotnet/extensions#1222) part of aspnet/AspNetCoredotnet/extensions#8237\n\nCommit migrated from https://github.com/dotnet/extensions/commit/42e9a7d712d1b513c32961dca7ad6d0bd33d0fae --- src/Testing/src/AzurePipelines.cs | 17 ++++ src/Testing/src/HelixQueues.cs | 26 ++++++ src/Testing/src/xunit/FlakyAttribute.cs | 75 +++++++++++++++ src/Testing/src/xunit/FlakyTestDiscoverer.cs | 38 ++++++++ src/Testing/test/FlakyAttributeTest.cs | 97 ++++++++++++++++++++ 5 files changed, 253 insertions(+) create mode 100644 src/Testing/src/AzurePipelines.cs create mode 100644 src/Testing/src/HelixQueues.cs create mode 100644 src/Testing/src/xunit/FlakyAttribute.cs create mode 100644 src/Testing/src/xunit/FlakyTestDiscoverer.cs create mode 100644 src/Testing/test/FlakyAttributeTest.cs diff --git a/src/Testing/src/AzurePipelines.cs b/src/Testing/src/AzurePipelines.cs new file mode 100644 index 0000000000..ae1eac3b90 --- /dev/null +++ b/src/Testing/src/AzurePipelines.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Reflection; + +namespace Microsoft.AspNetCore.Testing +{ + public static class AzurePipelines + { + public const string All = Prefix + "All"; + public const string Windows = OsPrefix + "Windows_NT"; + public const string macOS = OsPrefix + "Darwin"; + public const string Linux = OsPrefix + "Linux"; + + private const string Prefix = "AzP:"; + private const string OsPrefix = Prefix + "OS:"; + } +} diff --git a/src/Testing/src/HelixQueues.cs b/src/Testing/src/HelixQueues.cs new file mode 100644 index 0000000000..84828b6b83 --- /dev/null +++ b/src/Testing/src/HelixQueues.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Reflection; + +namespace Microsoft.AspNetCore.Testing +{ + public static class HelixQueues + { + public const string All = Prefix + "All"; + + public const string Fedora28Amd64 = QueuePrefix + "Fedora.28." + Amd64Suffix; + public const string Fedora27Amd64 = QueuePrefix + "Fedora.27." + Amd64Suffix; + public const string Redhat7Amd64 = QueuePrefix + "Redhat.7." + Amd64Suffix; + public const string Debian9Amd64 = QueuePrefix + "Debian.9." + Amd64Suffix; + public const string Debian8Amd64 = QueuePrefix + "Debian.8." + Amd64Suffix; + public const string Centos7Amd64 = QueuePrefix + "Centos.7." + Amd64Suffix; + public const string Ubuntu1604Amd64 = QueuePrefix + "Ubuntu.1604." + Amd64Suffix; + public const string Ubuntu1810Amd64 = QueuePrefix + "Ubuntu.1810." + Amd64Suffix; + public const string macOS1012Amd64 = QueuePrefix + "OSX.1012." + Amd64Suffix; + public const string Windows10Amd64 = QueuePrefix + "Windows.10.Amd64.ClientRS4.VS2017.Open"; // Doesn't have the default suffix! + + private const string Prefix = "Helix:"; + private const string QueuePrefix = Prefix + "Queue:"; + private const string Amd64Suffix = "Amd64.Open"; + } +} diff --git a/src/Testing/src/xunit/FlakyAttribute.cs b/src/Testing/src/xunit/FlakyAttribute.cs new file mode 100644 index 0000000000..b613a9bf4d --- /dev/null +++ b/src/Testing/src/xunit/FlakyAttribute.cs @@ -0,0 +1,75 @@ +using System; +using System.Collections.Generic; +using Xunit.Sdk; + +namespace Microsoft.AspNetCore.Testing.xunit +{ + /// + /// Marks a test as "Flaky" so that the build will sequester it and ignore failures. + /// + /// + /// + /// This attribute works by applying xUnit.net "Traits" based on the criteria specified in the attribute + /// properties. Once these traits are applied, build scripts can include/exclude tests based on them. + /// + /// + /// All flakiness-related traits start with Flaky: and are grouped first by the process running the tests: Azure Pipelines (AzP) or Helix. + /// Then there is a segment specifying the "selector" which indicates where the test is flaky. Finally a segment specifying the value of that selector. + /// The value of these traits is always either "true" or the trait is not present. We encode the entire selector in the name of the trait because xUnit.net only + /// provides "==" and "!=" operators for traits, there is no way to check if a trait "contains" or "does not contain" a value. VSTest does support "contains" checks + /// but does not appear to support "does not contain" checks. Using this pattern means we can use simple "==" and "!=" checks to either only run flaky tests, or exclude + /// flaky tests. + /// + /// + /// + /// + /// [Fact] + /// [Flaky("...", HelixQueues.Fedora28Amd64, AzurePipelines.macOS)] + /// public void FlakyTest() + /// { + /// // Flakiness + /// } + /// + /// + /// + /// The above example generates the following facets: + /// + /// + /// + /// + /// Flaky:Helix:Queue:Fedora.28.Amd64.Open = true + /// + /// + /// Flaky:AzP:OS:Darwin = true + /// + /// + /// + /// + /// Given the above attribute, the Azure Pipelines macOS run can easily filter this test out by passing -notrait "Flaky:AzP:OS:all=true" -notrait "Flaky:AzP:OS:Darwin=true" + /// to xunit.console.exe. Similarly, it can run only flaky tests using -trait "Flaky:AzP:OS:all=true" -trait "Flaky:AzP:OS:Darwin=true" + /// + /// + [TraitDiscoverer("Microsoft.AspNetCore.Testing.xunit.FlakyTestDiscoverer", "Microsoft.AspNetCore.Testing")] + [AttributeUsage(AttributeTargets.Method)] + public sealed class FlakyAttribute : Attribute, ITraitAttribute + { + /// + /// Gets a URL to a GitHub issue tracking this flaky test. + /// + public string GitHubIssueUrl { get; } + + public IReadOnlyList Filters { get; } + + /// + /// Initializes a new instance of the class with the specified and a list of . If no + /// filters are provided, the test is considered flaky in all environments. + /// + /// The URL to a GitHub issue tracking this flaky test. + /// A list of filters that define where this test is flaky. Use values in and . + public FlakyAttribute(string gitHubIssueUrl, params string[] filters) + { + GitHubIssueUrl = gitHubIssueUrl; + Filters = new List(filters); + } + } +} diff --git a/src/Testing/src/xunit/FlakyTestDiscoverer.cs b/src/Testing/src/xunit/FlakyTestDiscoverer.cs new file mode 100644 index 0000000000..344b9b2378 --- /dev/null +++ b/src/Testing/src/xunit/FlakyTestDiscoverer.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Xunit.Abstractions; +using Xunit.Sdk; + +namespace Microsoft.AspNetCore.Testing.xunit +{ + public class FlakyTestDiscoverer : ITraitDiscoverer + { + public IEnumerable> GetTraits(IAttributeInfo traitAttribute) + { + if (traitAttribute is ReflectionAttributeInfo attribute && attribute.Attribute is FlakyAttribute flakyAttribute) + { + return GetTraitsCore(flakyAttribute); + } + else + { + throw new InvalidOperationException("The 'Flaky' attribute is only supported via reflection."); + } + } + + private IEnumerable> GetTraitsCore(FlakyAttribute attribute) + { + if (attribute.Filters.Count > 0) + { + foreach (var filter in attribute.Filters) + { + yield return new KeyValuePair($"Flaky:{filter}", "true"); + } + } + else + { + yield return new KeyValuePair($"Flaky:All", "true"); + } + } + } +} diff --git a/src/Testing/test/FlakyAttributeTest.cs b/src/Testing/test/FlakyAttributeTest.cs new file mode 100644 index 0000000000..e9accf6274 --- /dev/null +++ b/src/Testing/test/FlakyAttributeTest.cs @@ -0,0 +1,97 @@ +using Microsoft.AspNetCore.Testing.xunit; +using System; +using System.Collections.Generic; +using Xunit; + +namespace Microsoft.AspNetCore.Testing.Tests +{ + public class FlakyAttributeTest + { + [Fact] + [Flaky("http://example.com")] + public void AlwaysFlaky() + { + if (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("HELIX")) || !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("AGENT_OS"))) + { + throw new Exception("Flaky!"); + } + } + + [Fact] + [Flaky("http://example.com", HelixQueues.All)] + public void FlakyInHelixOnly() + { + if (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("HELIX"))) + { + throw new Exception("Flaky on Helix!"); + } + } + + [Fact] + [Flaky("http://example.com", HelixQueues.macOS1012Amd64, HelixQueues.Fedora28Amd64)] + public void FlakyInSpecificHelixQueue() + { + // Today we don't run Extensions tests on Helix, but this test should light up when we do. + var queueName = Environment.GetEnvironmentVariable("HELIX"); + if (!string.IsNullOrEmpty(queueName)) + { + var failingQueues = new HashSet(StringComparer.OrdinalIgnoreCase) { HelixQueues.macOS1012Amd64, HelixQueues.Fedora28Amd64 }; + if (failingQueues.Contains(queueName)) + { + throw new Exception($"Flaky on Helix Queue '{queueName}' !"); + } + } + } + + [Fact] + [Flaky("http://example.com", AzurePipelines.All)] + public void FlakyInAzPOnly() + { + if (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("AGENT_OS"))) + { + throw new Exception("Flaky on AzP!"); + } + } + + [Fact] + [Flaky("http://example.com", AzurePipelines.Windows)] + public void FlakyInAzPWindowsOnly() + { + if (string.Equals(Environment.GetEnvironmentVariable("AGENT_OS"), AzurePipelines.Windows)) + { + throw new Exception("Flaky on AzP Windows!"); + } + } + + [Fact] + [Flaky("http://example.com", AzurePipelines.macOS)] + public void FlakyInAzPmacOSOnly() + { + if (string.Equals(Environment.GetEnvironmentVariable("AGENT_OS"), AzurePipelines.macOS)) + { + throw new Exception("Flaky on AzP macOS!"); + } + } + + [Fact] + [Flaky("http://example.com", AzurePipelines.Linux)] + public void FlakyInAzPLinuxOnly() + { + if (string.Equals(Environment.GetEnvironmentVariable("AGENT_OS"), AzurePipelines.Linux)) + { + throw new Exception("Flaky on AzP Linux!"); + } + } + + [Fact] + [Flaky("http://example.com", AzurePipelines.Linux, AzurePipelines.macOS)] + public void FlakyInAzPNonWindowsOnly() + { + var agentOs = Environment.GetEnvironmentVariable("AGENT_OS"); + if (string.Equals(agentOs, "Linux") || string.Equals(agentOs, AzurePipelines.macOS)) + { + throw new Exception("Flaky on AzP non-Windows!"); + } + } + } +} From d5a386b0a98a97235346c456dabff6cd54bcb241 Mon Sep 17 00:00:00 2001 From: Hao Kung Date: Thu, 7 Mar 2019 12:25:22 -0800 Subject: [PATCH 44/52] Remove newtonsoft ref and sharedsource false flag (dotnet/extensions#1219) \n\nCommit migrated from https://github.com/dotnet/extensions/commit/d563ecb057c7b5687be1a2f8aeb949280f0df75f --- src/Configuration.KeyPerFile/Directory.Build.props | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Configuration.KeyPerFile/Directory.Build.props b/src/Configuration.KeyPerFile/Directory.Build.props index fe35a9faec..63d0c8b102 100644 --- a/src/Configuration.KeyPerFile/Directory.Build.props +++ b/src/Configuration.KeyPerFile/Directory.Build.props @@ -4,5 +4,6 @@ true configuration + $(NoWarn);PKG0001 From 3e9a592422f4fe9a64545b2b0ecc7884b725e7fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Chalet?= Date: Fri, 8 Mar 2019 00:42:29 +0100 Subject: [PATCH 45/52] Make the generic IStringLocalizer interface covariant (dotnet/extensions#1117) Make the generic IStringLocalizer interface covariant \n\nCommit migrated from https://github.com/dotnet/extensions/commit/90020a9608688a259c7e653b4096270568337290 --- ...ocalization.Abstractions.netstandard2.0.cs | 2 +- .../Abstractions/src/IStringLocalizerOfT.cs | 4 +- .../test/StringLocalizerOfTTest.cs | 159 ++++++++++++++++++ 3 files changed, 162 insertions(+), 3 deletions(-) create mode 100644 src/Localization/Localization/test/StringLocalizerOfTTest.cs diff --git a/src/Localization/Abstractions/ref/Microsoft.Extensions.Localization.Abstractions.netstandard2.0.cs b/src/Localization/Abstractions/ref/Microsoft.Extensions.Localization.Abstractions.netstandard2.0.cs index 3f9f422021..174cac28e5 100644 --- a/src/Localization/Abstractions/ref/Microsoft.Extensions.Localization.Abstractions.netstandard2.0.cs +++ b/src/Localization/Abstractions/ref/Microsoft.Extensions.Localization.Abstractions.netstandard2.0.cs @@ -16,7 +16,7 @@ namespace Microsoft.Extensions.Localization Microsoft.Extensions.Localization.IStringLocalizer Create(string baseName, string location); Microsoft.Extensions.Localization.IStringLocalizer Create(System.Type resourceSource); } - public partial interface IStringLocalizer : Microsoft.Extensions.Localization.IStringLocalizer + public partial interface IStringLocalizer : Microsoft.Extensions.Localization.IStringLocalizer { } public partial class LocalizedString diff --git a/src/Localization/Abstractions/src/IStringLocalizerOfT.cs b/src/Localization/Abstractions/src/IStringLocalizerOfT.cs index 695678a900..bdc2a1c7b7 100644 --- a/src/Localization/Abstractions/src/IStringLocalizerOfT.cs +++ b/src/Localization/Abstractions/src/IStringLocalizerOfT.cs @@ -7,8 +7,8 @@ namespace Microsoft.Extensions.Localization /// Represents an that provides strings for . /// /// The to provide strings for. - public interface IStringLocalizer : IStringLocalizer + public interface IStringLocalizer : IStringLocalizer { } -} \ No newline at end of file +} diff --git a/src/Localization/Localization/test/StringLocalizerOfTTest.cs b/src/Localization/Localization/test/StringLocalizerOfTTest.cs new file mode 100644 index 0000000000..ce06e74d1c --- /dev/null +++ b/src/Localization/Localization/test/StringLocalizerOfTTest.cs @@ -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.Globalization; +using Moq; +using Xunit; + +namespace Microsoft.Extensions.Localization +{ + public class StringLocalizerOfTTest + { + [Fact] + public void Constructor_ThrowsAnExceptionForNullFactory() + { + // Arrange, act and assert + var exception = Assert.Throws( + () => new StringLocalizer(factory: null)); + + Assert.Equal("factory", exception.ParamName); + } + + [Fact] + public void Constructor_ResolvesLocalizerFromFactory() + { + // Arrange + var factory = new Mock(); + + // Act + _ = new StringLocalizer(factory.Object); + + // Assert + factory.Verify(mock => mock.Create(typeof(object)), Times.Once()); + } + + [Fact] + public void WithCulture_InvokesWithCultureFromInnerLocalizer() + { + // Arrange + var factory = new Mock(); + var innerLocalizer = new Mock(); + factory.Setup(mock => mock.Create(typeof(object))) + .Returns(innerLocalizer.Object); + + var localizer = new StringLocalizer(factory.Object); + + // Act +#pragma warning disable CS0618 + localizer.WithCulture(CultureInfo.GetCultureInfo("fr-FR")); +#pragma warning restore CS0618 + + // Assert +#pragma warning disable CS0618 + innerLocalizer.Verify(mock => mock.WithCulture(CultureInfo.GetCultureInfo("fr-FR")), Times.Once()); +#pragma warning restore CS0618 + } + + [Fact] + public void Indexer_ThrowsAnExceptionForNullName() + { + // Arrange + var factory = new Mock(); + var innerLocalizer = new Mock(); + factory.Setup(mock => mock.Create(typeof(object))) + .Returns(innerLocalizer.Object); + + var localizer = new StringLocalizer(factory.Object); + + // Act and assert + var exception = Assert.Throws(() => localizer[name: null]); + + Assert.Equal("name", exception.ParamName); + } + + [Fact] + public void Indexer_InvokesIndexerFromInnerLocalizer() + { + // Arrange + var factory = new Mock(); + var innerLocalizer = new Mock(); + factory.Setup(mock => mock.Create(typeof(object))) + .Returns(innerLocalizer.Object); + + var localizer = new StringLocalizer(factory.Object); + + // Act + _ = localizer["Hello world"]; + + // Assert + innerLocalizer.Verify(mock => mock["Hello world"], Times.Once()); + } + + [Fact] + public void Indexer_ThrowsAnExceptionForNullName_WithArguments() + { + // Arrange + var factory = new Mock(); + var innerLocalizer = new Mock(); + factory.Setup(mock => mock.Create(typeof(object))) + .Returns(innerLocalizer.Object); + + var localizer = new StringLocalizer(factory.Object); + + // Act and assert + var exception = Assert.Throws(() => localizer[name: null]); + + Assert.Equal("name", exception.ParamName); + } + + [Fact] + public void Indexer_InvokesIndexerFromInnerLocalizer_WithArguments() + { + // Arrange + var factory = new Mock(); + var innerLocalizer = new Mock(); + factory.Setup(mock => mock.Create(typeof(object))) + .Returns(innerLocalizer.Object); + + var localizer = new StringLocalizer(factory.Object); + + // Act + _ = localizer["Welcome, {0}", "Bob"]; + + // Assert + innerLocalizer.Verify(mock => mock["Welcome, {0}", "Bob"], Times.Once()); + } + + [Fact] + public void GetAllStrings_InvokesGetAllStringsFromInnerLocalizer() + { + // Arrange + var factory = new Mock(); + var innerLocalizer = new Mock(); + factory.Setup(mock => mock.Create(typeof(object))) + .Returns(innerLocalizer.Object); + + var localizer = new StringLocalizer(factory.Object); + + // Act + localizer.GetAllStrings(includeParentCultures: true); + + // Assert + innerLocalizer.Verify(mock => mock.GetAllStrings(true), Times.Once()); + } + + [Fact] + public void StringLocalizer_CanBeCastToBaseType() + { + // Arrange and act + IStringLocalizer localizer = new StringLocalizer(Mock.Of()); + + // Assert + Assert.NotNull(localizer); + } + + private class BaseType { } + private class DerivedType : BaseType { } + } +} From c9ff2af852cfd8f874a8e8bcf9d2971915405942 Mon Sep 17 00:00:00 2001 From: Javier Calvarro Nelson Date: Mon, 11 Mar 2019 18:00:14 +0100 Subject: [PATCH 46/52] Skip test until we move to the new .NET Core implementation (dotnet/extensions#1156) \n\nCommit migrated from https://github.com/dotnet/extensions/commit/4636215c9b813fef51be55bf4853848bed221a6e --- .../Microsoft.JSInterop/test/JSInProcessRuntimeBaseTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/JSInterop/Microsoft.JSInterop/test/JSInProcessRuntimeBaseTest.cs b/src/JSInterop/Microsoft.JSInterop/test/JSInProcessRuntimeBaseTest.cs index 36474fe407..a13d53677a 100644 --- a/src/JSInterop/Microsoft.JSInterop/test/JSInProcessRuntimeBaseTest.cs +++ b/src/JSInterop/Microsoft.JSInterop/test/JSInProcessRuntimeBaseTest.cs @@ -10,7 +10,7 @@ namespace Microsoft.JSInterop.Tests { public class JSInProcessRuntimeBaseTest { - [Fact] + [Fact(Skip = "https://github.com/aspnet/AspNetCore-Internal/issues/1807#issuecomment-470756811")] public void DispatchesSyncCallsAndDeserializesResults() { // Arrange From 2d1743a2f672201f3886ea084fde069ca74d3f86 Mon Sep 17 00:00:00 2001 From: Andrew Stanton-Nurse Date: Tue, 19 Mar 2019 18:43:25 -0700 Subject: [PATCH 47/52] clean up flaky filter constants (dotnet/extensions#1248) \n\nCommit migrated from https://github.com/dotnet/extensions/commit/3b3a23eb1a2dc7dbd57ab192da9856ed3ad8e8ea --- src/Testing/src/AzurePipelines.cs | 17 ---------- src/Testing/src/FlakyOn.cs | 41 +++++++++++++++++++++++++ src/Testing/src/HelixQueues.cs | 28 ++++++----------- src/Testing/src/xunit/FlakyAttribute.cs | 23 ++++++++++++-- src/Testing/test/FlakyAttributeTest.cs | 26 ++++++++-------- 5 files changed, 84 insertions(+), 51 deletions(-) delete mode 100644 src/Testing/src/AzurePipelines.cs create mode 100644 src/Testing/src/FlakyOn.cs diff --git a/src/Testing/src/AzurePipelines.cs b/src/Testing/src/AzurePipelines.cs deleted file mode 100644 index ae1eac3b90..0000000000 --- a/src/Testing/src/AzurePipelines.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Reflection; - -namespace Microsoft.AspNetCore.Testing -{ - public static class AzurePipelines - { - public const string All = Prefix + "All"; - public const string Windows = OsPrefix + "Windows_NT"; - public const string macOS = OsPrefix + "Darwin"; - public const string Linux = OsPrefix + "Linux"; - - private const string Prefix = "AzP:"; - private const string OsPrefix = Prefix + "OS:"; - } -} diff --git a/src/Testing/src/FlakyOn.cs b/src/Testing/src/FlakyOn.cs new file mode 100644 index 0000000000..81d9299043 --- /dev/null +++ b/src/Testing/src/FlakyOn.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Microsoft.AspNetCore.Testing +{ + public static class FlakyOn + { + public const string All = "All"; + + public static class Helix + { + public const string All = QueuePrefix + "All"; + + public const string Fedora28Amd64 = QueuePrefix + HelixQueues.Fedora28Amd64; + public const string Fedora27Amd64 = QueuePrefix + HelixQueues.Fedora27Amd64; + public const string Redhat7Amd64 = QueuePrefix + HelixQueues.Redhat7Amd64; + public const string Debian9Amd64 = QueuePrefix + HelixQueues.Debian9Amd64; + public const string Debian8Amd64 = QueuePrefix + HelixQueues.Debian8Amd64; + public const string Centos7Amd64 = QueuePrefix + HelixQueues.Centos7Amd64; + public const string Ubuntu1604Amd64 = QueuePrefix + HelixQueues.Ubuntu1604Amd64; + public const string Ubuntu1810Amd64 = QueuePrefix + HelixQueues.Ubuntu1810Amd64; + public const string macOS1012Amd64 = QueuePrefix + HelixQueues.macOS1012Amd64; + public const string Windows10Amd64 = QueuePrefix + HelixQueues.Windows10Amd64; + + private const string Prefix = "Helix:"; + private const string QueuePrefix = Prefix + "Queue:"; + } + + public static class AzP + { + public const string All = Prefix + "All"; + public const string Windows = OsPrefix + "Windows_NT"; + public const string macOS = OsPrefix + "Darwin"; + public const string Linux = OsPrefix + "Linux"; + + private const string Prefix = "AzP:"; + private const string OsPrefix = Prefix + "OS:"; + } + } +} diff --git a/src/Testing/src/HelixQueues.cs b/src/Testing/src/HelixQueues.cs index 84828b6b83..ef5e4d1f5a 100644 --- a/src/Testing/src/HelixQueues.cs +++ b/src/Testing/src/HelixQueues.cs @@ -1,26 +1,18 @@ -using System; -using System.Collections.Generic; -using System.Reflection; - namespace Microsoft.AspNetCore.Testing { public static class HelixQueues { - public const string All = Prefix + "All"; + public const string Fedora28Amd64 = "Fedora.28." + Amd64Suffix; + public const string Fedora27Amd64 = "Fedora.27." + Amd64Suffix; + public const string Redhat7Amd64 = "Redhat.7." + Amd64Suffix; + public const string Debian9Amd64 = "Debian.9." + Amd64Suffix; + public const string Debian8Amd64 = "Debian.8." + Amd64Suffix; + public const string Centos7Amd64 = "Centos.7." + Amd64Suffix; + public const string Ubuntu1604Amd64 = "Ubuntu.1604." + Amd64Suffix; + public const string Ubuntu1810Amd64 = "Ubuntu.1810." + Amd64Suffix; + public const string macOS1012Amd64 = "OSX.1012." + Amd64Suffix; + public const string Windows10Amd64 = "Windows.10.Amd64.ClientRS4.VS2017.Open"; // Doesn't have the default suffix! - public const string Fedora28Amd64 = QueuePrefix + "Fedora.28." + Amd64Suffix; - public const string Fedora27Amd64 = QueuePrefix + "Fedora.27." + Amd64Suffix; - public const string Redhat7Amd64 = QueuePrefix + "Redhat.7." + Amd64Suffix; - public const string Debian9Amd64 = QueuePrefix + "Debian.9." + Amd64Suffix; - public const string Debian8Amd64 = QueuePrefix + "Debian.8." + Amd64Suffix; - public const string Centos7Amd64 = QueuePrefix + "Centos.7." + Amd64Suffix; - public const string Ubuntu1604Amd64 = QueuePrefix + "Ubuntu.1604." + Amd64Suffix; - public const string Ubuntu1810Amd64 = QueuePrefix + "Ubuntu.1810." + Amd64Suffix; - public const string macOS1012Amd64 = QueuePrefix + "OSX.1012." + Amd64Suffix; - public const string Windows10Amd64 = QueuePrefix + "Windows.10.Amd64.ClientRS4.VS2017.Open"; // Doesn't have the default suffix! - - private const string Prefix = "Helix:"; - private const string QueuePrefix = Prefix + "Queue:"; private const string Amd64Suffix = "Amd64.Open"; } } diff --git a/src/Testing/src/xunit/FlakyAttribute.cs b/src/Testing/src/xunit/FlakyAttribute.cs index b613a9bf4d..f58026c7ca 100644 --- a/src/Testing/src/xunit/FlakyAttribute.cs +++ b/src/Testing/src/xunit/FlakyAttribute.cs @@ -64,12 +64,29 @@ namespace Microsoft.AspNetCore.Testing.xunit /// Initializes a new instance of the class with the specified and a list of . If no /// filters are provided, the test is considered flaky in all environments. /// + /// + /// At least one filter is required. + /// /// The URL to a GitHub issue tracking this flaky test. - /// A list of filters that define where this test is flaky. Use values in and . - public FlakyAttribute(string gitHubIssueUrl, params string[] filters) + /// The first filter that indicates where the test is flaky. Use a value from . + /// A list of additional filters that define where this test is flaky. Use values in . + public FlakyAttribute(string gitHubIssueUrl, string firstFilter, params string[] additionalFilters) { + if(string.IsNullOrEmpty(gitHubIssueUrl)) + { + throw new ArgumentNullException(nameof(gitHubIssueUrl)); + } + + if(string.IsNullOrEmpty(firstFilter)) + { + throw new ArgumentNullException(nameof(firstFilter)); + } + GitHubIssueUrl = gitHubIssueUrl; - Filters = new List(filters); + var filters = new List(); + filters.Add(firstFilter); + filters.AddRange(additionalFilters); + Filters = filters; } } } diff --git a/src/Testing/test/FlakyAttributeTest.cs b/src/Testing/test/FlakyAttributeTest.cs index e9accf6274..7837bd8711 100644 --- a/src/Testing/test/FlakyAttributeTest.cs +++ b/src/Testing/test/FlakyAttributeTest.cs @@ -8,8 +8,8 @@ namespace Microsoft.AspNetCore.Testing.Tests public class FlakyAttributeTest { [Fact] - [Flaky("http://example.com")] - public void AlwaysFlaky() + [Flaky("http://example.com", FlakyOn.All)] + public void AlwaysFlakyInCI() { if (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("HELIX")) || !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("AGENT_OS"))) { @@ -18,7 +18,7 @@ namespace Microsoft.AspNetCore.Testing.Tests } [Fact] - [Flaky("http://example.com", HelixQueues.All)] + [Flaky("http://example.com", FlakyOn.Helix.All)] public void FlakyInHelixOnly() { if (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("HELIX"))) @@ -28,7 +28,7 @@ namespace Microsoft.AspNetCore.Testing.Tests } [Fact] - [Flaky("http://example.com", HelixQueues.macOS1012Amd64, HelixQueues.Fedora28Amd64)] + [Flaky("http://example.com", FlakyOn.Helix.macOS1012Amd64, FlakyOn.Helix.Fedora28Amd64)] public void FlakyInSpecificHelixQueue() { // Today we don't run Extensions tests on Helix, but this test should light up when we do. @@ -44,7 +44,7 @@ namespace Microsoft.AspNetCore.Testing.Tests } [Fact] - [Flaky("http://example.com", AzurePipelines.All)] + [Flaky("http://example.com", FlakyOn.AzP.All)] public void FlakyInAzPOnly() { if (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("AGENT_OS"))) @@ -54,41 +54,41 @@ namespace Microsoft.AspNetCore.Testing.Tests } [Fact] - [Flaky("http://example.com", AzurePipelines.Windows)] + [Flaky("http://example.com", FlakyOn.AzP.Windows)] public void FlakyInAzPWindowsOnly() { - if (string.Equals(Environment.GetEnvironmentVariable("AGENT_OS"), AzurePipelines.Windows)) + if (string.Equals(Environment.GetEnvironmentVariable("AGENT_OS"), "Windows_NT")) { throw new Exception("Flaky on AzP Windows!"); } } [Fact] - [Flaky("http://example.com", AzurePipelines.macOS)] + [Flaky("http://example.com", FlakyOn.AzP.macOS)] public void FlakyInAzPmacOSOnly() { - if (string.Equals(Environment.GetEnvironmentVariable("AGENT_OS"), AzurePipelines.macOS)) + if (string.Equals(Environment.GetEnvironmentVariable("AGENT_OS"), "Darwin")) { throw new Exception("Flaky on AzP macOS!"); } } [Fact] - [Flaky("http://example.com", AzurePipelines.Linux)] + [Flaky("http://example.com", FlakyOn.AzP.Linux)] public void FlakyInAzPLinuxOnly() { - if (string.Equals(Environment.GetEnvironmentVariable("AGENT_OS"), AzurePipelines.Linux)) + if (string.Equals(Environment.GetEnvironmentVariable("AGENT_OS"), "Linux")) { throw new Exception("Flaky on AzP Linux!"); } } [Fact] - [Flaky("http://example.com", AzurePipelines.Linux, AzurePipelines.macOS)] + [Flaky("http://example.com", FlakyOn.AzP.Linux, FlakyOn.AzP.macOS)] public void FlakyInAzPNonWindowsOnly() { var agentOs = Environment.GetEnvironmentVariable("AGENT_OS"); - if (string.Equals(agentOs, "Linux") || string.Equals(agentOs, AzurePipelines.macOS)) + if (string.Equals(agentOs, "Linux") || string.Equals(agentOs, "Darwin")) { throw new Exception("Flaky on AzP non-Windows!"); } From ffc4b007645683a85dbb57146ce9caf6e9ce6fdc Mon Sep 17 00:00:00 2001 From: Ryan Nowak Date: Sun, 17 Feb 2019 16:16:56 -0800 Subject: [PATCH 48/52] Generalize and harden ST-sync-context \n\nCommit migrated from https://github.com/dotnet/extensions/commit/247bb34a63a0ef097396cb939189b70e3a12321a --- .../SingleThreadedSynchronizationContext.cs | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 src/Shared/test/Shared.Tests/SingleThreadedSynchronizationContext.cs diff --git a/src/Shared/test/Shared.Tests/SingleThreadedSynchronizationContext.cs b/src/Shared/test/Shared.Tests/SingleThreadedSynchronizationContext.cs new file mode 100644 index 0000000000..77312e0a05 --- /dev/null +++ b/src/Shared/test/Shared.Tests/SingleThreadedSynchronizationContext.cs @@ -0,0 +1,45 @@ +// 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.Concurrent; +using System.Threading; + +namespace Microsoft.Extensions.Internal +{ + internal class SingleThreadedSynchronizationContext : SynchronizationContext + { + private readonly BlockingCollection<(SendOrPostCallback Callback, object State)> _queue = new BlockingCollection<(SendOrPostCallback Callback, object State)>(); + + public override void Send(SendOrPostCallback d, object state) // Sync operations + { + throw new NotSupportedException($"{nameof(SingleThreadedSynchronizationContext)} does not support synchronous operations."); + } + + public override void Post(SendOrPostCallback d, object state) // Async operations + { + _queue.Add((d, state)); + } + + public static void Run(Action action) + { + var previous = Current; + var context = new SingleThreadedSynchronizationContext(); + SetSynchronizationContext(context); + try + { + action(); + + while (context._queue.TryTake(out var item)) + { + item.Callback(item.State); + } + } + finally + { + context._queue.CompleteAdding(); + SetSynchronizationContext(previous); + } + } + } +} From 2849483895f57bc259ebc1ff16957feb1b3add45 Mon Sep 17 00:00:00 2001 From: Ryan Nowak Date: Sun, 17 Feb 2019 16:29:24 -0800 Subject: [PATCH 49/52] Make IHealthCheckService work in ST-sync-context \n\nCommit migrated from https://github.com/dotnet/extensions/commit/2b673c89c20193e2d2d0fd8aa200987a4f2505e7 --- .../src/DefaultHealthCheckService.cs | 7 ++-- .../test/DefaultHealthCheckServiceTest.cs | 34 +++++++++++++++++++ ...ions.Diagnostics.HealthChecks.Tests.csproj | 4 +++ 3 files changed, 40 insertions(+), 5 deletions(-) diff --git a/src/HealthChecks/HealthChecks/src/DefaultHealthCheckService.cs b/src/HealthChecks/HealthChecks/src/DefaultHealthCheckService.cs index 56ce966d18..c2c9084e0a 100644 --- a/src/HealthChecks/HealthChecks/src/DefaultHealthCheckService.cs +++ b/src/HealthChecks/HealthChecks/src/DefaultHealthCheckService.cs @@ -54,7 +54,7 @@ namespace Microsoft.Extensions.Diagnostics.HealthChecks { foreach (var registration in registrations) { - tasks[index++] = RunCheckAsync(scope, registration, cancellationToken); + tasks[index++] = Task.Run(() => RunCheckAsync(scope, registration, cancellationToken), cancellationToken); } await Task.WhenAll(tasks).ConfigureAwait(false); @@ -75,8 +75,6 @@ namespace Microsoft.Extensions.Diagnostics.HealthChecks private async Task RunCheckAsync(IServiceScope scope, HealthCheckRegistration registration, CancellationToken cancellationToken) { - await Task.Yield(); - cancellationToken.ThrowIfCancellationRequested(); var healthCheck = registration.Factory(scope.ServiceProvider); @@ -104,7 +102,7 @@ namespace Microsoft.Extensions.Diagnostics.HealthChecks checkCancellationToken = timeoutCancellationTokenSource.Token; } - result = await healthCheck.CheckHealthAsync(context, checkCancellationToken); + result = await healthCheck.CheckHealthAsync(context, checkCancellationToken).ConfigureAwait(false); var duration = stopwatch.GetElapsedTime(); @@ -118,7 +116,6 @@ namespace Microsoft.Extensions.Diagnostics.HealthChecks Log.HealthCheckEnd(_logger, registration, entry, duration); Log.HealthCheckData(_logger, registration, entry); } - catch (OperationCanceledException ex) when (!cancellationToken.IsCancellationRequested) { var duration = stopwatch.GetElapsedTime(); diff --git a/src/HealthChecks/HealthChecks/test/DefaultHealthCheckServiceTest.cs b/src/HealthChecks/HealthChecks/test/DefaultHealthCheckServiceTest.cs index ba0a2f32d5..38442edb93 100644 --- a/src/HealthChecks/HealthChecks/test/DefaultHealthCheckServiceTest.cs +++ b/src/HealthChecks/HealthChecks/test/DefaultHealthCheckServiceTest.cs @@ -8,6 +8,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Testing; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Internal; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Testing; using Microsoft.Extensions.Options; @@ -450,6 +451,39 @@ namespace Microsoft.Extensions.Diagnostics.HealthChecks }); } + [Fact] + public void CheckHealthAsync_WorksInSingleThreadedSyncContext() + { + // Arrange + var service = CreateHealthChecksService(b => + { + b.AddAsyncCheck("test", async () => + { + await Task.Delay(1).ConfigureAwait(false); + return HealthCheckResult.Healthy(); + }); + }); + + var hangs = true; + + // Act + using (var cts = new CancellationTokenSource(TimeSpan.FromSeconds(3))) + { + var token = cts.Token; + token.Register(() => throw new OperationCanceledException(token)); + + SingleThreadedSynchronizationContext.Run(() => + { + // Act + service.CheckHealthAsync(token).GetAwaiter().GetResult(); + hangs = false; + }); + } + + // Assert + Assert.False(hangs); + } + private static DefaultHealthCheckService CreateHealthChecksService(Action configure) { var services = new ServiceCollection(); diff --git a/src/HealthChecks/HealthChecks/test/Microsoft.Extensions.Diagnostics.HealthChecks.Tests.csproj b/src/HealthChecks/HealthChecks/test/Microsoft.Extensions.Diagnostics.HealthChecks.Tests.csproj index 163b618900..08cd6a35f1 100644 --- a/src/HealthChecks/HealthChecks/test/Microsoft.Extensions.Diagnostics.HealthChecks.Tests.csproj +++ b/src/HealthChecks/HealthChecks/test/Microsoft.Extensions.Diagnostics.HealthChecks.Tests.csproj @@ -12,4 +12,8 @@ + + + + From a8a3fbab33cbb863e1cb660a728df21c13adf0c5 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" Date: Wed, 27 Mar 2019 14:40:19 +0000 Subject: [PATCH 50/52] [master] Update dependencies from dotnet/arcade (dotnet/extensions#1279) * Update dependencies from https://github.com/dotnet/arcade build 20190321.6 - Microsoft.DotNet.Arcade.Sdk - 1.0.0-beta.19171.6 - Microsoft.DotNet.GenAPI - 1.0.0-beta.19171.6 - Microsoft.DotNet.Helix.Sdk - 2.0.0-beta.19171.6 * Use Arcade NuSpec helpers * Fix Release flaky test leg * Fix Release flaky non-Windows test legs * Update dependencies from https://github.com/dotnet/arcade build 20190325.9 - Microsoft.DotNet.Arcade.Sdk - 1.0.0-beta.19175.9 - Microsoft.DotNet.GenAPI - 1.0.0-beta.19175.9 - Microsoft.DotNet.Helix.Sdk - 2.0.0-beta.19175.9 * Update dependencies from https://github.com/dotnet/arcade build 20190326.14 - Microsoft.DotNet.Arcade.Sdk - 1.0.0-beta.19176.14 - Microsoft.DotNet.GenAPI - 1.0.0-beta.19176.14 - Microsoft.DotNet.Helix.Sdk - 2.0.0-beta.19176.14 \n\nCommit migrated from https://github.com/dotnet/extensions/commit/d353f7b0204136d352423d44ad6df0505d17a376 --- ...t.Extensions.FileProviders.Embedded.csproj | 39 +++++++------------ ...t.Extensions.FileProviders.Embedded.nuspec | 12 +----- 2 files changed, 16 insertions(+), 35 deletions(-) diff --git a/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.csproj b/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.csproj index 792b9ff5b3..7d84c19f9d 100644 --- a/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.csproj +++ b/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.csproj @@ -22,31 +22,22 @@ - + - - id=$(PackageId); - version=$(PackageVersion); - authors=$(Authors); - description=$(Description); - tags=$(PackageTags.Replace(';', ' ')); - licenseUrl=$(PackageLicenseUrl); - projectUrl=$(PackageProjectUrl); - iconUrl=$(PackageIconUrl); - repositoryUrl=$(RepositoryUrl); - repositoryCommit=$(RepositoryCommit); - copyright=$(Copyright); - targetframework=$(TargetFramework); - AssemblyName=$(AssemblyName); - - OutputBinary=@(BuiltProjectOutputGroupOutput); - OutputSymbol=@(DebugSymbolsProjectOutputGroupOutput); - OutputDocumentation=@(DocumentationProjectOutputGroupOutput); - - - TaskAssemblyNetStandard=$(ArtifactsDir)bin\$(AssemblyName).Manifest.Task\$(Configuration)\netstandard2.0\$(AssemblyName).Manifest.Task.dll; - TaskSymbolNetStandard=$(ArtifactsDir)bin\$(AssemblyName).Manifest.Task\$(Configuration)\netstandard2.0\$(AssemblyName).Manifest.Task.pdb; - + $(PackageTags.Replace(';',' ')) + <_OutputBinary>@(BuiltProjectOutputGroupOutput) + <_OutputSymbol>@(DebugSymbolsProjectOutputGroupOutput) + <_OutputDocumentation>@(DocumentationProjectOutputGroupOutput) + + + + + + + + + + diff --git a/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.nuspec b/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.nuspec index ff6d385add..4a33eb6a95 100644 --- a/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.nuspec +++ b/src/FileProviders/Embedded/src/Microsoft.Extensions.FileProviders.Embedded.nuspec @@ -1,17 +1,7 @@ - $id$ - $version$ - $authors$ - true - $licenseUrl$ - $projectUrl$ - $iconUrl$ - $description$ - $copyright$ - $tags$ - + $CommonMetadataElements$ From f5c756c93533023c24dbcaa3c6d423c522cdb0e7 Mon Sep 17 00:00:00 2001 From: Ryan Nowak Date: Wed, 27 Mar 2019 19:42:23 -0700 Subject: [PATCH 51/52] Fix dotnet/extensions#8612 JSInterop throws truncated call stack The fix for this is to use more ExceptionDispatchInfo! Basically everwhere that we handle an exception is now an EDI. It's easy to pass these around and they do the right thing as far as perserving the stack in. I de-factored this code a little bit to make all of this work, but it's now pretty unambiguously correct upon inspection. I started out wanting to get rid of the continue-with because we've found that pretty error prone overall. However making this method async-void started to ask more questions than it answered. What happens if serialization of the exception fails? \n\nCommit migrated from https://github.com/dotnet/extensions/commit/0a27fd3ad6604ca1f1278db5e01224a29fcab00d --- .../src/DotNetDispatcher.cs | 69 ++++++++----------- .../Microsoft.JSInterop/src/JSRuntimeBase.cs | 5 ++ .../test/DotNetDispatcherTest.cs | 64 +++++++++++++++++ 3 files changed, 97 insertions(+), 41 deletions(-) diff --git a/src/JSInterop/Microsoft.JSInterop/src/DotNetDispatcher.cs b/src/JSInterop/Microsoft.JSInterop/src/DotNetDispatcher.cs index 0f346bbfdd..ac936e670a 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/DotNetDispatcher.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/DotNetDispatcher.cs @@ -7,6 +7,7 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Reflection; +using System.Runtime.ExceptionServices; using System.Threading.Tasks; namespace Microsoft.JSInterop @@ -72,8 +73,10 @@ namespace Microsoft.JSInterop ? null : jsRuntimeBaseInstance.ArgSerializerStrategy.FindDotNetObject(dotNetObjectId); + // Using ExceptionDispatchInfo here throughout because we want to always preserve + // original stack traces. object syncResult = null; - Exception syncException = null; + ExceptionDispatchInfo syncException = null; try { @@ -81,45 +84,38 @@ namespace Microsoft.JSInterop } catch (Exception ex) { - syncException = ex; + syncException = ExceptionDispatchInfo.Capture(ex); } // If there was no callId, the caller does not want to be notified about the result - if (callId != null) + if (callId == null) { - // Invoke and coerce the result to a Task so the caller can use the same async API - // for both synchronous and asynchronous methods - var task = CoerceToTask(syncResult, syncException); - - task.ContinueWith(completedTask => + return; + } + else if (syncException != null) + { + // Threw synchronously, let's respond. + jsRuntimeBaseInstance.EndInvokeDotNet(callId, false, syncException); + } + else if (syncResult is Task task) + { + // Returned a task - we need to continue that task and then report an exception + // or return the value. + task.ContinueWith(t => { - try + if (t.Exception != null) { - var result = TaskGenericsUtil.GetTaskResult(completedTask); - jsRuntimeBaseInstance.EndInvokeDotNet(callId, true, result); + var exception = t.Exception.GetBaseException(); + jsRuntimeBaseInstance.EndInvokeDotNet(callId, false, ExceptionDispatchInfo.Capture(exception)); } - catch (Exception ex) - { - ex = UnwrapException(ex); - jsRuntimeBaseInstance.EndInvokeDotNet(callId, false, ex); - } - }); - } - } - private static Task CoerceToTask(object syncResult, Exception syncException) - { - if (syncException != null) - { - return Task.FromException(syncException); - } - else if (syncResult is Task syncResultTask) - { - return syncResultTask; + var result = TaskGenericsUtil.GetTaskResult(task); + jsRuntimeBaseInstance.EndInvokeDotNet(callId, true, result); + }, TaskScheduler.Current); } else { - return Task.FromResult(syncResult); + jsRuntimeBaseInstance.EndInvokeDotNet(callId, true, syncResult); } } @@ -175,9 +171,10 @@ namespace Microsoft.JSInterop { return methodInfo.Invoke(targetInstance, suppliedArgs); } - catch (Exception ex) + catch (TargetInvocationException tie) when (tie.InnerException != null) { - throw UnwrapException(ex); + ExceptionDispatchInfo.Capture(tie.InnerException).Throw(); + throw null; // unreachable } } @@ -285,15 +282,5 @@ namespace Microsoft.JSInterop return loadedAssemblies.FirstOrDefault(a => a.GetName().Name.Equals(assemblyName, StringComparison.Ordinal)) ?? throw new ArgumentException($"There is no loaded assembly with the name '{assemblyName}'."); } - - private static Exception UnwrapException(Exception ex) - { - while ((ex is AggregateException || ex is TargetInvocationException) && ex.InnerException != null) - { - ex = ex.InnerException; - } - - return ex; - } } } diff --git a/src/JSInterop/Microsoft.JSInterop/src/JSRuntimeBase.cs b/src/JSInterop/Microsoft.JSInterop/src/JSRuntimeBase.cs index 09379396cf..d18bc7f4fe 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/JSRuntimeBase.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/JSRuntimeBase.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Concurrent; +using System.Runtime.ExceptionServices; using System.Threading; using System.Threading.Tasks; @@ -80,6 +81,10 @@ namespace Microsoft.JSInterop { resultOrException = resultOrException.ToString(); } + else if (!success && resultOrException is ExceptionDispatchInfo edi) + { + resultOrException = edi.SourceException.ToString(); + } // We pass 0 as the async handle because we don't want the JS-side code to // send back any notification (we're just providing a result for an existing async call) diff --git a/src/JSInterop/Microsoft.JSInterop/test/DotNetDispatcherTest.cs b/src/JSInterop/Microsoft.JSInterop/test/DotNetDispatcherTest.cs index 93ef9a2498..e3f91a6fd0 100644 --- a/src/JSInterop/Microsoft.JSInterop/test/DotNetDispatcherTest.cs +++ b/src/JSInterop/Microsoft.JSInterop/test/DotNetDispatcherTest.cs @@ -285,6 +285,54 @@ namespace Microsoft.JSInterop.Tests Assert.Equal(2468, resultDto2.IntVal); }); + + [Fact] + public Task CanInvokeSyncThrowingMethod() => WithJSRuntime(async jsRuntime => + { + // Arrange + + // Act + var callId = "123"; + var resultTask = jsRuntime.NextInvocationTask; + DotNetDispatcher.BeginInvoke(callId, thisAssemblyName, nameof(ThrowingClass.ThrowingMethod), default, default); + + await resultTask; // This won't throw, it sets properties on the jsRuntime. + + // Assert + var result = Json.Deserialize(jsRuntime.LastInvocationArgsJson); + Assert.Equal(callId, result[0]); + Assert.False((bool)result[1]); // Fails + + // Make sure the method that threw the exception shows up in the call stack + // https://github.com/aspnet/AspNetCore/issues/8612 + var exception = (string)result[2]; + Assert.Contains(nameof(ThrowingClass.ThrowingMethod), exception); + }); + + [Fact] + public Task CanInvokeAsyncThrowingMethod() => WithJSRuntime(async jsRuntime => + { + // Arrange + + // Act + var callId = "123"; + var resultTask = jsRuntime.NextInvocationTask; + DotNetDispatcher.BeginInvoke(callId, thisAssemblyName, nameof(ThrowingClass.AsyncThrowingMethod), default, default); + + await resultTask; // This won't throw, it sets properties on the jsRuntime. + + // Assert + var result = Json.Deserialize(jsRuntime.LastInvocationArgsJson); + Assert.Equal(callId, result[0]); + Assert.False((bool)result[1]); // Fails + + // Make sure the method that threw the exception shows up in the call stack + // https://github.com/aspnet/AspNetCore/issues/8612 + var exception = (string)result[2]; + Assert.Contains(nameof(ThrowingClass.AsyncThrowingMethod), exception); + }); + + Task WithJSRuntime(Action testCode) { return WithJSRuntime(jsRuntime => @@ -413,6 +461,22 @@ namespace Microsoft.JSInterop.Tests public int IntVal { get; set; } } + public class ThrowingClass + { + [JSInvokable] + public static string ThrowingMethod() + { + throw new InvalidTimeZoneException(); + } + + [JSInvokable] + public static async Task AsyncThrowingMethod() + { + await Task.Yield(); + throw new InvalidTimeZoneException(); + } + } + public class TestJSRuntime : JSInProcessRuntimeBase { private TaskCompletionSource _nextInvocationTcs = new TaskCompletionSource(); From 76e0fc251c104a6acc9f4d8f86463dfe8c2cea4e Mon Sep 17 00:00:00 2001 From: Justin Kotalik Date: Tue, 9 Apr 2019 14:03:12 -0700 Subject: [PATCH 52/52] Add Repeat attribute (dotnet/extensions#1375) \n\nCommit migrated from https://github.com/dotnet/extensions/commit/13a00b0557e51aefa8132781def19f334a829614 --- src/Testing/src/xunit/ConditionalFactAttribute.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Testing/src/xunit/ConditionalFactAttribute.cs b/src/Testing/src/xunit/ConditionalFactAttribute.cs index 7448b48d8c..ce37df2e56 100644 --- a/src/Testing/src/xunit/ConditionalFactAttribute.cs +++ b/src/Testing/src/xunit/ConditionalFactAttribute.cs @@ -12,4 +12,4 @@ namespace Microsoft.AspNetCore.Testing.xunit public class ConditionalFactAttribute : FactAttribute { } -} \ No newline at end of file +}