From 9faa27b8ebcfa500d05363dbcb381cd500a7b8aa Mon Sep 17 00:00:00 2001
From: Roger Barreto <19890735+rogerbarreto@users.noreply.github.com>
Date: Tue, 5 Aug 2025 13:55:44 +0100
Subject: [PATCH] .NET: Clean / address some message warnings (#291)
* WIP
* Structured Output sample
* Update dotnet/samples/GettingStarted/Steps/Step06_ChatClientAgent_StructuredOutputs.cs
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Address xml and comment targeting the Structured Output context
* Update with proposed fix for Persistent ChatClient
* Address PR feedback
* Address minor warnings
* Address initialization
* Address initialization
* Address PR comments, update suggestions
* Revert changes to NullableAttributese.cs
---------
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Chris <66376200+crickman@users.noreply.github.com>
---
dotnet/.editorconfig | 6 ++++
...atClientAgent_UsingCodeInterpreterTools.cs | 31 ++++++++-----------
...p04_ChatClientAgent_DependencyInjection.cs | 2 +-
...tep06_ChatClientAgent_StructuredOutputs.cs | 16 +++++-----
...07_ChatClientAgent_UsingFileSearchTools.cs | 15 +++------
.../HelloHttpApi.Web/AgentClient.cs | 2 +-
.../NullableAttributes.cs | 2 +-
.../DynamicallyAccessedMemberTypes.cs | 2 ++
.../ConcurrentOrchestration.cs | 17 ++++++----
.../NewPersistentAgentsChatClient.cs | 2 ++
.../ChatCompletion/ChatClientAgent.cs | 5 ++-
.../src/Shared/Samples/TestConfiguration.cs | 17 ----------
dotnet/tests/Directory.Build.props | 1 +
.../AgentRunResponseTests.cs | 2 +-
.../AgentRunResponseUpdatesTests.cs | 2 +-
.../JsonSerializationTests.cs | 4 +--
.../ChatClientAgentExtensionsTests.cs | 2 ++
.../ChatClientAgentThreadTests.cs | 4 +--
.../OpenAIResponseFixture.cs | 4 +--
19 files changed, 63 insertions(+), 73 deletions(-)
diff --git a/dotnet/.editorconfig b/dotnet/.editorconfig
index 75b647fee3..76f90b6f33 100644
--- a/dotnet/.editorconfig
+++ b/dotnet/.editorconfig
@@ -246,6 +246,12 @@ dotnet_diagnostic.IDE1006.severity = warning # Naming rule violations
dotnet_diagnostic.IDE0046.severity = suggestion # If statement can be simplified
dotnet_diagnostic.IDE0056.severity = suggestion # Indexing can be simplified
dotnet_diagnostic.IDE0057.severity = suggestion # Substring can be simplified
+dotnet_diagnostic.IDE0079.severity = none # Remove unnecessary suppression
+dotnet_diagnostic.IDE0290.severity = none # Use primary constructor
+dotnet_diagnostic.IDE0046.severity = none # if statement can be simplified
+dotnet_diagnostic.IDE0057.severity = none # Substring can be simplified
+dotnet_diagnostic.IDE0042.severity = none # Variable declaration can be deconstructed
+dotnet_diagnostic.IDE0047.severity = none # Parentheses can be removed
dotnet_diagnostic.CA2007.severity = error # Do not directly await a Task
dotnet_diagnostic.VSTHRD111.severity = error # Use .ConfigureAwait(bool)
diff --git a/dotnet/samples/GettingStarted/Steps/Step03_ChatClientAgent_UsingCodeInterpreterTools.cs b/dotnet/samples/GettingStarted/Steps/Step03_ChatClientAgent_UsingCodeInterpreterTools.cs
index a968ac6ef7..ea4c3ed8da 100644
--- a/dotnet/samples/GettingStarted/Steps/Step03_ChatClientAgent_UsingCodeInterpreterTools.cs
+++ b/dotnet/samples/GettingStarted/Steps/Step03_ChatClientAgent_UsingCodeInterpreterTools.cs
@@ -105,26 +105,21 @@ public sealed class Step03_ChatClientAgent_UsingCodeInterpreterTools(ITestOutput
/// Provider of the chat client that is used to determine how to extract the output.
/// The code interpreter output as a string.
private static string? GetCodeInterpreterOutput(object rawRepresentation, ChatClientProviders provider)
- {
- switch (provider)
+ => provider switch
{
- case ChatClientProviders.OpenAIAssistant
- when rawRepresentation is OpenAI.Assistants.RunStepDetailsUpdate stepDetails:
- return $"{stepDetails.CodeInterpreterInput}{string.Join(
- string.Empty,
- stepDetails.CodeInterpreterOutputs.SelectMany(l => l.Logs)
- )}";
+ ChatClientProviders.OpenAIAssistant
+ when rawRepresentation is OpenAI.Assistants.RunStepDetailsUpdate stepDetails => $"{stepDetails.CodeInterpreterInput}{string.Join(
+ string.Empty,
+ stepDetails.CodeInterpreterOutputs.SelectMany(l => l.Logs)
+ )}",
- case ChatClientProviders.AzureAIAgentsPersistent
- when rawRepresentation is Azure.AI.Agents.Persistent.RunStepDetailsUpdate stepDetails:
- return $"{stepDetails.CodeInterpreterInput}{string.Join(
- string.Empty,
- stepDetails.CodeInterpreterOutputs.OfType().SelectMany(l => l.Logs)
- )}";
- }
-
- return null;
- }
+ ChatClientProviders.AzureAIAgentsPersistent
+ when rawRepresentation is Azure.AI.Agents.Persistent.RunStepDetailsUpdate stepDetails => $"{stepDetails.CodeInterpreterInput}{string.Join(
+ string.Empty,
+ stepDetails.CodeInterpreterOutputs.OfType().SelectMany(l => l.Logs)
+ )}",
+ _ => null,
+ };
#endregion
}
diff --git a/dotnet/samples/GettingStarted/Steps/Step04_ChatClientAgent_DependencyInjection.cs b/dotnet/samples/GettingStarted/Steps/Step04_ChatClientAgent_DependencyInjection.cs
index a793adf3c4..45c9f57342 100644
--- a/dotnet/samples/GettingStarted/Steps/Step04_ChatClientAgent_DependencyInjection.cs
+++ b/dotnet/samples/GettingStarted/Steps/Step04_ChatClientAgent_DependencyInjection.cs
@@ -40,7 +40,7 @@ public sealed class Step04_ChatClientAgent_DependencyInjection(ITestOutputHelper
loggerFactory: sp.GetRequiredService()));
// Build the service provider.
- using var serviceProvider = services.BuildServiceProvider();
+ await using var serviceProvider = services.BuildServiceProvider();
// Get the agent from the service provider.
var agent = serviceProvider.GetRequiredService();
diff --git a/dotnet/samples/GettingStarted/Steps/Step06_ChatClientAgent_StructuredOutputs.cs b/dotnet/samples/GettingStarted/Steps/Step06_ChatClientAgent_StructuredOutputs.cs
index c49c081316..059086aa2a 100644
--- a/dotnet/samples/GettingStarted/Steps/Step06_ChatClientAgent_StructuredOutputs.cs
+++ b/dotnet/samples/GettingStarted/Steps/Step06_ChatClientAgent_StructuredOutputs.cs
@@ -23,14 +23,16 @@ public sealed class Step06_ChatClientAgent_StructuredOutputs(ITestOutputHelper o
[InlineData(ChatClientProviders.OpenAIResponses)]
public async Task RunWithCustomSchema(ChatClientProviders provider)
{
- var agentOptions = new ChatClientAgentOptions(name: "HelpfulAssistant", instructions: "You are a helpful assistant.");
- agentOptions.ChatOptions = new()
+ var agentOptions = new ChatClientAgentOptions(name: "HelpfulAssistant", instructions: "You are a helpful assistant.")
{
- ResponseFormat = ChatResponseFormatJson.ForJsonSchema(
- schema: AIJsonUtilities.CreateJsonSchema(typeof(PersonInfo)),
- schemaName: "PersonInfo",
- schemaDescription: "Information about a person including their name, age, and occupation"
- )
+ ChatOptions = new()
+ {
+ ResponseFormat = ChatResponseFormatJson.ForJsonSchema(
+ schema: AIJsonUtilities.CreateJsonSchema(typeof(PersonInfo)),
+ schemaName: "PersonInfo",
+ schemaDescription: "Information about a person including their name, age, and occupation"
+ )
+ }
};
// Create the server-side agent Id when applicable (depending on the provider).
diff --git a/dotnet/samples/GettingStarted/Steps/Step07_ChatClientAgent_UsingFileSearchTools.cs b/dotnet/samples/GettingStarted/Steps/Step07_ChatClientAgent_UsingFileSearchTools.cs
index a4fcf2570e..d00b1bdb64 100644
--- a/dotnet/samples/GettingStarted/Steps/Step07_ChatClientAgent_UsingFileSearchTools.cs
+++ b/dotnet/samples/GettingStarted/Steps/Step07_ChatClientAgent_UsingFileSearchTools.cs
@@ -100,17 +100,12 @@ public sealed class Step07_ChatClientAgent_UsingFileSearchTools(ITestOutputHelpe
}
private Task CreateVectorStoreAsync(IEnumerable fileIds, ChatClientProviders provider)
- {
- switch (provider)
+ => provider switch
{
- case ChatClientProviders.OpenAIAssistant:
- return CreateVectorStoreOpenAIAssistantAsync(fileIds);
- case ChatClientProviders.AzureAIAgentsPersistent:
- return CreateVectorStoreAzureAIAgentsPersistentAsync(fileIds);
- default:
- throw new NotSupportedException($"Client provider {provider} is not supported.");
- }
- }
+ ChatClientProviders.OpenAIAssistant => CreateVectorStoreOpenAIAssistantAsync(fileIds),
+ ChatClientProviders.AzureAIAgentsPersistent => CreateVectorStoreAzureAIAgentsPersistentAsync(fileIds),
+ _ => throw new NotSupportedException($"Client provider {provider} is not supported."),
+ };
private async Task CreateVectorStoreOpenAIAssistantAsync(IEnumerable fileIds)
{
diff --git a/dotnet/samples/HelloHttpApi/HelloHttpApi.Web/AgentClient.cs b/dotnet/samples/HelloHttpApi/HelloHttpApi.Web/AgentClient.cs
index 7761cfdc31..8a17efbb32 100644
--- a/dotnet/samples/HelloHttpApi/HelloHttpApi.Web/AgentClient.cs
+++ b/dotnet/samples/HelloHttpApi/HelloHttpApi.Web/AgentClient.cs
@@ -33,7 +33,7 @@ public class AgentClient(HttpClient httpClient, ILogger logger)
using var response = await httpClient.SendAsync(requestMessage, HttpCompletionOption.ResponseHeadersRead, cancellationToken);
response.EnsureSuccessStatusCode();
- using var stream = await response.Content.ReadAsStreamAsync(cancellationToken);
+ await using var stream = await response.Content.ReadAsStreamAsync(cancellationToken);
using var reader = new StreamReader(stream);
string? line;
diff --git a/dotnet/src/LegacySupport/DiagnosticAttributes/NullableAttributes.cs b/dotnet/src/LegacySupport/DiagnosticAttributes/NullableAttributes.cs
index 393f24d58b..3f1baec540 100644
--- a/dotnet/src/LegacySupport/DiagnosticAttributes/NullableAttributes.cs
+++ b/dotnet/src/LegacySupport/DiagnosticAttributes/NullableAttributes.cs
@@ -1,6 +1,6 @@
// Copyright (c) Microsoft. All rights reserved.
-#pragma warning disable CA1019
+#pragma warning disable CA1019, RCS1251, IDE0300
namespace System.Diagnostics.CodeAnalysis;
diff --git a/dotnet/src/LegacySupport/TrimAttributes/DynamicallyAccessedMemberTypes.cs b/dotnet/src/LegacySupport/TrimAttributes/DynamicallyAccessedMemberTypes.cs
index c8663103a5..8f756b8c4b 100644
--- a/dotnet/src/LegacySupport/TrimAttributes/DynamicallyAccessedMemberTypes.cs
+++ b/dotnet/src/LegacySupport/TrimAttributes/DynamicallyAccessedMemberTypes.cs
@@ -1,5 +1,7 @@
// Copyright (c) Microsoft. All rights reserved.
+#pragma warning disable RCS1157 // Composite enum value contains undefined flag
+
namespace System.Diagnostics.CodeAnalysis;
///
diff --git a/dotnet/src/Microsoft.Agents.Orchestration/ConcurrentOrchestration.cs b/dotnet/src/Microsoft.Agents.Orchestration/ConcurrentOrchestration.cs
index a28c203db3..670731efe8 100644
--- a/dotnet/src/Microsoft.Agents.Orchestration/ConcurrentOrchestration.cs
+++ b/dotnet/src/Microsoft.Agents.Orchestration/ConcurrentOrchestration.cs
@@ -41,12 +41,17 @@ public partial class ConcurrentOrchestration : OrchestratingAgent
return f;
}
- return static async (responses, cancellationToken) =>
- new AgentRunResponse([.. responses.Where(r => r.Messages.Count > 0).Select(r =>
- {
- var messages = r.Messages;
- return messages.Count > 0 ? messages[messages.Count - 1] : new();
- })]);
+ return (responses, cancellationToken)
+ => Task.FromResult(
+ new AgentRunResponse([.. responses
+ .Where(r => r.Messages.Count > 0)
+ .Select(r =>
+ {
+ var messages = r.Messages;
+ return messages.Count > 0 ? messages[messages.Count - 1] : new();
+ })
+ ])
+ );
}
set => this._aggregationFunc = value;
}
diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.AzureAI/NewPersistentAgentsChatClient.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.AzureAI/NewPersistentAgentsChatClient.cs
index 5507171516..59ff21fbd3 100644
--- a/dotnet/src/Microsoft.Extensions.AI.Agents.AzureAI/NewPersistentAgentsChatClient.cs
+++ b/dotnet/src/Microsoft.Extensions.AI.Agents.AzureAI/NewPersistentAgentsChatClient.cs
@@ -509,6 +509,7 @@ namespace Azure.AI.Agents.Persistent
// We need to extract the run ID and ensure that the ToolOutput we send back to Azure
// is only the call ID.
string[]? runAndCallIDs;
+#pragma warning disable CA1031 // Do not catch general exception types
try
{
runAndCallIDs = JsonSerializer.Deserialize(frc.CallId, AgentsChatClientJsonContext.Default.StringArray);
@@ -517,6 +518,7 @@ namespace Azure.AI.Agents.Persistent
{
continue;
}
+#pragma warning restore CA1031 // Do not catch general exception types
if (runAndCallIDs is null ||
runAndCallIDs.Length != 2 ||
diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents/ChatCompletion/ChatClientAgent.cs b/dotnet/src/Microsoft.Extensions.AI.Agents/ChatCompletion/ChatClientAgent.cs
index 1d0616e0f6..a664e214e0 100644
--- a/dotnet/src/Microsoft.Extensions.AI.Agents/ChatCompletion/ChatClientAgent.cs
+++ b/dotnet/src/Microsoft.Extensions.AI.Agents/ChatCompletion/ChatClientAgent.cs
@@ -2,7 +2,6 @@
using System;
using System.Collections.Generic;
-using System.Linq;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
@@ -126,7 +125,7 @@ public sealed class ChatClientAgent : AIAgent
}
// Convert the chat response messages to a valid IReadOnlyCollection for notification signatures below.
- var chatResponseMessages = chatResponse.Messages as IReadOnlyCollection ?? chatResponse.Messages.ToArray();
+ var chatResponseMessages = chatResponse.Messages as IReadOnlyCollection ?? [.. chatResponse.Messages];
await this.NotifyThreadOfNewMessagesAsync(chatClientThread, chatResponseMessages, cancellationToken).ConfigureAwait(false);
@@ -174,7 +173,7 @@ public sealed class ChatClientAgent : AIAgent
}
var chatResponse = responseUpdates.ToChatResponse();
- var chatResponseMessages = chatResponse.Messages as IReadOnlyCollection ?? chatResponse.Messages.ToArray();
+ var chatResponseMessages = chatResponse.Messages as IReadOnlyCollection ?? [.. chatResponse.Messages];
// We can derive the type of supported thread from whether we have a conversation id,
// so let's update it and set the conversation id for the service thread case.
diff --git a/dotnet/src/Shared/Samples/TestConfiguration.cs b/dotnet/src/Shared/Samples/TestConfiguration.cs
index 89a1404d2d..be436ee9f7 100644
--- a/dotnet/src/Shared/Samples/TestConfiguration.cs
+++ b/dotnet/src/Shared/Samples/TestConfiguration.cs
@@ -72,28 +72,11 @@ public sealed class TestConfiguration
this._configRoot = configRoot;
}
- ///
- /// Provides access to the configuration root for the application.
- ///
- private static IConfigurationRoot? ConfigurationRoot => s_instance?._configRoot;
-
///
/// Gets the configuration settings for the AzureAI integration.
///
public static AzureAIConfig AzureAI => LoadSection();
- ///
- /// Retrieves a configuration section based on the specified key.
- ///
- /// The key identifying the configuration section to retrieve. Cannot be null or empty.
- /// The corresponding to the specified key.
- /// Thrown if the configuration root is not initialized or the specified key does not correspond to a valid section.
- private static IConfigurationSection GetSection(string caller)
- {
- return s_instance?._configRoot.GetSection(caller) ??
- throw new InvalidOperationException(caller);
- }
-
private static T LoadSection([CallerMemberName] string? caller = null)
{
if (s_instance is null)
diff --git a/dotnet/tests/Directory.Build.props b/dotnet/tests/Directory.Build.props
index 3a8ad53b48..9fb9d9b797 100644
--- a/dotnet/tests/Directory.Build.props
+++ b/dotnet/tests/Directory.Build.props
@@ -8,6 +8,7 @@
false
net472;net9.0
b7762d10-e29b-4bb1-8b74-b6d69a667dd4
+ $(NoWarn);Moq1410;xUnit2023
diff --git a/dotnet/tests/Microsoft.Extensions.AI.Agents.Abstractions.UnitTests/AgentRunResponseTests.cs b/dotnet/tests/Microsoft.Extensions.AI.Agents.Abstractions.UnitTests/AgentRunResponseTests.cs
index 1ef375901b..46610d7138 100644
--- a/dotnet/tests/Microsoft.Extensions.AI.Agents.Abstractions.UnitTests/AgentRunResponseTests.cs
+++ b/dotnet/tests/Microsoft.Extensions.AI.Agents.Abstractions.UnitTests/AgentRunResponseTests.cs
@@ -48,7 +48,7 @@ public class AgentRunResponseTests
{
ChatResponse chatResponse = new()
{
- AdditionalProperties = new(),
+ AdditionalProperties = [],
CreatedAt = new DateTimeOffset(2022, 1, 1, 0, 0, 0, TimeSpan.Zero),
Messages = [new(ChatRole.Assistant, "This is a test message.")],
RawRepresentation = new object(),
diff --git a/dotnet/tests/Microsoft.Extensions.AI.Agents.Abstractions.UnitTests/AgentRunResponseUpdatesTests.cs b/dotnet/tests/Microsoft.Extensions.AI.Agents.Abstractions.UnitTests/AgentRunResponseUpdatesTests.cs
index 55596ffe72..ae8fdb1da0 100644
--- a/dotnet/tests/Microsoft.Extensions.AI.Agents.Abstractions.UnitTests/AgentRunResponseUpdatesTests.cs
+++ b/dotnet/tests/Microsoft.Extensions.AI.Agents.Abstractions.UnitTests/AgentRunResponseUpdatesTests.cs
@@ -29,7 +29,7 @@ public class AgentRunResponseUpdateTests
{
ChatResponseUpdate chatResponseUpdate = new()
{
- AdditionalProperties = new(),
+ AdditionalProperties = [],
AuthorName = "author",
Contents = [new TextContent("hello")],
ConversationId = "conversationId",
diff --git a/dotnet/tests/Microsoft.Extensions.AI.Agents.Runtime.Abstractions.UnitTests/JsonSerializationTests.cs b/dotnet/tests/Microsoft.Extensions.AI.Agents.Runtime.Abstractions.UnitTests/JsonSerializationTests.cs
index efd59c2987..78b71a833e 100644
--- a/dotnet/tests/Microsoft.Extensions.AI.Agents.Runtime.Abstractions.UnitTests/JsonSerializationTests.cs
+++ b/dotnet/tests/Microsoft.Extensions.AI.Agents.Runtime.Abstractions.UnitTests/JsonSerializationTests.cs
@@ -18,9 +18,9 @@ public class JsonSerializationTests
{
WriteIndented = false, // Use compact JSON for easier testing
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
- Converters = { new JsonStringEnumConverter() }
+ Converters = { new JsonStringEnumConverter() },
+ TypeInfoResolver = ActorJsonContext.Default
};
- this._options.TypeInfoResolver = ActorJsonContext.Default;
}
#region ActorMessage Tests
diff --git a/dotnet/tests/Microsoft.Extensions.AI.Agents.UnitTests/ChatCompletion/ChatClientAgentExtensionsTests.cs b/dotnet/tests/Microsoft.Extensions.AI.Agents.UnitTests/ChatCompletion/ChatClientAgentExtensionsTests.cs
index 1152e6da02..52ec5f74f2 100644
--- a/dotnet/tests/Microsoft.Extensions.AI.Agents.UnitTests/ChatCompletion/ChatClientAgentExtensionsTests.cs
+++ b/dotnet/tests/Microsoft.Extensions.AI.Agents.UnitTests/ChatCompletion/ChatClientAgentExtensionsTests.cs
@@ -7,6 +7,8 @@ using System.Threading;
using System.Threading.Tasks;
using Moq;
+#pragma warning disable RCS1196 // Call extension method as instance method
+
namespace Microsoft.Extensions.AI.Agents.UnitTests.ChatCompletion;
public class ChatClientAgentExtensionsTests
diff --git a/dotnet/tests/Microsoft.Extensions.AI.Agents.UnitTests/ChatCompletion/ChatClientAgentThreadTests.cs b/dotnet/tests/Microsoft.Extensions.AI.Agents.UnitTests/ChatCompletion/ChatClientAgentThreadTests.cs
index 07ed61ceb0..9fd357eea0 100644
--- a/dotnet/tests/Microsoft.Extensions.AI.Agents.UnitTests/ChatCompletion/ChatClientAgentThreadTests.cs
+++ b/dotnet/tests/Microsoft.Extensions.AI.Agents.UnitTests/ChatCompletion/ChatClientAgentThreadTests.cs
@@ -24,8 +24,8 @@ public class ChatClientAgentThreadTests
var thread = new ChatClientAgentThread();
// Assert
- Assert.IsAssignableFrom(thread);
- Assert.IsAssignableFrom(thread);
+ Assert.IsType(thread, exactMatch: false);
+ Assert.IsType(thread, exactMatch: false);
}
///
diff --git a/dotnet/tests/OpenAIResponse.IntegrationTests/OpenAIResponseFixture.cs b/dotnet/tests/OpenAIResponse.IntegrationTests/OpenAIResponseFixture.cs
index 6a0a24c7d4..3acb3b3cb5 100644
--- a/dotnet/tests/OpenAIResponse.IntegrationTests/OpenAIResponseFixture.cs
+++ b/dotnet/tests/OpenAIResponse.IntegrationTests/OpenAIResponseFixture.cs
@@ -52,9 +52,7 @@ public class OpenAIResponseFixture(bool store) : IChatClientAgentFixture
// Concatenate the previous messages with the response message to get a full chat history
// that includes the current response.
- return previousMessages
- .Concat([responseMessage])
- .ToList();
+ return [.. previousMessages, responseMessage];
}
return await chatClientThread.GetMessagesAsync().ToListAsync();