Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ indent_style = space
tab_width = 4

# New line preferences
end_of_line = crlf
end_of_line = lf
insert_final_newline = true

#### .NET Coding Conventions ####
Expand Down
11 changes: 11 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
* text=auto eol=lf
*.cs text eol=lf
*.csproj text eol=lf
*.props text eol=lf
*.targets text eol=lf
*.yml text eol=lf
*.yaml text eol=lf
*.json text eol=lf
*.md text eol=lf
*.sh text eol=lf
*.editorconfig text eol=lf
3 changes: 2 additions & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ jobs:
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: '9.0.x'
dotnet-version: '11.0.x'
include-prerelease: true

- name: Restore
run: dotnet restore
Expand Down
3 changes: 2 additions & 1 deletion .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ jobs:
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: '9.0.x'
dotnet-version: '11.0.x'
include-prerelease: true

- name: Verify version alignment
run: |
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -761,4 +761,5 @@ $RECYCLE.BIN/
*.db-shm
*.db-wal

plans/

17 changes: 6 additions & 11 deletions CSharpEssentials.Any/Any.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,17 +51,12 @@ public static class Any

internal static (int index, object? value) Deserialize(Dictionary<int, Type> typeMap, int index, object? value)
{
#if NET6_0_OR_GREATER

ArgumentNullException.ThrowIfNull(value);

#else

if (value is null)

throw new ArgumentNullException(nameof(value));

#endif
#if NET6_0_OR_GREATER
ArgumentNullException.ThrowIfNull(value);
#else
if (value is null)
throw new ArgumentNullException(nameof(value));
#endif

if (!typeMap.TryGetValue(index, out Type? type))
throw new InvalidOperationException($"{index} is not valid index for Any<>");
Expand Down
2 changes: 1 addition & 1 deletion CSharpEssentials.Any/AnyActionResult.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@ public readonly record struct AnyActionResult<TResult>(AnyActionStatus Status, T
{
public static implicit operator AnyActionResult<TResult>(TResult? result) => new(AnyActionStatus.Executed, result);
public static implicit operator AnyActionResult<TResult>(AnyActionStatus status) => new(status, default);
}
}
5 changes: 3 additions & 2 deletions CSharpEssentials.Any/AnyT2.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

using System.Text.Json.Serialization;
using CSharpEssentials.Json;

Expand Down Expand Up @@ -34,7 +35,7 @@ public AnyActionStatus Switch(
Action<T0>? first = null,
Action<T1>? second = null)
{
if (Value is null)
if (Value is null)
throw new InvalidOperationException("Value cannot be null");
switch (Index)
{
Expand All @@ -53,7 +54,7 @@ public AnyActionResult<TResult> Match<TResult>(
Func<T0, TResult>? first = null,
Func<T1, TResult>? second = null)
{
if (Value is null)
if (Value is null)
throw new InvalidOperationException("Value cannot be null");
return Index switch
{
Expand Down
5 changes: 3 additions & 2 deletions CSharpEssentials.Any/AnyT3.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

using System.Text.Json.Serialization;
using CSharpEssentials.Json;

Expand Down Expand Up @@ -41,7 +42,7 @@ public AnyActionStatus Switch(
Action<T1>? second = null,
Action<T2>? third = null)
{
if (Value is null)
if (Value is null)
throw new InvalidOperationException("Value cannot be null");
switch (Index)
{
Expand All @@ -64,7 +65,7 @@ public AnyActionResult<TResult> Match<TResult>(
Func<T1, TResult>? second = null,
Func<T2, TResult>? third = null)
{
if (Value is null)
if (Value is null)
throw new InvalidOperationException("Value cannot be null");
return Index switch
{
Expand Down
5 changes: 3 additions & 2 deletions CSharpEssentials.Any/AnyT4.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

using System.Text.Json.Serialization;
using CSharpEssentials.Json;

Expand Down Expand Up @@ -48,7 +49,7 @@ public AnyActionStatus Switch(
Action<T2>? third = null,
Action<T3>? fourth = null)
{
if (Value is null)
if (Value is null)
throw new InvalidOperationException("Value cannot be null");
switch (Index)
{
Expand Down Expand Up @@ -76,7 +77,7 @@ public AnyActionResult<TResult> Match<TResult>(
Func<T2, TResult>? third = null,
Func<T3, TResult>? fourth = null)
{
if (Value is null)
if (Value is null)
throw new InvalidOperationException("Value cannot be null");
return Index switch
{
Expand Down
5 changes: 3 additions & 2 deletions CSharpEssentials.Any/AnyT5.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

using System.Text.Json.Serialization;
using CSharpEssentials.Json;

Expand Down Expand Up @@ -55,7 +56,7 @@ public AnyActionStatus Switch(
Action<T3>? fourth = null,
Action<T4>? fifth = null)
{
if (Value is null)
if (Value is null)
throw new InvalidOperationException("Value cannot be null");
switch (Index)
{
Expand Down Expand Up @@ -86,7 +87,7 @@ public AnyActionResult<TResult> Match<TResult>(
Func<T3, TResult>? fourth = null,
Func<T4, TResult>? fifth = null)
{
if (Value is null)
if (Value is null)
throw new InvalidOperationException("Value cannot be null");
return Index switch
{
Expand Down
5 changes: 3 additions & 2 deletions CSharpEssentials.Any/AnyT6.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

using System.Text.Json.Serialization;
using CSharpEssentials.Json;

Expand Down Expand Up @@ -63,7 +64,7 @@ public AnyActionStatus Switch(
Action<T4>? fifth = null,
Action<T5>? sixth = null)
{
if (Value is null)
if (Value is null)
throw new InvalidOperationException("Value cannot be null");
switch (Index)
{
Expand Down Expand Up @@ -98,7 +99,7 @@ public AnyActionResult<TResult> Match<TResult>(
Func<T4, TResult>? fifth = null,
Func<T5, TResult>? sixth = null)
{
if (Value is null)
if (Value is null)
throw new InvalidOperationException("Value cannot be null");
return Index switch
{
Expand Down
5 changes: 3 additions & 2 deletions CSharpEssentials.Any/AnyT7.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

using System.Text.Json.Serialization;
using CSharpEssentials.Json;

Expand Down Expand Up @@ -70,7 +71,7 @@ public AnyActionStatus Switch(
Action<T5>? sixth = null,
Action<T6>? seventh = null)
{
if (Value is null)
if (Value is null)
throw new InvalidOperationException("Value cannot be null");
switch (Index)
{
Expand Down Expand Up @@ -109,7 +110,7 @@ public AnyActionResult<TResult> Match<TResult>(
Func<T5, TResult>? sixth = null,
Func<T6, TResult>? seventh = null)
{
if (Value is null)
if (Value is null)
throw new InvalidOperationException("Value cannot be null");
return Index switch
{
Expand Down
5 changes: 3 additions & 2 deletions CSharpEssentials.Any/AnyT8.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

using System.Text.Json.Serialization;
using CSharpEssentials.Json;

Expand Down Expand Up @@ -77,7 +78,7 @@ public AnyActionStatus Switch(
Action<T6>? seventh = null,
Action<T7>? eighth = null)
{
if (Value is null)
if (Value is null)
throw new InvalidOperationException("Value cannot be null");
switch (Index)
{
Expand Down Expand Up @@ -120,7 +121,7 @@ public AnyActionResult<TResult> Match<TResult>(
Func<T6, TResult>? seventh = null,
Func<T7, TResult>? eighth = null)
{
if (Value is null)
if (Value is null)
throw new InvalidOperationException("Value cannot be null");
return Index switch
{
Expand Down
2 changes: 1 addition & 1 deletion CSharpEssentials.Any/CSharpEssentials.Any.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<PropertyGroup>
<!-- Multi-targeting support -->
<TargetFrameworks>net9.0;netstandard2.1</TargetFrameworks>
<TargetFrameworks>net11.0;net10.0;net9.0;netstandard2.1</TargetFrameworks>

<!-- Package-specific metadata -->
<PackageId>CSharpEssentials.Any</PackageId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<PropertyGroup>
<!-- Multi-targeting support -->
<TargetFrameworks>net9.0;net8.0</TargetFrameworks>
<TargetFrameworks>net11.0;net10.0;net9.0;net8.0</TargetFrameworks>

<PackageId>CSharpEssentials.AspNetCore</PackageId>
<Title>CSharp Essentials AspNetCore</Title>
Expand Down Expand Up @@ -49,7 +49,7 @@
</ItemGroup>

<!-- Package versions vary by target framework -->
<ItemGroup Condition="'$(TargetFramework)' == 'net8.0' or '$(TargetFramework)' == 'net9.0'">
<ItemGroup Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'net8.0'))">
<PackageReference Include="Asp.Versioning.Http" />
<PackageReference Include="Asp.Versioning.Mvc" />
<PackageReference Include="Asp.Versioning.Mvc.ApiExplorer" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ public sealed class EnhancedProblemDetails : ProblemDetails
#endif

[JsonPropertyName("errorCodes")]
public HashSet<string> ErrorCodes { get; set; } = new HashSet<string>();
public HashSet<string> ErrorCodes { get; set; } = [];

[JsonPropertyName("errorMessages")]
public HashSet<string> ErrorMessages { get; set; } = new HashSet<string>();
public HashSet<string> ErrorMessages { get; set; } = [];
}
12 changes: 9 additions & 3 deletions CSharpEssentials.AspNetCore/ProblemDetails/Extensions.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
using CSharpEssentials.Core;
using CSharpEssentials.Errors;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using CSharpEssentials.Core;

namespace CSharpEssentials.AspNetCore;

Expand Down Expand Up @@ -93,8 +93,8 @@ public static IActionResult ToActionResult(this ResultPattern.Interfaces.IResult
if (result.IsSuccess)
throw new InvalidOperationException("Cannot convert a successful result to an action result");
return result.Errors.ToActionResult(httpContext, extensions, statusCode);

}

/// <summary>
/// Converts an <see cref="Error"/> to an <see cref="IActionResult"/>.
/// </summary>
Expand Down Expand Up @@ -181,6 +181,7 @@ public static EnhancedProblemDetails ToProblemDetails(this ResultPattern.Interfa
throw new InvalidOperationException("Cannot convert a successful result to a problem details");
return result.Errors.ToProblemDetails(extensions, statusCode);
}

/// <summary>
/// Converts an <see cref="Error"/> to a <see cref="ProblemDetails"/> object.
/// </summary>
Expand All @@ -202,12 +203,17 @@ public static EnhancedProblemDetails ToProblemDetails(this ResultPattern.Interfa
/// <returns></returns>
public static EnhancedProblemDetails ToProblemDetails(this Error[] errors, ErrorMetadata? extensions = null, int? statusCode = null)
{
ErrorMetadata metadata = extensions.IsNotNull() ? new ErrorMetadata(extensions) : ErrorMetadata.CreateEmpty();
ErrorMetadata metadata = [];
if (extensions.IsNotNull())
foreach (KeyValuePair<string, object?> item in extensions)
metadata[item.Key] = item.Value;
Error error = errors.MaxBy(e => e.Type.ToHttpStatusCode());
statusCode ??= error.Type.ToHttpStatusCode();

#pragma warning disable IDE0028
var errorCodes = errors.Select(e => e.Code).ToHashSet();
var errorMessages = errors.Select(e => e.Description).ToHashSet();
#pragma warning restore IDE0028

var problemDetails = new EnhancedProblemDetails
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,9 @@ public override void OnActionExecuting(ActionExecutingContext context)
{
if (context.ModelState.IsValid)
return;
Error[] errors = context.ModelState
Error[] errors = [.. context.ModelState
.Where(arg => arg.Value != null)
.SelectMany(state => state.Value!.Errors.Select(x => Error.Validation($"validation.{state.Key}", x.ErrorMessage)))
.ToArray();
.SelectMany(state => state.Value!.Errors.Select(x => Error.Validation($"validation.{state.Key}", x.ErrorMessage)))];
context.Result = errors.ToActionResult(context.HttpContext);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
using CSharpEssentials.Errors;

namespace CSharpEssentials.AspNetCore;

public interface IResultErrorMapper
{
Microsoft.AspNetCore.Http.IResult Map(Error[] errors);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@

using CSharpEssentials.Errors;
using CSharpEssentials.ResultPattern;
using Microsoft.AspNetCore.Http;

namespace CSharpEssentials.AspNetCore;

public sealed class ResultEndpointFilter(IResultErrorMapper? mapper = null) : IEndpointFilter
{
public async ValueTask<object?> InvokeAsync(EndpointFilterInvocationContext context, EndpointFilterDelegate next)
{
object? result = await next(context);
if (result is null)
return result;

Type resultType = result.GetType();
if (resultType.IsGenericType && resultType.GetGenericTypeDefinition() == typeof(Result<>))
{
bool isSuccess = GetPropertyValue<bool>(result, resultType, nameof(Result.IsSuccess));
Error[] errors = GetPropertyValue<Error[]>(result, resultType, nameof(Result.Errors));

if (isSuccess)
{
object? value = resultType.GetProperty("Value")?.GetValue(result);
return TypedResults.Ok(value);
}

return mapper?.Map(errors) ?? Results.BadRequest(errors);
}

if (result is CSharpEssentials.ResultPattern.Interfaces.IResult r)
{
return r.IsSuccess
? Results.Ok()
: mapper?.Map(r.Errors) ?? Results.BadRequest(r.Errors);
}

return result;
}

private static T GetPropertyValue<T>(object instance, Type type, string propertyName)
=> (T)type.GetProperty(propertyName)!.GetValue(instance)!;
}
Loading
Loading