Skip to content

Commit 2f7364f

Browse files
Merge branch 'main' into seo/109720-csharp-outer-join-linq
2 parents cb2c3c2 + 9cb6192 commit 2f7364f

88 files changed

Lines changed: 1173 additions & 539 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎aspnetcore-features/HttpClientDefaultAndPerRequestTimeOut/HttpClientDefaultAndPerRequestTimeOut/HttpClientDefaultAndPerRequestTimeOut.csproj‎

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,14 @@
11
<Project Sdk="Microsoft.NET.Sdk.Web">
22

33
<PropertyGroup>
4-
<TargetFramework>net8.0</TargetFramework>
5-
<Nullable>disable</Nullable>
4+
<TargetFramework>net10.0</TargetFramework>
5+
<Nullable>enable</Nullable>
66
<ImplicitUsings>enable</ImplicitUsings>
77
<UserSecretsId>d0d8591a-b364-4f96-921f-d24a0e638079</UserSecretsId>
8-
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
9-
<DockerfileContext>.</DockerfileContext>
108
</PropertyGroup>
119

1210
<ItemGroup>
13-
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.19.6" />
11+
<PackageReference Include="Microsoft.Extensions.Http.Resilience" Version="10.10.0" />
1412
</ItemGroup>
1513

1614
<ItemGroup>

‎aspnetcore-features/HttpClientDefaultAndPerRequestTimeOut/HttpClientDefaultAndPerRequestTimeOut/Program.cs‎

Lines changed: 49 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,5 @@
1-
using Microsoft.AspNetCore.Builder;
2-
using Microsoft.AspNetCore.Mvc;
3-
using System.Net.Http;
4-
using System.Threading;
5-
61
var builder = WebApplication.CreateBuilder(args);
72

8-
// Add services to the container.
9-
10-
builder.Services.AddControllers();
11-
123
builder.Services.AddHttpClient("TestClient", (sp, httpClient) =>
134
{
145
var configuration = sp.GetRequiredService<IConfiguration>();
@@ -18,15 +9,17 @@
189
httpClient.Timeout = TimeSpan.FromSeconds(timeoutSeconds);
1910
});
2011

21-
var app = builder.Build();
22-
23-
// Configure the HTTP request pipeline.
24-
25-
app.UseHttpsRedirection();
26-
27-
app.UseAuthorization();
12+
builder.Services.AddHttpClient("ResilientClient", httpClient =>
13+
{
14+
httpClient.BaseAddress = new Uri("http://localhost:5000");
15+
})
16+
.AddStandardResilienceHandler(options =>
17+
{
18+
options.AttemptTimeout.Timeout = TimeSpan.FromSeconds(2);
19+
options.TotalRequestTimeout.Timeout = TimeSpan.FromSeconds(8);
20+
});
2821

29-
app.MapControllers();
22+
var app = builder.Build();
3023

3124
app.MapGet("/api/delay-4-seconds", async (CancellationToken cancellationToken) => await Task.Delay(TimeSpan.FromSeconds(4), cancellationToken));
3225

@@ -36,7 +29,7 @@
3629

3730
try
3831
{
39-
var response = await httpClient.GetAsync("/api/delay-4-seconds");
32+
using var response = await httpClient.GetAsync("/api/delay-4-seconds");
4033

4134
return Results.Ok();
4235
}
@@ -53,7 +46,7 @@
5346

5447
try
5548
{
56-
var response = await httpClient.GetAsync($"/api/delay-4-seconds", cancellationToken);
49+
using var response = await httpClient.GetAsync($"/api/delay-4-seconds", cancellationToken);
5750

5851
return Results.Ok();
5952
}
@@ -71,7 +64,7 @@
7164

7265
try
7366
{
74-
var response = await httpClient.GetAsync("/api/delay-4-seconds", tokenSource.Token);
67+
using var response = await httpClient.GetAsync("/api/delay-4-seconds", tokenSource.Token);
7568

7669
return Results.Ok();
7770
}
@@ -83,4 +76,40 @@
8376
}
8477
});
8578

79+
app.MapGet("/api/test-timeout-vs-cancellation", async (IHttpClientFactory httpClientFactory, CancellationToken cancellationToken) =>
80+
{
81+
var httpClient = httpClientFactory.CreateClient("TestClient");
82+
83+
try
84+
{
85+
using var response = await httpClient.GetAsync("/api/delay-4-seconds", cancellationToken);
86+
87+
return Results.Ok();
88+
}
89+
catch (OperationCanceledException ex) when (ex.InnerException is TimeoutException)
90+
{
91+
return Results.Text("The request timed out");
92+
}
93+
catch (OperationCanceledException)
94+
{
95+
return Results.Text("The caller canceled the request");
96+
}
97+
});
98+
99+
app.MapGet("/api/test-resilience-timeout", async (IHttpClientFactory httpClientFactory) =>
100+
{
101+
var httpClient = httpClientFactory.CreateClient("ResilientClient");
102+
103+
try
104+
{
105+
using var response = await httpClient.GetAsync("/api/delay-4-seconds");
106+
107+
return Results.Ok();
108+
}
109+
catch (Exception ex)
110+
{
111+
return Results.Text($"{ex.GetType().Name} after {httpClient.Timeout}");
112+
}
113+
});
114+
86115
app.Run();

