diff --git a/Directory.Packages.props b/Directory.Packages.props
index f37bd594e4c..7923f760fc8 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -7,8 +7,9 @@
-
+
+
diff --git a/src/Directory.Build.targets b/src/Directory.Build.targets
index b75f72b30b1..f79f7d13d44 100644
--- a/src/Directory.Build.targets
+++ b/src/Directory.Build.targets
@@ -126,4 +126,13 @@
+
+
+
+
+
+
+
diff --git a/src/Microsoft.Diagnostics.Monitoring.Extension.Common/Microsoft.Diagnostics.Monitoring.Extension.Common.csproj b/src/Microsoft.Diagnostics.Monitoring.Extension.Common/Microsoft.Diagnostics.Monitoring.Extension.Common.csproj
index 8879d6f8eb6..f9ada28f3cc 100644
--- a/src/Microsoft.Diagnostics.Monitoring.Extension.Common/Microsoft.Diagnostics.Monitoring.Extension.Common.csproj
+++ b/src/Microsoft.Diagnostics.Monitoring.Extension.Common/Microsoft.Diagnostics.Monitoring.Extension.Common.csproj
@@ -1,4 +1,4 @@
-
+
Library
@@ -11,6 +11,7 @@
+
diff --git a/src/Microsoft.Diagnostics.Monitoring.Options/AuthenticationOptions.Validate.cs b/src/Microsoft.Diagnostics.Monitoring.Options/AuthenticationOptions.Validate.cs
index d684f739526..6b11722b7a1 100644
--- a/src/Microsoft.Diagnostics.Monitoring.Options/AuthenticationOptions.Validate.cs
+++ b/src/Microsoft.Diagnostics.Monitoring.Options/AuthenticationOptions.Validate.cs
@@ -20,7 +20,7 @@ IEnumerable IValidatableObject.Validate(ValidationContext vali
results.Add(
new ValidationResult(
string.Format(
- OptionsDisplayStrings.ErrorMessage_MultipleAuthenticationModesSpecified)));
+ OptionsDisplayStrings.ErrorMessage_MultipleAuthenticationModesSpecified), [nameof(MonitorApiKey), nameof(AzureAd)]));
}
return results;
diff --git a/src/Microsoft.Diagnostics.Monitoring.Options/GlobalCounterOptions.cs b/src/Microsoft.Diagnostics.Monitoring.Options/GlobalCounterOptions.cs
index c360b5184d1..ff3c222d629 100644
--- a/src/Microsoft.Diagnostics.Monitoring.Options/GlobalCounterOptions.cs
+++ b/src/Microsoft.Diagnostics.Monitoring.Options/GlobalCounterOptions.cs
@@ -64,7 +64,8 @@ public IEnumerable Validate(ValidationContext validationContex
{
// We prefix the validation error with the provider.
results.AddRange(providerResults.Select(r => new ValidationResult(
- string.Format(CultureInfo.CurrentCulture, OptionsDisplayStrings.ErrorMessage_NestedProviderValidationError, provider, r.ErrorMessage))));
+ string.Format(CultureInfo.CurrentCulture, OptionsDisplayStrings.ErrorMessage_NestedProviderValidationError, provider, r.ErrorMessage),
+ [nameof(Providers)])));
}
}
}
diff --git a/src/Tests/CollectionRuleActions.UnitTests/CollectionRuleActions.UnitTests.csproj b/src/Tests/CollectionRuleActions.UnitTests/CollectionRuleActions.UnitTests.csproj
index aa0591b54fc..f98166d3e64 100644
--- a/src/Tests/CollectionRuleActions.UnitTests/CollectionRuleActions.UnitTests.csproj
+++ b/src/Tests/CollectionRuleActions.UnitTests/CollectionRuleActions.UnitTests.csproj
@@ -7,6 +7,7 @@
+
@@ -14,6 +15,7 @@
+
diff --git a/src/Tests/CollectionRuleActions.UnitTests/ExecuteActionTests.cs b/src/Tests/CollectionRuleActions.UnitTests/ExecuteActionTests.cs
index e5bf473507b..5c062e23516 100644
--- a/src/Tests/CollectionRuleActions.UnitTests/ExecuteActionTests.cs
+++ b/src/Tests/CollectionRuleActions.UnitTests/ExecuteActionTests.cs
@@ -1,11 +1,15 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using Microsoft.AspNetCore.Http.Validation;
+using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.Diagnostics.Monitoring.TestCommon;
using Microsoft.Diagnostics.Tools.Monitor;
using Microsoft.Diagnostics.Tools.Monitor.CollectionRules.Actions;
using Microsoft.Diagnostics.Tools.Monitor.CollectionRules.Exceptions;
using Microsoft.Diagnostics.Tools.Monitor.CollectionRules.Options.Actions;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Options;
using System;
using System.Globalization;
using System.IO;
@@ -18,17 +22,23 @@
namespace CollectionRuleActions.UnitTests
{
+ public class ExecuteActionFixture : WebApplicationFactory
+ {
+ }
+
[TargetFrameworkMonikerTrait(TargetFrameworkMonikerExtensions.CurrentTargetFrameworkMoniker)]
[Collection(TestCollections.CollectionRuleActions)]
- public sealed class ExecuteActionTests
+ public sealed class ExecuteActionTests : IClassFixture
{
private static readonly TimeSpan DefaultTimeout = TimeSpan.FromSeconds(30);
private readonly ITestOutputHelper _outputHelper;
+ private readonly ExecuteActionFixture _fixture;
- public ExecuteActionTests(ITestOutputHelper outputHelper)
+ public ExecuteActionTests(ITestOutputHelper outputHelper, ExecuteActionFixture fixture)
{
_outputHelper = outputHelper;
+ _fixture = fixture;
}
[Fact]
@@ -173,9 +183,10 @@ private static void ValidateActionResult(CollectionRuleActionResult result, stri
Assert.Equal(expectedExitCode, actualExitCode);
}
- private static async Task ValidateAction(Action optionsCallback, Func actionCallback)
+ private async Task ValidateAction(Action optionsCallback, Func actionCallback)
{
- ExecuteActionFactory factory = new();
+ var validationOptions = _fixture.Services.GetService>();
+ ExecuteActionFactory factory = new(_fixture.Services, validationOptions);
ExecuteOptions options = new();
diff --git a/src/Tests/Microsoft.Diagnostics.Monitoring.Tool.FunctionalTests/AuthenticationTests.cs b/src/Tests/Microsoft.Diagnostics.Monitoring.Tool.FunctionalTests/AuthenticationTests.cs
index e13ae742e76..555348cba9d 100644
--- a/src/Tests/Microsoft.Diagnostics.Monitoring.Tool.FunctionalTests/AuthenticationTests.cs
+++ b/src/Tests/Microsoft.Diagnostics.Monitoring.Tool.FunctionalTests/AuthenticationTests.cs
@@ -472,9 +472,6 @@ public async Task AllowsConfiguredIssuer()
[Theory]
// Guids that don't match should get rejected
[InlineData("980d2b17-71e1-4313-a084-c077e962680c", "10253b7a-454d-41bb-a3f5-5f2e6b26ed93", HttpStatusCode.Forbidden)]
- // Empty string isn't valid even when signed and configured correctly
- [InlineData("", "", HttpStatusCode.Unauthorized)]
- [InlineData("10253b7a-454d-41bb-a3f5-5f2e6b26ed93", "", HttpStatusCode.Unauthorized)]
[InlineData("", "10253b7a-454d-41bb-a3f5-5f2e6b26ed93", HttpStatusCode.Forbidden)]
public async Task RejectsBadSubject(string jwtSubject, string configSubject, HttpStatusCode expectedError)
{
@@ -501,6 +498,25 @@ public async Task RejectsBadSubject(string jwtSubject, string configSubject, Htt
Assert.Equal(expectedError, statusCodeException.StatusCode);
}
+ ///
+ /// This tests that an empty configured subject fails on startup.
+ ///
+ [Fact]
+ public async Task DoesNotStart_With_EmptySubject()
+ {
+ var configSubject = "";
+ await using MonitorCollectRunner toolRunner = new(_outputHelper);
+ toolRunner.DisableMetricsViaCommandLine = true;
+
+ _outputHelper.WriteLine("Generating API key.");
+
+ JwtPayload newPayload = GetJwtPayload(AuthConstants.ApiKeyJwtAudience, configSubject, AuthConstants.ApiKeyJwtInternalIssuer, AuthConstants.ApiKeyJwtDefaultExpiration);
+
+ toolRunner.ConfigurationFromEnvironment.UseApiKey(SecurityAlgorithms.EcdsaSha384, configSubject, newPayload, out string token);
+
+ await Assert.ThrowsAsync(toolRunner.StartAsync);
+ }
+
[Fact]
public async Task RejectsMissingExpiration()
{
diff --git a/src/Tests/Microsoft.Diagnostics.Monitoring.Tool.UnitTestCommon/Microsoft.Diagnostics.Monitoring.Tool.UnitTestCommon.csproj b/src/Tests/Microsoft.Diagnostics.Monitoring.Tool.UnitTestCommon/Microsoft.Diagnostics.Monitoring.Tool.UnitTestCommon.csproj
index d8db2356aea..5f0b9f3f651 100644
--- a/src/Tests/Microsoft.Diagnostics.Monitoring.Tool.UnitTestCommon/Microsoft.Diagnostics.Monitoring.Tool.UnitTestCommon.csproj
+++ b/src/Tests/Microsoft.Diagnostics.Monitoring.Tool.UnitTestCommon/Microsoft.Diagnostics.Monitoring.Tool.UnitTestCommon.csproj
@@ -3,6 +3,7 @@
$(ToolTargetFrameworks)
true
+ true
diff --git a/src/Tests/Microsoft.Diagnostics.Monitoring.Tool.UnitTestCommon/TestHostHelper.cs b/src/Tests/Microsoft.Diagnostics.Monitoring.Tool.UnitTestCommon/TestHostHelper.cs
index 02c48fa9433..795b4fb01e9 100644
--- a/src/Tests/Microsoft.Diagnostics.Monitoring.Tool.UnitTestCommon/TestHostHelper.cs
+++ b/src/Tests/Microsoft.Diagnostics.Monitoring.Tool.UnitTestCommon/TestHostHelper.cs
@@ -124,6 +124,8 @@ public static IHost CreateHost(
services.AddSingleton();
services.AddSingleton();
services.AddSingleton();
+ ValidatableTypes.AddValidation(services);
+ TestValidatableTypes.AddValidation(services);
servicesCallback?.Invoke(services);
})
.Build();
diff --git a/src/Tests/Microsoft.Diagnostics.Monitoring.Tool.UnitTestCommon/TestValidatableType.cs b/src/Tests/Microsoft.Diagnostics.Monitoring.Tool.UnitTestCommon/TestValidatableType.cs
new file mode 100644
index 00000000000..f2b2bd4ead0
--- /dev/null
+++ b/src/Tests/Microsoft.Diagnostics.Monitoring.Tool.UnitTestCommon/TestValidatableType.cs
@@ -0,0 +1,20 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Microsoft.AspNetCore.Http.Validation;
+using Microsoft.AspNetCore.Http.Validation.Generated;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace Microsoft.Diagnostics.Monitoring.TestCommon
+{
+ [ValidatableType]
+ internal sealed class TestValidatableTypes
+ {
+ public required PassThroughOptions PassThroughOptions { get; init; }
+
+ public static void AddValidation(IServiceCollection services)
+ {
+ TestGeneratedServiceCollectionExtensions.AddValidation(services);
+ }
+ }
+}
diff --git a/src/Tests/Microsoft.Diagnostics.Monitoring.Tool.UnitTestCommon/ValidatableInfoResolver.cs b/src/Tests/Microsoft.Diagnostics.Monitoring.Tool.UnitTestCommon/ValidatableInfoResolver.cs
new file mode 100644
index 00000000000..06509a455b9
--- /dev/null
+++ b/src/Tests/Microsoft.Diagnostics.Monitoring.Tool.UnitTestCommon/ValidatableInfoResolver.cs
@@ -0,0 +1,743 @@
+#nullable enable annotations
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+#nullable enable
+
+namespace System.Runtime.CompilerServices
+{
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.ValidationsGenerator, Version=10.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", "10.0.0.0")]
+ [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
+ file sealed class InterceptsLocationAttribute : System.Attribute
+ {
+ public InterceptsLocationAttribute(int version, string data)
+ {
+ }
+ }
+}
+
+namespace Microsoft.AspNetCore.Http.Validation.Generated
+{
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.ValidationsGenerator, Version=10.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", "10.0.0.0")]
+ file sealed class GeneratedValidatablePropertyInfo : global::Microsoft.AspNetCore.Http.Validation.ValidatablePropertyInfo
+ {
+ public GeneratedValidatablePropertyInfo(
+ [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties)]
+ global::System.Type containingType,
+ global::System.Type propertyType,
+ string name,
+ string displayName) : base(containingType, propertyType, name, displayName)
+ {
+ ContainingType = containingType;
+ Name = name;
+ }
+
+ internal global::System.Type ContainingType { get; }
+ internal string Name { get; }
+
+ protected override global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes()
+ => ValidationAttributeCache.GetValidationAttributes(ContainingType, Name);
+ }
+
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.ValidationsGenerator, Version=10.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", "10.0.0.0")]
+ file sealed class GeneratedValidatableTypeInfo : global::Microsoft.AspNetCore.Http.Validation.ValidatableTypeInfo
+ {
+ public GeneratedValidatableTypeInfo(
+ [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)]
+ global::System.Type type,
+ ValidatablePropertyInfo[] members) : base(type, members) { }
+ }
+
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.ValidationsGenerator, Version=10.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", "10.0.0.0")]
+ file class GeneratedValidatableInfoResolver : global::Microsoft.AspNetCore.Http.Validation.IValidatableInfoResolver
+ {
+ public bool TryGetValidatableTypeInfo(global::System.Type type, [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out global::Microsoft.AspNetCore.Http.Validation.IValidatableInfo? validatableInfo)
+ {
+ validatableInfo = null;
+ if (type == typeof(global::System.Reflection.MethodBase))
+ {
+ validatableInfo = CreateMethodBase();
+ return true;
+ }
+ if (type == typeof(global::System.Reflection.ConstructorInfo))
+ {
+ validatableInfo = CreateConstructorInfo();
+ return true;
+ }
+ if (type == typeof(global::System.Reflection.CustomAttributeTypedArgument))
+ {
+ validatableInfo = CreateCustomAttributeTypedArgument();
+ return true;
+ }
+ if (type == typeof(global::System.Reflection.CustomAttributeNamedArgument))
+ {
+ validatableInfo = CreateCustomAttributeNamedArgument();
+ return true;
+ }
+ if (type == typeof(global::System.Reflection.CustomAttributeData))
+ {
+ validatableInfo = CreateCustomAttributeData();
+ return true;
+ }
+ if (type == typeof(global::System.Reflection.ParameterInfo))
+ {
+ validatableInfo = CreateParameterInfo();
+ return true;
+ }
+ if (type == typeof(global::System.Reflection.MethodInfo))
+ {
+ validatableInfo = CreateMethodInfo();
+ return true;
+ }
+ if (type == typeof(global::System.Reflection.EventInfo))
+ {
+ validatableInfo = CreateEventInfo();
+ return true;
+ }
+ if (type == typeof(global::System.Reflection.FieldInfo))
+ {
+ validatableInfo = CreateFieldInfo();
+ return true;
+ }
+ if (type == typeof(global::System.Reflection.PropertyInfo))
+ {
+ validatableInfo = CreatePropertyInfo();
+ return true;
+ }
+ if (type == typeof(global::System.Reflection.TypeInfo))
+ {
+ validatableInfo = CreateTypeInfo();
+ return true;
+ }
+ if (type == typeof(global::System.Reflection.Assembly))
+ {
+ validatableInfo = CreateAssembly();
+ return true;
+ }
+ if (type == typeof(global::System.Guid))
+ {
+ validatableInfo = CreateGuid();
+ return true;
+ }
+ if (type == typeof(global::System.Reflection.Module))
+ {
+ validatableInfo = CreateModule();
+ return true;
+ }
+ if (type == typeof(global::System.Reflection.MemberInfo))
+ {
+ validatableInfo = CreateMemberInfo();
+ return true;
+ }
+ if (type == typeof(global::System.Type))
+ {
+ validatableInfo = CreateType();
+ return true;
+ }
+ if (type == typeof(global::Microsoft.Diagnostics.Tools.Monitor.CollectionRules.Options.Actions.BaseRecordOptions))
+ {
+ validatableInfo = CreateBaseRecordOptions();
+ return true;
+ }
+ if (type == typeof(global::Microsoft.Diagnostics.Monitoring.TestCommon.PassThroughOptions))
+ {
+ validatableInfo = CreatePassThroughOptions();
+ return true;
+ }
+ if (type == typeof(global::Microsoft.Diagnostics.Monitoring.TestCommon.TestValidatableTypes))
+ {
+ validatableInfo = CreateTestValidatableTypes();
+ return true;
+ }
+
+ return false;
+ }
+
+ // No-ops, rely on runtime code for ParameterInfo-based resolution
+ public bool TryGetValidatableParameterInfo(global::System.Reflection.ParameterInfo parameterInfo, [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out global::Microsoft.AspNetCore.Http.Validation.IValidatableInfo? validatableInfo)
+ {
+ validatableInfo = null;
+ return false;
+ }
+
+ private ValidatableTypeInfo CreateMethodBase()
+ {
+ return new GeneratedValidatableTypeInfo(
+ type: typeof(global::System.Reflection.MethodBase),
+ members: []
+ );
+ }
+ private ValidatableTypeInfo CreateConstructorInfo()
+ {
+ return new GeneratedValidatableTypeInfo(
+ type: typeof(global::System.Reflection.ConstructorInfo),
+ members: []
+ );
+ }
+ private ValidatableTypeInfo CreateCustomAttributeTypedArgument()
+ {
+ return new GeneratedValidatableTypeInfo(
+ type: typeof(global::System.Reflection.CustomAttributeTypedArgument),
+ members: [
+ new GeneratedValidatablePropertyInfo(
+ containingType: typeof(global::System.Reflection.CustomAttributeTypedArgument),
+ propertyType: typeof(global::System.Type),
+ name: "ArgumentType",
+ displayName: "ArgumentType"
+ ),
+ ]
+ );
+ }
+ private ValidatableTypeInfo CreateCustomAttributeNamedArgument()
+ {
+ return new GeneratedValidatableTypeInfo(
+ type: typeof(global::System.Reflection.CustomAttributeNamedArgument),
+ members: [
+ new GeneratedValidatablePropertyInfo(
+ containingType: typeof(global::System.Reflection.CustomAttributeNamedArgument),
+ propertyType: typeof(global::System.Reflection.MemberInfo),
+ name: "MemberInfo",
+ displayName: "MemberInfo"
+ ),
+ new GeneratedValidatablePropertyInfo(
+ containingType: typeof(global::System.Reflection.CustomAttributeNamedArgument),
+ propertyType: typeof(global::System.Reflection.CustomAttributeTypedArgument),
+ name: "TypedValue",
+ displayName: "TypedValue"
+ ),
+ ]
+ );
+ }
+ private ValidatableTypeInfo CreateCustomAttributeData()
+ {
+ return new GeneratedValidatableTypeInfo(
+ type: typeof(global::System.Reflection.CustomAttributeData),
+ members: [
+ new GeneratedValidatablePropertyInfo(
+ containingType: typeof(global::System.Reflection.CustomAttributeData),
+ propertyType: typeof(global::System.Type),
+ name: "AttributeType",
+ displayName: "AttributeType"
+ ),
+ new GeneratedValidatablePropertyInfo(
+ containingType: typeof(global::System.Reflection.CustomAttributeData),
+ propertyType: typeof(global::System.Reflection.ConstructorInfo),
+ name: "Constructor",
+ displayName: "Constructor"
+ ),
+ new GeneratedValidatablePropertyInfo(
+ containingType: typeof(global::System.Reflection.CustomAttributeData),
+ propertyType: typeof(global::System.Collections.Generic.IList),
+ name: "ConstructorArguments",
+ displayName: "ConstructorArguments"
+ ),
+ new GeneratedValidatablePropertyInfo(
+ containingType: typeof(global::System.Reflection.CustomAttributeData),
+ propertyType: typeof(global::System.Collections.Generic.IList),
+ name: "NamedArguments",
+ displayName: "NamedArguments"
+ ),
+ ]
+ );
+ }
+ private ValidatableTypeInfo CreateParameterInfo()
+ {
+ return new GeneratedValidatableTypeInfo(
+ type: typeof(global::System.Reflection.ParameterInfo),
+ members: [
+ new GeneratedValidatablePropertyInfo(
+ containingType: typeof(global::System.Reflection.ParameterInfo),
+ propertyType: typeof(global::System.Collections.Generic.IEnumerable),
+ name: "CustomAttributes",
+ displayName: "CustomAttributes"
+ ),
+ new GeneratedValidatablePropertyInfo(
+ containingType: typeof(global::System.Reflection.ParameterInfo),
+ propertyType: typeof(global::System.Reflection.MemberInfo),
+ name: "Member",
+ displayName: "Member"
+ ),
+ new GeneratedValidatablePropertyInfo(
+ containingType: typeof(global::System.Reflection.ParameterInfo),
+ propertyType: typeof(global::System.Type),
+ name: "ParameterType",
+ displayName: "ParameterType"
+ ),
+ ]
+ );
+ }
+ private ValidatableTypeInfo CreateMethodInfo()
+ {
+ return new GeneratedValidatableTypeInfo(
+ type: typeof(global::System.Reflection.MethodInfo),
+ members: [
+ new GeneratedValidatablePropertyInfo(
+ containingType: typeof(global::System.Reflection.MethodInfo),
+ propertyType: typeof(global::System.Reflection.MemberTypes),
+ name: "MemberType",
+ displayName: "MemberType"
+ ),
+ new GeneratedValidatablePropertyInfo(
+ containingType: typeof(global::System.Reflection.MethodInfo),
+ propertyType: typeof(global::System.Reflection.ParameterInfo),
+ name: "ReturnParameter",
+ displayName: "ReturnParameter"
+ ),
+ new GeneratedValidatablePropertyInfo(
+ containingType: typeof(global::System.Reflection.MethodInfo),
+ propertyType: typeof(global::System.Type),
+ name: "ReturnType",
+ displayName: "ReturnType"
+ ),
+ ]
+ );
+ }
+ private ValidatableTypeInfo CreateEventInfo()
+ {
+ return new GeneratedValidatableTypeInfo(
+ type: typeof(global::System.Reflection.EventInfo),
+ members: [
+ new GeneratedValidatablePropertyInfo(
+ containingType: typeof(global::System.Reflection.EventInfo),
+ propertyType: typeof(global::System.Reflection.MethodInfo),
+ name: "AddMethod",
+ displayName: "AddMethod"
+ ),
+ new GeneratedValidatablePropertyInfo(
+ containingType: typeof(global::System.Reflection.EventInfo),
+ propertyType: typeof(global::System.Type),
+ name: "EventHandlerType",
+ displayName: "EventHandlerType"
+ ),
+ new GeneratedValidatablePropertyInfo(
+ containingType: typeof(global::System.Reflection.EventInfo),
+ propertyType: typeof(global::System.Reflection.MemberTypes),
+ name: "MemberType",
+ displayName: "MemberType"
+ ),
+ new GeneratedValidatablePropertyInfo(
+ containingType: typeof(global::System.Reflection.EventInfo),
+ propertyType: typeof(global::System.Reflection.MethodInfo),
+ name: "RaiseMethod",
+ displayName: "RaiseMethod"
+ ),
+ new GeneratedValidatablePropertyInfo(
+ containingType: typeof(global::System.Reflection.EventInfo),
+ propertyType: typeof(global::System.Reflection.MethodInfo),
+ name: "RemoveMethod",
+ displayName: "RemoveMethod"
+ ),
+ ]
+ );
+ }
+ private ValidatableTypeInfo CreateFieldInfo()
+ {
+ return new GeneratedValidatableTypeInfo(
+ type: typeof(global::System.Reflection.FieldInfo),
+ members: [
+ new GeneratedValidatablePropertyInfo(
+ containingType: typeof(global::System.Reflection.FieldInfo),
+ propertyType: typeof(global::System.Type),
+ name: "FieldType",
+ displayName: "FieldType"
+ ),
+ new GeneratedValidatablePropertyInfo(
+ containingType: typeof(global::System.Reflection.FieldInfo),
+ propertyType: typeof(global::System.Reflection.MemberTypes),
+ name: "MemberType",
+ displayName: "MemberType"
+ ),
+ ]
+ );
+ }
+ private ValidatableTypeInfo CreatePropertyInfo()
+ {
+ return new GeneratedValidatableTypeInfo(
+ type: typeof(global::System.Reflection.PropertyInfo),
+ members: [
+ new GeneratedValidatablePropertyInfo(
+ containingType: typeof(global::System.Reflection.PropertyInfo),
+ propertyType: typeof(global::System.Reflection.MethodInfo),
+ name: "GetMethod",
+ displayName: "GetMethod"
+ ),
+ new GeneratedValidatablePropertyInfo(
+ containingType: typeof(global::System.Reflection.PropertyInfo),
+ propertyType: typeof(global::System.Reflection.MemberTypes),
+ name: "MemberType",
+ displayName: "MemberType"
+ ),
+ new GeneratedValidatablePropertyInfo(
+ containingType: typeof(global::System.Reflection.PropertyInfo),
+ propertyType: typeof(global::System.Type),
+ name: "PropertyType",
+ displayName: "PropertyType"
+ ),
+ new GeneratedValidatablePropertyInfo(
+ containingType: typeof(global::System.Reflection.PropertyInfo),
+ propertyType: typeof(global::System.Reflection.MethodInfo),
+ name: "SetMethod",
+ displayName: "SetMethod"
+ ),
+ ]
+ );
+ }
+ private ValidatableTypeInfo CreateTypeInfo()
+ {
+ return new GeneratedValidatableTypeInfo(
+ type: typeof(global::System.Reflection.TypeInfo),
+ members: [
+ new GeneratedValidatablePropertyInfo(
+ containingType: typeof(global::System.Reflection.TypeInfo),
+ propertyType: typeof(global::System.Collections.Generic.IEnumerable),
+ name: "DeclaredConstructors",
+ displayName: "DeclaredConstructors"
+ ),
+ new GeneratedValidatablePropertyInfo(
+ containingType: typeof(global::System.Reflection.TypeInfo),
+ propertyType: typeof(global::System.Collections.Generic.IEnumerable),
+ name: "DeclaredEvents",
+ displayName: "DeclaredEvents"
+ ),
+ new GeneratedValidatablePropertyInfo(
+ containingType: typeof(global::System.Reflection.TypeInfo),
+ propertyType: typeof(global::System.Collections.Generic.IEnumerable),
+ name: "DeclaredFields",
+ displayName: "DeclaredFields"
+ ),
+ new GeneratedValidatablePropertyInfo(
+ containingType: typeof(global::System.Reflection.TypeInfo),
+ propertyType: typeof(global::System.Collections.Generic.IEnumerable),
+ name: "DeclaredMembers",
+ displayName: "DeclaredMembers"
+ ),
+ new GeneratedValidatablePropertyInfo(
+ containingType: typeof(global::System.Reflection.TypeInfo),
+ propertyType: typeof(global::System.Collections.Generic.IEnumerable),
+ name: "DeclaredMethods",
+ displayName: "DeclaredMethods"
+ ),
+ new GeneratedValidatablePropertyInfo(
+ containingType: typeof(global::System.Reflection.TypeInfo),
+ propertyType: typeof(global::System.Collections.Generic.IEnumerable),
+ name: "DeclaredNestedTypes",
+ displayName: "DeclaredNestedTypes"
+ ),
+ new GeneratedValidatablePropertyInfo(
+ containingType: typeof(global::System.Reflection.TypeInfo),
+ propertyType: typeof(global::System.Collections.Generic.IEnumerable),
+ name: "DeclaredProperties",
+ displayName: "DeclaredProperties"
+ ),
+ new GeneratedValidatablePropertyInfo(
+ containingType: typeof(global::System.Reflection.TypeInfo),
+ propertyType: typeof(global::System.Collections.Generic.IEnumerable),
+ name: "ImplementedInterfaces",
+ displayName: "ImplementedInterfaces"
+ ),
+ ]
+ );
+ }
+ private ValidatableTypeInfo CreateAssembly()
+ {
+ return new GeneratedValidatableTypeInfo(
+ type: typeof(global::System.Reflection.Assembly),
+ members: [
+ new GeneratedValidatablePropertyInfo(
+ containingType: typeof(global::System.Reflection.Assembly),
+ propertyType: typeof(global::System.Collections.Generic.IEnumerable),
+ name: "CustomAttributes",
+ displayName: "CustomAttributes"
+ ),
+ new GeneratedValidatablePropertyInfo(
+ containingType: typeof(global::System.Reflection.Assembly),
+ propertyType: typeof(global::System.Collections.Generic.IEnumerable),
+ name: "DefinedTypes",
+ displayName: "DefinedTypes"
+ ),
+ new GeneratedValidatablePropertyInfo(
+ containingType: typeof(global::System.Reflection.Assembly),
+ propertyType: typeof(global::System.Reflection.MethodInfo),
+ name: "EntryPoint",
+ displayName: "EntryPoint"
+ ),
+ new GeneratedValidatablePropertyInfo(
+ containingType: typeof(global::System.Reflection.Assembly),
+ propertyType: typeof(global::System.Collections.Generic.IEnumerable),
+ name: "ExportedTypes",
+ displayName: "ExportedTypes"
+ ),
+ new GeneratedValidatablePropertyInfo(
+ containingType: typeof(global::System.Reflection.Assembly),
+ propertyType: typeof(global::System.Reflection.Module),
+ name: "ManifestModule",
+ displayName: "ManifestModule"
+ ),
+ new GeneratedValidatablePropertyInfo(
+ containingType: typeof(global::System.Reflection.Assembly),
+ propertyType: typeof(global::System.Collections.Generic.IEnumerable),
+ name: "Modules",
+ displayName: "Modules"
+ ),
+ ]
+ );
+ }
+ private ValidatableTypeInfo CreateGuid()
+ {
+ return new GeneratedValidatableTypeInfo(
+ type: typeof(global::System.Guid),
+ members: [
+ // https://github.com/dotnet/aspnetcore/issues/61525
+ // new GeneratedValidatablePropertyInfo(
+ // containingType: typeof(global::System.Guid),
+ // propertyType: typeof(global::System.Guid),
+ // name: "AllBitsSet",
+ // displayName: "AllBitsSet"
+ // ),
+ ]
+ );
+ }
+ private ValidatableTypeInfo CreateModule()
+ {
+ return new GeneratedValidatableTypeInfo(
+ type: typeof(global::System.Reflection.Module),
+ members: [
+ new GeneratedValidatablePropertyInfo(
+ containingType: typeof(global::System.Reflection.Module),
+ propertyType: typeof(global::System.Reflection.Assembly),
+ name: "Assembly",
+ displayName: "Assembly"
+ ),
+ new GeneratedValidatablePropertyInfo(
+ containingType: typeof(global::System.Reflection.Module),
+ propertyType: typeof(global::System.Collections.Generic.IEnumerable),
+ name: "CustomAttributes",
+ displayName: "CustomAttributes"
+ ),
+ new GeneratedValidatablePropertyInfo(
+ containingType: typeof(global::System.Reflection.Module),
+ propertyType: typeof(global::System.Guid),
+ name: "ModuleVersionId",
+ displayName: "ModuleVersionId"
+ ),
+ ]
+ );
+ }
+ private ValidatableTypeInfo CreateMemberInfo()
+ {
+ return new GeneratedValidatableTypeInfo(
+ type: typeof(global::System.Reflection.MemberInfo),
+ members: [
+ new GeneratedValidatablePropertyInfo(
+ containingType: typeof(global::System.Reflection.MemberInfo),
+ propertyType: typeof(global::System.Collections.Generic.IEnumerable),
+ name: "CustomAttributes",
+ displayName: "CustomAttributes"
+ ),
+ new GeneratedValidatablePropertyInfo(
+ containingType: typeof(global::System.Reflection.MemberInfo),
+ propertyType: typeof(global::System.Type),
+ name: "DeclaringType",
+ displayName: "DeclaringType"
+ ),
+ new GeneratedValidatablePropertyInfo(
+ containingType: typeof(global::System.Reflection.MemberInfo),
+ propertyType: typeof(global::System.Reflection.MemberTypes),
+ name: "MemberType",
+ displayName: "MemberType"
+ ),
+ new GeneratedValidatablePropertyInfo(
+ containingType: typeof(global::System.Reflection.MemberInfo),
+ propertyType: typeof(global::System.Reflection.Module),
+ name: "Module",
+ displayName: "Module"
+ ),
+ new GeneratedValidatablePropertyInfo(
+ containingType: typeof(global::System.Reflection.MemberInfo),
+ propertyType: typeof(global::System.Type),
+ name: "ReflectedType",
+ displayName: "ReflectedType"
+ ),
+ ]
+ );
+ }
+ private ValidatableTypeInfo CreateType()
+ {
+ return new GeneratedValidatableTypeInfo(
+ type: typeof(global::System.Type),
+ members: [
+ new GeneratedValidatablePropertyInfo(
+ containingType: typeof(global::System.Type),
+ propertyType: typeof(global::System.Reflection.Assembly),
+ name: "Assembly",
+ displayName: "Assembly"
+ ),
+ new GeneratedValidatablePropertyInfo(
+ containingType: typeof(global::System.Type),
+ propertyType: typeof(global::System.Type),
+ name: "BaseType",
+ displayName: "BaseType"
+ ),
+ new GeneratedValidatablePropertyInfo(
+ containingType: typeof(global::System.Type),
+ propertyType: typeof(global::System.Reflection.MethodBase),
+ name: "DeclaringMethod",
+ displayName: "DeclaringMethod"
+ ),
+ new GeneratedValidatablePropertyInfo(
+ containingType: typeof(global::System.Type),
+ propertyType: typeof(global::System.Type),
+ name: "DeclaringType",
+ displayName: "DeclaringType"
+ ),
+ new GeneratedValidatablePropertyInfo(
+ containingType: typeof(global::System.Type),
+ propertyType: typeof(global::System.Type[]),
+ name: "GenericTypeArguments",
+ displayName: "GenericTypeArguments"
+ ),
+ new GeneratedValidatablePropertyInfo(
+ containingType: typeof(global::System.Type),
+ propertyType: typeof(global::System.Guid),
+ name: "GUID",
+ displayName: "GUID"
+ ),
+ new GeneratedValidatablePropertyInfo(
+ containingType: typeof(global::System.Type),
+ propertyType: typeof(global::System.Reflection.MemberTypes),
+ name: "MemberType",
+ displayName: "MemberType"
+ ),
+ new GeneratedValidatablePropertyInfo(
+ containingType: typeof(global::System.Type),
+ propertyType: typeof(global::System.Reflection.Module),
+ name: "Module",
+ displayName: "Module"
+ ),
+ new GeneratedValidatablePropertyInfo(
+ containingType: typeof(global::System.Type),
+ propertyType: typeof(global::System.Type),
+ name: "ReflectedType",
+ displayName: "ReflectedType"
+ ),
+ new GeneratedValidatablePropertyInfo(
+ containingType: typeof(global::System.Type),
+ propertyType: typeof(global::System.Reflection.ConstructorInfo),
+ name: "TypeInitializer",
+ displayName: "TypeInitializer"
+ ),
+ new GeneratedValidatablePropertyInfo(
+ containingType: typeof(global::System.Type),
+ propertyType: typeof(global::System.Type),
+ name: "UnderlyingSystemType",
+ displayName: "UnderlyingSystemType"
+ ),
+ ]
+ );
+ }
+ private ValidatableTypeInfo CreateBaseRecordOptions()
+ {
+ return new GeneratedValidatableTypeInfo(
+ type: typeof(global::Microsoft.Diagnostics.Tools.Monitor.CollectionRules.Options.Actions.BaseRecordOptions),
+ members: [
+ // https://github.com/dotnet/aspnetcore/issues/61379
+ // new GeneratedValidatablePropertyInfo(
+ // containingType: typeof(global::Microsoft.Diagnostics.Tools.Monitor.CollectionRules.Options.Actions.BaseRecordOptions),
+ // propertyType: typeof(global::System.Type),
+ // name: "EqualityContract",
+ // displayName: "EqualityContract"
+ // ),
+ ]
+ );
+ }
+ private ValidatableTypeInfo CreatePassThroughOptions()
+ {
+ return new GeneratedValidatableTypeInfo(
+ type: typeof(global::Microsoft.Diagnostics.Monitoring.TestCommon.PassThroughOptions),
+ members: [
+ // https://github.com/dotnet/aspnetcore/issues/61379
+ // new GeneratedValidatablePropertyInfo(
+ // containingType: typeof(global::Microsoft.Diagnostics.Monitoring.TestCommon.PassThroughOptions),
+ // propertyType: typeof(global::System.Type),
+ // name: "EqualityContract",
+ // displayName: "EqualityContract"
+ // ),
+ ]
+ );
+ }
+ private ValidatableTypeInfo CreateTestValidatableTypes()
+ {
+ return new GeneratedValidatableTypeInfo(
+ type: typeof(global::Microsoft.Diagnostics.Monitoring.TestCommon.TestValidatableTypes),
+ members: [
+ new GeneratedValidatablePropertyInfo(
+ containingType: typeof(global::Microsoft.Diagnostics.Monitoring.TestCommon.TestValidatableTypes),
+ propertyType: typeof(global::Microsoft.Diagnostics.Monitoring.TestCommon.PassThroughOptions),
+ name: "PassThroughOptions",
+ displayName: "PassThroughOptions"
+ ),
+ ]
+ );
+ }
+
+ }
+
+ // Added internal wrapper to let the generated extension method be accessed from test code
+ // without conflicting with GeneratedServiceCollectionExtensions in product code.
+ internal static class TestGeneratedServiceCollectionExtensions
+ {
+ public static global::Microsoft.Extensions.DependencyInjection.IServiceCollection AddValidation(global::Microsoft.Extensions.DependencyInjection.IServiceCollection services)
+ {
+ return GeneratedServiceCollectionExtensions.AddValidation(services);
+ }
+ }
+
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.ValidationsGenerator, Version=10.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", "10.0.0.0")]
+ file static class GeneratedServiceCollectionExtensions
+ {
+ // [global::System.Runtime.CompilerServices.InterceptsLocationAttribute(1, "8vl1G447Wi3Pc94851ZKGx+IAABWYWxpZGF0YWJsZUluZm9SZXNvbHZlci5jcw==")]
+ // Removed InterceptsLocation since we are calling the extension method directly from a checked-in copy of the
+ // generated code, and interceptors are not designed for use in checked-in code.
+ public static global::Microsoft.Extensions.DependencyInjection.IServiceCollection AddValidation(this global::Microsoft.Extensions.DependencyInjection.IServiceCollection services, global::System.Action? configureOptions = null)
+ {
+ // Use non-extension method to avoid infinite recursion.
+ return global::Microsoft.Extensions.DependencyInjection.ValidationServiceCollectionExtensions.AddValidation(services, options =>
+ {
+ options.Resolvers.Insert(0, new GeneratedValidatableInfoResolver());
+ if (configureOptions is not null)
+ {
+ configureOptions(options);
+ }
+ });
+ }
+ }
+
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.ValidationsGenerator, Version=10.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", "10.0.0.0")]
+ file static class ValidationAttributeCache
+ {
+ private sealed record CacheKey(global::System.Type ContainingType, string PropertyName);
+ private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _cache = new();
+
+ public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes(
+ global::System.Type containingType,
+ string propertyName)
+ {
+ var key = new CacheKey(containingType, propertyName);
+ return _cache.GetOrAdd(key, static k =>
+ {
+ var property = k.ContainingType.GetProperty(k.PropertyName);
+ if (property == null)
+ {
+ return [];
+ }
+
+ return [.. global::System.Reflection.CustomAttributeExtensions.GetCustomAttributes(property, inherit: true)];
+ });
+ }
+ }
+}
diff --git a/src/Tests/Microsoft.Diagnostics.Monitoring.Tool.UnitTests/CollectionRuleOptionsTests.cs b/src/Tests/Microsoft.Diagnostics.Monitoring.Tool.UnitTests/CollectionRuleOptionsTests.cs
index 31cbb996d67..ed3ba328fda 100644
--- a/src/Tests/Microsoft.Diagnostics.Monitoring.Tool.UnitTests/CollectionRuleOptionsTests.cs
+++ b/src/Tests/Microsoft.Diagnostics.Monitoring.Tool.UnitTests/CollectionRuleOptionsTests.cs
@@ -195,14 +195,12 @@ public Task CollectionRuleOptions_EventCounterTrigger_PropertyValidation()
ex =>
{
string[] failures = ex.Failures.ToArray();
- // Property validation failures will short-circuit the remainder of the validation
- // rules, thus only observe 3 errors when one might expect 4 (the fourth being that
- // either GreaterThan or LessThan should be specified).
- Assert.Equal(3, failures.Length);
+ Assert.Equal(4, failures.Length);
VerifyRequiredMessage(failures, 0, nameof(EventCounterOptions.ProviderName));
VerifyRequiredMessage(failures, 1, nameof(EventCounterOptions.CounterName));
VerifyRangeMessage(failures, 2, nameof(EventCounterOptions.SlidingWindowDuration),
TriggerOptionsConstants.SlidingWindowDuration_MinValue, TriggerOptionsConstants.SlidingWindowDuration_MaxValue);
+ VerifyEitherRequiredMessage(failures, 3, nameof(EventCounterOptions.GreaterThan), nameof(EventCounterOptions.LessThan));
});
}
@@ -362,13 +360,12 @@ public Task CollectionRuleOptions_EventMeterTrigger_PropertyValidation()
ex =>
{
string[] failures = ex.Failures.ToArray();
- // Property validation failures will short-circuit the remainder of the validation
- // rules, thus only observe 3 errors when one might expect 4 (GreaterThan or LessThan should be specified).
- Assert.Equal(3, failures.Length);
+ Assert.Equal(4, failures.Length);
VerifyRequiredMessage(failures, 0, nameof(EventMeterOptions.MeterName));
VerifyRequiredMessage(failures, 1, nameof(EventMeterOptions.InstrumentName));
VerifyRangeMessage(failures, 2, nameof(EventMeterOptions.SlidingWindowDuration),
TriggerOptionsConstants.SlidingWindowDuration_MinValue, TriggerOptionsConstants.SlidingWindowDuration_MaxValue);
+ VerifyEitherRequiredMessage(failures, 3, nameof(EventMeterOptions.GreaterThan), nameof(EventMeterOptions.LessThan));
});
}
diff --git a/src/Tests/Microsoft.Diagnostics.Monitoring.Tool.UnitTests/CollectionRulePipelineTests.cs b/src/Tests/Microsoft.Diagnostics.Monitoring.Tool.UnitTests/CollectionRulePipelineTests.cs
index 75ca11aca3d..a3889d34d03 100644
--- a/src/Tests/Microsoft.Diagnostics.Monitoring.Tool.UnitTests/CollectionRulePipelineTests.cs
+++ b/src/Tests/Microsoft.Diagnostics.Monitoring.Tool.UnitTests/CollectionRulePipelineTests.cs
@@ -473,8 +473,8 @@ public Task CollectionRulePipeline_ActionCountLimitSlidingDurationTest(TargetFra
{
const int IterationCount = 5;
const int ExpectedActionExecutionCount = 3;
- TimeSpan SlidingWindowDuration = TimeSpan.FromMilliseconds(100);
- TimeSpan ClockIncrementDuration = TimeSpan.FromMilliseconds(10);
+ TimeSpan SlidingWindowDuration = TimeSpan.FromMilliseconds(1000);
+ TimeSpan ClockIncrementDuration = TimeSpan.FromMilliseconds(100);
MockTimeProvider timeProvider = new();
ManualTriggerService triggerService = new();
diff --git a/src/Tests/Microsoft.Diagnostics.Monitoring.Tool.UnitTests/ExtensionManifestTests.cs b/src/Tests/Microsoft.Diagnostics.Monitoring.Tool.UnitTests/ExtensionManifestTests.cs
index b3794d70476..1dd630b3384 100644
--- a/src/Tests/Microsoft.Diagnostics.Monitoring.Tool.UnitTests/ExtensionManifestTests.cs
+++ b/src/Tests/Microsoft.Diagnostics.Monitoring.Tool.UnitTests/ExtensionManifestTests.cs
@@ -1,8 +1,12 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using Microsoft.AspNetCore.Http.Validation;
using Microsoft.Diagnostics.Monitoring.TestCommon;
using Microsoft.Diagnostics.Tools.Monitor.Extensibility;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Options;
+using Microsoft.AspNetCore.Mvc.Testing;
using System.IO;
using System.Text.Json;
using Xunit;
@@ -10,18 +14,24 @@
namespace Microsoft.Diagnostics.Monitoring.Tool.UnitTests
{
+ public class ExtensionManifestFixture : WebApplicationFactory
+ {
+ }
+
[TargetFrameworkMonikerTrait(TargetFrameworkMonikerExtensions.CurrentTargetFrameworkMoniker)]
- public sealed class ExtensionManifestTests
+ public sealed class ExtensionManifestTests : IClassFixture
{
private const string ExpectedName = "CustomEgress";
private const string ExpectedExecutableName = "CustomExecutable";
private const string ExpectedAssemblyName = "CustomAssembly";
private readonly ITestOutputHelper _outputHelper;
+ private readonly ExtensionManifestFixture _fixture;
- public ExtensionManifestTests(ITestOutputHelper outputHelper)
+ public ExtensionManifestTests(ITestOutputHelper outputHelper, ExtensionManifestFixture fixture)
{
_outputHelper = outputHelper;
+ _fixture = fixture;
}
[Fact]
@@ -91,7 +101,8 @@ public void ExtensionManifest_EmptyObject_ThrowOnValidate()
Assert.Null(manifest.AssemblyFileName);
Assert.Null(manifest.ExecutableFileName);
- ExtensionException ex = Assert.Throws(manifest.Validate);
+ var validationOptions = _fixture.Services.GetRequiredService>().Value;
+ ExtensionException ex = Assert.Throws(() => manifest.Validate(_fixture.Services, validationOptions));
Assert.Null(ex.InnerException);
}
@@ -115,7 +126,8 @@ public void ExtensionManifest_NameOnly_ThrowOnValidate()
Assert.Null(manifest.AssemblyFileName);
Assert.Null(manifest.ExecutableFileName);
- ExtensionException ex = Assert.Throws(manifest.Validate);
+ var validationOptions = _fixture.Services.GetRequiredService>().Value;
+ ExtensionException ex = Assert.Throws(() => manifest.Validate(_fixture.Services, validationOptions));
Assert.Null(ex.InnerException);
}
@@ -141,7 +153,8 @@ public void ExtensionManifest_ExecutableAndAssembly_ThrowOnValidate()
Assert.Equal(ExpectedAssemblyName, manifest.AssemblyFileName);
Assert.Equal(ExpectedExecutableName, manifest.ExecutableFileName);
- ExtensionException ex = Assert.Throws(manifest.Validate);
+ var validationOptions = _fixture.Services.GetRequiredService>().Value;
+ ExtensionException ex = Assert.Throws(() => manifest.Validate(_fixture.Services, validationOptions));
Assert.Null(ex.InnerException);
}
@@ -166,7 +179,8 @@ public void ExtensionManifest_NameAndAssembly_Valid()
Assert.Equal(ExpectedAssemblyName, manifest.AssemblyFileName);
Assert.Null(manifest.ExecutableFileName);
- manifest.Validate();
+ var validationOptions = _fixture.Services.GetRequiredService>().Value;
+ manifest.Validate(_fixture.Services, validationOptions);
}
[Fact]
@@ -190,7 +204,8 @@ public void ExtensionManifest_NameAndExecutable_Valid()
Assert.Null(manifest.AssemblyFileName);
Assert.Equal(ExpectedExecutableName, manifest.ExecutableFileName);
- manifest.Validate();
+ var validationOptions = _fixture.Services.GetRequiredService>().Value;
+ manifest.Validate(_fixture.Services, validationOptions);
}
private static Stream CreateManifestStream(TemporaryDirectory dir, out string path)
diff --git a/src/Tests/Microsoft.Diagnostics.Monitoring.Tool.UnitTests/FileSystemEgressExtensionTests.cs b/src/Tests/Microsoft.Diagnostics.Monitoring.Tool.UnitTests/FileSystemEgressExtensionTests.cs
index 9a9ba33bf8b..5989905b166 100644
--- a/src/Tests/Microsoft.Diagnostics.Monitoring.Tool.UnitTests/FileSystemEgressExtensionTests.cs
+++ b/src/Tests/Microsoft.Diagnostics.Monitoring.Tool.UnitTests/FileSystemEgressExtensionTests.cs
@@ -1,12 +1,15 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using Microsoft.AspNetCore.Http.Validation;
+using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.Diagnostics.Monitoring.TestCommon;
using Microsoft.Diagnostics.Tools.Monitor;
using Microsoft.Diagnostics.Tools.Monitor.Egress;
using Microsoft.Diagnostics.Tools.Monitor.Egress.Configuration;
using Microsoft.Diagnostics.Tools.Monitor.Egress.FileSystem;
using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Moq;
@@ -22,8 +25,12 @@
namespace Microsoft.Diagnostics.Monitoring.Tool.UnitTests
{
+ public class FileSystemEgressExtensionFixture : WebApplicationFactory
+ {
+ }
+
[TargetFrameworkMonikerTrait(TargetFrameworkMonikerExtensions.CurrentTargetFrameworkMoniker)]
- public sealed class FileSystemEgressExtensionTests
+ public sealed class FileSystemEgressExtensionTests : IClassFixture
{
const string ProviderName = "TestProvider";
const string ExpectedFileName = "EgressedData.txt";
@@ -35,10 +42,12 @@ public sealed class FileSystemEgressExtensionTests
FileSystemEgressProviderOptions.CopyBufferSize_MaxValue.ToString());
private readonly ITestOutputHelper _outputHelper;
+ private readonly FileSystemEgressExtensionFixture _fixture;
- public FileSystemEgressExtensionTests(ITestOutputHelper outputHelper)
+ public FileSystemEgressExtensionTests(ITestOutputHelper outputHelper, FileSystemEgressExtensionFixture fixture)
{
_outputHelper = outputHelper;
+ _fixture = fixture;
}
[Fact]
@@ -145,7 +154,7 @@ public async Task FileSystemEgressExtension_IntermediateDirectoryPath_Success()
Assert.False(intermediateDirInfo.EnumerateFiles().Any(), "Intermediate directory should not contain any files.");
}
- private static IEgressExtension CreateExtension(Action callback = null)
+ private IEgressExtension CreateExtension(Action callback = null)
{
List configProviders = new()
{
@@ -163,6 +172,8 @@ private static IEgressExtension CreateExtension(Action cal
Mock> mockLogger = new();
Mock mockServiceProvider = new();
+ mockServiceProvider.Setup(provider => provider.GetService(typeof(IOptions)))
+ .Returns(_fixture.Services.GetRequiredService>());
return new FileSystemEgressExtension(mockServiceProvider.Object, mockConfigurationProvider.Object, mockLogger.Object);
}
diff --git a/src/Tests/Microsoft.Diagnostics.Monitoring.Tool.UnitTests/Microsoft.Diagnostics.Monitoring.Tool.UnitTests.csproj b/src/Tests/Microsoft.Diagnostics.Monitoring.Tool.UnitTests/Microsoft.Diagnostics.Monitoring.Tool.UnitTests.csproj
index cb44deedb4b..43d46599cc9 100644
--- a/src/Tests/Microsoft.Diagnostics.Monitoring.Tool.UnitTests/Microsoft.Diagnostics.Monitoring.Tool.UnitTests.csproj
+++ b/src/Tests/Microsoft.Diagnostics.Monitoring.Tool.UnitTests/Microsoft.Diagnostics.Monitoring.Tool.UnitTests.csproj
@@ -8,6 +8,7 @@
+
@@ -15,6 +16,7 @@
+
diff --git a/src/Tests/Microsoft.Diagnostics.Monitoring.Tool.UnitTests/Options/AzureAdOptionsTests.cs b/src/Tests/Microsoft.Diagnostics.Monitoring.Tool.UnitTests/Options/AzureAdOptionsTests.cs
index 95966d09f04..3ab0b08d058 100644
--- a/src/Tests/Microsoft.Diagnostics.Monitoring.Tool.UnitTests/Options/AzureAdOptionsTests.cs
+++ b/src/Tests/Microsoft.Diagnostics.Monitoring.Tool.UnitTests/Options/AzureAdOptionsTests.cs
@@ -1,8 +1,13 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using Microsoft.AspNetCore.Http.Validation;
+using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.Diagnostics.Monitoring.TestCommon;
using Microsoft.Diagnostics.Tools.Monitor;
+using Microsoft.Diagnostics.Tools.Monitor.CollectionRules.Options;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Options;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
@@ -10,9 +15,20 @@
namespace Microsoft.Diagnostics.Monitoring.Tool.UnitTests.Options
{
+ public class AzureAdOptionsTestsFixture : WebApplicationFactory
+ {
+ }
+
[TargetFrameworkMonikerTrait(TargetFrameworkMonikerExtensions.CurrentTargetFrameworkMoniker)]
- public class AzureAdOptionsTests
+ public class AzureAdOptionsTests : IClassFixture
{
+ private readonly AzureAdOptionsTestsFixture _fixture;
+
+ public AzureAdOptionsTests(AzureAdOptionsTestsFixture fixture)
+ {
+ _fixture = fixture;
+ }
+
private static AzureAdOptions GetDefaultOptions()
{
return new AzureAdOptions
@@ -32,9 +48,10 @@ public void AzureAdOptions_Requires_Role()
options.RequiredRole = null;
List results = new();
+ var validationOptions = _fixture.Services.GetRequiredService>().Value;
// Act
- bool isValid = Validator.TryValidateObject(options, new(options), results, validateAllProperties: true);
+ bool isValid = ValidationHelper.TryValidateObject(options, typeof(AzureAdOptions), validationOptions, _fixture.Services, results);
// Assert
Assert.False(isValid);
@@ -50,9 +67,10 @@ public void AzureAdOptions_Requires_TenantId()
options.TenantId = null;
List results = new();
+ var validationOptions = _fixture.Services.GetRequiredService>().Value;
// Act
- bool isValid = Validator.TryValidateObject(options, new(options), results, validateAllProperties: true);
+ bool isValid = ValidationHelper.TryValidateObject(options, typeof(AzureAdOptions), validationOptions, _fixture.Services, results);
// Assert
Assert.False(isValid);
@@ -68,9 +86,10 @@ public void AzureAdOptions_Requires_ClientId()
options.ClientId = null;
List results = new();
+ var validationOptions = _fixture.Services.GetRequiredService>().Value;
// Act
- bool isValid = Validator.TryValidateObject(options, new(options), results, validateAllProperties: true);
+ bool isValid = ValidationHelper.TryValidateObject(options, typeof(AzureAdOptions), validationOptions, _fixture.Services, results);
// Assert
Assert.False(isValid);
diff --git a/src/Tests/Microsoft.Diagnostics.Monitoring.Tool.UnitTestsSample/Microsoft.Diagnostics.Monitoring.Tool.UnitTestsSample.csproj b/src/Tests/Microsoft.Diagnostics.Monitoring.Tool.UnitTestsSample/Microsoft.Diagnostics.Monitoring.Tool.UnitTestsSample.csproj
new file mode 100644
index 00000000000..6b94db64edf
--- /dev/null
+++ b/src/Tests/Microsoft.Diagnostics.Monitoring.Tool.UnitTestsSample/Microsoft.Diagnostics.Monitoring.Tool.UnitTestsSample.csproj
@@ -0,0 +1,13 @@
+
+
+
+ $(ToolTargetFrameworks)
+ enable
+ enable
+
+
+
+
+
+
+
diff --git a/src/Tests/Microsoft.Diagnostics.Monitoring.Tool.UnitTestsSample/Program.cs b/src/Tests/Microsoft.Diagnostics.Monitoring.Tool.UnitTestsSample/Program.cs
new file mode 100644
index 00000000000..00b3a88b98f
--- /dev/null
+++ b/src/Tests/Microsoft.Diagnostics.Monitoring.Tool.UnitTestsSample/Program.cs
@@ -0,0 +1,14 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Microsoft.Diagnostics.Tools.Monitor;
+
+var builder = WebApplication.CreateBuilder(new WebApplicationOptions
+{
+ ContentRootPath = AppContext.BaseDirectory
+});
+
+ValidatableTypes.AddValidation(builder.Services);
+builder.Build();
+
+public partial class Program {}
diff --git a/src/Tools/dotnet-monitor/Auth/ApiKey/MonitorApiKeyPostConfigure.cs b/src/Tools/dotnet-monitor/Auth/ApiKey/MonitorApiKeyPostConfigure.cs
index 2cae0a91b75..1746ba63581 100644
--- a/src/Tools/dotnet-monitor/Auth/ApiKey/MonitorApiKeyPostConfigure.cs
+++ b/src/Tools/dotnet-monitor/Auth/ApiKey/MonitorApiKeyPostConfigure.cs
@@ -1,7 +1,10 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using Microsoft.AspNetCore.Http.Validation;
using Microsoft.Diagnostics.Monitoring.WebApi;
+using Microsoft.Diagnostics.Tools.Monitor.CollectionRules.Options;
+using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
@@ -21,20 +24,25 @@ internal sealed class MonitorApiKeyPostConfigure :
{
private readonly ILogger _logger;
private readonly IOptionsMonitor _apiKeyOptions;
+ private readonly IServiceProvider _serviceProvider;
+ private readonly ValidationOptions _validationOptions;
public MonitorApiKeyPostConfigure(
+ IServiceProvider serviceProvider,
ILogger logger,
IOptionsMonitor apiKeyOptions)
{
_logger = logger;
_apiKeyOptions = apiKeyOptions;
+ _serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider));
+ _validationOptions = serviceProvider.GetRequiredService>().Value;
}
public void PostConfigure(string? name, MonitorApiKeyConfiguration options)
{
MonitorApiKeyOptions sourceOptions = _apiKeyOptions.CurrentValue;
- IList errors = new List();
+ List errors = new List();
// If nothing is set, lets not attach an error and instead pass along the blank config
if (sourceOptions.Subject == null && sourceOptions.PublicKey == null)
@@ -48,11 +56,12 @@ public void PostConfigure(string? name, MonitorApiKeyConfiguration options)
// Some options are configured (but may not be valid)
options.Configured = true;
- Validator.TryValidateObject(
+ ValidationHelper.TryValidateObject(
sourceOptions,
- new ValidationContext(sourceOptions, null, null),
- errors,
- validateAllProperties: true);
+ typeof(MonitorApiKeyOptions),
+ _validationOptions,
+ _serviceProvider,
+ errors);
string? jwkJson = null;
try
diff --git a/src/Tools/dotnet-monitor/Auth/AuthConfiguratorFactory.cs b/src/Tools/dotnet-monitor/Auth/AuthConfiguratorFactory.cs
index a16e1aa9216..983499440da 100644
--- a/src/Tools/dotnet-monitor/Auth/AuthConfiguratorFactory.cs
+++ b/src/Tools/dotnet-monitor/Auth/AuthConfiguratorFactory.cs
@@ -1,12 +1,16 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using Microsoft.AspNetCore.Http.Validation;
using Microsoft.Diagnostics.Tools.Monitor.Auth.ApiKey;
using Microsoft.Diagnostics.Tools.Monitor.Auth.AzureAd;
using Microsoft.Diagnostics.Tools.Monitor.Auth.NoAuth;
+using Microsoft.Diagnostics.Tools.Monitor.CollectionRules.Options;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Configuration.Binder.SourceGeneration;
using Microsoft.Extensions.Hosting;
+using Microsoft.Extensions.Options;
+using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
@@ -22,7 +26,7 @@ internal enum StartupAuthenticationMode
internal static class AuthConfiguratorFactory
{
- public static IAuthenticationConfigurator Create(StartupAuthenticationMode startupAuthMode, HostBuilderContext context)
+ public static IAuthenticationConfigurator Create(StartupAuthenticationMode startupAuthMode, HostBuilderContext context, IServiceProvider services)
{
switch (startupAuthMode)
{
@@ -47,12 +51,12 @@ public static IAuthenticationConfigurator Create(StartupAuthenticationMode start
if (authConfigSection.Exists())
{
authConfigSection.Bind_AuthenticationOptions(authOptions);
- ValidateAuthConfigSection(authOptions, authConfigSection.Path);
+ ValidateAuthConfigSection(authOptions, authConfigSection.Path, services);
}
if (authOptions.AzureAd != null)
{
- ValidateAuthConfigSection(authOptions.AzureAd, ConfigurationPath.Combine(authConfigSection.Path, ConfigurationKeys.AzureAd));
+ ValidateAuthConfigSection(authOptions.AzureAd, ConfigurationPath.Combine(authConfigSection.Path, ConfigurationKeys.AzureAd), services);
return new AzureAdAuthConfigurator(authOptions.AzureAd);
}
@@ -63,10 +67,11 @@ public static IAuthenticationConfigurator Create(StartupAuthenticationMode start
}
}
- private static void ValidateAuthConfigSection(T options, string configurationPath) where T : notnull
+ private static void ValidateAuthConfigSection(T options, string configurationPath, IServiceProvider services) where T : notnull
{
+ var validationOptions = services.GetRequiredService>().Value;
List results = new();
- if (!Validator.TryValidateObject(options, new ValidationContext(options), results, validateAllProperties: true))
+ if (!ValidationHelper.TryValidateObject(options, typeof(T), validationOptions, services, results))
{
throw new DeferredAuthenticationValidationException(configurationPath, results);
}
diff --git a/src/Tools/dotnet-monitor/CollectionRules/Actions/CollectDumpAction.cs b/src/Tools/dotnet-monitor/CollectionRules/Actions/CollectDumpAction.cs
index 9647ee347a0..be3c7f8fc1c 100644
--- a/src/Tools/dotnet-monitor/CollectionRules/Actions/CollectDumpAction.cs
+++ b/src/Tools/dotnet-monitor/CollectionRules/Actions/CollectDumpAction.cs
@@ -1,15 +1,17 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using Microsoft.AspNetCore.Http.Validation;
using Microsoft.Diagnostics.Monitoring.WebApi;
using Microsoft.Diagnostics.Monitoring.WebApi.Models;
+using Microsoft.Diagnostics.Tools.Monitor.CollectionRules.Options;
using Microsoft.Diagnostics.Tools.Monitor.CollectionRules.Configuration;
using Microsoft.Diagnostics.Tools.Monitor.CollectionRules.Options.Actions;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Configuration.Binder.SourceGeneration;
using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Options;
using System;
-using System.ComponentModel.DataAnnotations;
using Utils = Microsoft.Diagnostics.Monitoring.WebApi.Utilities;
namespace Microsoft.Diagnostics.Tools.Monitor.CollectionRules.Actions
@@ -18,10 +20,12 @@ internal sealed class CollectDumpActionFactory :
ICollectionRuleActionFactory
{
private readonly IServiceProvider _serviceProvider;
+ private readonly ValidationOptions _validationOptions;
public CollectDumpActionFactory(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider));
+ _validationOptions = serviceProvider.GetRequiredService>().Value;
}
public ICollectionRuleAction Create(IProcessInfo processInfo, CollectDumpOptions options)
@@ -31,8 +35,7 @@ public ICollectionRuleAction Create(IProcessInfo processInfo, CollectDumpOptions
throw new ArgumentNullException(nameof(options));
}
- ValidationContext context = new(options, _serviceProvider, items: null);
- Validator.ValidateObject(options, context, validateAllProperties: true);
+ ValidationHelper.ValidateObject(options, typeof(CollectDumpOptions), _validationOptions, _serviceProvider);
return new CollectDumpAction(_serviceProvider, processInfo, options);
}
diff --git a/src/Tools/dotnet-monitor/CollectionRules/Actions/CollectExceptionsAction.cs b/src/Tools/dotnet-monitor/CollectionRules/Actions/CollectExceptionsAction.cs
index e6aacfd4a99..feb71ba2177 100644
--- a/src/Tools/dotnet-monitor/CollectionRules/Actions/CollectExceptionsAction.cs
+++ b/src/Tools/dotnet-monitor/CollectionRules/Actions/CollectExceptionsAction.cs
@@ -1,20 +1,31 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using Microsoft.AspNetCore.Http.Validation;
using Microsoft.Diagnostics.Monitoring.WebApi;
+using Microsoft.Diagnostics.Tools.Monitor.CollectionRules.Options;
using Microsoft.Diagnostics.Tools.Monitor.CollectionRules.Options.Actions;
using Microsoft.Diagnostics.Tools.Monitor.CollectionRules.Configuration;
using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Options;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Configuration.Binder.SourceGeneration;
using System;
-using System.ComponentModel.DataAnnotations;
using Utils = Microsoft.Diagnostics.Monitoring.WebApi.Utilities;
namespace Microsoft.Diagnostics.Tools.Monitor.CollectionRules.Actions
{
internal sealed class CollectExceptionsActionFactory : ICollectionRuleActionFactory
{
+ private readonly IServiceProvider _serviceProvider;
+ private readonly ValidationOptions _validationOptions;
+
+ public CollectExceptionsActionFactory(IServiceProvider serviceProvider, IOptions validationOptions)
+ {
+ _serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider));
+ _validationOptions = validationOptions?.Value ?? throw new ArgumentNullException(nameof(validationOptions));
+ }
+
public ICollectionRuleAction Create(IProcessInfo processInfo, CollectExceptionsOptions options)
{
if (null == options)
@@ -22,8 +33,7 @@ public ICollectionRuleAction Create(IProcessInfo processInfo, CollectExceptionsO
throw new ArgumentNullException(nameof(options));
}
- ValidationContext context = new(options, processInfo.EndpointInfo.ServiceProvider, items: null);
- Validator.ValidateObject(options, context, validateAllProperties: true);
+ ValidationHelper.ValidateObject(options, typeof(CollectExceptionsOptions), _validationOptions, _serviceProvider);
return new CollectExceptionsAction(processInfo, options);
}
diff --git a/src/Tools/dotnet-monitor/CollectionRules/Actions/CollectGCDumpAction.cs b/src/Tools/dotnet-monitor/CollectionRules/Actions/CollectGCDumpAction.cs
index 525be20357c..5d7c0388409 100644
--- a/src/Tools/dotnet-monitor/CollectionRules/Actions/CollectGCDumpAction.cs
+++ b/src/Tools/dotnet-monitor/CollectionRules/Actions/CollectGCDumpAction.cs
@@ -6,9 +6,11 @@
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Configuration.Binder.SourceGeneration;
using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Options;
+using Microsoft.Diagnostics.Tools.Monitor.CollectionRules.Options;
using System;
-using System.ComponentModel.DataAnnotations;
using Utils = Microsoft.Diagnostics.Monitoring.WebApi.Utilities;
+using Microsoft.AspNetCore.Http.Validation;
using Microsoft.Diagnostics.Tools.Monitor.CollectionRules.Configuration;
namespace Microsoft.Diagnostics.Tools.Monitor.CollectionRules.Actions
@@ -17,10 +19,12 @@ internal sealed class CollectGCDumpActionFactory :
ICollectionRuleActionFactory
{
private readonly IServiceProvider _serviceProvider;
+ private readonly ValidationOptions _validationOptions;
public CollectGCDumpActionFactory(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider));
+ _validationOptions = serviceProvider.GetRequiredService>().Value;
}
public ICollectionRuleAction Create(IProcessInfo processInfo, CollectGCDumpOptions options)
@@ -29,9 +33,7 @@ public ICollectionRuleAction Create(IProcessInfo processInfo, CollectGCDumpOptio
{
throw new ArgumentNullException(nameof(options));
}
-
- ValidationContext context = new(options, _serviceProvider, items: null);
- Validator.ValidateObject(options, context, validateAllProperties: true);
+ ValidationHelper.ValidateObject(options, typeof(CollectGCDumpOptions), _validationOptions, _serviceProvider);
return new CollectGCDumpAction(_serviceProvider, processInfo, options);
}
diff --git a/src/Tools/dotnet-monitor/CollectionRules/Actions/CollectLiveMetricsAction.cs b/src/Tools/dotnet-monitor/CollectionRules/Actions/CollectLiveMetricsAction.cs
index d1c0d85be70..f29ca44a407 100644
--- a/src/Tools/dotnet-monitor/CollectionRules/Actions/CollectLiveMetricsAction.cs
+++ b/src/Tools/dotnet-monitor/CollectionRules/Actions/CollectLiveMetricsAction.cs
@@ -1,9 +1,11 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using Microsoft.AspNetCore.Http.Validation;
using Microsoft.Diagnostics.Monitoring.EventPipe;
using Microsoft.Diagnostics.Monitoring.WebApi;
using Microsoft.Diagnostics.Monitoring.WebApi.Models;
+using Microsoft.Diagnostics.Tools.Monitor.CollectionRules.Options;
using Microsoft.Diagnostics.Tools.Monitor.CollectionRules.Options.Actions;
using Microsoft.Diagnostics.Tools.Monitor.CollectionRules.Configuration;
using Microsoft.Extensions.Configuration;
@@ -11,7 +13,6 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using System;
-using System.ComponentModel.DataAnnotations;
using Utils = Microsoft.Diagnostics.Monitoring.WebApi.Utilities;
namespace Microsoft.Diagnostics.Tools.Monitor.CollectionRules.Actions
@@ -20,10 +21,12 @@ internal sealed class CollectLiveMetricsActionFactory :
ICollectionRuleActionFactory
{
private readonly IServiceProvider _serviceProvider;
+ private readonly ValidationOptions _validationOptions;
public CollectLiveMetricsActionFactory(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider));
+ _validationOptions = serviceProvider.GetRequiredService>().Value;
}
public ICollectionRuleAction Create(IProcessInfo processInfo, CollectLiveMetricsOptions options)
@@ -33,8 +36,7 @@ public ICollectionRuleAction Create(IProcessInfo processInfo, CollectLiveMetrics
throw new ArgumentNullException(nameof(options));
}
- ValidationContext context = new(options, _serviceProvider, items: null);
- Validator.ValidateObject(options, context, validateAllProperties: true);
+ ValidationHelper.ValidateObject(options, typeof(CollectLiveMetricsOptions), _validationOptions, _serviceProvider);
return new CollectLiveMetricsAction(_serviceProvider, processInfo, options);
}
diff --git a/src/Tools/dotnet-monitor/CollectionRules/Actions/CollectLogsAction.cs b/src/Tools/dotnet-monitor/CollectionRules/Actions/CollectLogsAction.cs
index 8b85db24af2..b6c76c66eda 100644
--- a/src/Tools/dotnet-monitor/CollectionRules/Actions/CollectLogsAction.cs
+++ b/src/Tools/dotnet-monitor/CollectionRules/Actions/CollectLogsAction.cs
@@ -1,18 +1,20 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using Microsoft.AspNetCore.Http.Validation;
using Microsoft.Diagnostics.Monitoring.EventPipe;
using Microsoft.Diagnostics.Monitoring.Options;
using Microsoft.Diagnostics.Monitoring.WebApi;
+using Microsoft.Diagnostics.Tools.Monitor.CollectionRules.Options;
using Microsoft.Diagnostics.Tools.Monitor.CollectionRules.Options.Actions;
using Microsoft.Diagnostics.Tools.Monitor.CollectionRules.Configuration;
using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Options;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Configuration.Binder.SourceGeneration;
using System;
using System.Collections.Generic;
-using System.ComponentModel.DataAnnotations;
using Utils = Microsoft.Diagnostics.Monitoring.WebApi.Utilities;
namespace Microsoft.Diagnostics.Tools.Monitor.CollectionRules.Actions
@@ -21,10 +23,12 @@ internal sealed class CollectLogsActionFactory :
ICollectionRuleActionFactory
{
private readonly IServiceProvider _serviceProvider;
+ private readonly ValidationOptions _validationOptions;
public CollectLogsActionFactory(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider));
+ _validationOptions = serviceProvider.GetRequiredService>().Value;
}
public ICollectionRuleAction Create(IProcessInfo processInfo, CollectLogsOptions options)
@@ -34,8 +38,7 @@ public ICollectionRuleAction Create(IProcessInfo processInfo, CollectLogsOptions
throw new ArgumentNullException(nameof(options));
}
- ValidationContext context = new(options, _serviceProvider, items: null);
- Validator.ValidateObject(options, context, validateAllProperties: true);
+ ValidationHelper.ValidateObject(options, typeof(CollectLogsOptions), _validationOptions, _serviceProvider);
return new CollectLogsAction(_serviceProvider, processInfo, options);
}
diff --git a/src/Tools/dotnet-monitor/CollectionRules/Actions/CollectStacksAction.cs b/src/Tools/dotnet-monitor/CollectionRules/Actions/CollectStacksAction.cs
index e0cfb15b4b3..2bcc4084994 100644
--- a/src/Tools/dotnet-monitor/CollectionRules/Actions/CollectStacksAction.cs
+++ b/src/Tools/dotnet-monitor/CollectionRules/Actions/CollectStacksAction.cs
@@ -1,14 +1,16 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using Microsoft.AspNetCore.Http.Validation;
using Microsoft.Diagnostics.Monitoring.WebApi;
+using Microsoft.Diagnostics.Tools.Monitor.CollectionRules.Options;
using Microsoft.Diagnostics.Tools.Monitor.CollectionRules.Options.Actions;
using Microsoft.Diagnostics.Tools.Monitor.CollectionRules.Configuration;
using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Options;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Configuration.Binder.SourceGeneration;
using System;
-using System.ComponentModel.DataAnnotations;
using Utils = Microsoft.Diagnostics.Monitoring.WebApi.Utilities;
namespace Microsoft.Diagnostics.Tools.Monitor.CollectionRules.Actions
@@ -16,10 +18,12 @@ namespace Microsoft.Diagnostics.Tools.Monitor.CollectionRules.Actions
internal sealed class CollectStacksActionFactory : ICollectionRuleActionFactory
{
private readonly IServiceProvider _serviceProvider;
+ private readonly ValidationOptions _validationOptions;
public CollectStacksActionFactory(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider));
+ _validationOptions = serviceProvider.GetRequiredService>().Value;
}
public ICollectionRuleAction Create(IProcessInfo processInfo, CollectStacksOptions options)
@@ -29,8 +33,7 @@ public ICollectionRuleAction Create(IProcessInfo processInfo, CollectStacksOptio
throw new ArgumentNullException(nameof(options));
}
- ValidationContext context = new(options, _serviceProvider, items: null);
- Validator.ValidateObject(options, context, validateAllProperties: true);
+ ValidationHelper.ValidateObject(options, typeof(CollectStacksOptions), _validationOptions, _serviceProvider);
return new CollectStacksAction(_serviceProvider, processInfo, options);
}
diff --git a/src/Tools/dotnet-monitor/CollectionRules/Actions/CollectTraceAction.cs b/src/Tools/dotnet-monitor/CollectionRules/Actions/CollectTraceAction.cs
index a3c53868d36..fb3b69d8940 100644
--- a/src/Tools/dotnet-monitor/CollectionRules/Actions/CollectTraceAction.cs
+++ b/src/Tools/dotnet-monitor/CollectionRules/Actions/CollectTraceAction.cs
@@ -1,9 +1,11 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using Microsoft.AspNetCore.Http.Validation;
using Microsoft.Diagnostics.Monitoring.EventPipe;
using Microsoft.Diagnostics.Monitoring.WebApi;
using Microsoft.Diagnostics.Monitoring.WebApi.Models;
+using Microsoft.Diagnostics.Tools.Monitor.CollectionRules.Options;
using Microsoft.Diagnostics.Tools.Monitor.CollectionRules.Options.Actions;
using Microsoft.Diagnostics.Tools.Monitor.CollectionRules.Configuration;
using Microsoft.Extensions.DependencyInjection;
@@ -11,7 +13,6 @@
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Configuration.Binder.SourceGeneration;
using System;
-using System.ComponentModel.DataAnnotations;
using Utils = Microsoft.Diagnostics.Monitoring.WebApi.Utilities;
namespace Microsoft.Diagnostics.Tools.Monitor.CollectionRules.Actions
@@ -20,6 +21,7 @@ internal sealed class CollectTraceActionFactory :
ICollectionRuleActionFactory
{
private readonly IServiceProvider _serviceProvider;
+ private readonly ValidationOptions _validationOptions;
public ICollectionRuleAction Create(IProcessInfo processInfo, CollectTraceOptions options)
{
@@ -28,8 +30,7 @@ public ICollectionRuleAction Create(IProcessInfo processInfo, CollectTraceOption
throw new ArgumentNullException(nameof(options));
}
- ValidationContext context = new(options, _serviceProvider, items: null);
- Validator.ValidateObject(options, context, validateAllProperties: true);
+ ValidationHelper.ValidateObject(options, typeof(CollectTraceOptions), _validationOptions, _serviceProvider);
return new CollectTraceAction(_serviceProvider, processInfo, options);
}
@@ -37,6 +38,7 @@ public ICollectionRuleAction Create(IProcessInfo processInfo, CollectTraceOption
public CollectTraceActionFactory(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider));
+ _validationOptions = serviceProvider.GetRequiredService>().Value;
}
private sealed class CollectTraceAction :
diff --git a/src/Tools/dotnet-monitor/CollectionRules/Actions/CollectionRuleActionOperations.cs b/src/Tools/dotnet-monitor/CollectionRules/Actions/CollectionRuleActionOperations.cs
index 746af4f906a..e92f1510ff9 100644
--- a/src/Tools/dotnet-monitor/CollectionRules/Actions/CollectionRuleActionOperations.cs
+++ b/src/Tools/dotnet-monitor/CollectionRules/Actions/CollectionRuleActionOperations.cs
@@ -89,7 +89,7 @@ public bool TryValidateOptions(
}
else
{
- results.Add(new ValidationResult(string.Format(CultureInfo.InvariantCulture, Strings.ErrorMessage_UnknownActionType, actionName)));
+ results.Add(new ValidationResult(string.Format(CultureInfo.InvariantCulture, Strings.ErrorMessage_UnknownActionType, actionName), [actionName]));
return false;
}
}
diff --git a/src/Tools/dotnet-monitor/CollectionRules/Actions/ExecuteAction.cs b/src/Tools/dotnet-monitor/CollectionRules/Actions/ExecuteAction.cs
index 072bb23241b..67daa210c87 100644
--- a/src/Tools/dotnet-monitor/CollectionRules/Actions/ExecuteAction.cs
+++ b/src/Tools/dotnet-monitor/CollectionRules/Actions/ExecuteAction.cs
@@ -1,15 +1,17 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using Microsoft.AspNetCore.Http.Validation;
using Microsoft.Diagnostics.Monitoring.EventPipe;
using Microsoft.Diagnostics.Monitoring.WebApi;
+using Microsoft.Diagnostics.Tools.Monitor.CollectionRules.Options;
using Microsoft.Diagnostics.Tools.Monitor.CollectionRules.Options.Actions;
+using Microsoft.Extensions.Options;
using Microsoft.Diagnostics.Tools.Monitor.CollectionRules.Configuration;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Configuration.Binder.SourceGeneration;
using System;
using System.Collections.Generic;
-using System.ComponentModel.DataAnnotations;
using System.Diagnostics;
using System.Globalization;
using System.IO;
@@ -21,6 +23,15 @@ namespace Microsoft.Diagnostics.Tools.Monitor.CollectionRules.Actions
internal sealed class ExecuteActionFactory :
ICollectionRuleActionFactory
{
+ private readonly IServiceProvider _serviceProvider;
+ private readonly ValidationOptions _validationOptions;
+
+ public ExecuteActionFactory(IServiceProvider serviceProvider, IOptions validationOptions)
+ {
+ _serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider));
+ _validationOptions = validationOptions?.Value ?? throw new ArgumentNullException(nameof(validationOptions));
+ }
+
public ICollectionRuleAction Create(IProcessInfo processInfo, ExecuteOptions options)
{
if (null == options)
@@ -28,8 +39,7 @@ public ICollectionRuleAction Create(IProcessInfo processInfo, ExecuteOptions opt
throw new ArgumentNullException(nameof(options));
}
- ValidationContext context = new(options, null, items: null);
- Validator.ValidateObject(options, context, validateAllProperties: true);
+ ValidationHelper.ValidateObject(options, typeof(ExecuteOptions), _validationOptions, _serviceProvider);
return new ExecuteAction(processInfo, options);
}
diff --git a/src/Tools/dotnet-monitor/CollectionRules/Actions/GetEnvironmentVariableAction.cs b/src/Tools/dotnet-monitor/CollectionRules/Actions/GetEnvironmentVariableAction.cs
index da2c1a777b7..6af2b909470 100644
--- a/src/Tools/dotnet-monitor/CollectionRules/Actions/GetEnvironmentVariableAction.cs
+++ b/src/Tools/dotnet-monitor/CollectionRules/Actions/GetEnvironmentVariableAction.cs
@@ -1,16 +1,19 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using Microsoft.AspNetCore.Http.Validation;
using Microsoft.Diagnostics.Monitoring.WebApi;
using Microsoft.Diagnostics.NETCore.Client;
-using Microsoft.Diagnostics.Tools.Monitor.CollectionRules.Configuration;
+using Microsoft.Diagnostics.Tools.Monitor.CollectionRules.Options;
using Microsoft.Diagnostics.Tools.Monitor.CollectionRules.Options.Actions;
using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Options;
+using Microsoft.Diagnostics.Tools.Monitor.CollectionRules.Configuration;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Configuration.Binder.SourceGeneration;
using System;
using System.Collections.Generic;
-using System.ComponentModel.DataAnnotations;
using System.Threading;
using System.Threading.Tasks;
@@ -21,11 +24,13 @@ internal sealed partial class GetEnvironmentVariableActionFactory :
{
private readonly IServiceProvider _serviceProvider;
private readonly ILogger _logger;
+ private readonly ValidationOptions _validationOptions;
public GetEnvironmentVariableActionFactory(IServiceProvider serviceProvider, ILogger logger)
{
_serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider));
_logger = logger ?? throw new ArgumentNullException(nameof(serviceProvider));
+ _validationOptions = serviceProvider.GetRequiredService>().Value;
}
public ICollectionRuleAction Create(IProcessInfo processInfo, GetEnvironmentVariableOptions options)
@@ -35,8 +40,7 @@ public ICollectionRuleAction Create(IProcessInfo processInfo, GetEnvironmentVari
throw new ArgumentNullException(nameof(options));
}
- ValidationContext context = new(options, _serviceProvider, items: null);
- Validator.ValidateObject(options, context, validateAllProperties: true);
+ ValidationHelper.ValidateObject(options, typeof(GetEnvironmentVariableOptions), _validationOptions, _serviceProvider);
return new GetEnvironmentVariableAction(_logger, processInfo, options);
}
diff --git a/src/Tools/dotnet-monitor/CollectionRules/Actions/LoadProfilerAction.cs b/src/Tools/dotnet-monitor/CollectionRules/Actions/LoadProfilerAction.cs
index 46f336a90f5..4ec1a9b8d18 100644
--- a/src/Tools/dotnet-monitor/CollectionRules/Actions/LoadProfilerAction.cs
+++ b/src/Tools/dotnet-monitor/CollectionRules/Actions/LoadProfilerAction.cs
@@ -1,15 +1,18 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using Microsoft.AspNetCore.Http.Validation;
using Microsoft.Diagnostics.Monitoring.WebApi;
using Microsoft.Diagnostics.NETCore.Client;
+using Microsoft.Diagnostics.Tools.Monitor.CollectionRules.Options;
using Microsoft.Diagnostics.Tools.Monitor.CollectionRules.Options.Actions;
using Microsoft.Diagnostics.Tools.Monitor.CollectionRules.Configuration;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Configuration.Binder.SourceGeneration;
using System;
-using System.ComponentModel.DataAnnotations;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Options;
using System.Threading;
using System.Threading.Tasks;
@@ -20,11 +23,13 @@ internal sealed class LoadProfilerActionFactory :
{
private readonly IServiceProvider _serviceProvider;
private readonly ILogger _logger;
+ private readonly ValidationOptions _validationOptions;
public LoadProfilerActionFactory(IServiceProvider serviceProvider, ILogger logger)
{
_serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider));
_logger = logger ?? throw new ArgumentNullException(nameof(serviceProvider));
+ _validationOptions = serviceProvider.GetRequiredService>().Value;
}
public ICollectionRuleAction Create(IProcessInfo processInfo, LoadProfilerOptions options)
@@ -34,8 +39,7 @@ public ICollectionRuleAction Create(IProcessInfo processInfo, LoadProfilerOption
throw new ArgumentNullException(nameof(options));
}
- ValidationContext context = new(options, _serviceProvider, items: null);
- Validator.ValidateObject(options, context, validateAllProperties: true);
+ ValidationHelper.ValidateObject(options, typeof(LoadProfilerOptions), _validationOptions, _serviceProvider);
return new LoadProfilerAction(_logger, processInfo, options);
}
diff --git a/src/Tools/dotnet-monitor/CollectionRules/Actions/SetEnvironmentVariableAction.cs b/src/Tools/dotnet-monitor/CollectionRules/Actions/SetEnvironmentVariableAction.cs
index 66101ea2cdc..c501057751b 100644
--- a/src/Tools/dotnet-monitor/CollectionRules/Actions/SetEnvironmentVariableAction.cs
+++ b/src/Tools/dotnet-monitor/CollectionRules/Actions/SetEnvironmentVariableAction.cs
@@ -1,15 +1,18 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using Microsoft.AspNetCore.Http.Validation;
using Microsoft.Diagnostics.Monitoring.WebApi;
using Microsoft.Diagnostics.NETCore.Client;
+using Microsoft.Diagnostics.Tools.Monitor.CollectionRules.Options;
using Microsoft.Diagnostics.Tools.Monitor.CollectionRules.Options.Actions;
-using Microsoft.Diagnostics.Tools.Monitor.CollectionRules.Configuration;
+using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
+using Microsoft.Diagnostics.Tools.Monitor.CollectionRules.Configuration;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Configuration.Binder.SourceGeneration;
using System;
-using System.ComponentModel.DataAnnotations;
using System.Threading;
using System.Threading.Tasks;
@@ -20,11 +23,13 @@ internal sealed partial class SetEnvironmentVariableActionFactory :
{
private readonly IServiceProvider _serviceProvider;
private readonly ILogger _logger;
+ private readonly ValidationOptions _validationOptions;
public SetEnvironmentVariableActionFactory(IServiceProvider serviceProvider, ILogger logger)
{
_serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
+ _validationOptions = serviceProvider.GetRequiredService>().Value;
}
public ICollectionRuleAction Create(IProcessInfo processInfo, SetEnvironmentVariableOptions options)
@@ -34,8 +39,7 @@ public ICollectionRuleAction Create(IProcessInfo processInfo, SetEnvironmentVari
throw new ArgumentNullException(nameof(options));
}
- ValidationContext context = new(options, _serviceProvider, items: null);
- Validator.ValidateObject(options, context, validateAllProperties: true);
+ ValidationHelper.ValidateObject(options, typeof(SetEnvironmentVariableOptions), _validationOptions, _serviceProvider);
return new SetEnvironmentVariableAction(_logger, processInfo, options);
}
diff --git a/src/Tools/dotnet-monitor/CollectionRules/Configuration/CollectionRulePostConfigureNamedOptions.cs b/src/Tools/dotnet-monitor/CollectionRules/Configuration/CollectionRulePostConfigureNamedOptions.cs
index 46a5ab241f5..d992fb085c4 100644
--- a/src/Tools/dotnet-monitor/CollectionRules/Configuration/CollectionRulePostConfigureNamedOptions.cs
+++ b/src/Tools/dotnet-monitor/CollectionRules/Configuration/CollectionRulePostConfigureNamedOptions.cs
@@ -56,7 +56,8 @@ private void ResolveActionList(CollectionRuleOptions ruleOptions, IConfiguration
// The Section Key is the action index; the value (if present) is the name of the template
if (SectionHasValue(actionSection))
{
- TryGetTemplate(ruleOptions, nameof(ruleOptions.Actions), _templateOptions.CollectionRuleActions, actionSection.Value, out CollectionRuleActionOptions templateActionOptions);
+ var memberName = nameof(ruleOptions.Actions) + "[" + ruleOptions.Actions.Count.ToString(CultureInfo.InvariantCulture) + "]";
+ TryGetTemplate(ruleOptions, memberName, _templateOptions.CollectionRuleActions, actionSection.Value, out CollectionRuleActionOptions templateActionOptions);
ruleOptions.Actions.Add(templateActionOptions);
}
@@ -100,7 +101,7 @@ private void ResolveFilterList(CollectionRuleOptions ruleOptions, IConfiguration
// The Section Key is the filter index; the value (if present) is the name of the template
if (SectionHasValue(filterSection))
{
- TryGetTemplate(ruleOptions, nameof(ruleOptions.Filters), _templateOptions.CollectionRuleFilters, filterSection.Value, out ProcessFilterDescriptor templateFilterOptions);
+ TryGetTemplate(ruleOptions, nameof(ruleOptions.Filters), _templateOptions.CollectionRuleFilters, filterSection.Value, out ProcessFilterDescriptor templateFilterOptions);
ruleOptions.Filters.Add(templateFilterOptions);
}
diff --git a/src/Tools/dotnet-monitor/CollectionRules/Options/Actions/CollectExceptionsOptions.Validate.cs b/src/Tools/dotnet-monitor/CollectionRules/Options/Actions/CollectExceptionsOptions.Validate.cs
index 05cf725f5f4..01f2d44124d 100644
--- a/src/Tools/dotnet-monitor/CollectionRules/Options/Actions/CollectExceptionsOptions.Validate.cs
+++ b/src/Tools/dotnet-monitor/CollectionRules/Options/Actions/CollectExceptionsOptions.Validate.cs
@@ -27,7 +27,7 @@ IEnumerable IValidatableObject.Validate(ValidationContext vali
string.Format(
CultureInfo.InvariantCulture,
Strings.ErrorMessage_DisabledFeature,
- nameof(CollectExceptionsAction))));
+ nameof(CollectExceptionsAction)), [nameof(CollectExceptionsAction)]));
}
return results;
diff --git a/src/Tools/dotnet-monitor/CollectionRules/Options/Actions/CollectLogsOptions.Validate.cs b/src/Tools/dotnet-monitor/CollectionRules/Options/Actions/CollectLogsOptions.Validate.cs
index e8ede3ded8c..624db8b4c86 100644
--- a/src/Tools/dotnet-monitor/CollectionRules/Options/Actions/CollectLogsOptions.Validate.cs
+++ b/src/Tools/dotnet-monitor/CollectionRules/Options/Actions/CollectLogsOptions.Validate.cs
@@ -1,4 +1,4 @@
-// Licensed to the .NET Foundation under one or more agreements.
+// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using Microsoft.Extensions.Logging;
@@ -23,7 +23,7 @@ IEnumerable IValidatableObject.Validate(ValidationContext vali
RequiredAttribute requiredAttribute = new();
EnumDataTypeAttribute enumValidationAttribute = new(typeof(LogLevel));
- ValidationContext filterSpecsContext = new(FilterSpecs, validationContext, validationContext.Items);
+ ValidationContext filterSpecsContext = new(FilterSpecs, nameof(FilterSpecs), validationContext, validationContext.Items);
filterSpecsContext.MemberName = nameof(FilterSpecs);
// Validate that the category is not null and that the level is a valid level value.
diff --git a/src/Tools/dotnet-monitor/CollectionRules/Options/Actions/CollectStacksOptions.Validate.cs b/src/Tools/dotnet-monitor/CollectionRules/Options/Actions/CollectStacksOptions.Validate.cs
index 362985a54c8..27eee18bf69 100644
--- a/src/Tools/dotnet-monitor/CollectionRules/Options/Actions/CollectStacksOptions.Validate.cs
+++ b/src/Tools/dotnet-monitor/CollectionRules/Options/Actions/CollectStacksOptions.Validate.cs
@@ -26,7 +26,7 @@ IEnumerable IValidatableObject.Validate(ValidationContext vali
string.Format(
CultureInfo.InvariantCulture,
Strings.ErrorMessage_DisabledFeature,
- nameof(CollectionRules.Actions.CollectStacksAction))));
+ nameof(CollectionRules.Actions.CollectStacksAction)), [nameof(CollectionRules.Actions.CollectStacksAction)]));
}
return results;
diff --git a/src/Tools/dotnet-monitor/CollectionRules/Options/Actions/CollectTraceOptions.Validate.cs b/src/Tools/dotnet-monitor/CollectionRules/Options/Actions/CollectTraceOptions.Validate.cs
index 6874ec2354b..9107de46d08 100644
--- a/src/Tools/dotnet-monitor/CollectionRules/Options/Actions/CollectTraceOptions.Validate.cs
+++ b/src/Tools/dotnet-monitor/CollectionRules/Options/Actions/CollectTraceOptions.Validate.cs
@@ -1,6 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using Microsoft.AspNetCore.Http.Validation;
using Microsoft.Diagnostics.Monitoring.WebApi;
using Microsoft.Diagnostics.Monitoring.WebApi.Models;
using Microsoft.Diagnostics.Monitoring.WebApi.Validation;
@@ -53,7 +54,7 @@ IEnumerable IValidatableObject.Validate(ValidationContext vali
CultureInfo.InvariantCulture,
Strings.ErrorMessage_TwoFieldsCannotBeSpecified,
nameof(Profile),
- nameof(StoppingEvent))));
+ nameof(StoppingEvent)), [nameof(Profile), nameof(StoppingEvent)]));
}
if (HasProviders())
@@ -66,7 +67,7 @@ IEnumerable IValidatableObject.Validate(ValidationContext vali
CultureInfo.InvariantCulture,
Strings.ErrorMessage_TwoFieldsCannotBeSpecified,
nameof(Profile),
- nameof(Providers))));
+ nameof(Providers)), [nameof(Profile), nameof(Providers)]));
}
}
else if (HasProviders())
@@ -80,17 +81,20 @@ IEnumerable IValidatableObject.Validate(ValidationContext vali
}
catch (OptionsValidationException e)
{
- results.AddRange(e.Failures.Select((string failure) => new ValidationResult(e.Message)));
+ results.AddRange(e.Failures.Select((string failure) => new ValidationResult(e.Message, [e.OptionsType.Name])));
}
// Validate that each provider is valid.
+ // Necessary to work around the generated validation code not recursing into List? members:
+ // https://github.com/dotnet/aspnetcore/issues/61737
int index = 0;
foreach (EventPipeProvider provider in Providers)
{
- ValidationContext providerContext = new(provider, validationContext, validationContext.Items);
+ ValidationContext providerContext = new(provider, nameof(Providers), validationContext, validationContext.Items);
providerContext.MemberName = nameof(Providers) + "[" + index.ToString(CultureInfo.InvariantCulture) + "]";
- Validator.TryValidateObject(provider, providerContext, results, validateAllProperties: true);
+ var validationOptions = validationContext.GetRequiredService>().Value;
+ ValidationHelper.TryValidateObject(provider, typeof(EventPipeProvider), validationOptions, providerContext, results);
if (counterOptions != null && !CounterValidator.ValidateProvider(counterOptions, provider, out string? errorMessage))
{
@@ -108,7 +112,7 @@ IEnumerable IValidatableObject.Validate(ValidationContext vali
CultureInfo.InvariantCulture,
Strings.ErrorMessage_TwoFieldsMissing,
nameof(Profile),
- nameof(Providers))));
+ nameof(Providers)), [nameof(Profile), nameof(Providers)]));
}
if (HasStoppingEvent())
@@ -124,7 +128,7 @@ IEnumerable IValidatableObject.Validate(ValidationContext vali
Strings.ErrorMessage_MissingStoppingEventProvider,
nameof(StoppingEvent),
StoppingEvent.ProviderName,
- nameof(Providers))));
+ nameof(Providers)), [nameof(StoppingEvent), nameof(Providers)]));
}
}
diff --git a/src/Tools/dotnet-monitor/CollectionRules/Options/Actions/RequiredGuidAttribute.cs b/src/Tools/dotnet-monitor/CollectionRules/Options/Actions/RequiredGuidAttribute.cs
index e23e9a230a4..117674c27f1 100644
--- a/src/Tools/dotnet-monitor/CollectionRules/Options/Actions/RequiredGuidAttribute.cs
+++ b/src/Tools/dotnet-monitor/CollectionRules/Options/Actions/RequiredGuidAttribute.cs
@@ -20,10 +20,14 @@ public override string FormatErrorMessage(string name)
protected override ValidationResult? IsValid(object? value, ValidationContext validationContext)
{
+ if (validationContext.MemberName is null)
+ {
+ throw new ArgumentNullException(nameof(validationContext.MemberName));
+ }
if (!(value is Guid))
{
return new ValidationResult(
- FormatErrorMessage(validationContext.DisplayName));
+ FormatErrorMessage(validationContext.DisplayName), [validationContext.MemberName]);
}
Guid guidVal = (Guid)value;
@@ -31,7 +35,7 @@ public override string FormatErrorMessage(string name)
if (guidVal == Guid.Empty)
{
return new ValidationResult(
- FormatErrorMessage(validationContext.DisplayName));
+ FormatErrorMessage(validationContext.DisplayName), [validationContext.MemberName]);
}
return ValidationResult.Success;
diff --git a/src/Tools/dotnet-monitor/CollectionRules/Options/Actions/ValidateEgressProviderAttribute.cs b/src/Tools/dotnet-monitor/CollectionRules/Options/Actions/ValidateEgressProviderAttribute.cs
index 5e77e45e06c..6743a5395a0 100644
--- a/src/Tools/dotnet-monitor/CollectionRules/Options/Actions/ValidateEgressProviderAttribute.cs
+++ b/src/Tools/dotnet-monitor/CollectionRules/Options/Actions/ValidateEgressProviderAttribute.cs
@@ -14,10 +14,14 @@ internal sealed class ValidateEgressProviderAttribute :
{
protected override ValidationResult? IsValid(object? value, ValidationContext validationContext)
{
+ if (validationContext.MemberName is null)
+ {
+ throw new ArgumentNullException(nameof(validationContext.MemberName));
+ }
if (!(value is string))
{
return new ValidationResult(
- FormatErrorMessage(validationContext.DisplayName));
+ FormatErrorMessage(validationContext.DisplayName), [validationContext.MemberName]);
}
string egressProvider = (string)value;
@@ -33,7 +37,7 @@ internal sealed class ValidateEgressProviderAttribute :
string.Format(
CultureInfo.InvariantCulture,
Strings.ErrorMessage_EgressProviderDoesNotExist,
- egressProvider));
+ egressProvider), [validationContext.MemberName]);
}
return ValidationResult.Success;
diff --git a/src/Tools/dotnet-monitor/CollectionRules/Options/CollectionRuleActionOptions.Validate.cs b/src/Tools/dotnet-monitor/CollectionRules/Options/CollectionRuleActionOptions.Validate.cs
index 45d41c6977a..ede73aff565 100644
--- a/src/Tools/dotnet-monitor/CollectionRules/Options/CollectionRuleActionOptions.Validate.cs
+++ b/src/Tools/dotnet-monitor/CollectionRules/Options/CollectionRuleActionOptions.Validate.cs
@@ -18,6 +18,10 @@ IEnumerable IValidatableObject.Validate(ValidationContext vali
if (!string.IsNullOrEmpty(Type))
{
+ validationContext = new ValidationContext(this, displayName: nameof(Settings), validationContext, items: null)
+ {
+ MemberName = nameof(Settings)
+ };
actionOperations.TryValidateOptions(Type, Settings, validationContext, results);
}
diff --git a/src/Tools/dotnet-monitor/CollectionRules/Options/CollectionRuleOptions.Validate.cs b/src/Tools/dotnet-monitor/CollectionRules/Options/CollectionRuleOptions.Validate.cs
index 2af986cc1a8..60e5949f33a 100644
--- a/src/Tools/dotnet-monitor/CollectionRules/Options/CollectionRuleOptions.Validate.cs
+++ b/src/Tools/dotnet-monitor/CollectionRules/Options/CollectionRuleOptions.Validate.cs
@@ -1,4 +1,4 @@
-// Licensed to the .NET Foundation under one or more agreements.
+// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
#nullable disable
@@ -27,21 +27,6 @@ IEnumerable IValidatableObject.Validate(ValidationContext vali
return results;
}
- ValidationContext filtersContext = new(Filters, validationContext, validationContext.Items);
- filtersContext.MemberName = nameof(Filters);
- ValidationHelper.TryValidateItems(Filters, filtersContext, results);
-
- if (null != Trigger)
- {
- ValidationContext triggerContext = new(Trigger, validationContext, validationContext.Items);
- triggerContext.MemberName = nameof(Trigger);
- Validator.TryValidateObject(Trigger, triggerContext, results);
- }
-
- ValidationContext actionsContext = new(Actions, validationContext, validationContext.Items);
- actionsContext.MemberName = nameof(Actions);
- ValidationHelper.TryValidateItems(Actions, actionsContext, results);
-
var actionNames = new HashSet(StringComparer.Ordinal);
foreach (CollectionRuleActionOptions option in Actions)
{
diff --git a/src/Tools/dotnet-monitor/CollectionRules/Options/Triggers/EventCounterOptions.Validate.cs b/src/Tools/dotnet-monitor/CollectionRules/Options/Triggers/EventCounterOptions.Validate.cs
index 44c132c45ee..689ba328238 100644
--- a/src/Tools/dotnet-monitor/CollectionRules/Options/Triggers/EventCounterOptions.Validate.cs
+++ b/src/Tools/dotnet-monitor/CollectionRules/Options/Triggers/EventCounterOptions.Validate.cs
@@ -19,7 +19,7 @@ IEnumerable IValidatableObject.Validate(ValidationContext vali
string.Format(
Strings.ErrorMessage_TwoFieldsMissing,
nameof(GreaterThan),
- nameof(LessThan))));
+ nameof(LessThan)), [nameof(GreaterThan), nameof(LessThan)]));
}
else if (GreaterThan.HasValue && LessThan.HasValue && LessThan.Value < GreaterThan.Value)
{
@@ -28,7 +28,7 @@ IEnumerable IValidatableObject.Validate(ValidationContext vali
string.Format(
Strings.ErrorMessage_FieldMustBeLessThanOtherField,
nameof(GreaterThan),
- nameof(LessThan))));
+ nameof(LessThan)), [nameof(GreaterThan), nameof(LessThan)]));
}
return results;
diff --git a/src/Tools/dotnet-monitor/CollectionRules/Options/Triggers/EventCounterShortcuts/IEventCounterShortcuts.Validate.cs b/src/Tools/dotnet-monitor/CollectionRules/Options/Triggers/EventCounterShortcuts/IEventCounterShortcuts.Validate.cs
index 5f81bfb93ca..8081f9df8d3 100644
--- a/src/Tools/dotnet-monitor/CollectionRules/Options/Triggers/EventCounterShortcuts/IEventCounterShortcuts.Validate.cs
+++ b/src/Tools/dotnet-monitor/CollectionRules/Options/Triggers/EventCounterShortcuts/IEventCounterShortcuts.Validate.cs
@@ -19,7 +19,7 @@ IEnumerable IValidatableObject.Validate(ValidationContext vali
string.Format(
Strings.ErrorMessage_FieldMustBeLessThanOtherField,
nameof(GreaterThan),
- nameof(LessThan))));
+ nameof(LessThan)), [nameof(GreaterThan), nameof(LessThan)]));
}
return results;
diff --git a/src/Tools/dotnet-monitor/CollectionRules/Options/Triggers/EventMeterOptions.Validate.cs b/src/Tools/dotnet-monitor/CollectionRules/Options/Triggers/EventMeterOptions.Validate.cs
index 3ac644373fb..30952baf8dd 100644
--- a/src/Tools/dotnet-monitor/CollectionRules/Options/Triggers/EventMeterOptions.Validate.cs
+++ b/src/Tools/dotnet-monitor/CollectionRules/Options/Triggers/EventMeterOptions.Validate.cs
@@ -19,7 +19,7 @@ IEnumerable IValidatableObject.Validate(ValidationContext vali
string.Format(
Strings.ErrorMessage_TwoFieldsMissing,
nameof(GreaterThan),
- nameof(LessThan))));
+ nameof(LessThan)), [nameof(GreaterThan), nameof(LessThan)]));
}
else if (GreaterThan.HasValue && LessThan.HasValue && LessThan.Value < GreaterThan.Value)
{
@@ -28,7 +28,7 @@ IEnumerable IValidatableObject.Validate(ValidationContext vali
string.Format(
Strings.ErrorMessage_FieldMustBeLessThanOtherField,
nameof(GreaterThan),
- nameof(LessThan))));
+ nameof(LessThan)), [nameof(GreaterThan), nameof(LessThan)]));
}
return results;
diff --git a/src/Tools/dotnet-monitor/CollectionRules/Options/ValidationHelper.cs b/src/Tools/dotnet-monitor/CollectionRules/Options/ValidationHelper.cs
index c99543df9c0..c6fd02c9f80 100644
--- a/src/Tools/dotnet-monitor/CollectionRules/Options/ValidationHelper.cs
+++ b/src/Tools/dotnet-monitor/CollectionRules/Options/ValidationHelper.cs
@@ -1,31 +1,23 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using Microsoft.AspNetCore.Http.Validation;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Reflection;
+using System.Threading;
+#if EXTENSION
+namespace Microsoft.Diagnostics.Monitoring.Extension.Common
+#else
namespace Microsoft.Diagnostics.Tools.Monitor.CollectionRules.Options
+#endif
{
internal static class ValidationHelper
{
- public static void TryValidateItems(IEnumerable