-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Adding support for Order as an integer property.
Should resolve #987. Signed off by: Bryan Boettcher <bryan.boettcher@gmail.com>
- Loading branch information
1 parent
6baaeef
commit 9167b53
Showing
11 changed files
with
368 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
using System; | ||
using System.Collections.Concurrent; | ||
using System.Reflection; | ||
|
||
namespace MediatR.Internal; | ||
|
||
/// <summary> | ||
/// Contains utility methods for working with implementations of request | ||
/// handlers. | ||
/// </summary> | ||
public static class HandlerExtensions | ||
{ | ||
private static readonly ConcurrentDictionary<Type, PropertyInfo?> PropertyCache = new(); | ||
|
||
/// <summary> | ||
/// Inspects the <paramref name="value"/> parameter for a public Order property | ||
/// that is gettable and an integer. If found, it uses the value of this property, | ||
/// otherwise returning null. | ||
/// </summary> | ||
/// <param name="value">The value to inspect.</param> | ||
/// <returns>The value of the "Order" property or null if not found.</returns> | ||
public static int? GetOrderIfExists(this object value) | ||
{ | ||
var orderProperty = PropertyCache.GetOrAdd(value.GetType(), valueType => | ||
{ | ||
var orderProperty = valueType.GetProperty("Order"); | ||
return orderProperty ?? null; | ||
}); | ||
|
||
var orderValue = orderProperty?.GetValue(value); | ||
if (orderValue is not int i) return null; | ||
|
||
return i; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
namespace MediatR.Pipeline; | ||
|
||
/// <summary> | ||
/// Optional interface that can be used to decorate any | ||
/// existing handlers to add an Order property | ||
/// </summary> | ||
public interface IOrderableHandler | ||
{ | ||
/// <summary> | ||
/// Defines the order this handler should execute relative to others. Smaller numbers | ||
/// execute sooner, and negative numbers are supported. | ||
/// </summary> | ||
int Order { get; } | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
86 changes: 86 additions & 0 deletions
86
test/MediatR.Tests/Pipeline/Ordering/OrderedExceptionTests.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Text; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
using Lamar; | ||
using MediatR.Internal; | ||
using MediatR.Pipeline; | ||
using Shouldly; | ||
using Xunit; | ||
|
||
namespace MediatR.Tests.Pipeline.Ordering; | ||
public class OrderedExceptionTests | ||
{ | ||
public class MyRequest : IRequest<MyResponse> | ||
{ | ||
public string Message { get; set; } = "initial"; | ||
} | ||
|
||
public class MyResponse | ||
{ | ||
public string? Message { get; set; } | ||
} | ||
|
||
public class DefaultHandler : IRequestHandler<MyRequest, MyResponse> | ||
{ | ||
public Task<MyResponse> Handle(MyRequest request, CancellationToken cancellationToken) => | ||
throw new InvalidOperationException("this should break"); | ||
} | ||
|
||
public class DefaultExceptionHandler : IRequestExceptionHandler<MyRequest, MyResponse, InvalidOperationException> | ||
{ | ||
public Task Handle(MyRequest request, InvalidOperationException exception, RequestExceptionHandlerState<MyResponse> state, | ||
CancellationToken cancellationToken) | ||
{ | ||
state.SetHandled(new MyResponse { Message = "default-exception" }); | ||
return Task.CompletedTask; | ||
} | ||
} | ||
|
||
public class FirstExceptionHandler : IRequestExceptionHandler<MyRequest, MyResponse, InvalidOperationException> | ||
{ | ||
public int Order => -100; | ||
|
||
public Task Handle(MyRequest request, InvalidOperationException exception, RequestExceptionHandlerState<MyResponse> state, | ||
CancellationToken cancellationToken) | ||
{ | ||
state.SetHandled(new MyResponse { Message = "first-exception" }); | ||
return Task.CompletedTask; | ||
} | ||
} | ||
|
||
|
||
[Fact] | ||
public async Task Should_run_exceptionprocessors() | ||
{ | ||
var container = new Container(cfg => | ||
{ | ||
cfg.Scan(scanner => | ||
{ | ||
scanner.AssemblyContainingType(typeof(DefaultHandler)); | ||
scanner.IncludeNamespaceContainingType<MyRequest>(); | ||
scanner.WithDefaultConventions(); | ||
scanner.AddAllTypesOf(typeof(IRequestHandler<,>)); | ||
scanner.AddAllTypesOf(typeof(IRequestExceptionHandler<,,>)); | ||
}); | ||
cfg.For(typeof(IPipelineBehavior<,>)).Add(typeof(RequestExceptionProcessorBehavior<,>)); | ||
cfg.For<IMediator>().Use<Mediator>(); | ||
}); | ||
|
||
var mediator = container.GetInstance<IMediator>(); | ||
|
||
var response = await mediator.Send(new MyRequest()); | ||
|
||
// the exception handler just resets the message, so this should be the correct one | ||
response.Message.ShouldBe("first-exception"); | ||
} | ||
|
||
[Fact] | ||
public void Order_is_discoverable() | ||
{ | ||
(new DefaultExceptionHandler()).GetOrderIfExists().GetValueOrDefault(0).ShouldBe(0); | ||
(new FirstExceptionHandler()).GetOrderIfExists().GetValueOrDefault(0).ShouldBe(-100); | ||
} | ||
} |
Oops, something went wrong.