Skip to content

Commit 874f7aa

Browse files
deeprobineerhardt
andauthored
[API Implementation]: Expose StopTheHostException (#69404)
* Implement `HostAbortedException` proposal * Apply suggestions * Add solution file Co-authored-by: Eric Erhardt <[email protected]>
1 parent 219f748 commit 874f7aa

File tree

6 files changed

+163
-7
lines changed

6 files changed

+163
-7
lines changed
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
2+
Microsoft Visual Studio Solution File, Format Version 12.00
3+
# Visual Studio Version 17
4+
VisualStudioVersion = 17.2.32516.85
5+
MinimumVisualStudioVersion = 10.0.40219.1
6+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.HostFactoryResolver.Sources", "src\Microsoft.Extensions.HostFactoryResolver.Sources.csproj", "{317C456F-8F26-4441-93F7-9042B0DC7119}"
7+
EndProject
8+
Global
9+
GlobalSection(SolutionConfigurationPlatforms) = preSolution
10+
Debug|Any CPU = Debug|Any CPU
11+
Release|Any CPU = Release|Any CPU
12+
EndGlobalSection
13+
GlobalSection(ProjectConfigurationPlatforms) = postSolution
14+
{317C456F-8F26-4441-93F7-9042B0DC7119}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15+
{317C456F-8F26-4441-93F7-9042B0DC7119}.Debug|Any CPU.Build.0 = Debug|Any CPU
16+
{317C456F-8F26-4441-93F7-9042B0DC7119}.Release|Any CPU.ActiveCfg = Release|Any CPU
17+
{317C456F-8F26-4441-93F7-9042B0DC7119}.Release|Any CPU.Build.0 = Release|Any CPU
18+
EndGlobalSection
19+
GlobalSection(SolutionProperties) = preSolution
20+
HideSolutionNode = FALSE
21+
EndGlobalSection
22+
GlobalSection(ExtensibilityGlobals) = postSolution
23+
SolutionGuid = {80C3C947-5A43-4A55-9E1C-D62738588863}
24+
EndGlobalSection
25+
EndGlobal

src/libraries/Microsoft.Extensions.HostFactoryResolver/src/HostFactoryResolver.cs

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,7 @@ public object CreateHost()
247247
// build to throw
248248
_hostTcs.TrySetException(new InvalidOperationException("The entry point exited without ever building an IHost."));
249249
}
250-
catch (TargetInvocationException tie) when (tie.InnerException is StopTheHostException)
250+
catch (TargetInvocationException tie) when (tie.InnerException is HostAbortedException)
251251
{
252252
// The host was stopped by our own logic
253253
}
@@ -341,15 +341,10 @@ public void OnNext(KeyValuePair<string, object?> value)
341341
if (_stopApplication)
342342
{
343343
// Stop the host from running further
344-
throw new StopTheHostException();
344+
throw new HostAbortedException();
345345
}
346346
}
347347
}
348-
349-
private sealed class StopTheHostException : Exception
350-
{
351-
352-
}
353348
}
354349
}
355350
}

