Skip to content

Commit c4c9130

Browse files
authored
Enable nullable reference types and refactor code (#217)
This commit introduces nullable reference types across various files, enhancing code safety by explicitly indicating which properties can be null. Key changes include: - Updated `AspNetExtensions.cs` to use the null-forgiving operator `!` for method calls and property accesses. - Changed a private field in `AuthAgent.cs` from mutable to readonly to prevent modification after initialization. - Modified `WeatherForecastAgent` and `WeatherForecastAgentResponse` classes to use nullable types for properties. - Updated project files (`.csproj`) to enable nullable reference types. - Cleaned up `Program.cs` files by removing unnecessary using directives and ensuring configuration values are treated as non-nullable. - Minor refactoring for improved readability and maintainability, including the use of target-typed `new()` expressions. These changes collectively aim to improve code quality and maintainability.
1 parent 4958fd6 commit c4c9130

24 files changed

+87
-102
lines changed

samples/basic/authorization/auto-signin/dotnet/AspNetExtensions.cs

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,6 @@
1919
using System.Net.Http;
2020
using System.Threading.Tasks;
2121

22-
namespace Microsoft.Agents.AspNetAuthentication;
23-
2422
public static class AspNetExtensions
2523
{
2624
private static readonly ConcurrentDictionary<string, ConfigurationManager<OpenIdConnectConfiguration>> _openIdMetadataCache = new();
@@ -56,7 +54,7 @@ public static void AddAgentAspNetAuthentication(this IServiceCollection services
5654
return;
5755
}
5856

59-
services.AddAgentAspNetAuthentication(tokenValidationSection.Get<TokenValidationOptions>());
57+
services.AddAgentAspNetAuthentication(tokenValidationSection.Get<TokenValidationOptions>()!);
6058
}
6159

6260
/// <summary>
@@ -154,7 +152,7 @@ public static void AddAgentAspNetAuthentication(this IServiceCollection services
154152
return;
155153
}
156154

157-
string[] parts = authorizationHeader?.Split(' ');
155+
string[] parts = authorizationHeader?.Split(' ')!;
158156
if (parts.Length != 2 || parts[0] != "Bearer")
159157
{
160158
// Default to AadTokenValidation handling
@@ -164,7 +162,7 @@ public static void AddAgentAspNetAuthentication(this IServiceCollection services
164162
}
165163

166164
JwtSecurityToken token = new(parts[1]);
167-
string issuer = token.Claims.FirstOrDefault(claim => claim.Type == AuthenticationConstants.IssuerClaim)?.Value;
165+
string issuer = token.Claims.FirstOrDefault(claim => claim.Type == AuthenticationConstants.IssuerClaim)?.Value!;
168166

169167
if (validationOptions.AzureBotServiceTokenHandling && AuthenticationConstants.BotFrameworkTokenIssuer.Equals(issuer))
170168
{
@@ -209,17 +207,17 @@ public static void AddAgentAspNetAuthentication(this IServiceCollection services
209207

210208
public class TokenValidationOptions
211209
{
212-
public IList<string> Audiences { get; set; }
210+
public IList<string>? Audiences { get; set; }
213211

214212
/// <summary>
215213
/// TenantId of the Azure Bot. Optional but recommended.
216214
/// </summary>
217-
public string TenantId { get; set; }
215+
public string? TenantId { get; set; }
218216

219217
/// <summary>
220218
/// Additional valid issuers. Optional, in which case the Public Azure Bot Service issuers are used.
221219
/// </summary>
222-
public IList<string> ValidIssuers { get; set; }
220+
public IList<string>? ValidIssuers { get; set; }
223221

224222
/// <summary>
225223
/// Can be omitted, in which case public Azure Bot Service and Azure Cloud metadata urls are used.
@@ -231,14 +229,14 @@ public class TokenValidationOptions
231229
/// </summary>
232230
/// <see cref="AuthenticationConstants.PublicAzureBotServiceOpenIdMetadataUrl"/>
233231
/// <see cref="AuthenticationConstants.GovAzureBotServiceOpenIdMetadataUrl"/>
234-
public string AzureBotServiceOpenIdMetadataUrl { get; set; }
232+
public string? AzureBotServiceOpenIdMetadataUrl { get; set; }
235233

236234
/// <summary>
237235
/// Entra OpenIdMetadataUrl. Optional, in which case default value depends on IsGov.
238236
/// </summary>
239237
/// <see cref="AuthenticationConstants.PublicOpenIdMetadataUrl"/>
240238
/// <see cref="AuthenticationConstants.GovOpenIdMetadataUrl"/>
241-
public string OpenIdMetadataUrl { get; set; }
239+
public string? OpenIdMetadataUrl { get; set; }
242240

243241
/// <summary>
244242
/// Determines if Azure Bot Service tokens are handled. Defaults to true and should always be true until Azure Bot Service sends Entra ID token.

samples/basic/authorization/auto-signin/dotnet/AuthAgent.cs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,13 @@
1515
using System.Threading;
1616
using System.Threading.Tasks;
1717

18-
18+
namespace AutoSignIn;
1919
public class AuthAgent : AgentApplication
2020
{
2121
/// <summary>
2222
/// Default Sign In Name
2323
/// </summary>
24-
private string _defaultDisplayName = "Unknown User";
24+
private readonly string _defaultDisplayName = "Unknown User";
2525

2626
/// <summary>
2727
/// Describes the agent registration for the Authorization Agent
@@ -83,7 +83,7 @@ private async Task WelcomeMessageAsync(ITurnContext turnContext, ITurnState turn
8383
if (member.Id != turnContext.Activity.Recipient.Id)
8484
{
8585
string displayName = await GetDisplayName(turnContext);
86-
StringBuilder sb = new StringBuilder();
86+
StringBuilder sb = new();
8787
sb.AppendLine($"Welcome to the AutoSignIn Example, **{displayName}**!");
8888
sb.AppendLine("This Agent automatically signs you in when you first connect.");
8989
sb.AppendLine("You can use the following commands to interact with the agent:");
@@ -126,13 +126,13 @@ private async Task OnMe(ITurnContext turnContext, ITurnState turnState, Cancella
126126
}
127127

128128
// Just to verify we in fact have two different tokens. This wouldn't be needed in a production Agent and here just to verify sample setup.
129-
if (await UserAuthorization.GetTurnTokenAsync(turnContext, UserAuthorization.DefaultHandlerName, cancellationToken: cancellationToken) == await UserAuthorization.GetTurnTokenAsync(turnContext, "me"))
129+
if (await UserAuthorization.GetTurnTokenAsync(turnContext, UserAuthorization.DefaultHandlerName, cancellationToken: cancellationToken) == await UserAuthorization.GetTurnTokenAsync(turnContext, "me", cancellationToken))
130130
{
131131
await turnContext.SendActivityAsync($"It would seem '{UserAuthorization.DefaultHandlerName}' and 'me' are using the same OAuth Connection", cancellationToken: cancellationToken);
132132
return;
133133
}
134134

135-
var meInfo = $"Name: {displayName}\r\nJob Title: {graphInfo["jobTitle"].GetValue<string>()}\r\nEmail: {graphInfo["mail"].GetValue<string>()}";
135+
var meInfo = $"Name: {displayName}\r\nJob Title: {graphInfo["jobTitle"]!.GetValue<string>()}\r\nEmail: {graphInfo["mail"]!.GetValue<string>()}";
136136
await turnContext.SendActivityAsync(meInfo, cancellationToken: cancellationToken);
137137
}
138138

@@ -179,7 +179,7 @@ private async Task OnUserSignInFailure(ITurnContext turnContext, ITurnState turn
179179
{
180180
// Raise a notification to the user that the sign-in process failed. In a production Agent, this would be used
181181
// to display alternative ways to get help, or in some cases transfer to a live agent.
182-
await turnContext.SendActivityAsync($"Sign In: Failed to login to '{handlerName}': {response.Cause}/{response.Error.Message}", cancellationToken: cancellationToken);
182+
await turnContext.SendActivityAsync($"Sign In: Failed to login to '{handlerName}': {response.Cause}/{response.Error!.Message}", cancellationToken: cancellationToken);
183183
}
184184

185185
/// <summary>
@@ -191,7 +191,7 @@ private async Task<string> GetDisplayName(ITurnContext turnContext)
191191
var graphInfo = await GetGraphInfo(turnContext, UserAuthorization.DefaultHandlerName);
192192
if (graphInfo != null)
193193
{
194-
displayName = graphInfo!["displayName"].GetValue<string>();
194+
displayName = graphInfo!["displayName"]!.GetValue<string>();
195195
}
196196
return displayName;
197197
}
@@ -208,14 +208,14 @@ private async Task<JsonNode> GetGraphInfo(ITurnContext turnContext, string handl
208208
if (response.IsSuccessStatusCode)
209209
{
210210
var content = await response.Content.ReadAsStringAsync();
211-
return JsonNode.Parse(content);
211+
return JsonNode.Parse(content)!;
212212
}
213213
}
214214
catch (Exception ex)
215215
{
216216
// Handle error response from Graph API
217217
System.Diagnostics.Trace.WriteLine($"Error getting display name: {ex.Message}");
218218
}
219-
return null;
219+
return null!;
220220
}
221221
}

samples/basic/authorization/auto-signin/dotnet/AutoSignIn.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
<PropertyGroup>
44
<TargetFramework>net8.0</TargetFramework>
55
<LangVersion>latest</LangVersion>
6+
<Nullable>enable</Nullable>
67
</PropertyGroup>
78

89
<ItemGroup>

samples/basic/authorization/auto-signin/dotnet/Program.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// Copyright (c) Microsoft Corporation. All rights reserved.
22
// Licensed under the MIT License.
33

4-
using Microsoft.Agents.AspNetAuthentication;
4+
using AutoSignIn;
55
using Microsoft.Agents.Builder;
66
using Microsoft.Agents.Hosting.AspNetCore;
77
using Microsoft.Agents.Storage;

samples/basic/authorization/obo-authorization/dotnet/AspNetExtensions.cs

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,6 @@
1919
using System.Net.Http;
2020
using System.Threading.Tasks;
2121

22-
namespace Microsoft.Agents.AspNetAuthentication;
23-
2422
public static class AspNetExtensions
2523
{
2624
private static readonly ConcurrentDictionary<string, ConfigurationManager<OpenIdConnectConfiguration>> _openIdMetadataCache = new();
@@ -56,7 +54,7 @@ public static void AddAgentAspNetAuthentication(this IServiceCollection services
5654
return;
5755
}
5856

59-
services.AddAgentAspNetAuthentication(tokenValidationSection.Get<TokenValidationOptions>());
57+
services.AddAgentAspNetAuthentication(tokenValidationSection.Get<TokenValidationOptions>()!);
6058
}
6159

6260
/// <summary>
@@ -154,7 +152,7 @@ public static void AddAgentAspNetAuthentication(this IServiceCollection services
154152
return;
155153
}
156154

157-
string[] parts = authorizationHeader?.Split(' ');
155+
string[] parts = authorizationHeader?.Split(' ')!;
158156
if (parts.Length != 2 || parts[0] != "Bearer")
159157
{
160158
// Default to AadTokenValidation handling
@@ -164,7 +162,7 @@ public static void AddAgentAspNetAuthentication(this IServiceCollection services
164162
}
165163

166164
JwtSecurityToken token = new(parts[1]);
167-
string issuer = token.Claims.FirstOrDefault(claim => claim.Type == AuthenticationConstants.IssuerClaim)?.Value;
165+
string issuer = token.Claims.FirstOrDefault(claim => claim.Type == AuthenticationConstants.IssuerClaim)?.Value!;
168166

169167
if (validationOptions.AzureBotServiceTokenHandling && AuthenticationConstants.BotFrameworkTokenIssuer.Equals(issuer))
170168
{
@@ -209,17 +207,17 @@ public static void AddAgentAspNetAuthentication(this IServiceCollection services
209207

210208
public class TokenValidationOptions
211209
{
212-
public IList<string> Audiences { get; set; }
210+
public IList<string>? Audiences { get; set; }
213211

214212
/// <summary>
215213
/// TenantId of the Azure Bot. Optional but recommended.
216214
/// </summary>
217-
public string TenantId { get; set; }
215+
public string? TenantId { get; set; }
218216

219217
/// <summary>
220218
/// Additional valid issuers. Optional, in which case the Public Azure Bot Service issuers are used.
221219
/// </summary>
222-
public IList<string> ValidIssuers { get; set; }
220+
public IList<string>? ValidIssuers { get; set; }
223221

224222
/// <summary>
225223
/// Can be omitted, in which case public Azure Bot Service and Azure Cloud metadata urls are used.
@@ -231,14 +229,14 @@ public class TokenValidationOptions
231229
/// </summary>
232230
/// <see cref="AuthenticationConstants.PublicAzureBotServiceOpenIdMetadataUrl"/>
233231
/// <see cref="AuthenticationConstants.GovAzureBotServiceOpenIdMetadataUrl"/>
234-
public string AzureBotServiceOpenIdMetadataUrl { get; set; }
232+
public string? AzureBotServiceOpenIdMetadataUrl { get; set; }
235233

236234
/// <summary>
237235
/// Entra OpenIdMetadataUrl. Optional, in which case default value depends on IsGov.
238236
/// </summary>
239237
/// <see cref="AuthenticationConstants.PublicOpenIdMetadataUrl"/>
240238
/// <see cref="AuthenticationConstants.GovOpenIdMetadataUrl"/>
241-
public string OpenIdMetadataUrl { get; set; }
239+
public string? OpenIdMetadataUrl { get; set; }
242240

243241
/// <summary>
244242
/// Determines if Azure Bot Service tokens are handled. Defaults to true and should always be true until Azure Bot Service sends Entra ID token.

samples/basic/authorization/obo-authorization/dotnet/OBOAuthorization.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
<PropertyGroup>
44
<TargetFramework>net8.0</TargetFramework>
55
<LangVersion>latest</LangVersion>
6+
<Nullable>enable</Nullable>
67
</PropertyGroup>
78

89
<ItemGroup>

samples/basic/authorization/obo-authorization/dotnet/Program.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
// Copyright (c) Microsoft Corporation. All rights reserved.
22
// Licensed under the MIT License.
33

4-
using Microsoft.Agents.AspNetAuthentication;
54
using Microsoft.Agents.Builder;
65
using Microsoft.Agents.Builder.App;
76
using Microsoft.Agents.CopilotStudio.Client;
@@ -45,7 +44,7 @@ CopilotClient GetClient(AgentApplication app, ITurnContext turnContext)
4544

4645
return new CopilotClient(
4746
settings,
48-
sp.GetService<IHttpClientFactory>(),
47+
sp.GetService<IHttpClientFactory>()!,
4948
tokenProviderFunction: async (s) =>
5049
{
5150
// In this sample, the Azure Bot OAuth Connection is configured to return an
@@ -106,7 +105,7 @@ CopilotClient GetClient(AgentApplication app, ITurnContext turnContext)
106105
// Called when the OAuth flow fails
107106
app.UserAuthorization.OnUserSignInFailure(async (turnContext, turnState, handlerName, response, initiatingActivity, cancellationToken) =>
108107
{
109-
await turnContext.SendActivityAsync($"SignIn failed with '{handlerName}': {response.Cause}/{response.Error.Message}", cancellationToken: cancellationToken);
108+
await turnContext.SendActivityAsync($"SignIn failed with '{handlerName}': {response.Cause}/{response.Error!.Message}", cancellationToken: cancellationToken);
110109
});
111110

112111
return app;

samples/basic/azureai-streaming-poem-agent/dotnet/AspNetExtensions.cs

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,6 @@
1919
using System.Net.Http;
2020
using System.Threading.Tasks;
2121

22-
namespace Microsoft.Agents.AspNetAuthentication;
23-
2422
public static class AspNetExtensions
2523
{
2624
private static readonly ConcurrentDictionary<string, ConfigurationManager<OpenIdConnectConfiguration>> _openIdMetadataCache = new();
@@ -56,7 +54,7 @@ public static void AddAgentAspNetAuthentication(this IServiceCollection services
5654
return;
5755
}
5856

59-
services.AddAgentAspNetAuthentication(tokenValidationSection.Get<TokenValidationOptions>());
57+
services.AddAgentAspNetAuthentication(tokenValidationSection.Get<TokenValidationOptions>()!);
6058
}
6159

6260
/// <summary>
@@ -154,7 +152,7 @@ public static void AddAgentAspNetAuthentication(this IServiceCollection services
154152
return;
155153
}
156154

157-
string[] parts = authorizationHeader?.Split(' ');
155+
string[] parts = authorizationHeader?.Split(' ')!;
158156
if (parts.Length != 2 || parts[0] != "Bearer")
159157
{
160158
// Default to AadTokenValidation handling
@@ -164,7 +162,7 @@ public static void AddAgentAspNetAuthentication(this IServiceCollection services
164162
}
165163

166164
JwtSecurityToken token = new(parts[1]);
167-
string issuer = token.Claims.FirstOrDefault(claim => claim.Type == AuthenticationConstants.IssuerClaim)?.Value;
165+
string issuer = token.Claims.FirstOrDefault(claim => claim.Type == AuthenticationConstants.IssuerClaim)?.Value!;
168166

169167
if (validationOptions.AzureBotServiceTokenHandling && AuthenticationConstants.BotFrameworkTokenIssuer.Equals(issuer))
170168
{
@@ -209,17 +207,17 @@ public static void AddAgentAspNetAuthentication(this IServiceCollection services
209207

210208
public class TokenValidationOptions
211209
{
212-
public IList<string> Audiences { get; set; }
210+
public IList<string>? Audiences { get; set; }
213211

214212
/// <summary>
215213
/// TenantId of the Azure Bot. Optional but recommended.
216214
/// </summary>
217-
public string TenantId { get; set; }
215+
public string? TenantId { get; set; }
218216

219217
/// <summary>
220218
/// Additional valid issuers. Optional, in which case the Public Azure Bot Service issuers are used.
221219
/// </summary>
222-
public IList<string> ValidIssuers { get; set; }
220+
public IList<string>? ValidIssuers { get; set; }
223221

224222
/// <summary>
225223
/// Can be omitted, in which case public Azure Bot Service and Azure Cloud metadata urls are used.
@@ -231,14 +229,14 @@ public class TokenValidationOptions
231229
/// </summary>
232230
/// <see cref="AuthenticationConstants.PublicAzureBotServiceOpenIdMetadataUrl"/>
233231
/// <see cref="AuthenticationConstants.GovAzureBotServiceOpenIdMetadataUrl"/>
234-
public string AzureBotServiceOpenIdMetadataUrl { get; set; }
232+
public string? AzureBotServiceOpenIdMetadataUrl { get; set; }
235233

236234
/// <summary>
237235
/// Entra OpenIdMetadataUrl. Optional, in which case default value depends on IsGov.
238236
/// </summary>
239237
/// <see cref="AuthenticationConstants.PublicOpenIdMetadataUrl"/>
240238
/// <see cref="AuthenticationConstants.GovOpenIdMetadataUrl"/>
241-
public string OpenIdMetadataUrl { get; set; }
239+
public string? OpenIdMetadataUrl { get; set; }
242240

243241
/// <summary>
244242
/// Determines if Azure Bot Service tokens are handled. Defaults to true and should always be true until Azure Bot Service sends Entra ID token.

samples/basic/azureai-streaming-poem-agent/dotnet/Program.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
// Licensed under the MIT License.
33

44
using Azure.AI.OpenAI;
5-
using Microsoft.Agents.AspNetAuthentication;
65
using Microsoft.Agents.Builder;
76
using Microsoft.Agents.Hosting.AspNetCore;
87
using Microsoft.Agents.Storage;
@@ -23,8 +22,8 @@
2322
builder.Services.AddTransient<ChatClient>(sp =>
2423
{
2524
return new AzureOpenAIClient(
26-
new Uri(builder.Configuration["AIServices:AzureOpenAI:Endpoint"]),
27-
new ApiKeyCredential(builder.Configuration["AIServices:AzureOpenAI:ApiKey"]))
25+
new Uri(builder.Configuration["AIServices:AzureOpenAI:Endpoint"]!),
26+
new ApiKeyCredential(builder.Configuration["AIServices:AzureOpenAI:ApiKey"]!))
2827
.GetChatClient(builder.Configuration["AIServices:AzureOpenAI:DeploymentName"]);
2928
});
3029

samples/basic/azureai-streaming-poem-agent/dotnet/StreamingAgent.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ namespace StreamingMessageAgent;
1414

1515
public class StreamingAgent : AgentApplication
1616
{
17-
private ChatClient _chatClient;
17+
private readonly ChatClient _chatClient;
1818

1919
/// <summary>
2020
/// Example of a streaming response agent using the Azure OpenAI ChatClient.
@@ -90,7 +90,7 @@ You break your poems into stanzas
9090
if (update.ContentUpdate.Count > 0)
9191
{
9292
if (!string.IsNullOrEmpty(update.ContentUpdate[0]?.Text))
93-
turnContext.StreamingResponse.QueueTextChunk(update.ContentUpdate[0]?.Text);
93+
turnContext.StreamingResponse.QueueTextChunk(update.ContentUpdate[0]?.Text!);
9494
}
9595
}
9696
}

0 commit comments

Comments
 (0)