‎aspnetcore-features/HttpClientDefaultAndPerRequestTimeOut/Tests/HttpClientDefaultAndPerRequestTimeOut.Tests/EndpointsLiveTests.cs‎

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,4 +55,30 @@ public async Task GivenCustomTokenIsCombinedWithGlobalAndRequestToken_WhenTheReq
5555
// Assert
5656
await Assert.ThrowsAsync<TaskCanceledException>(() => client.GetAsync("/api/test-combined-timeout", cts.Token));
5757
}
58+
59+
[Fact]
60+
public async Task WhenTheTimeoutElapses_ThenTheOperationCanceledExceptionNestsATimeoutException()
61+
{
62+
// Arrange
63+
var client = _factory.CreateClient();
64+
65+
// Act
66+
var responseMessage = await client.GetStringAsync("/api/test-timeout-vs-cancellation");
67+
68+
// Assert
69+
Assert.Equal("The request timed out", responseMessage);
70+
}
71+
72+
[Fact]
73+
public async Task WhenTheResilienceHandlerTimesOut_ThenItThrowsTimeoutRejectedExceptionAndDisablesHttpClientTimeout()
74+
{
75+
// Arrange
76+
var client = _factory.CreateClient();
77+
78+
// Act
79+
var responseMessage = await client.GetStringAsync("/api/test-resilience-timeout");
80+
81+
// Assert
82+
Assert.Equal("TimeoutRejectedException after -00:00:00.0010000", responseMessage);
83+
}
5884
}

‎aspnetcore-features/HttpClientDefaultAndPerRequestTimeOut/Tests/HttpClientDefaultAndPerRequestTimeOut.Tests/HttpClientDefaultAndPerRequestTimeOut.Tests.csproj‎

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<TargetFramework>net8.0</TargetFramework>
4+
<TargetFramework>net10.0</TargetFramework>
55
<ImplicitUsings>enable</ImplicitUsings>
66
<Nullable>enable</Nullable>
77

@@ -10,11 +10,11 @@
1010
</PropertyGroup>
1111

1212
<ItemGroup>
13-
<PackageReference Include="coverlet.collector" Version="6.0.0" />
14-
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="8.0.4" />
15-
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
16-
<PackageReference Include="xunit" Version="2.5.3" />
17-
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.3" />
13+
<PackageReference Include="coverlet.collector" Version="10.0.1" />
14+
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="10.0.12" />
15+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.10.1" />
16+
<PackageReference Include="xunit" Version="2.9.3" />
17+
<PackageReference Include="xunit.runner.visualstudio" Version="4.0.0" />
1818
</ItemGroup>
1919

2020
<ItemGroup>

‎collections-dictionary/GetDictionaryKeyByValue/GetDictionaryKeyByValue/DictionaryHelperBenchmark.cs‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ public class DictionaryHelperBenchmark
1313

1414
private readonly DictionaryHelper _dictionaryHelper = new(_benchmarkDict, "NonExistentValue");
1515

16+
private ReverseDictionaryLookup _reverseDictionaryLookup = null!;
17+
1618
private static Dictionary<string, string> GetDictionary()
1719
{
1820
var benchmarkDict = new Dictionary<string, string>(100000);
@@ -26,6 +28,10 @@ private static Dictionary<string, string> GetDictionary()
2628
return benchmarkDict;
2729
}
2830

31+
[GlobalSetup]
32+
public void Setup()
33+
=> _reverseDictionaryLookup = new(_benchmarkDict);
34+
2935
[Benchmark]
3036
public string? UseReverseDictionary()
3137
=> _dictionaryHelper.UseReverseDictionary();
@@ -41,4 +47,12 @@ private static Dictionary<string, string> GetDictionary()
4147
[Benchmark]
4248
public string? LoopThroughTheKeys()
4349
=> _dictionaryHelper.LoopThroughKeys();
50+
51+
[Benchmark]
52+
public string? UsePrebuiltReverseDictionary()
53+
=> _reverseDictionaryLookup.GetKeyFromReverseDictionary("NonExistentValue");
54+
55+
[Benchmark]
56+
public string? UsePrebuiltFrozenDictionary()
57+
=> _reverseDictionaryLookup.GetKeyFromFrozenReverseDictionary("NonExistentValue");
4458
}

‎collections-dictionary/GetDictionaryKeyByValue/GetDictionaryKeyByValue/GetDictionaryKeyByValue.csproj‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@
22

33
<PropertyGroup>
44
<OutputType>Exe</OutputType>
5-
<TargetFramework>net8.0</TargetFramework>
5+
<TargetFramework>net10.0</TargetFramework>
66
<ImplicitUsings>enable</ImplicitUsings>
77
<Nullable>enable</Nullable>
88
</PropertyGroup>
99

1010
<ItemGroup>
11-
<PackageReference Include="BenchmarkDotNet" Version="0.13.12" />
11+
<PackageReference Include="BenchmarkDotNet" Version="0.15.8" />
1212
</ItemGroup>
1313

