Skip to content

Commit c43786b

Browse files
Merge pull request with .NET 9 changes
.NET 9 v2.0.0 - Does not target anything before .NET 9. Use 1.4.6 for legacy frameworks.
2 parents cf9d281 + eea09f6 commit c43786b

File tree

11 files changed

+168
-81
lines changed

11 files changed

+168
-81
lines changed

.github/workflows/main.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,12 @@ jobs:
1515
NUGET_SOURCE: https://api.nuget.org/v3/index.json
1616

1717
steps:
18-
- uses: actions/checkout@v3
18+
- uses: actions/checkout@v4
1919

2020
- name: Setup .NET
21-
uses: actions/setup-dotnet@v2
21+
uses: actions/setup-dotnet@v4
2222
with:
23-
dotnet-version: 6.0.x
23+
dotnet-version: 9.x
2424

2525
- name: Restore dependencies
2626
run: dotnet restore ./src/OneBitSoftware.Utilities.OperationResult.sln

.github/workflows/pull-request-validation.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,12 @@ jobs:
1515
NUGET_SOURCE: https://api.nuget.org/v3/index.json
1616

1717
steps:
18-
- uses: actions/checkout@v3
18+
- uses: actions/checkout@v4
1919

2020
- name: Setup .NET
21-
uses: actions/setup-dotnet@v2
21+
uses: actions/setup-dotnet@v4
2222
with:
23-
dotnet-version: 6.0.x
23+
dotnet-version: 9.x
2424

2525
- name: Restore dependencies
2626
run: dotnet restore ./src/OneBitSoftware.Utilities.OperationResult.sln

src/OneBitSoftware.Utilities.OperationResult.sln

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,14 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OneBitSoftware.Utilities.Op
77
EndProject
88
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OneBitSoftware.Utilities.OperationResultTests", "..\tests\OneBitSoftware.Utilities.OperationResultTests\OneBitSoftware.Utilities.OperationResultTests.csproj", "{142313C6-5DC0-4428-AE63-487B8D41552E}"
99
EndProject
10+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{02EA681E-C7D8-13C7-8484-4AC65E1B71E8}"
11+
EndProject
12+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{6F29D051-AD77-482A-99A7-4E5ED288AB22}"
13+
ProjectSection(SolutionItems) = preProject
14+
..\.github\workflows\main.yml = ..\.github\workflows\main.yml
15+
..\.github\workflows\pull-request-validation.yml = ..\.github\workflows\pull-request-validation.yml
16+
EndProjectSection
17+
EndProject
1018
Global
1119
GlobalSection(SolutionConfigurationPlatforms) = preSolution
1220
Debug|Any CPU = Debug|Any CPU
@@ -25,6 +33,9 @@ Global
2533
GlobalSection(SolutionProperties) = preSolution
2634
HideSolutionNode = FALSE
2735
EndGlobalSection
36+
GlobalSection(NestedProjects) = preSolution
37+
{6F29D051-AD77-482A-99A7-4E5ED288AB22} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
38+
EndGlobalSection
2839
GlobalSection(ExtensibilityGlobals) = postSolution
2940
SolutionGuid = {3A384A11-1CD9-4A02-A6BB-3EC782DBF254}
3041
EndGlobalSection

