Skip to content
This repository has been archived by the owner on Feb 15, 2023. It is now read-only.

Commit

Permalink
Updating to 8.0; adding more tests and docs
Browse files Browse the repository at this point in the history
  • Loading branch information
jbogard committed Dec 30, 2019
1 parent 4bb410a commit 5c4bf2f
Show file tree
Hide file tree
Showing 4 changed files with 130 additions and 8 deletions.
22 changes: 20 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,30 @@ or with an assembly:
services.AddMediatR(typeof(Startup).GetTypeInfo().Assembly);
```

Supports generic variance of handlers.
This registers:

- `IMediator` as transient
- `IRequestHandler<>` concrete implementations as transient
- `INotificationHandler<>` concrete implementations as transient
- `IRequestPreProcessor<>` concrete implementations as transient
- `IRequestHandler<>` concrete implementations as transient
- `IRequestPostProcessor<,>` concrete implementations as transient
- `IRequestExceptionHandler<,,>` concrete implementations as transient

This also registers open generic implementations for:

- `INotificationHandler<>`
- `IRequestPreProcessor<>`
- `IRequestHandler<>`
- `IRequestPostProcessor<,>`
- `IRequestExceptionHandler<,,>`

Keep in mind that the built-in container does not support constrained open generics. If you want this behavior, you will need to add any one of the conforming containers.

To customize registration, such as lifecycle or the registration type:

```c#
services.AddMediatR(cfg => cfg.Using<MyCustomMediator>().AsSingleton(), typeof(Startup));
```

To register behaviors, pre- or post-processors, register them individually before or after calling `AddMediatR`.
To register behaviors, register them individually before or after calling `AddMediatR`.
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,15 @@ public static void AddMediatRClasses(IServiceCollection services, IEnumerable<As
ConnectImplementationsToTypesClosing(typeof(IRequestPreProcessor<>), services, assembliesToScan, true);
ConnectImplementationsToTypesClosing(typeof(IRequestPostProcessor<,>), services, assembliesToScan, true);
ConnectImplementationsToTypesClosing(typeof(IRequestExceptionHandler<,,>), services, assembliesToScan, true);
ConnectImplementationsToTypesClosing(typeof(IRequestExceptionAction<,>), services, assembliesToScan, true);

var multiOpenInterfaces = new[]
{
typeof(INotificationHandler<>),
typeof(IRequestPreProcessor<>),
typeof(IRequestPostProcessor<,>)
typeof(IRequestPostProcessor<,>),
typeof(IRequestExceptionHandler<,,>),
typeof(IRequestExceptionAction<,>)
};

foreach (var multiOpenInterface in multiOpenInterfaces)
Expand Down Expand Up @@ -215,6 +218,7 @@ public static void AddRequiredServices(IServiceCollection services, MediatRServi
services.AddTransient<ServiceFactory>(p => p.GetService);
services.AddTransient(typeof(IPipelineBehavior<,>), typeof(RequestPreProcessorBehavior<,>));
services.AddTransient(typeof(IPipelineBehavior<,>), typeof(RequestPostProcessorBehavior<,>));
services.AddTransient(typeof(IPipelineBehavior<,>), typeof(RequestExceptionActionProcessorBehavior<,>));
services.AddTransient(typeof(IPipelineBehavior<,>), typeof(RequestExceptionProcessorBehavior<,>));
services.Add(new ServiceDescriptor(typeof(IMediator), serviceConfiguration.MediatorImplementationType, serviceConfiguration.Lifetime));
}
Expand Down
23 changes: 23 additions & 0 deletions src/TestApp/PingPongExceptionHandlers.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using MediatR.Pipeline;
Expand All @@ -14,4 +15,26 @@ public Task Handle(Ping request, ApplicationException exception, RequestExceptio
return Task.CompletedTask;
}
}

public class PingPongExceptionActionForType1 : IRequestExceptionAction<Ping, ApplicationException>
{
private readonly TextWriter _output;

public PingPongExceptionActionForType1(TextWriter output) => _output = output;

public Task Execute(Ping request, ApplicationException exception, CancellationToken cancellationToken)
=> _output.WriteLineAsync("Logging exception 1");
}

public class PingPongExceptionActionForType2 : IRequestExceptionAction<Ping, ApplicationException>
{
private readonly TextWriter _output;

public PingPongExceptionActionForType2(TextWriter output) => _output = output;

public Task Execute(Ping request, ApplicationException exception, CancellationToken cancellationToken)
=> _output.WriteLineAsync("Logging exception 2");
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,62 @@ public Task Process(Ping request, Pong response, CancellationToken cancellationT
}
}

public class PingPongGenericExceptionAction : IRequestExceptionAction<Ping>
{
private readonly Logger _output;

public PingPongGenericExceptionAction(Logger output) => _output = output;

public Task Execute(Ping request, Exception exception, CancellationToken cancellationToken)
{
_output.Messages.Add("Logging generic exception");

return Task.CompletedTask;
}
}

public class PingPongApplicationExceptionAction : IRequestExceptionAction<Ping, ApplicationException>
{
private readonly Logger _output;

public PingPongApplicationExceptionAction(Logger output) => _output = output;

public Task Execute(Ping request, ApplicationException exception, CancellationToken cancellationToken)
{
_output.Messages.Add("Logging ApplicationException exception");

return Task.CompletedTask;
}
}

public class PingPongExceptionActionForType1 : IRequestExceptionAction<Ping, SystemException>
{
private readonly Logger _output;

public PingPongExceptionActionForType1(Logger output) => _output = output;

public Task Execute(Ping request, SystemException exception, CancellationToken cancellationToken)
{
_output.Messages.Add("Logging exception 1");

return Task.CompletedTask;
}
}

public class PingPongExceptionActionForType2 : IRequestExceptionAction<Ping, SystemException>
{
private readonly Logger _output;

public PingPongExceptionActionForType2(Logger output) => _output = output;

public Task Execute(Ping request, SystemException exception, CancellationToken cancellationToken)
{
_output.Messages.Add("Logging exception 2");

return Task.CompletedTask;
}
}

public class PingPongExceptionHandlerForType : IRequestExceptionHandler<Ping, Pong, ApplicationException>
{
public Task Handle(Ping request, ApplicationException exception, RequestExceptionHandlerState<Pong> state, CancellationToken cancellationToken)
Expand All @@ -243,9 +299,13 @@ public Task Handle(Ping request, ApplicationException exception, RequestExceptio

public class PingPongGenericExceptionHandler : IRequestExceptionHandler<Ping, Pong>
{
private readonly Logger _output;

public PingPongGenericExceptionHandler(Logger output) => _output = output;

public Task Handle(Ping request, Exception exception, RequestExceptionHandlerState<Pong> state, CancellationToken cancellationToken)
{
state.SetHandled(new Pong { Message = exception.Message + " Handled by Generic Type" });
_output.Messages.Add(exception.Message + " Logged by Generic Type");

return Task.CompletedTask;
}
Expand Down Expand Up @@ -365,11 +425,11 @@ public async Task Should_pick_up_specific_exception_behaviors()
var response = await mediator.Send(new Ping {Message = "Ping", ThrowAction = msg => throw new ApplicationException(msg.Message + " Thrown")});

response.Message.ShouldBe("Ping Thrown Handled by Specific Type");

output.Messages.ShouldNotContain("Logging ApplicationException exception");
}

[Fact]
public async Task Should_pick_up_base_exception_behaviors()
public void Should_pick_up_base_exception_behaviors()
{
var output = new Logger();
IServiceCollection services = new ServiceCollection();
Expand All @@ -379,10 +439,27 @@ public async Task Should_pick_up_base_exception_behaviors()

var mediator = provider.GetService<IMediator>();

var response = await mediator.Send(new Ping {Message = "Ping", ThrowAction = msg => throw new Exception(msg.Message + " Thrown")});
Should.Throw<Exception>(async () => await mediator.Send(new Ping {Message = "Ping", ThrowAction = msg => throw new Exception(msg.Message + " Thrown")}));

output.Messages.ShouldContain("Ping Thrown Logged by Generic Type");
output.Messages.ShouldContain("Logging generic exception");
}

[Fact]
public void Should_pick_up_exception_actions()
{
var output = new Logger();
IServiceCollection services = new ServiceCollection();
services.AddSingleton(output);
services.AddMediatR(typeof(Ping).GetTypeInfo().Assembly);
var provider = services.BuildServiceProvider();

var mediator = provider.GetService<IMediator>();

response.Message.ShouldBe("Ping Thrown Handled by Generic Type");
Should.Throw<SystemException>(async () => await mediator.Send(new Ping {Message = "Ping", ThrowAction = msg => throw new SystemException(msg.Message + " Thrown")}));

output.Messages.ShouldContain("Logging exception 1");
output.Messages.ShouldContain("Logging exception 2");
}

[Fact(Skip = "MS DI does not support constrained generics yet, see https://github.com/aspnet/DependencyInjection/issues/471")]
Expand Down

0 comments on commit 5c4bf2f

Please sign in to comment.