1414
</Project>
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
using System.Collections.Frozen;
2+
3+
namespace GetDictionaryKeyByValue;
4+
5+
public class ReverseDictionaryLookup(Dictionary<string, string> dict)
6+
{
7+
private readonly Dictionary<string, string> _reverseDict = BuildReverseDictionary(dict);
8+
9+
private readonly FrozenDictionary<string, string> _frozenReverseDict
10+
= BuildReverseDictionary(dict).ToFrozenDictionary();
11+
12+
public string? GetKeyFromReverseDictionary(string value)
13+
{
14+
_reverseDict.TryGetValue(value, out var key);
15+
16+
return key;
17+
}
18+
19+
public string? GetKeyFromFrozenReverseDictionary(string value)
20+
{
21+
_frozenReverseDict.TryGetValue(value, out var key);
22+
23+
return key;
24+
}
25+
26+
private static Dictionary<string, string> BuildReverseDictionary(Dictionary<string, string> dict)
27+
{
28+
var reverseDict = new Dictionary<string, string>(dict.Count);
29+
foreach (var keyValuePair in dict)
30+
{
31+
reverseDict.TryAdd(keyValuePair.Value, keyValuePair.Key);
32+
}
33+
34+
return reverseDict;
35+
}
36+
}

‎collections-dictionary/GetDictionaryKeyByValue/Tests/DictionaryHelperNullTest.cs‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ private static readonly Dictionary<string, string> _dictionary
1616

1717
private readonly DictionaryHelper _dictionaryHelper = new(_dictionary, "NonExistentValue");
1818

19+
private readonly ReverseDictionaryLookup _reverseDictionaryLookup = new(_dictionary);
20+
1921
[Fact]
2022
public void GivenANonExistentValue_WhenUseReverseDictionaryIsCalled_ThenReturnsNull()
2123
{
@@ -47,4 +49,20 @@ public void GivenANonExistentValue_WhenLoopThroughKeysIsCalled_ThenReturnsNull()
4749

4850
Assert.Null(result);
4951
}
52+
53+
[Fact]
54+
public void GivenANonExistentValue_WhenGetKeyFromReverseDictionaryIsCalled_ThenReturnsNull()
55+
{
56+
var result = _reverseDictionaryLookup.GetKeyFromReverseDictionary("NonExistentValue");
57+
58+
Assert.Null(result);
59+
}
60+
61+
[Fact]
62+
public void GivenANonExistentValue_WhenGetKeyFromFrozenReverseDictionaryIsCalled_ThenReturnsNull()
63+
{
64+
var result = _reverseDictionaryLookup.GetKeyFromFrozenReverseDictionary("NonExistentValue");
65+
66+
Assert.Null(result);
67+
}
5068
}

‎collections-dictionary/GetDictionaryKeyByValue/Tests/DictionaryHelperPositiveTest.cs‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ private static readonly Dictionary<string, string> _dictionary
1515

1616
private readonly DictionaryHelper _dictionaryHelper = new(_dictionary, "value3");
1717

18+
private readonly ReverseDictionaryLookup _reverseDictionaryLookup = new(_dictionary);
19+
1820
private static readonly string _expectedKey = "key3";
1921

2022
[Fact]
@@ -48,4 +50,20 @@ public void GivenAnExistentValue_WhenLoopThroughKeysIsCalled_ThenReturnsTheDesir
4850

4951
Assert.Equal(_expectedKey, result);
5052
}
53+
54+
[Fact]
55+
public void GivenAnExistentValue_WhenGetKeyFromReverseDictionaryIsCalled_ThenReturnsTheDesiredKey()
56+
{
57+
var result = _reverseDictionaryLookup.GetKeyFromReverseDictionary("value3");
58+
59+
Assert.Equal(_expectedKey, result);
60+
}
61+
62+
[Fact]
63+
public void GivenAnExistentValue_WhenGetKeyFromFrozenReverseDictionaryIsCalled_ThenReturnsTheDesiredKey()
64+
{
65+
var result = _reverseDictionaryLookup.GetKeyFromFrozenReverseDictionary("value3");
66+
67+
Assert.Equal(_expectedKey, result);
68+
}
5169
}

‎collections-dictionary/GetDictionaryKeyByValue/Tests/Tests.csproj‎

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<TargetFramework>net8.0</TargetFramework>
4+
<TargetFramework>net10.0</TargetFramework>
55
<ImplicitUsings>enable</ImplicitUsings>
66
<Nullable>enable</Nullable>
77

@@ -10,10 +10,10 @@
1010
</PropertyGroup>
1111

1212
<ItemGroup>
13-
<PackageReference Include="coverlet.collector" Version="6.0.0" />
14-
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
15-
<PackageReference Include="xunit" Version="2.5.3" />
16-
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.3" />
13+
<PackageReference Include="coverlet.collector" Version="10.0.1" />
14+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.10.1" />
15+
<PackageReference Include="xunit" Version="2.9.3" />
16+
<PackageReference Include="xunit.runner.visualstudio" Version="4.0.0" />
1717
</ItemGroup>
1818

1919
<ItemGroup>

0 commit comments

Comments
 (0)