src/OneBitSoftware.Utilities.OperationResult/Errors/IOperationError.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
namespace OneBitSoftware.Utilities.Errors
22
{
3+
using Microsoft.Extensions.Logging;
4+
35
public interface IOperationError
46
{
57
int? Code { get; set; }

src/OneBitSoftware.Utilities.OperationResult/Errors/OperationError.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
namespace OneBitSoftware.Utilities.Errors
22
{
33
using System.Text;
4+
using Microsoft.Extensions.Logging;
45

56
public class OperationError : IOperationError
67
{

src/OneBitSoftware.Utilities.OperationResult/OneBitSoftware.Utilities.OperationResult.csproj

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

33
<PropertyGroup>
4-
<TargetFrameworks>net6.0;net5.0</TargetFrameworks>
4+
<TargetFrameworks>net9.0</TargetFrameworks>
55
<ImplicitUsings>disable</ImplicitUsings>
66
<Nullable>enable</Nullable>
77
</PropertyGroup>
@@ -14,7 +14,7 @@
1414
</ItemGroup>
1515

1616
<ItemGroup>
17-
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="6.0.3" />
17+
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.2" />
1818
</ItemGroup>
1919

2020
<PropertyGroup>
@@ -32,7 +32,7 @@
3232
<PackageRequireLicenseAcceptance>False</PackageRequireLicenseAcceptance>
3333
<PackageReadmeFile>README.md</PackageReadmeFile>
3434
<PackageTags>OneBitSoftware; OperationResult;</PackageTags>
35-
<Version>1.4.6</Version>
35+
<Version>2.0.0</Version>
3636
</PropertyGroup>
3737

3838
</Project>

src/OneBitSoftware.Utilities.OperationResult/OperationResult.cs

Lines changed: 43 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,14 @@
1313
/// </summary>
1414
public class OperationResult
1515
{
16-
private readonly List<string> _successMessages = new List<string>();
16+
/// <summary>
17+
/// Contains <see cref="IOperationError"/> instances that have not been logged.
18+
/// </summary>
19+
private readonly List<(IOperationError Error, LogLevel? LogLevel)> _errorsNotLogged = new();
1720

18-
protected readonly ILogger? _logger;
21+
private readonly List<string> _successMessages = new();
22+
23+
private readonly ILogger? _logger;
1924

2025
/// <summary>
2126
/// Gets or sets a value indicating whether the operation is successful or not.
@@ -44,7 +49,7 @@ public IEnumerable<string>? SuccessMessages
4449
/// Gets an <see cref="List{T}"/> containing the error codes and messages of the <see cref="OperationResult{T}" />.
4550
/// </summary>
4651
public List<IOperationError> Errors { get; internal set; } = new List<IOperationError>();
47-
52+
4853
/// <summary>
4954
/// Gets or sets the first exception that resulted from the operation.
5055
/// </summary>
@@ -93,6 +98,19 @@ public OperationResult AppendErrors(OperationResult otherOperationResult)
9398
{
9499
if (otherOperationResult is null) return this;
95100

101+
// store any messages for logging at a later stage, when merged to an OperationResult with a logger.
102+
if (this._logger is null)
103+
{
104+
this._errorsNotLogged.AddRange(otherOperationResult._errorsNotLogged);
105+
}
106+
else
107+
{
108+
foreach (var (error, logLevel) in otherOperationResult._errorsNotLogged)
109+
this.LogInternal(error, logLevel);
110+
111+
otherOperationResult._errorsNotLogged.Clear();
112+
}
113+
96114
// Append the error message without logging (presuming that there is already a log message).
97115
foreach (var error in otherOperationResult.Errors) this.AppendErrorInternal(error);
98116

@@ -146,25 +164,19 @@ public OperationResult AppendError<T>(string message, int? code = null, LogLevel
146164
public OperationResult AppendError(IOperationError error, LogLevel? logLevel = LogLevel.Error)
147165
{
148166
this.AppendErrorInternal(error);
149-
150-
if (this._logger != null)
151-
{
152-
#pragma warning disable CA2254 // Template should be a static expression
153-
this._logger.Log(GetLogLevel(logLevel), error.Message);
154-
#pragma warning restore CA2254 // Template should be a static expression
155-
}
167+
this.LogInternal(error, logLevel);
156168

157169
return this;
158170
}
159171

160172
/// <summary>
161-
/// Appends an exception to the error message collection and logs the full exception as an Error <see cref="LogEventLevel"/> level. A call to this method will set the Success property to false.
173+
/// Appends an exception to the error message collection and logs the full exception as an Error <see cref="LogLevel"/> level. A call to this method will set the Success property to false.
162174
/// </summary>
163175
/// <param name="exception">The exception to log.</param>
164176
/// <param name="errorCode">The error code.</param>
165-
/// <param name="logLevel">The <see cref="LogEventLevel"/> logging severity.</param>
177+
/// <param name="logLevel">The <see cref="LogLevel"/> logging severity.</param>
166178
/// <returns>The current instance of the <see cref="OperationResult"/>.</returns>
167-
public OperationResult AppendException(Exception exception, int? errorCode = null, LogLevel? logLevel = null)
179+
public OperationResult AppendException(Exception exception, int errorCode = 0, LogLevel? logLevel = null)
168180
{
169181
if (exception is null) throw new ArgumentNullException(nameof(exception));
170182

@@ -210,14 +222,28 @@ public static OperationResult FromError(string message, int? code = null, LogLev
210222
return result.AppendError(message, code, logLevel, details);
211223
}
212224

213-
// TODO: this method needs completing.
214-
protected static LogLevel GetLogLevel(LogLevel? optionalLevel) => optionalLevel ?? LogLevel.Error;
215-
216225
/// <summary>
217226
/// Appends an <see cref="IOperationError"/> to the internal errors collection.
218227
/// </summary>
219228
/// <param name="error">An instance of <see cref="IOperationError"/> to add to the internal errors collection.</param>
220229
protected void AppendErrorInternal(IOperationError error) => this.Errors.Add(error);
230+
231+
/// <summary>
232+
/// Logs to the internal logger if it is set, otherwise it will add the error to the internal errors collection.
233+
/// </summary>
234+
/// <param name="error">The <see cref="IOperationError"/> to log.</param>
235+
/// <param name="logLevel">The log level.</param>
236+
private void LogInternal(IOperationError error, LogLevel? logLevel)
237+
{
238+
if (this._logger is null)
239+
{
240+
this._errorsNotLogged.Add((Error: error, LogLevel: logLevel));
241+
}
242+
else
243+
{
244+
this._logger.Log(logLevel ?? LogLevel.Error, error.Message);
245+
}
246+
}
221247
}
222248

223249
/// <summary>
@@ -284,61 +310,14 @@ public OperationResult(TResult resultObject) : base()
284310
return this;
285311
}
286312

287-
/// <summary>
288-
/// Appends an <see cref="IOperationError"/> to the internal errors collection.
289-
/// </summary>
290-
/// <param name="error">An instance of <see cref="IOperationError"/> to add to the internal errors collection.</param>
291-
/// <param name="logLevel">The logging level.</param>
292-
/// <returns>The current instance of the <see cref="OperationResult"/>.</returns>
293-
public new OperationResult<TResult> AppendError(IOperationError error, LogLevel? logLevel = LogLevel.Error)
294-
{
295-
base.AppendErrorInternal(error);
296-
297-
if (this._logger != null)
298-
{
299-
#pragma warning disable CA2254 // Template should be a static expression
300-
this._logger.Log(GetLogLevel(logLevel), error.Message);
301-
#pragma warning restore CA2254 // Template should be a static expression
302-
}
303-
304-
return this;
305-
}
306-
307-
/// <summary>
308-
/// Appends error messages from <paramref name="otherOperationResult"/> to the current instance.
309-
/// </summary>
310-
/// <param name="otherOperationResult">The <see cref="OperationResult"/> to append from.</param>
311-
/// <typeparam name="TOther">A type that inherits from <see cref="OperationResult"/>.</typeparam>
312-
/// <returns>The original <see cref="OperationResult"/> with the appended messages from <paramref name="otherOperationResult"/>.</returns>
313-
[Obsolete("Please use AppendErrors instead. This method will be removed to avoid confusion.")]
314-
public OperationResult<TResult> AppendErrorMessages<TOther>(TOther otherOperationResult)
315-
where TOther : OperationResult
316-
{
317-
base.AppendErrors(otherOperationResult);
318-
319-
return this;
320-
}
321-
322-
/// <summary>
323-
/// Appends error from <paramref name="otherOperationResult"/> to the current instance.
324-
/// </summary>
325-
/// <param name="otherOperationResult">The <see cref="OperationResult"/> to append from.</param>
326-
/// <returns>The original <see cref="OperationResult"/> with the appended messages from <paramref name="otherOperationResult"/>.</returns>
327-
public new OperationResult<TResult> AppendErrors(OperationResult otherOperationResult)
328-
{
329-
base.AppendErrors(otherOperationResult);
330-
331-
return this;
332-
}
333-
334313
/// <summary>
335314
/// Appends an exception to the error message collection and logs the full exception as an Error <see cref="LogEventLevel"/> level. A call to this method will set the Success property to false.
336315
/// </summary>
337316
/// <param name="exception">The exception to log.</param>
338317
/// <param name="errorCode">The error code.</param>
339318
/// <param name="logLevel">The <see cref="LogEventLevel"/> logging severity.</param>
340319
/// <returns>The current instance of the <see cref="OperationResult{TResult}"/>.</returns>
341-
public new OperationResult<TResult> AppendException(Exception exception, int? errorCode = null, LogLevel? logLevel = null)
320+
public new OperationResult<TResult> AppendException(Exception exception, int errorCode = 0, LogLevel? logLevel = null)
342321
{
343322
base.AppendException(exception, errorCode, logLevel);
344323

src/OneBitSoftware.Utilities.OperationResult/PolymorphicOperationErrorSerializer.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions
5454
if (this._typeMappings.TryGetValue(value.GetType(), out var typeValue) == false) throw new InvalidOperationException($"Model of type {value.GetType()} cannot be successfully serialized.");
5555

5656
var tempBufferWriter = new ArrayBufferWriter<byte>();
57-
var tempWriter = new Utf8JsonWriter(tempBufferWriter);
57+
var tempWriter = new Utf8JsonWriter(tempBufferWriter); // TODO: dispose with using var
5858

5959
var fallbackDeserializationOptions = this.ConstructSafeFallbackOptions(options);
6060
JsonSerializer.Serialize(tempWriter, value, value.GetType(), fallbackDeserializationOptions);

tests/OneBitSoftware.Utilities.OperationResultTests/OneBitSoftware.Utilities.OperationResultTests.csproj

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

33
<PropertyGroup>
4-
<TargetFramework>net6.0</TargetFramework>
4+
<TargetFramework>net9.0</TargetFramework>
55
<ImplicitUsings>enable</ImplicitUsings>
66
<Nullable>enable</Nullable>
77
</PropertyGroup>
88

99
<ItemGroup>
10-
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="7.0.0" />
11-
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.0" />
12-
<PackageReference Include="Newtonsoft.Json" Version="13.0.2" />
13-
<PackageReference Include="xunit" Version="2.4.2" />
14-
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
10+
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="9.0.2" />
11+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.13.0" />
12+
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
13+
<PackageReference Include="xunit" Version="2.9.3" />
14+
<PackageReference Include="xunit.runner.visualstudio" Version="3.0.2">
1515
<PrivateAssets>all</PrivateAssets>
1616
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
1717
</PackageReference>

tests/OneBitSoftware.Utilities.OperationResultTests/OperationResultAppendErrorsTests.cs

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ public void AppendErrorsT_ShouldListAllErrors()
6262
operationResultBase.AppendError(message2, errorCode2, LogLevel.Debug, detail2);
6363

6464
// Act - AppendErrorMessages is to be removed
65-
operationResultBase.AppendErrorMessages(operationResultTarget);
65+
operationResultBase.AppendErrors(operationResultTarget);
6666

6767
// Assert
6868
Assert.False(operationResultBase.Success);
@@ -111,4 +111,70 @@ public void AppendErrorsStringInt_ShouldListAllErrors()
111111
Assert.NotNull(operationResultBase.Errors.Single(r => r.Message.Equals(message2)));
112112
Assert.NotNull(operationResultBase.Errors.Single(r => r.Details is not null && r.Details.Equals(detail2)));
113113
}
114+
115+
[Fact]
116+
public void AppendErrors_ShouldLogWhenCreatedWithALogger()
117+
{
118+
// Arrange
119+
var testLogger = new TestLogger();
120+
var operationResultNoLogger = new OperationResult();
121+
var operationResultWithLogger = new OperationResult(testLogger);
122+
123+
// Act
124+
operationResultNoLogger.AppendError("test");
125+
operationResultWithLogger.AppendErrors(operationResultNoLogger);
126+
127+
// Assert
128+
Assert.Equal(1, testLogger.LogMessages.Count);
129+
}
130+
131+
[Fact]
132+
public void AppendErrors_ShouldLogOnceWhenCreatedWithALogger()
133+
{
134+
// Arrange
135+
var testLogger = new TestLogger();
136+
var operationResultWithLogger = new OperationResult(testLogger);
137+
var operationResultWithLogger2 = new OperationResult(testLogger);
138+
139+
// Act
140+
operationResultWithLogger2.AppendError("test");
141+
operationResultWithLogger.AppendErrors(operationResultWithLogger2);
142+
143+
// Assert
144+
Assert.Equal(1, testLogger.LogMessages.Count);
145+
}
146+
147+
[Fact]
148+
public void AppendErrors_ShouldLogOnceWhenNestingWithALogger()
149+
{
150+
// Arrange
151+
var testLogger = new TestLogger();
152+
var operationResultWithLogger = new OperationResult(testLogger);
153+
var operationResultWithLogger2 = new OperationResult(testLogger);
154+
var operationResultWithLogger3 = new OperationResult(testLogger);
155+
156+
// Act
157+
operationResultWithLogger3.AppendError("test1");
158+
operationResultWithLogger2.AppendError("test2");
159+
operationResultWithLogger.AppendErrors(operationResultWithLogger2);
160+
161+
// Assert
162+
Assert.Equal(2, testLogger.LogMessages.Count);
163+
}
164+
165+
[Fact]
166+
public void AppendErrors_ShouldLogWhenCreatedWithNoLogger()
167+
{
168+
// Arrange
169+
var testLogger = new TestLogger();
170+
var operationResultNoLogger = new OperationResult();
171+
var operationResultWithLogger = new OperationResult(testLogger);
172+
173+
// Act
174+
operationResultWithLogger.AppendError("test");
175+
operationResultNoLogger.AppendErrors(operationResultNoLogger);
176+
177+
// Assert
178+
Assert.Equal(1, testLogger.LogMessages.Count);
179+
}
114180
}

0 commit comments

Comments
 (0)