src/libraries/Microsoft.Extensions.Hosting/ref/Microsoft.Extensions.Hosting.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,12 @@ public static partial class Host
3030
public static Microsoft.Extensions.Hosting.IHostBuilder CreateDefaultBuilder() { throw null; }
3131
public static Microsoft.Extensions.Hosting.IHostBuilder CreateDefaultBuilder(string[]? args) { throw null; }
3232
}
33+
public sealed partial class HostAbortedException : System.Exception
34+
{
35+
public HostAbortedException() { }
36+
public HostAbortedException(string? message) { }
37+
public HostAbortedException(string? message, System.Exception? innerException) { }
38+
}
3339
public sealed partial class HostApplicationBuilder
3440
{
3541
public HostApplicationBuilder() { }
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
using System;
4+
using System.Runtime.Serialization;
5+
6+
namespace Microsoft.Extensions.Hosting
7+
{
8+
/// <summary>
9+
/// The exception that is thrown upon <see cref="IHost"/> abortion.
10+
/// </summary>
11+
[Serializable]
12+
public sealed class HostAbortedException : Exception
13+
{
14+
private HostAbortedException(SerializationInfo info, StreamingContext context) : base(info, context) { }
15+
16+
/// <summary>
17+
/// Initializes a new instance of the <see cref="HostAbortedException"/> class
18+
/// with a system-supplied error message.
19+
/// </summary>
20+
public HostAbortedException() : base(SR.HostAbortedExceptionMessage) { }
21+
22+
/// <summary>
23+
/// Initializes a new instance of the <see cref="HostAbortedException"/> class
24+
/// with a specified error message.
25+
/// </summary>
26+
/// <param name="message">
27+
/// The error message that explains the reason for the exception.
28+
/// </param>
29+
/// <remarks>
30+
/// The content of <paramref name="message"/> is intended to be understood by humans.
31+
/// The caller of this constructor is required to ensure that this string has been localized for the
32+
/// current system culture.
33+
/// </remarks>
34+
public HostAbortedException(string? message) : base(message) { }
35+
36+
/// <summary>
37+
/// Initializes a new instance of the <see cref="HostAbortedException"/> class
38+
/// with a specified error message and a reference to the inner exception that
39+
/// is the cause of this exception.
40+
/// </summary>
41+
/// <param name="message">
42+
/// The error message that explains the reason for the exception.
43+
/// </param>
44+
/// <param name="innerException">
45+
/// The exception that is the cause of the current exception.
46+
/// </param>
47+
/// <remarks>
48+
/// The content of <paramref name="message"/> is intended to be understood by humans.
49+
/// The caller of this constructor is required to ensure that this string has been localized for the
50+
/// current system culture.
51+
/// </remarks>
52+
public HostAbortedException(string? message, Exception? innerException) : base(message, innerException) { }
53+
}
54+
}

src/libraries/Microsoft.Extensions.Hosting/src/Resources/Strings.resx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,9 @@
135135
<data name="EnvironmentNameChangeNotSupoprted" xml:space="preserve">
136136
<value>The environment name changed from "{0}" to "{1}". Changing host configuration is not supported.</value>
137137
</data>
138+
<data name="HostAbortedExceptionMessage" xml:space="preserve">
139+
<value>The host was aborted.</value>
140+
</data>
138141
<data name="IHostApplicationLifetimeReplacementNotSupported" xml:space="preserve">
139142
<value>Replacing IHostApplicationLifetime is not supported.</value>
140143
</data>
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System;
5+
using System.IO;
6+
using System.Runtime.Serialization.Formatters.Binary;
7+
using Xunit;
8+
9+
namespace Microsoft.Extensions.Hosting.Unit.Tests
10+
{
11+
public sealed class HostAbortedExceptionTests
12+
{
13+
[Fact]
14+
public void TestEmptyException()
15+
{
16+
var exception = new HostAbortedException();
17+
Assert.Null(exception.InnerException);
18+
Assert.Throws<HostAbortedException>(new Action(() => throw exception));
19+
}
20+
21+
[Theory]
22+
[InlineData("The host was aborted.", false)]
23+
[InlineData("The host was aborted.", true)]
24+
public void TestException(string? message, bool innerException)
25+
{
26+
HostAbortedException exception = innerException
27+
? new HostAbortedException(message, new Exception())
28+
: new HostAbortedException(message);
29+
30+
Assert.Equal(message, exception.Message);
31+
if (innerException)
32+
{
33+
Assert.NotNull(exception.InnerException);
34+
}
35+
36+
HostAbortedException thrownException = Assert.Throws<HostAbortedException>(new Action(() => throw exception));
37+
Assert.Equal(message, thrownException.Message);
38+
}
39+
40+
[Theory]
41+
[InlineData("Test Message")]
42+
[InlineData(null)]
43+
public void TestSerialization(string? message)
44+
{
45+
var exception = new HostAbortedException(message);
46+
using var serializationStream = new MemoryStream();
47+
48+
var formatter = new BinaryFormatter();
49+
formatter.Serialize(serializationStream, exception);
50+
51+
using var deserializationStream = new MemoryStream(serializationStream.ToArray());
52+
HostAbortedException deserializedException = (HostAbortedException) formatter.Deserialize(deserializationStream);
53+
54+
Assert.Equal(exception.Message, deserializedException.Message);
55+
Assert.Null(deserializedException.InnerException);
56+
}
57+
58+
[Fact]
59+
public void TestSerializationDefaultConstructor() {
60+
var exception = new HostAbortedException();
61+
using var serializationStream = new MemoryStream();
62+
63+
var formatter = new BinaryFormatter();
64+
formatter.Serialize(serializationStream, exception);
65+
66+
using var deserializationStream = new MemoryStream(serializationStream.ToArray());
67+
HostAbortedException deserializedException = (HostAbortedException)formatter.Deserialize(deserializationStream);
68+
69+
Assert.Equal(exception.Message, deserializedException.Message);
70+
Assert.Null(deserializedException.InnerException);
71+
}
72+
}
73+
}

0 commit comments

Comments
 (0)