From 07190aa59406d5711d7a38243c4a649538e1ee0f Mon Sep 17 00:00:00 2001 From: Vladimir Pecanac Date: Sun, 30 Aug 2026 21:42:00 +0200 Subject: [PATCH] Build a query string: retarget net10.0, safe UriBuilder helper, CS8620 fix - net7.0 -> net10.0 across BuildQueryString, BuildQueryStringTests and BooksAPI - Package moves: Microsoft.Extensions.DependencyInjection 10.0.11, Microsoft.NET.Test.Sdk 18.9.0, MSTest.TestAdapter/TestFramework 4.3.3, Moq 4.20.72, coverlet.collector 10.0.1, Swashbuckle.AspNetCore 10.2.3 - Add BuildUrlWithQueryStringUsingUriBuilderSafely: UriBuilder composed with QueryString.Create, plus a round-trip test over 'Tom & Jerry' and 'C#' - Fix CS8620: declare the caller dictionaries as Dictionary where the helper takes nullable values (BooksApiService and the matching tests) - Program.cs: top-level statements --- .../BuildQueryString/BooksAPI/BooksAPI.csproj | 4 +- .../BuildQueryString/BooksApiService.cs | 4 +- .../BuildQueryString/BuildQueryString.csproj | 6 +-- .../BuildQueryString/Program.cs | 46 ++++++++----------- .../BuildQueryString/QueryStringHelper.cs | 14 ++++++ .../BuildQueryStringUnitTests.csproj | 12 ++--- .../QueryStringHelperUnitTests.cs | 27 ++++++++++- 7 files changed, 71 insertions(+), 42 deletions(-) diff --git a/dotnet-querystrings/BuildQueryString/BooksAPI/BooksAPI.csproj b/dotnet-querystrings/BuildQueryString/BooksAPI/BooksAPI.csproj index 8f873a5d8f..213738b245 100644 --- a/dotnet-querystrings/BuildQueryString/BooksAPI/BooksAPI.csproj +++ b/dotnet-querystrings/BuildQueryString/BooksAPI/BooksAPI.csproj @@ -1,13 +1,13 @@ - net7.0 + net10.0 enable enable - + diff --git a/dotnet-querystrings/BuildQueryString/BuildQueryString/BooksApiService.cs b/dotnet-querystrings/BuildQueryString/BuildQueryString/BooksApiService.cs index 570c484ebb..50019e0467 100644 --- a/dotnet-querystrings/BuildQueryString/BuildQueryString/BooksApiService.cs +++ b/dotnet-querystrings/BuildQueryString/BuildQueryString/BooksApiService.cs @@ -61,7 +61,7 @@ public async Task GetWithQueryParamsUsingParseQueryStringMethod(string a public async Task GetWithQueryParamsUsingAddQueryStringMethod(string author, string language) { - var query = new Dictionary + var query = new Dictionary { { "author", author }, { "language", language } @@ -83,7 +83,7 @@ public async Task GetWithQueryParamsUsingQueryBuilderClass(string author public async Task GetWithQueryParamsUsingCreateMethod(string author, string language) { - var query = new Dictionary + var query = new Dictionary { { "author", author }, { "language", language } diff --git a/dotnet-querystrings/BuildQueryString/BuildQueryString/BuildQueryString.csproj b/dotnet-querystrings/BuildQueryString/BuildQueryString/BuildQueryString.csproj index 2bb2ef6f60..ea250ce283 100644 --- a/dotnet-querystrings/BuildQueryString/BuildQueryString/BuildQueryString.csproj +++ b/dotnet-querystrings/BuildQueryString/BuildQueryString/BuildQueryString.csproj @@ -2,14 +2,14 @@ Exe - net7.0 + net10.0 enable enable - - + + diff --git a/dotnet-querystrings/BuildQueryString/BuildQueryString/Program.cs b/dotnet-querystrings/BuildQueryString/BuildQueryString/Program.cs index bd0bacf9d2..9bb6c1d8d4 100644 --- a/dotnet-querystrings/BuildQueryString/BuildQueryString/Program.cs +++ b/dotnet-querystrings/BuildQueryString/BuildQueryString/Program.cs @@ -1,35 +1,27 @@ -using Microsoft.Extensions.DependencyInjection; +using BuildQueryString; +using Microsoft.Extensions.DependencyInjection; -namespace BuildQueryString -{ - public class Program - { - static async Task Main(string[] args) - { - var serviceProvider = new ServiceCollection() - .AddTransient() - .AddTransient() - .BuildServiceProvider(); +var serviceProvider = new ServiceCollection() + .AddTransient() + .AddTransient() + .BuildServiceProvider(); - var booksService = serviceProvider.GetRequiredService(); +var booksService = serviceProvider.GetRequiredService(); - Console.WriteLine("\n***************** Build the Query String Using StringConcatenation ***************\n"); - Console.WriteLine(await booksService.GetWithQueryParamsUsingStringConcatenation("George Orwell", "english")); +Console.WriteLine("\n***************** Build the Query String Using StringConcatenation ***************\n"); +Console.WriteLine(await booksService.GetWithQueryParamsUsingStringConcatenation("George Orwell", "english")); - Console.WriteLine("\n***************** Build the Query String Using UriBuilder Class ***************\n"); - Console.WriteLine(await booksService.GetWithQueryParamsUsingUriBuilder("Jane Austen", "english")); +Console.WriteLine("\n***************** Build the Query String Using UriBuilder Class ***************\n"); +Console.WriteLine(await booksService.GetWithQueryParamsUsingUriBuilder("Jane Austen", "english")); - Console.WriteLine("\n***************** Build the Query String Using ParseQueryString Method ***************\n"); - Console.WriteLine(await booksService.GetWithQueryParamsUsingParseQueryStringMethod("Agatha Christie", "english")); +Console.WriteLine("\n***************** Build the Query String Using ParseQueryString Method ***************\n"); +Console.WriteLine(await booksService.GetWithQueryParamsUsingParseQueryStringMethod("Agatha Christie", "english")); - Console.WriteLine("\n***************** Build the Query String Using AddQueryString Method ***************\n"); - Console.WriteLine(await booksService.GetWithQueryParamsUsingAddQueryStringMethod("Haruki Murakami", "japanese")); +Console.WriteLine("\n***************** Build the Query String Using AddQueryString Method ***************\n"); +Console.WriteLine(await booksService.GetWithQueryParamsUsingAddQueryStringMethod("Haruki Murakami", "japanese")); - Console.WriteLine("\n***************** Build the Query String Using QueryBuilder Class ***************\n"); - Console.WriteLine(await booksService.GetWithQueryParamsUsingQueryBuilderClass("Gabriel Garcia", "spanish")); +Console.WriteLine("\n***************** Build the Query String Using QueryBuilder Class ***************\n"); +Console.WriteLine(await booksService.GetWithQueryParamsUsingQueryBuilderClass("Gabriel Garcia", "spanish")); - Console.WriteLine("\n***************** Build the Query String Using QueryString Create Method ***************\n"); - Console.WriteLine(await booksService.GetWithQueryParamsUsingCreateMethod("Leo Tolstoy", "russian")); - } - } -} \ No newline at end of file +Console.WriteLine("\n***************** Build the Query String Using QueryString Create Method ***************\n"); +Console.WriteLine(await booksService.GetWithQueryParamsUsingCreateMethod("Leo Tolstoy", "russian")); diff --git a/dotnet-querystrings/BuildQueryString/BuildQueryString/QueryStringHelper.cs b/dotnet-querystrings/BuildQueryString/BuildQueryString/QueryStringHelper.cs index 11ee3ba9fd..ae63b6d597 100644 --- a/dotnet-querystrings/BuildQueryString/BuildQueryString/QueryStringHelper.cs +++ b/dotnet-querystrings/BuildQueryString/BuildQueryString/QueryStringHelper.cs @@ -31,6 +31,20 @@ public static string BuildUrlWithQueryStringUsingUriBuilder(string basePath, Dic return fullApiUrl; } + public static string BuildUrlWithQueryStringUsingUriBuilderSafely( + string basePath, Dictionary queryParams) + { + var uriBuilder = new UriBuilder(basePath) + { + Query = QueryString.Create(queryParams).Value + }; + + var fullApiUrl = uriBuilder.Uri.AbsoluteUri; + Console.WriteLine($"Full API Url: {fullApiUrl}"); + + return fullApiUrl; + } + public static string BuildUrlWithQueryStringUsingParseQueryStringMethod( string basePath, Dictionary queryParams) { diff --git a/dotnet-querystrings/BuildQueryString/BuildQueryStringTests/BuildQueryStringUnitTests.csproj b/dotnet-querystrings/BuildQueryString/BuildQueryStringTests/BuildQueryStringUnitTests.csproj index d5909a5997..30687b3938 100644 --- a/dotnet-querystrings/BuildQueryString/BuildQueryStringTests/BuildQueryStringUnitTests.csproj +++ b/dotnet-querystrings/BuildQueryString/BuildQueryStringTests/BuildQueryStringUnitTests.csproj @@ -1,7 +1,7 @@ - net7.0 + net10.0 enable enable @@ -10,11 +10,11 @@ - - - - - + + + + + diff --git a/dotnet-querystrings/BuildQueryString/BuildQueryStringTests/QueryStringHelperUnitTests.cs b/dotnet-querystrings/BuildQueryString/BuildQueryStringTests/QueryStringHelperUnitTests.cs index b3a41a9898..451615d413 100644 --- a/dotnet-querystrings/BuildQueryString/BuildQueryStringTests/QueryStringHelperUnitTests.cs +++ b/dotnet-querystrings/BuildQueryString/BuildQueryStringTests/QueryStringHelperUnitTests.cs @@ -1,4 +1,5 @@ using BuildQueryString; +using Microsoft.AspNetCore.WebUtilities; using Microsoft.VisualStudio.TestTools.UnitTesting; namespace BuildQueryStringTests @@ -48,6 +49,28 @@ public void GivenBasePathAndQueryParams_WhenBuildUrlWithQueryStringUsingUriBuild Assert.AreEqual(expectedApiUrl, result); } + [TestMethod] + public void GivenValuesWithQueryDelimiters_WhenBuildUrlWithQueryStringUsingUriBuilderSafely_ThenValuesRoundTrip() + { + // Arrange + var dict = new Dictionary + { + { "author", "Tom & Jerry" }, + { "tag", "C#" } + }; + var expectedApiUrl = "https://localhost:7220/api/Books?author=Tom%20%26%20Jerry&tag=C%23"; + + // Act + var result = QueryStringHelper.BuildUrlWithQueryStringUsingUriBuilderSafely(basePath, dict); + + //Assert + Assert.AreEqual(expectedApiUrl, result); + + var parsed = QueryHelpers.ParseQuery(new Uri(result).Query); + Assert.AreEqual("Tom & Jerry", parsed["author"].ToString()); + Assert.AreEqual("C#", parsed["tag"].ToString()); + } + [TestMethod] public void GivenBasePathAndQueryParams_WhenBuildUrlWithQueryStringUsingParseQueryStringMethod_ThenCorrectApiUrlIsBuilt() { @@ -74,7 +97,7 @@ public void GivenBasePathAndQueryParams_WhenBuildUrlWithQueryStringUsingAddQuery // Arrange var author = "Haruki Murakami"; var language = "japanese"; - var dict = new Dictionary + var dict = new Dictionary { { "author", author }, { "language", language } @@ -114,7 +137,7 @@ public void GivenBasePathAndQueryParams_WhenBuildurlWithQueryStringUsingCreateMe // Arrange var author = "Leo Tolstoy"; var language = "russian"; - var dict = new Dictionary + var dict = new Dictionary { { "author", author }, { "language", language }