├── src
├── tests
│ └── NimbleMediator.Tests
│ │ ├── GlobalUsings.cs
│ │ ├── Requests
│ │ ├── MyRequestWithoutResponse.cs
│ │ └── MyRequestWithResponse.cs
│ │ ├── Helpers.cs
│ │ ├── SenderTests.cs
│ │ ├── NimbleMediator.Tests.csproj
│ │ └── ServiceCollectionExtensionsTests.cs
└── NimbleMediator
│ ├── Contracts
│ ├── INotification.cs
│ ├── IMediator.cs
│ ├── INotificationPublisher.cs
│ ├── IRequest.cs
│ ├── INotificationHandler.cs
│ ├── IPublisher.cs
│ ├── IRequestHandler.cs
│ └── ISender.cs
│ ├── Implementations
│ ├── NotificationPublisherType.cs
│ ├── Mediator.cs
│ ├── Mediator.Publish.cs
│ ├── NotificationPublishers
│ │ ├── TaskWhenAllPublisher.cs
│ │ ├── ForeachAwaitStopOnFirstExceptionPublisher.cs
│ │ └── ForeachAwaitRobustPublisher.cs
│ └── Mediator.Send.cs
│ ├── ServiceExtensions
│ ├── ServiceExtensions.cs
│ └── NimbleMediatorConfig.cs
│ └── NimbleMediator.csproj
├── assets
├── logo.png
└── logo_128.png
├── benchmarks
├── send_benchmark.png
├── publish_foreachawait_benchmark.png
├── publish_taskwhenall_benchmark.png
└── MediatorsBenchmark
│ ├── Program.cs
│ ├── MediatorsBenchmark.csproj
│ ├── MediatorsBenchmark.sln
│ ├── Requests
│ ├── MediatR.cs
│ └── NimbleMediator.cs
│ ├── Notifications
│ ├── MediatR.cs
│ └── NimbleMediator.cs
│ ├── SendBenchmark.cs
│ ├── ForeachAwaitBenchmark.cs
│ └── TaskWhenAllBenchmark.cs
├── .cz.toml
├── .pre-commit-config.yaml
├── samples
├── NimbleMediator.Samples.ForeachAwaitRobustPublisher
│ ├── NimbleMediator.Samples.ForeachAwaitRobustPublisher.csproj
│ └── Program.cs
└── NimbleMediator.Samples.ForechAwaitStopOnFirstExceptionPublisher
│ ├── NimbleMediator.Samples.ForechAwaitStopOnFirstExceptionPublisher.csproj
│ └── Program.cs
├── CHANGELOG.md
├── CONTRIBUTING.md
├── NimbleMediator.sln
├── README.md
├── .gitignore
├── .editorconfig
└── LICENSE
/src/tests/NimbleMediator.Tests/GlobalUsings.cs:
--------------------------------------------------------------------------------
1 | global using Xunit;
--------------------------------------------------------------------------------
/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/baranacikgoz/NimbleMediator/HEAD/assets/logo.png
--------------------------------------------------------------------------------
/assets/logo_128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/baranacikgoz/NimbleMediator/HEAD/assets/logo_128.png
--------------------------------------------------------------------------------
/benchmarks/send_benchmark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/baranacikgoz/NimbleMediator/HEAD/benchmarks/send_benchmark.png
--------------------------------------------------------------------------------
/benchmarks/publish_foreachawait_benchmark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/baranacikgoz/NimbleMediator/HEAD/benchmarks/publish_foreachawait_benchmark.png
--------------------------------------------------------------------------------
/benchmarks/publish_taskwhenall_benchmark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/baranacikgoz/NimbleMediator/HEAD/benchmarks/publish_taskwhenall_benchmark.png
--------------------------------------------------------------------------------
/.cz.toml:
--------------------------------------------------------------------------------
1 | [tool.commitizen]
2 | name = "cz_conventional_commits"
3 | tag_format = "v$version"
4 | version_scheme = "semver"
5 | version = "1.2.0"
6 | update_changelog_on_bump = true
7 |
--------------------------------------------------------------------------------
/.pre-commit-config.yaml:
--------------------------------------------------------------------------------
1 | repos:
2 | - hooks:
3 | - id: commitizen
4 | - id: commitizen-branch
5 | stages:
6 | - push
7 | repo: https://github.com/commitizen-tools/commitizen
8 | rev: v3.12.0
9 |
--------------------------------------------------------------------------------
/src/NimbleMediator/Contracts/INotification.cs:
--------------------------------------------------------------------------------
1 | namespace NimbleMediator.Contracts;
2 |
3 | ///
4 | /// Marker interface for notifications.
5 | ///
6 | public interface INotification
7 | {
8 |
9 | }
10 |
--------------------------------------------------------------------------------
/benchmarks/MediatorsBenchmark/Program.cs:
--------------------------------------------------------------------------------
1 | using BenchmarkDotNet.Running;
2 | using MediatorsBenchmark;
3 |
4 | BenchmarkRunner.Run();
5 | BenchmarkRunner.Run();
6 | BenchmarkRunner.Run();
--------------------------------------------------------------------------------
/src/NimbleMediator/Contracts/IMediator.cs:
--------------------------------------------------------------------------------
1 | namespace NimbleMediator.Contracts;
2 |
3 | ///
4 | /// Defines a mediator with the ability to send requests and publish notifications.
5 | ///
6 | public interface IMediator : ISender, IPublisher
7 | {
8 |
9 | }
10 |
--------------------------------------------------------------------------------
/src/NimbleMediator/Contracts/INotificationPublisher.cs:
--------------------------------------------------------------------------------
1 | using NimbleMediator.Contracts;
2 |
3 | namespace NimbleMediator;
4 |
5 | public interface INotificationPublisher
6 | {
7 | Task PublishAsync(TNotification notification, IEnumerable> handlers, CancellationToken cancellationToken)
8 | where TNotification : INotification;
9 | }
--------------------------------------------------------------------------------
/src/NimbleMediator/Contracts/IRequest.cs:
--------------------------------------------------------------------------------
1 | namespace NimbleMediator.Contracts;
2 | #pragma warning disable S2326 // Unused type parameters should be removed
3 |
4 | ///
5 | /// Marker interface for requests with response.
6 | ///
7 | ///
8 |
9 | public interface IRequest
10 | {
11 | }
12 |
13 | ///
14 | /// Marker interface for requests without response.
15 | ///
16 | public interface IRequest
17 | {
18 | }
19 |
20 | #pragma warning restore S2326
--------------------------------------------------------------------------------
/src/NimbleMediator/Implementations/NotificationPublisherType.cs:
--------------------------------------------------------------------------------
1 | namespace NimbleMediator.Implementations;
2 |
3 | ///
4 | /// Defines the type of notification publisher.
5 | ///
6 | public enum NotificationPublisherType
7 | {
8 | ///
9 | /// Publishes notifications by sequentially awaiting with a loop.
10 | ///
11 | ForeachAwait,
12 |
13 | ///
14 | /// Publishes notifications by awaiting with Task.WhenAll().
15 | ///
16 | TaskWhenAll
17 | }
18 |
--------------------------------------------------------------------------------
/src/tests/NimbleMediator.Tests/Requests/MyRequestWithoutResponse.cs:
--------------------------------------------------------------------------------
1 | using NimbleMediator.Contracts;
2 |
3 | namespace NimbleMediator.Tests;
4 |
5 | public class MyRequestWithoutResponse : IRequest
6 | {
7 | public string Name { get; set; } = "Baran";
8 | }
9 |
10 | public class MyRequestWithoutResponseHandler : IRequestHandler
11 | {
12 | public ValueTask HandleAsync(MyRequestWithoutResponse request, CancellationToken cancellationToken)
13 | {
14 | return ValueTask.CompletedTask;
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/tests/NimbleMediator.Tests/Requests/MyRequestWithResponse.cs:
--------------------------------------------------------------------------------
1 | using NimbleMediator.Contracts;
2 |
3 | namespace NimbleMediator.Tests;
4 |
5 | public class MyRequestWithResponse : IRequest
6 | {
7 | public string Name { get; set; } = "Baran";
8 | }
9 |
10 | public class MyRequestWithResponseHandler : IRequestHandler
11 | {
12 | public ValueTask HandleAsync(MyRequestWithResponse request, CancellationToken cancellationToken)
13 | {
14 | return ValueTask.FromResult(request.Name);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/tests/NimbleMediator.Tests/Helpers.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.DependencyInjection;
2 | using NimbleMediator.ServiceExtensions;
3 |
4 | namespace NimbleMediator.Tests;
5 |
6 | public static class Helpers
7 | {
8 | public static ServiceProvider SetupServiceProvider()
9 | {
10 | var services = new ServiceCollection();
11 |
12 | services.AddNimbleMediator(config =>
13 | {
14 | config.RegisterServicesFromAssembly(typeof(MyRequestWithResponse).Assembly);
15 | });
16 |
17 | return services.BuildServiceProvider();
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/NimbleMediator/Contracts/INotificationHandler.cs:
--------------------------------------------------------------------------------
1 | namespace NimbleMediator.Contracts;
2 |
3 | ///
4 | /// Defines a notification handler.
5 | ///
6 | ///
7 | public interface INotificationHandler
8 | where TNotification : INotification
9 | {
10 | ///
11 | /// Handles the notification.
12 | ///
13 | ///
14 | ///
15 | ///
16 | Task HandleAsync(TNotification notification, CancellationToken cancellationToken);
17 | }
--------------------------------------------------------------------------------
/samples/NimbleMediator.Samples.ForeachAwaitRobustPublisher/NimbleMediator.Samples.ForeachAwaitRobustPublisher.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | net8.0
6 | enable
7 | enable
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/src/NimbleMediator/Contracts/IPublisher.cs:
--------------------------------------------------------------------------------
1 | using NimbleMediator.Contracts;
2 |
3 | namespace NimbleMediator;
4 |
5 | ///
6 | /// Defines an event publisher.
7 | ///
8 | public interface IPublisher
9 | {
10 | ///
11 | /// Publishes the notification.
12 | ///
13 | ///
14 | ///
15 | ///
16 | ///
17 | Task PublishAsync(TNotification notification, CancellationToken cancellationToken)
18 | where TNotification : INotification;
19 | }
20 |
--------------------------------------------------------------------------------
/src/NimbleMediator/ServiceExtensions/ServiceExtensions.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using Microsoft.Extensions.DependencyInjection;
3 | using NimbleMediator.Contracts;
4 | using NimbleMediator.Implementations;
5 |
6 | namespace NimbleMediator.ServiceExtensions;
7 |
8 | public static class ServiceCollectionExtensions
9 | {
10 | public static IServiceCollection AddNimbleMediator(this IServiceCollection services, Action configAction)
11 | {
12 | var config = new NimbleMediatorConfig(services);
13 |
14 | configAction(config);
15 |
16 | config.RegisterServicesInternal();
17 |
18 | return services;
19 | }
20 | }
--------------------------------------------------------------------------------
/samples/NimbleMediator.Samples.ForechAwaitStopOnFirstExceptionPublisher/NimbleMediator.Samples.ForechAwaitStopOnFirstExceptionPublisher.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | net8.0
6 | enable
7 | enable
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/src/NimbleMediator/Implementations/Mediator.cs:
--------------------------------------------------------------------------------
1 |
2 | using Microsoft.Extensions.DependencyInjection;
3 | using NimbleMediator.Contracts;
4 |
5 | namespace NimbleMediator.Implementations;
6 |
7 | ///
8 | /// Implementation of .
9 | ///
10 | public partial class Mediator : IMediator
11 | {
12 | private readonly IServiceProvider _serviceProvider;
13 | private readonly Dictionary _publisherTypeMappings;
14 |
15 | public Mediator(IServiceProvider serviceProvider, Dictionary publisherTypeMappings)
16 | {
17 | _serviceProvider = serviceProvider;
18 | _publisherTypeMappings = publisherTypeMappings;
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/benchmarks/MediatorsBenchmark/MediatorsBenchmark.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | Exe
15 | net7.0
16 | enable
17 | enable
18 |
19 |
20 |
--------------------------------------------------------------------------------
/src/tests/NimbleMediator.Tests/SenderTests.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.DependencyInjection;
2 | using NimbleMediator.Contracts;
3 |
4 | namespace NimbleMediator.Tests;
5 |
6 | public class SenderTests
7 | {
8 | public SenderTests()
9 | {
10 | _sender = Helpers.SetupServiceProvider().GetRequiredService();
11 | }
12 | private readonly ISender _sender;
13 |
14 | [Fact]
15 | public async Task SendAsync_Should_Handle_Request()
16 | {
17 | var request = new MyRequestWithoutResponse();
18 |
19 | await _sender.SendAsync(request, CancellationToken.None);
20 | }
21 |
22 | [Fact]
23 | public async Task SendAsync_Should_Handle_Request_WithResponse()
24 | {
25 |
26 | var request = new MyRequestWithResponse()
27 | {
28 | Name = "Baran"
29 | };
30 |
31 | var result = await _sender.SendAsync(request, CancellationToken.None);
32 |
33 | Assert.True(result is string str && str.Equals("Baran"));
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/NimbleMediator/Contracts/IRequestHandler.cs:
--------------------------------------------------------------------------------
1 | namespace NimbleMediator.Contracts;
2 |
3 | ///
4 | /// Defines a request handler.
5 | ///
6 | ///
7 | ///
8 | public interface IRequestHandler
9 | where TRequest : IRequest
10 | where TResponse : notnull
11 | {
12 | ///
13 | /// Handles the request.
14 | ///
15 | ///
16 | ///
17 | ///
18 | ValueTask HandleAsync(TRequest request, CancellationToken cancellationToken);
19 | }
20 |
21 | public interface IRequestHandler
22 | where TRequest : IRequest
23 | {
24 | ///
25 | /// Handles the request.
26 | ///
27 | ///
28 | ///
29 | ///
30 | ValueTask HandleAsync(TRequest request, CancellationToken cancellationToken);
31 | }
--------------------------------------------------------------------------------
/src/NimbleMediator/Contracts/ISender.cs:
--------------------------------------------------------------------------------
1 | namespace NimbleMediator.Contracts;
2 |
3 | ///
4 | /// Defines a request sender.
5 | ///
6 | public interface ISender
7 | {
8 | ///
9 | /// Sends the request.
10 | ///
11 | ///
12 | ///
13 | ///
14 | ///
15 | ValueTask SendAsync(TRequest request, CancellationToken cancellationToken)
16 | where TRequest : IRequest;
17 |
18 | ///
19 | /// Sends the request.
20 | ///
21 | ///
22 | ///
23 | ///
24 | ///
25 | ///
26 | ValueTask SendAsync(TRequest request, CancellationToken cancellationToken)
27 | where TRequest : IRequest
28 | where TResponse : notnull;
29 | }
30 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## [1.0.0] & [1.0.1]
2 |
3 | - Initial release.
4 |
5 | ## [1.0.2]
6 |
7 | - Registered ISender and IPublisher also to the DI. This was forgotten in the initial release.
8 |
9 | ## [1.1.0]
10 |
11 | - Removed the type dictionary for SendAsync, now directly depends on the DI container to retrieve the handler.
12 | - Improved performance, now up to 3.5x faster and utilizes up to 16x less memory in certain cases.
13 | - Fixed the method call order dependency in NimbleMediatorConfig; it is now order-independent.
14 | - Overhauled notification handling; now supports notification publisher implementations via INotificationPublisher. Now maintains a Dictionary for notifications.
15 | - Added 3 default notification publishers: ForeachAwaitRobustPublisher, ForeachAwaitStopOnFirstExceptionPublisher, and TaskWhenAllPublisher.
16 | - Made minor changes to method names and signatures in NimbleMediatorConfig.
17 |
18 | ## [1.2.0]
19 | - Mediator implementation was singleton before, with this version it is **scoped** by default.
20 | - Added an api for explicitly setting the desired lifetime of the mediator.
--------------------------------------------------------------------------------
/src/tests/NimbleMediator.Tests/NimbleMediator.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0
5 | enable
6 | enable
7 |
8 | false
9 | true
10 |
11 |
12 |
13 |
14 |
15 |
16 | runtime; build; native; contentfiles; analyzers; buildtransitive
17 | all
18 |
19 |
20 | runtime; build; native; contentfiles; analyzers; buildtransitive
21 | all
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/benchmarks/MediatorsBenchmark/MediatorsBenchmark.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.5.002.0
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MediatorsBenchmark", "MediatorsBenchmark.csproj", "{68175B34-5100-4B68-AE19-B13F2FFF3910}"
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 | {68175B34-5100-4B68-AE19-B13F2FFF3910}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15 | {68175B34-5100-4B68-AE19-B13F2FFF3910}.Debug|Any CPU.Build.0 = Debug|Any CPU
16 | {68175B34-5100-4B68-AE19-B13F2FFF3910}.Release|Any CPU.ActiveCfg = Release|Any CPU
17 | {68175B34-5100-4B68-AE19-B13F2FFF3910}.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 = {AB989479-AD47-48FD-984E-C3229604DC32}
24 | EndGlobalSection
25 | EndGlobal
26 |
--------------------------------------------------------------------------------
/src/NimbleMediator/Implementations/Mediator.Publish.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.DependencyInjection;
2 | using NimbleMediator.Contracts;
3 |
4 | namespace NimbleMediator.Implementations;
5 |
6 | public partial class Mediator
7 | {
8 | ///
9 | /// Publishes a notification to all registered handlers.
10 | ///
11 | ///
12 | ///
13 | ///
14 | ///
15 | /// Thrown when one or more notification handlers fail.
16 | public Task PublishAsync(TNotification notification, CancellationToken cancellationToken)
17 | where TNotification : INotification
18 | {
19 | if (!_publisherTypeMappings.TryGetValue(typeof(TNotification), out var publisherType))
20 | {
21 | throw new InvalidOperationException($"No publisher type registered for {typeof(TNotification).Name}");
22 | }
23 |
24 | var handlers = _serviceProvider.GetServices>();
25 |
26 | var publisher = (INotificationPublisher)_serviceProvider.GetRequiredService(publisherType);
27 |
28 | return publisher.PublishAsync(notification, handlers, cancellationToken);
29 | }
30 | }
--------------------------------------------------------------------------------
/src/NimbleMediator/Implementations/NotificationPublishers/TaskWhenAllPublisher.cs:
--------------------------------------------------------------------------------
1 | using NimbleMediator.Contracts;
2 |
3 | namespace NimbleMediator.NotificationPublishers;
4 |
5 | public class TaskWhenAllPublisher : INotificationPublisher
6 | {
7 | public Task PublishAsync(TNotification notification, IEnumerable> handlers, CancellationToken cancellationToken)
8 | where TNotification : INotification
9 | {
10 | if (handlers is not INotificationHandler[] handlersArray)
11 | {
12 | throw new ArgumentException("The default Microsoft DI container should have returned an array of handlers.");
13 | }
14 |
15 | // If there is only one handler,
16 | // no need to allocate an array and no need to overhead of Task.WhenAll.
17 | if (handlersArray.Length == 1)
18 | {
19 |
20 | return handlersArray[0].HandleAsync(notification, cancellationToken);
21 | }
22 |
23 | // If there are more than one handlers, allocate an array and use Task.WhenAll.
24 | var tasks = new Task[handlersArray.Length];
25 |
26 | for (int i = 0; i < handlersArray.Length; i++)
27 | {
28 | tasks[i] = handlersArray[i].HandleAsync(notification, cancellationToken);
29 | }
30 |
31 | return Task.WhenAll(tasks);
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/NimbleMediator/Implementations/Mediator.Send.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.DependencyInjection;
2 | using NimbleMediator.Contracts;
3 |
4 | namespace NimbleMediator.Implementations;
5 |
6 | public partial class Mediator
7 | {
8 | ///
9 | /// Sends a request to the registered handler.
10 | ///
11 | ///
12 | ///
13 | ///
14 | ///
15 | public ValueTask SendAsync(TRequest request, CancellationToken cancellationToken)
16 | where TRequest : IRequest
17 | {
18 | var handler = _serviceProvider.GetRequiredService>();
19 | return handler.HandleAsync(request, cancellationToken);
20 | }
21 |
22 | ///
23 | /// Sends a request to the registered handler and returns the response.
24 | ///
25 | ///
26 | ///
27 | ///
28 | ///
29 | ///
30 | public ValueTask SendAsync(TRequest request, CancellationToken cancellationToken)
31 | where TRequest : IRequest
32 | where TResponse : notnull
33 | {
34 | var handler = _serviceProvider.GetRequiredService>();
35 | return handler.HandleAsync(request, cancellationToken);
36 | }
37 | }
--------------------------------------------------------------------------------
/benchmarks/MediatorsBenchmark/Requests/MediatR.cs:
--------------------------------------------------------------------------------
1 | using MediatR;
2 |
3 | namespace MediatorsBenchmark;
4 |
5 | public record MediatRRequestWithoutResponse(string Name) : IRequest;
6 |
7 | public class MediatRRequestWithoutResponseHandler : IRequestHandler
8 | {
9 | public Task Handle(MediatRRequestWithoutResponse request, CancellationToken cancellationToken)
10 | {
11 | return Task.CompletedTask;
12 | }
13 | }
14 |
15 | public record MediatRRequestWithResponse(string Name) : IRequest;
16 |
17 | public class MediatRRequestWithResponseHandler : IRequestHandler
18 | {
19 | public Task Handle(MediatRRequestWithResponse request, CancellationToken cancellationToken)
20 | {
21 | return Task.FromResult(request.Name);
22 | }
23 | }
24 |
25 | public record MediatRRequestWithoutResponseThrowsException(string Name) : IRequest;
26 |
27 | public class MediatRRequestWithoutResponseThrowsExceptionHandler : IRequestHandler
28 | {
29 | public Task Handle(MediatRRequestWithoutResponseThrowsException request, CancellationToken cancellationToken)
30 | {
31 | throw new Exception("Benchmark");
32 | }
33 | }
34 |
35 | public record MediatRRequestWithResponseThrowsException(string Name) : IRequest;
36 |
37 | public class MediatRRequestWithResponseThrowsExceptionHandler : IRequestHandler
38 | {
39 | public Task Handle(MediatRRequestWithResponseThrowsException request, CancellationToken cancellationToken)
40 | {
41 | throw new Exception("Benchmark");
42 | }
43 | }
--------------------------------------------------------------------------------
/benchmarks/MediatorsBenchmark/Requests/NimbleMediator.cs:
--------------------------------------------------------------------------------
1 | using NimbleMediator.Contracts;
2 |
3 | namespace MediatorsBenchmark;
4 |
5 | public record NimbleMediatorRequestWithoutResponse(string Name) : IRequest;
6 |
7 | public class NimbleMediatorRequestWithoutResponseHandler : IRequestHandler
8 | {
9 | public ValueTask HandleAsync(NimbleMediatorRequestWithoutResponse request, CancellationToken cancellationToken)
10 | {
11 | return ValueTask.CompletedTask;
12 | }
13 | }
14 |
15 | public record NimbleMediatorRequestWithResponse(string Name) : IRequest;
16 |
17 | public class NimbleMediatorRequestWithResponseHandler : IRequestHandler
18 | {
19 | public ValueTask HandleAsync(NimbleMediatorRequestWithResponse request, CancellationToken cancellationToken)
20 | {
21 | return ValueTask.FromResult(request.Name);
22 | }
23 | }
24 |
25 | public record NimbleMediatorRequestWithoutResponseThrowsException(string Name) : IRequest;
26 |
27 | public class NimbleMediatorRequestWithoutResponseThrowsExceptionHandler : IRequestHandler
28 | {
29 | public ValueTask HandleAsync(NimbleMediatorRequestWithoutResponseThrowsException request, CancellationToken cancellationToken)
30 | {
31 | throw new Exception("Benchmark");
32 | }
33 | }
34 |
35 | public record NimbleMediatorRequestWithResponseThrowsException(string Name) : IRequest;
36 |
37 | public class NimbleMediatorRequestWithResponseThrowsExceptionHandler : IRequestHandler
38 | {
39 | public ValueTask HandleAsync(NimbleMediatorRequestWithResponseThrowsException request, CancellationToken cancellationToken)
40 | {
41 | throw new Exception("Benchmark");
42 | }
43 | }
--------------------------------------------------------------------------------
/samples/NimbleMediator.Samples.ForeachAwaitRobustPublisher/Program.cs:
--------------------------------------------------------------------------------
1 | // See https://aka.ms/new-console-template for more information
2 | using Microsoft.Extensions.DependencyInjection;
3 | using NimbleMediator.Contracts;
4 | using NimbleMediator.NotificationPublishers;
5 | using NimbleMediator.ServiceExtensions;
6 |
7 | Console.WriteLine("Hello, World!");
8 |
9 | var services = new ServiceCollection();
10 |
11 | services.AddNimbleMediator(config =>
12 | {
13 | config.SetDefaultNotificationPublisher();
14 | config.RegisterServicesFromAssembly(typeof(MyNotification).Assembly);
15 | });
16 |
17 | var provider = services.BuildServiceProvider();
18 |
19 | var mediator = provider.GetRequiredService();
20 |
21 | try
22 | {
23 | await mediator.PublishAsync(new MyNotification(), CancellationToken.None);
24 | }
25 | catch (AggregateException ae)
26 | {
27 | Console.WriteLine("AggregateException:");
28 | foreach (var e in ae.InnerExceptions)
29 | {
30 | Console.WriteLine(e.Message);
31 | }
32 | }
33 | catch (Exception e)
34 | {
35 | Console.WriteLine("Exception:");
36 | Console.WriteLine(e.Message);
37 | }
38 |
39 | public class MyNotification : INotification
40 | {
41 | public string Name { get; set; }
42 | }
43 |
44 | public class MyNotificationHandler1 : INotificationHandler
45 | {
46 | public Task HandleAsync(MyNotification notification, CancellationToken cancellationToken)
47 | {
48 | Console.WriteLine($"Handling notification from {nameof(MyNotificationHandler1)}");
49 | return Task.CompletedTask;
50 | }
51 | }
52 |
53 | public class MyNotificationHandler2 : INotificationHandler
54 | {
55 | public Task HandleAsync(MyNotification notification, CancellationToken cancellationToken)
56 | {
57 | throw new Exception("Exception from MyNotificationHandler2");
58 | }
59 | }
60 |
61 | public class MyNotificationHandler3 : INotificationHandler
62 | {
63 | public Task HandleAsync(MyNotification notification, CancellationToken cancellationToken)
64 | {
65 | Console.WriteLine($"Handling notification from {nameof(MyNotificationHandler3)}");
66 | return Task.CompletedTask;
67 | }
68 | }
--------------------------------------------------------------------------------
/samples/NimbleMediator.Samples.ForechAwaitStopOnFirstExceptionPublisher/Program.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.DependencyInjection;
2 | using NimbleMediator.Contracts;
3 | using NimbleMediator.NotificationPublishers;
4 | using NimbleMediator.ServiceExtensions;
5 |
6 | var services = new ServiceCollection();
7 |
8 | services.AddNimbleMediator(config =>
9 | {
10 | config.SetDefaultNotificationPublisher();
11 | config.RegisterServicesFromAssembly(typeof(MyNotification).Assembly);
12 | });
13 |
14 | var provider = services.BuildServiceProvider();
15 |
16 | var mediator = provider.GetRequiredService();
17 |
18 | try
19 | {
20 | await mediator.PublishAsync(new MyNotification(), CancellationToken.None);
21 | }
22 | catch (AggregateException ae)
23 | {
24 | // This will not happen because the publisher stops on first exception.
25 | Console.WriteLine("AggregateException:");
26 | foreach (var e in ae.InnerExceptions)
27 | {
28 | Console.WriteLine(e.Message);
29 | }
30 | }
31 | catch (Exception e)
32 | {
33 | // This is the expected exception.
34 |
35 | Console.WriteLine("Exception:");
36 | Console.WriteLine(e.Message);
37 | }
38 |
39 | public class MyNotification : INotification
40 | {
41 | public string Name { get; set; }
42 | }
43 |
44 | public class MyNotificationHandler1 : INotificationHandler
45 | {
46 | public Task HandleAsync(MyNotification notification, CancellationToken cancellationToken)
47 | {
48 | Console.WriteLine($"Handling notification from {nameof(MyNotificationHandler1)}");
49 | return Task.CompletedTask;
50 | }
51 | }
52 |
53 | public class MyNotificationHandler2 : INotificationHandler
54 | {
55 | public Task HandleAsync(MyNotification notification, CancellationToken cancellationToken)
56 | {
57 | throw new Exception("Exception from MyNotificationHandler2");
58 | }
59 | }
60 |
61 | public class MyNotificationHandler3 : INotificationHandler
62 | {
63 | public Task HandleAsync(MyNotification notification, CancellationToken cancellationToken)
64 | {
65 | Console.WriteLine($"Handling notification from {nameof(MyNotificationHandler3)}");
66 | return Task.CompletedTask;
67 | }
68 | }
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing to NimbleMediator
2 |
3 | We love your input! We want to make contributing to this project as easy and transparent as possible, whether it's:
4 |
5 | - Reporting a bug
6 | - Discussing the current state of the code
7 | - Submitting a fix
8 | - Proposing new features
9 |
10 | ## We Develop with Github
11 |
12 | We use GitHub to host code, to track issues and feature requests, as well as accept pull requests.
13 |
14 | ## We Use [Github Flow](https://guides.github.com/introduction/flow/index.html)
15 |
16 | Pull requests are the best way to propose changes to the codebase. We actively welcome your pull requests.
17 |
18 | 1. Fork the repo and create your branch from `main`.
19 | 2. If you've added code that should be tested, add tests.
20 | 3. If you've changed APIs, update the documentation.
21 | 4. Ensure the test suite passes.
22 | 5. Make sure your code lints.
23 | 6. Issue that pull request!
24 |
25 | ## Commit Message Format
26 |
27 | We follow a specific commit message format. This leads to more readable messages that are easy to follow when looking through the project history.
28 |
29 | Each commit message consists of a **header**, and a **body** and/or a **footer**.
30 |
31 | **Example: ``[Tag]: Short description (fixes #1234)``**
32 |
33 | The `Tag` is one of the following:
34 |
35 | - **Feat** - for new features
36 | - **Fix** - for bug fixes
37 | - **Refactor** - for code that does not add a feature or fix a bug
38 | - **Perf** - for code that improves performance
39 | - **Style** - for code that does not alter how the code runs, just improves formatting and similar.
40 | - **Docs** - for changes to documentation only
41 | - **Test** - for adding missing tests or correcting existing tests
42 | - **CI** - for changes to CI configuration files and scripts
43 | - **Chore** - for repetitive tasks such as updating dependencies and so on.
44 |
45 | ## Use a Consistent Coding Style
46 |
47 | Let's ensure our codebase is consistent and clean.
48 |
49 | ## Report bugs using Github's [issues](https://github.com/briandk/transcriptase-atom/issues)
50 |
51 | We use GitHub issues to track public bugs. Report a bug by opening a new issue.
52 |
53 | ## Write bug reports with detail, background, and sample code
54 |
55 | **Great Bug Reports** tend to have:
56 |
57 | - A quick summary and/or background
58 | - Steps to reproduce
59 | - Be specific!
60 | - Give sample code if you can.
61 | - What you expected would happen
62 | - What actually happens
63 | - Notes (possibly including why you think this might be happening, or stuff you tried that didn't work)
64 |
--------------------------------------------------------------------------------
/src/NimbleMediator/NimbleMediator.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.1
5 | latest
6 | enable
7 | enable
8 | latest
9 | all
10 | all
11 | true
12 | true
13 | true
14 | CA1040
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 | NimbleMediator
31 | 1.2.0
32 | Baran Açıkgöz
33 | Significantly faster and memory optimized mediator implementation.
34 | True
35 | NimbleMediator
36 | https://github.com/baranacikgoz/NimbleMediator/blob/main/LICENSE
37 | https://github.com/baranacikgoz/NimbleMediator
38 | README.md
39 | https://github.com/baranacikgoz/NimbleMediator
40 | git
41 | mediator;Mediator;mediatr;MediatR;Nimble;NimbleMediator
42 | LICENSE
43 | True
44 | logo_128.png
45 |
46 |
47 | - Mediator implementation was singleton before, with this version it is scoped by default.
48 | - Added an api for explicitly setting the desired lifetime of the mediator.
49 |
50 |
51 |
52 |
53 |
54 |
55 | runtime; build; native; contentfiles; analyzers; buildtransitive
56 | all
57 |
58 |
59 |
60 |
--------------------------------------------------------------------------------
/src/NimbleMediator/Implementations/NotificationPublishers/ForeachAwaitStopOnFirstExceptionPublisher.cs:
--------------------------------------------------------------------------------
1 | using NimbleMediator.Contracts;
2 |
3 | namespace NimbleMediator.NotificationPublishers;
4 |
5 | ///
6 | /// The publisher that stops on first exception, not guarantees that all handlers are executed.
7 | /// If you want to ensure that all handlers are executed, use .
8 | /// It is relatively faster and less memory consuming due it's fail-fast nature.
9 | ///
10 | public class ForeachAwaitStopOnFirstExceptionPublisher : INotificationPublisher
11 | {
12 | ///
13 | /// Publishes a notification to all registered handlers.
14 | /// Stops on first exception, not guarantees that all handlers are executed.
15 | ///
16 | ///
17 | ///
18 | ///
19 | ///
20 | ///
21 | ///
22 | /// May throw an exception if an handler throws an exception.
23 | /// Does not wraps the exception with a different type of exception, throws directly.
24 | ///
25 | public Task PublishAsync(TNotification notification, IEnumerable> handlers, CancellationToken cancellationToken)
26 | where TNotification : INotification
27 | {
28 | if (handlers is not INotificationHandler[] handlersArray)
29 | {
30 | throw new ArgumentException("The default Microsoft DI container should have returned an array of handlers.");
31 | }
32 |
33 | // If there is only one handler, no need for a loop's overhead.
34 | if (handlersArray.Length == 1)
35 | {
36 | return handlersArray[0].HandleAsync(notification, cancellationToken);
37 | }
38 |
39 | return PublishAsyncInternal(notification, handlersArray, cancellationToken);
40 | }
41 |
42 | // This method is extracted to avoid call 'await' inside the PublishAsync method,
43 | // thus preventing the creation of an additional state machine and reducing overhead.
44 | // The state machine will only be created when the user calls the PublishAsync method outside of the library.
45 | // Yes, it seems like even beyond micro-optimization, but makes difference in benchmarks and high-throughput scenarios.
46 | private static async Task PublishAsyncInternal(TNotification notification, INotificationHandler[] handlersArray, CancellationToken cancellationToken)
47 | where TNotification : INotification
48 | {
49 | for (int i = 0; i < handlersArray.Length; i++)
50 | {
51 | await handlersArray[i].HandleAsync(notification, cancellationToken).ConfigureAwait(false);
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/benchmarks/MediatorsBenchmark/Notifications/MediatR.cs:
--------------------------------------------------------------------------------
1 | using MediatR;
2 |
3 | namespace MediatorsBenchmark;
4 |
5 | public record MediatRNotificationWith1Handler(string Name) : INotification;
6 |
7 | public class MediatRNotificationWith1HandlerHandler : INotificationHandler
8 | {
9 | public Task Handle(MediatRNotificationWith1Handler notification, CancellationToken cancellationToken)
10 | {
11 | return Task.CompletedTask;
12 | }
13 | }
14 |
15 | public record MediatRNotificationWith1HandlerThrowsException(string Name) : INotification;
16 |
17 | public class MediatRNotificationWith1HandlerThrowsExceptionHandler : INotificationHandler
18 | {
19 | public Task Handle(MediatRNotificationWith1HandlerThrowsException notification, CancellationToken cancellationToken)
20 | {
21 | throw new Exception("Benchmark");
22 | }
23 | }
24 |
25 | public record MediatRNotificationWith3Handlers(string Name) : INotification;
26 |
27 | public class MediatRNotificationWith3HandlersHandler1 : INotificationHandler
28 | {
29 | public Task Handle(MediatRNotificationWith3Handlers notification, CancellationToken cancellationToken)
30 | {
31 | return Task.CompletedTask;
32 | }
33 | }
34 |
35 | public class MediatRNotificationWith3HandlersHandler2 : INotificationHandler
36 | {
37 | public Task Handle(MediatRNotificationWith3Handlers notification, CancellationToken cancellationToken)
38 | {
39 | return Task.CompletedTask;
40 | }
41 | }
42 |
43 | public class MediatRNotificationWith3HandlersHandler3 : INotificationHandler
44 | {
45 | public Task Handle(MediatRNotificationWith3Handlers notification, CancellationToken cancellationToken)
46 | {
47 | return Task.CompletedTask;
48 | }
49 | }
50 |
51 | public record MediatRNotificationWith3Handlers1ThrowsException(string Name) : INotification;
52 |
53 | public class MediatRNotificationWith3Handlers1ThrowsExceptionHandler1 : INotificationHandler
54 | {
55 | public Task Handle(MediatRNotificationWith3Handlers1ThrowsException notification, CancellationToken cancellationToken)
56 | {
57 | return Task.CompletedTask;
58 | }
59 | }
60 |
61 | public class MediatRNotificationWith3Handlers1ThrowsExceptionHandler2 : INotificationHandler
62 | {
63 | public Task Handle(MediatRNotificationWith3Handlers1ThrowsException notification, CancellationToken cancellationToken)
64 | {
65 | throw new Exception("Benchmark");
66 | }
67 | }
68 |
69 | public record MediatRNotificationWith3Handlers1ThrowsExceptionHandler3 : INotificationHandler
70 | {
71 | public Task Handle(MediatRNotificationWith3Handlers1ThrowsException notification, CancellationToken cancellationToken)
72 | {
73 | return Task.CompletedTask;
74 | }
75 | }
--------------------------------------------------------------------------------
/benchmarks/MediatorsBenchmark/Notifications/NimbleMediator.cs:
--------------------------------------------------------------------------------
1 | using NimbleMediator.Contracts;
2 |
3 | namespace MediatorsBenchmark;
4 |
5 | public record NimbleMediatorNotificationWith1Handler(string Name) : INotification;
6 |
7 | public class NimbleMediatorNotificationWith1HandlerHandler : INotificationHandler
8 | {
9 | public Task HandleAsync(NimbleMediatorNotificationWith1Handler notification, CancellationToken cancellationToken)
10 | {
11 | return Task.CompletedTask;
12 | }
13 | }
14 |
15 | public record NimbleMediatorNotificationWith1HandlerThrowsException(string Name) : INotification;
16 |
17 | public class NimbleMediatorNotificationWith1HandlerThrowsExceptionHandler : INotificationHandler
18 | {
19 | public Task HandleAsync(NimbleMediatorNotificationWith1HandlerThrowsException notification, CancellationToken cancellationToken)
20 | {
21 | throw new Exception("Benchmark");
22 | }
23 | }
24 |
25 | public record NimbleMediatorNotificationWith3Handlers(string Name) : INotification;
26 |
27 | public class NimbleMediatorNotificationWith3HandlersHandler1 : INotificationHandler
28 | {
29 | public Task HandleAsync(NimbleMediatorNotificationWith3Handlers notification, CancellationToken cancellationToken)
30 | {
31 | return Task.CompletedTask;
32 | }
33 | }
34 |
35 | public class NimbleMediatorNotificationWith3HandlersHandler2 : INotificationHandler
36 | {
37 | public Task HandleAsync(NimbleMediatorNotificationWith3Handlers notification, CancellationToken cancellationToken)
38 | {
39 | return Task.CompletedTask;
40 | }
41 | }
42 |
43 | public class NimbleMediatorNotificationWith3HandlersHandler3 : INotificationHandler
44 | {
45 | public Task HandleAsync(NimbleMediatorNotificationWith3Handlers notification, CancellationToken cancellationToken)
46 | {
47 | return Task.CompletedTask;
48 | }
49 | }
50 |
51 | public record NimbleMediatorNotificationWith3Handlers1ThrowsException(string Name) : INotification;
52 |
53 | public class NimbleMediatorNotificationWith3Handlers1ThrowsExceptionHandler1 : INotificationHandler
54 | {
55 | public Task HandleAsync(NimbleMediatorNotificationWith3Handlers1ThrowsException notification, CancellationToken cancellationToken)
56 | {
57 | return Task.CompletedTask;
58 | }
59 | }
60 |
61 | public class NimbleMediatorNotificationWith3Handlers1ThrowsExceptionHandler2 : INotificationHandler
62 | {
63 | public Task HandleAsync(NimbleMediatorNotificationWith3Handlers1ThrowsException notification, CancellationToken cancellationToken)
64 | {
65 | throw new Exception("Benchmark");
66 | }
67 | }
68 |
69 | public class NimbleMediatorNotificationWith3Handlers1ThrowsExceptionHandler3 : INotificationHandler
70 | {
71 | public Task HandleAsync(NimbleMediatorNotificationWith3Handlers1ThrowsException notification, CancellationToken cancellationToken)
72 | {
73 | return Task.CompletedTask;
74 | }
75 | }
--------------------------------------------------------------------------------
/src/NimbleMediator/Implementations/NotificationPublishers/ForeachAwaitRobustPublisher.cs:
--------------------------------------------------------------------------------
1 | using NimbleMediator.Contracts;
2 |
3 | namespace NimbleMediator.NotificationPublishers;
4 |
5 | ///
6 | /// The robust publisher that does not stop publishing when one or more handlers fail ensuring that all handlers are executed.
7 | /// It is relatively slower and more memory consuming due it's robust nature.
8 | ///
9 | public class ForeachAwaitRobustPublisher : INotificationPublisher
10 | {
11 | private const string _aggregateExceptionMessage = "One or more notification handlers failed.";
12 |
13 | ///
14 | /// Publishes a notification to all registered handlers.
15 | /// Does not stop publishing when one or more handlers fail ensuring that all handlers are executed.
16 | ///
17 | ///
18 | ///
19 | ///
20 | ///
21 | ///
22 | /// Thrown when more than one handler is registered and more than one notification handlers fail.
23 | ///
24 | /// If only a single handler is registered and it throws an exception, that exception will be thrown directly, not wrapped in an AggregateException.
25 | ///
26 | public Task PublishAsync(TNotification notification, IEnumerable> handlers, CancellationToken cancellationToken)
27 | where TNotification : INotification
28 | {
29 | if (handlers is not INotificationHandler[] handlersArray)
30 | {
31 | throw new ArgumentException("The default Microsoft DI container should have returned an array of handlers.");
32 | }
33 |
34 | // If there is only one handler,
35 | // no need for a loop's overhead.
36 | if (handlersArray.Length == 1)
37 | {
38 | return handlersArray[0].HandleAsync(notification, cancellationToken);
39 | }
40 |
41 | return PublishAsyncInternal(notification, handlersArray, cancellationToken);
42 | }
43 |
44 | // This method is extracted to avoid call 'await' inside the PublishAsync method,
45 | // thus preventing the creation of an additional state machine and reducing overhead.
46 | // The state machine will only be created when the user calls the PublishAsync method outside of the library.
47 | // Yes, it seems like even beyond micro-optimization, but makes difference in benchmarks and high-throughput scenarios.
48 | private static async Task PublishAsyncInternal(TNotification notification, INotificationHandler[] handlersArray, CancellationToken cancellationToken) where TNotification : INotification
49 | {
50 | List? exceptions = null;
51 |
52 | for (int i = 0; i < handlersArray.Length; i++)
53 | {
54 | try
55 | {
56 | await handlersArray[i].HandleAsync(notification, cancellationToken).ConfigureAwait(false);
57 | }
58 | catch (Exception ex)
59 | {
60 | #pragma warning disable CA1508 // Compiler claims that the exceptions will always be null, but it is not.
61 | exceptions ??= new List(handlersArray.Length);
62 | #pragma warning restore CA1508
63 |
64 | exceptions.Add(ex);
65 | }
66 | }
67 |
68 | if (exceptions is not null)
69 | {
70 | throw new AggregateException(_aggregateExceptionMessage, exceptions);
71 | }
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/benchmarks/MediatorsBenchmark/SendBenchmark.cs:
--------------------------------------------------------------------------------
1 | using BenchmarkDotNet.Attributes;
2 | using NimbleMediator;
3 | using Microsoft.Extensions.DependencyInjection;
4 | using NimbleMediator.ServiceExtensions;
5 | using NimbleMediator.Implementations;
6 | using MediatR.NotificationPublishers;
7 | using BenchmarkDotNet.Configs;
8 |
9 | namespace MediatorsBenchmark;
10 |
11 | [MemoryDiagnoser]
12 | [GroupBenchmarksBy(BenchmarkLogicalGroupRule.ByCategory)]
13 | [HideColumns(new string[] { "Error", "StdDev", "RatioSD", "Gen0" })]
14 | public class SendBenchmark
15 | {
16 | public SendBenchmark()
17 | {
18 | var services = new ServiceCollection();
19 |
20 | services.AddNimbleMediator(config =>
21 | {
22 | config.RegisterServicesFromAssembly(typeof(NimbleMediatorRequestWithoutResponse).Assembly);
23 | });
24 |
25 | services.AddMediatR(cfg =>
26 | {
27 | cfg.RegisterServicesFromAssembly(typeof(MediatRRequestWithoutResponse).Assembly);
28 | });
29 |
30 | var provider = services.BuildServiceProvider();
31 |
32 | _mediatR = provider.GetRequiredService();
33 | _nimbleMediator = provider.GetRequiredService();
34 | }
35 | private readonly MediatR.IMediator _mediatR;
36 | private readonly NimbleMediator.Contracts.IMediator _nimbleMediator;
37 |
38 | private readonly NimbleMediatorRequestWithoutResponse _nimbleMediatorRequestWithoutResponse = new("Test");
39 | private readonly NimbleMediatorRequestWithResponse _nimbleMediatorRequestWithResponse = new("Test");
40 | private readonly MediatRRequestWithoutResponse _mediatRRequestWithoutResponse = new("Test");
41 | private readonly MediatRRequestWithResponse _mediatRRequestWithResponse = new("Test");
42 | private readonly NimbleMediatorRequestWithoutResponseThrowsException _nimbleMediatorRequestWithoutResponseThrowsException = new("Test");
43 | private readonly MediatRRequestWithoutResponseThrowsException _mediatRRequestWithoutResponseThrowsException = new("Test");
44 | private readonly NimbleMediatorRequestWithResponseThrowsException _nimbleMediatorRequestWithResponseThrowsException = new("Test");
45 | private readonly MediatRRequestWithResponseThrowsException _mediatRRequestWithResponseThrowsException = new("Test");
46 |
47 |
48 | [BenchmarkCategory("1"), Benchmark(Baseline = true)]
49 | public async ValueTask NimbleMediator_Send_WithoutResponse()
50 | {
51 | await _nimbleMediator.SendAsync(_nimbleMediatorRequestWithoutResponse, CancellationToken.None);
52 | }
53 |
54 | [BenchmarkCategory("1"), Benchmark]
55 | public async ValueTask MediatR_Send_WithoutResponse()
56 | {
57 | await _mediatR.Send(_mediatRRequestWithoutResponse, CancellationToken.None);
58 | }
59 |
60 | [BenchmarkCategory("2"), Benchmark(Baseline = true)]
61 | public async ValueTask NimbleMediator_Send_WithResponse()
62 | {
63 | return await _nimbleMediator.SendAsync(_nimbleMediatorRequestWithResponse, CancellationToken.None);
64 | }
65 |
66 | [BenchmarkCategory("2"), Benchmark]
67 | public async Task MediatR_Send_WithResponse()
68 | {
69 | return await _mediatR.Send(_mediatRRequestWithResponse, CancellationToken.None);
70 | }
71 |
72 | [BenchmarkCategory("3"), Benchmark(Baseline = true)]
73 | public async Task NimbleMediator_Send_WithoutResponse_ThrowsException()
74 | {
75 | try
76 | {
77 | await _nimbleMediator.SendAsync(_nimbleMediatorRequestWithoutResponseThrowsException, CancellationToken.None);
78 | }
79 | catch
80 | {
81 | // ignored
82 | }
83 | }
84 |
85 | [BenchmarkCategory("3"), Benchmark]
86 | public async Task MediatR_Send_WithoutResponse_ThrowsException()
87 | {
88 | try
89 | {
90 | await _mediatR.Send(_mediatRRequestWithoutResponseThrowsException, CancellationToken.None);
91 | }
92 | catch
93 | {
94 | // ignored
95 | }
96 | }
97 |
98 | [BenchmarkCategory("4"), Benchmark(Baseline = true)]
99 | public async Task NimbleMediator_Send_WithResponse_ThrowsException()
100 | {
101 | try
102 | {
103 | return await _nimbleMediator.SendAsync(_nimbleMediatorRequestWithResponseThrowsException, CancellationToken.None);
104 | }
105 | catch
106 | {
107 | return "Test";
108 | }
109 | }
110 |
111 | [BenchmarkCategory("4"), Benchmark]
112 | public async Task MediatR_Send_WithResponse_ThrowsException()
113 | {
114 | try
115 | {
116 | return await _mediatR.Send(_mediatRRequestWithResponseThrowsException, CancellationToken.None);
117 | }
118 | catch
119 | {
120 | return "Test";
121 | }
122 | }
123 | }
--------------------------------------------------------------------------------
/benchmarks/MediatorsBenchmark/ForeachAwaitBenchmark.cs:
--------------------------------------------------------------------------------
1 | using BenchmarkDotNet.Attributes;
2 | using BenchmarkDotNet.Configs;
3 | using MediatorsBenchmark;
4 | using Microsoft.Extensions.DependencyInjection;
5 | using NimbleMediator;
6 | using NimbleMediator.NotificationPublishers;
7 | using NimbleMediator.ServiceExtensions;
8 |
9 | namespace MediatorsBenchmark;
10 |
11 | [MemoryDiagnoser]
12 | [GroupBenchmarksBy(BenchmarkLogicalGroupRule.ByCategory)]
13 | [HideColumns(new string[] { "Error", "StdDev", "RatioSD", "Gen0" })]
14 | public class ForeachAwaitBenchmark
15 | {
16 | public ForeachAwaitBenchmark()
17 | {
18 | var services = new ServiceCollection();
19 |
20 | services.AddNimbleMediator(config =>
21 | {
22 | config.SetDefaultNotificationPublisher();
23 | config.RegisterServicesFromAssembly(typeof(NimbleMediatorRequestWithoutResponse).Assembly);
24 | });
25 |
26 | services.AddMediatR(cfg =>
27 | {
28 | cfg.RegisterServicesFromAssembly(typeof(MediatRRequestWithoutResponse).Assembly);
29 | cfg.NotificationPublisher = new MediatR.NotificationPublishers.ForeachAwaitPublisher();
30 | });
31 |
32 | var provider = services.BuildServiceProvider();
33 |
34 | _mediatR = provider.GetRequiredService();
35 | _nimbleMediator = provider.GetRequiredService();
36 | }
37 |
38 | private readonly MediatR.IMediator _mediatR;
39 | private readonly NimbleMediator.Contracts.IMediator _nimbleMediator;
40 |
41 | private readonly NimbleMediatorNotificationWith1Handler _nimbleMediatorNotificationWith1Handler = new("Test");
42 | private readonly NimbleMediatorNotificationWith1HandlerThrowsException _nimbleMediatorNotificationWith1HandlerThrowsException = new("Test");
43 | private readonly NimbleMediatorNotificationWith3Handlers _nimbleMediatorNotificationWith3Handlers = new("Test");
44 | private readonly NimbleMediatorNotificationWith3Handlers1ThrowsException _nimbleMediatorNotificationWith3Handlers1ThrowsException = new("Test");
45 | private readonly MediatRNotificationWith1Handler _mediatRNotificationWith1Handler = new("Test");
46 | private readonly MediatRNotificationWith1HandlerThrowsException _mediatRNotificationWith1HandlerThrowsException = new("Test");
47 | private readonly MediatRNotificationWith3Handlers _mediatRNotificationWith3Handlers = new("Test");
48 | private readonly MediatRNotificationWith3Handlers1ThrowsException _mediatRNotificationWith3Handlers1ThrowsException = new("Test");
49 |
50 | [BenchmarkCategory("1"), Benchmark(Baseline = true)]
51 | public async Task NimbleMediator_Publish_ForeachAwait_notification_has_1_handler()
52 | {
53 | await _nimbleMediator.PublishAsync(_nimbleMediatorNotificationWith1Handler, CancellationToken.None);
54 | }
55 |
56 | [BenchmarkCategory("1"), Benchmark]
57 | public async Task MediatR_Publish_ForeachAwait_notification_has_1_handler()
58 | {
59 | await _mediatR.Publish(_mediatRNotificationWith1Handler, CancellationToken.None);
60 | }
61 |
62 | [BenchmarkCategory("2"), Benchmark(Baseline = true)]
63 | public async Task NimbleMediator_Publish_ForeachAwait_notification_has_1_handler_ThrowsException()
64 | {
65 | try
66 | {
67 | await _nimbleMediator.PublishAsync(_nimbleMediatorNotificationWith1HandlerThrowsException, CancellationToken.None);
68 | }
69 | catch (Exception)
70 | {
71 | // ignored
72 | }
73 | }
74 |
75 | [BenchmarkCategory("2"), Benchmark]
76 | public async Task MediatR_Publish_ForeachAwait_notification_has_1_handler_ThrowsException()
77 | {
78 | try
79 | {
80 | await _mediatR.Publish(_mediatRNotificationWith1HandlerThrowsException, CancellationToken.None);
81 | }
82 | catch (Exception)
83 | {
84 | // ignored
85 | }
86 | }
87 |
88 | [BenchmarkCategory("3"), Benchmark(Baseline = true)]
89 | public async Task NimbleMediator_Publish_ForeachAwait_notification_has_3_handlers()
90 | {
91 | await _nimbleMediator.PublishAsync(_nimbleMediatorNotificationWith3Handlers, CancellationToken.None);
92 | }
93 |
94 | [BenchmarkCategory("3"), Benchmark]
95 | public async Task MediatR_Publish_ForeachAwait_notification_has_3_handlers()
96 | {
97 | await _mediatR.Publish(_mediatRNotificationWith3Handlers, CancellationToken.None);
98 | }
99 |
100 | [BenchmarkCategory("4"), Benchmark(Baseline = true)]
101 | public async Task NimbleMediator_Publish_ForeachAwait_notification_has_3_handlers_1_throws_exception()
102 | {
103 | try
104 | {
105 | await _nimbleMediator.PublishAsync(_nimbleMediatorNotificationWith3Handlers1ThrowsException, CancellationToken.None);
106 | }
107 | catch (Exception)
108 | {
109 | // ignored
110 | }
111 | }
112 |
113 | [BenchmarkCategory("4"), Benchmark]
114 | public async Task MediatR_Publish_ForeachAwait_notification_has_3_handlers_1_throws_exception()
115 | {
116 | try
117 | {
118 | await _mediatR.Publish(_mediatRNotificationWith3Handlers1ThrowsException, CancellationToken.None);
119 | }
120 | catch (Exception)
121 | {
122 | // ignored
123 | }
124 | }
125 |
126 | }
--------------------------------------------------------------------------------
/benchmarks/MediatorsBenchmark/TaskWhenAllBenchmark.cs:
--------------------------------------------------------------------------------
1 | using BenchmarkDotNet.Attributes;
2 | using NimbleMediator;
3 | using Microsoft.Extensions.DependencyInjection;
4 | using NimbleMediator.ServiceExtensions;
5 | using NimbleMediator.Implementations;
6 | using MediatR.NotificationPublishers;
7 | using BenchmarkDotNet.Configs;
8 |
9 | namespace MediatorsBenchmark;
10 |
11 | [MemoryDiagnoser]
12 | [GroupBenchmarksBy(BenchmarkLogicalGroupRule.ByCategory)]
13 | [HideColumns(new string[] { "Error", "StdDev", "RatioSD", "Gen0" })]
14 | public class TaskWhenAllBenchmark
15 | {
16 | public TaskWhenAllBenchmark()
17 | {
18 | var services = new ServiceCollection();
19 |
20 | services.AddNimbleMediator(config =>
21 | {
22 | config.SetDefaultNotificationPublisher();
23 | config.RegisterServicesFromAssembly(typeof(NimbleMediatorRequestWithoutResponse).Assembly);
24 |
25 | });
26 |
27 | services.AddMediatR(cfg =>
28 | {
29 | cfg.RegisterServicesFromAssembly(typeof(MediatRRequestWithoutResponse).Assembly);
30 | cfg.NotificationPublisher = new MediatR.NotificationPublishers.TaskWhenAllPublisher();
31 | });
32 |
33 | var provider = services.BuildServiceProvider();
34 |
35 | _mediatR = provider.GetRequiredService();
36 | _nimbleMediator = provider.GetRequiredService();
37 | }
38 | private readonly MediatR.IMediator _mediatR;
39 | private readonly NimbleMediator.Contracts.IMediator _nimbleMediator;
40 | private readonly NimbleMediatorNotificationWith1Handler _nimbleMediatorNotificationWith1Handler = new("Test");
41 | private readonly NimbleMediatorNotificationWith1HandlerThrowsException _nimbleMediatorNotificationWith1HandlerThrowsException = new("Test");
42 | private readonly NimbleMediatorNotificationWith3Handlers _nimbleMediatorNotificationWith3Handlers = new("Test");
43 | private readonly NimbleMediatorNotificationWith3Handlers1ThrowsException _nimbleMediatorNotificationWith3Handlers1ThrowsException = new("Test");
44 | private readonly MediatRNotificationWith1Handler _mediatRNotificationWithSingleHandler = new("Test");
45 | private readonly MediatRNotificationWith1HandlerThrowsException _mediatRNotificationWith1HandlerThrowsException = new("Test");
46 | private readonly MediatRNotificationWith3Handlers _mediatRNotificationWith3Handlers = new("Test");
47 | private readonly MediatRNotificationWith3Handlers1ThrowsException _mediatRNotificationWith3Handlers1ThrowsException = new("Test");
48 |
49 | [BenchmarkCategory("1"), Benchmark(Baseline = true)]
50 | public async Task NimbleMediator_Publish_TaskWhenAll_notification_has_1_handler()
51 | {
52 | await _nimbleMediator.PublishAsync(_nimbleMediatorNotificationWith1Handler, CancellationToken.None);
53 | }
54 |
55 | [BenchmarkCategory("1"), Benchmark]
56 | public async Task MediatR_Publish_TaskWhenAll_notification_has_1_handler()
57 | {
58 | await _mediatR.Publish(_mediatRNotificationWithSingleHandler, CancellationToken.None);
59 | }
60 |
61 | [BenchmarkCategory("2"), Benchmark(Baseline = true)]
62 | public async Task NimbleMediator_Publish_TaskWhenAll_notification_has_1_handler_ThrowsException()
63 | {
64 | try
65 | {
66 | await _nimbleMediator.PublishAsync(_nimbleMediatorNotificationWith1HandlerThrowsException, CancellationToken.None);
67 | }
68 | catch (Exception)
69 | {
70 | // ignored
71 | }
72 | }
73 |
74 | [BenchmarkCategory("2"), Benchmark]
75 | public async Task MediatR_Publish_TaskWhenAll_notification_has_1_handler_ThrowsException()
76 | {
77 | try
78 | {
79 | await _mediatR.Publish(_mediatRNotificationWith1HandlerThrowsException, CancellationToken.None);
80 | }
81 | catch (Exception)
82 | {
83 | // ignored
84 | }
85 | }
86 |
87 | [BenchmarkCategory("3"), Benchmark(Baseline = true)]
88 | public async Task NimbleMediator_Publish_TaskWhenAll_notification_has_3_handlers()
89 | {
90 | await _nimbleMediator.PublishAsync(_nimbleMediatorNotificationWith3Handlers, CancellationToken.None);
91 | }
92 |
93 | [BenchmarkCategory("3"), Benchmark]
94 | public async Task MediatR_Publish_TaskWhenAll_notification_has_3_handlers()
95 | {
96 | await _mediatR.Publish(_mediatRNotificationWith3Handlers, CancellationToken.None);
97 | }
98 |
99 | [BenchmarkCategory("4"), Benchmark(Baseline = true)]
100 | public async Task NimbleMediator_Publish_TaskWhenAll_notification_has_3_handlers_1_ThrowsException()
101 | {
102 | try
103 | {
104 | await _nimbleMediator.PublishAsync(_nimbleMediatorNotificationWith3Handlers1ThrowsException, CancellationToken.None);
105 | }
106 | catch (Exception)
107 | {
108 | // ignored
109 | }
110 | }
111 |
112 | [BenchmarkCategory("4"), Benchmark]
113 | public async Task MediatR_Publish_TaskWhenAll_notification_has_3_handlers_1_ThrowsException()
114 | {
115 | try
116 | {
117 | await _mediatR.Publish(_mediatRNotificationWith3Handlers1ThrowsException, CancellationToken.None);
118 | }
119 | catch (Exception)
120 | {
121 | // ignored
122 | }
123 | }
124 | }
--------------------------------------------------------------------------------
/NimbleMediator.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.7.34009.444
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{06B6F282-1C5D-4E62-B583-93D010553BFC}"
7 | EndProject
8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NimbleMediator", "src\NimbleMediator\NimbleMediator.csproj", "{203AEF6B-8DE8-4B2C-A3A4-9522A458F41C}"
9 | EndProject
10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{3379F0DA-B4C0-447C-B406-AE0F93E5510C}"
11 | EndProject
12 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "benchmarks", "benchmarks", "{A2AD59B6-3D96-441B-88F8-79CD2C9EEBF7}"
13 | ProjectSection(SolutionItems) = preProject
14 | benchmarks\publish_foreachawait_benchmark.png = benchmarks\publish_foreachawait_benchmark.png
15 | benchmarks\publish_taskwhenall_benchmark.png = benchmarks\publish_taskwhenall_benchmark.png
16 | benchmarks\send_benchmark.png = benchmarks\send_benchmark.png
17 | EndProjectSection
18 | EndProject
19 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MediatorsBenchmark", "benchmarks\MediatorsBenchmark\MediatorsBenchmark.csproj", "{749D1503-5255-4C90-BFF9-7FAD135C4AE1}"
20 | EndProject
21 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "assets", "assets", "{42714D44-2B41-40C6-8E9C-AD88D1CCEF12}"
22 | ProjectSection(SolutionItems) = preProject
23 | assets\logo.png = assets\logo.png
24 | assets\logo_128.png = assets\logo_128.png
25 | EndProjectSection
26 | EndProject
27 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{F82D56AF-9485-4A4E-8145-99B761FB110B}"
28 | ProjectSection(SolutionItems) = preProject
29 | .editorconfig = .editorconfig
30 | .gitignore = .gitignore
31 | CONTRIBUTING.md = CONTRIBUTING.md
32 | LICENSE = LICENSE
33 | README.md = README.md
34 | CHANGELOG.md = CHANGELOG.md
35 | EndProjectSection
36 | EndProject
37 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NimbleMediator.Tests", "src\tests\NimbleMediator.Tests\NimbleMediator.Tests.csproj", "{6EE21F4D-7B75-488F-88BC-89FBEEC5E816}"
38 | EndProject
39 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{B9C29E78-160E-45E7-AC68-F06B59D4659F}"
40 | EndProject
41 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NimbleMediator.Samples.ForeachAwaitRobustPublisher", "samples\NimbleMediator.Samples.ForeachAwaitRobustPublisher\NimbleMediator.Samples.ForeachAwaitRobustPublisher.csproj", "{8349BD67-E9D0-457E-85CE-4B666654D55D}"
42 | EndProject
43 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NimbleMediator.Samples.ForechAwaitStopOnFirstExceptionPublisher", "samples\NimbleMediator.Samples.ForechAwaitStopOnFirstExceptionPublisher\NimbleMediator.Samples.ForechAwaitStopOnFirstExceptionPublisher.csproj", "{EF0E0F07-7884-4EF7-B9AC-C7967DCA43A9}"
44 | EndProject
45 | Global
46 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
47 | Debug|Any CPU = Debug|Any CPU
48 | Release|Any CPU = Release|Any CPU
49 | EndGlobalSection
50 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
51 | {203AEF6B-8DE8-4B2C-A3A4-9522A458F41C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
52 | {203AEF6B-8DE8-4B2C-A3A4-9522A458F41C}.Debug|Any CPU.Build.0 = Debug|Any CPU
53 | {203AEF6B-8DE8-4B2C-A3A4-9522A458F41C}.Release|Any CPU.ActiveCfg = Release|Any CPU
54 | {203AEF6B-8DE8-4B2C-A3A4-9522A458F41C}.Release|Any CPU.Build.0 = Release|Any CPU
55 | {749D1503-5255-4C90-BFF9-7FAD135C4AE1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
56 | {749D1503-5255-4C90-BFF9-7FAD135C4AE1}.Debug|Any CPU.Build.0 = Debug|Any CPU
57 | {749D1503-5255-4C90-BFF9-7FAD135C4AE1}.Release|Any CPU.ActiveCfg = Release|Any CPU
58 | {749D1503-5255-4C90-BFF9-7FAD135C4AE1}.Release|Any CPU.Build.0 = Release|Any CPU
59 | {6EE21F4D-7B75-488F-88BC-89FBEEC5E816}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
60 | {6EE21F4D-7B75-488F-88BC-89FBEEC5E816}.Debug|Any CPU.Build.0 = Debug|Any CPU
61 | {6EE21F4D-7B75-488F-88BC-89FBEEC5E816}.Release|Any CPU.ActiveCfg = Release|Any CPU
62 | {6EE21F4D-7B75-488F-88BC-89FBEEC5E816}.Release|Any CPU.Build.0 = Release|Any CPU
63 | {8349BD67-E9D0-457E-85CE-4B666654D55D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
64 | {8349BD67-E9D0-457E-85CE-4B666654D55D}.Debug|Any CPU.Build.0 = Debug|Any CPU
65 | {8349BD67-E9D0-457E-85CE-4B666654D55D}.Release|Any CPU.ActiveCfg = Release|Any CPU
66 | {8349BD67-E9D0-457E-85CE-4B666654D55D}.Release|Any CPU.Build.0 = Release|Any CPU
67 | {EF0E0F07-7884-4EF7-B9AC-C7967DCA43A9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
68 | {EF0E0F07-7884-4EF7-B9AC-C7967DCA43A9}.Debug|Any CPU.Build.0 = Debug|Any CPU
69 | {EF0E0F07-7884-4EF7-B9AC-C7967DCA43A9}.Release|Any CPU.ActiveCfg = Release|Any CPU
70 | {EF0E0F07-7884-4EF7-B9AC-C7967DCA43A9}.Release|Any CPU.Build.0 = Release|Any CPU
71 | EndGlobalSection
72 | GlobalSection(SolutionProperties) = preSolution
73 | HideSolutionNode = FALSE
74 | EndGlobalSection
75 | GlobalSection(NestedProjects) = preSolution
76 | {203AEF6B-8DE8-4B2C-A3A4-9522A458F41C} = {06B6F282-1C5D-4E62-B583-93D010553BFC}
77 | {3379F0DA-B4C0-447C-B406-AE0F93E5510C} = {06B6F282-1C5D-4E62-B583-93D010553BFC}
78 | {749D1503-5255-4C90-BFF9-7FAD135C4AE1} = {A2AD59B6-3D96-441B-88F8-79CD2C9EEBF7}
79 | {6EE21F4D-7B75-488F-88BC-89FBEEC5E816} = {3379F0DA-B4C0-447C-B406-AE0F93E5510C}
80 | {8349BD67-E9D0-457E-85CE-4B666654D55D} = {B9C29E78-160E-45E7-AC68-F06B59D4659F}
81 | {EF0E0F07-7884-4EF7-B9AC-C7967DCA43A9} = {B9C29E78-160E-45E7-AC68-F06B59D4659F}
82 | EndGlobalSection
83 | GlobalSection(ExtensibilityGlobals) = postSolution
84 | SolutionGuid = {475E1217-4E58-4412-B9AE-66AB47E6C6F2}
85 | EndGlobalSection
86 | EndGlobal
87 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # NimbleMediator
2 |
3 | NimbleMediator is a significantly faster, lightweight and memory-efficient mediator pattern implementation for .NET, designed to be an alternative to popular mediator libraries.
4 |
5 | ## Features
6 |
7 | - **Faster Performance**: Designed to offer a speed advantage over similar packages, providing quicker request and notification processing times. (***~3,5*** times faster than MediatR)
8 | - **Less Memory Usage**: optimized to minimize memory allocations, helping to reduce the overall memory footprint. (uses ***~16x*** less memory than MediatR)
9 | - **Easy to Integrate**: Can be easily integrated into existing .NET projects, offering a simple and similar IMediator, ISender, IPublisher interfacses.
10 | - **Individualized Notification Publishers**: Allows for different publisher implementations for each notification, enabling developers to have the flexibility to choose the best publisher implementation for each notification. Also supports custom publisher implementations via INotificationPublisher interface.
11 |
12 | ## Getting Started
13 |
14 | ### Install
15 | Install the NimbleMediator with ```dotnet add package NimbleMediator``` or via NuGet package manager.
16 |
17 | ### Configure
18 | Set up NimbleMediator in your ``Startup.cs`` or ``Program.cs`` by utilizing the ``services.AddNimbleMediator()`` method and configuring your handlers and publishers as necessary.
19 |
20 | #### Register Handlers
21 | Register your handlers from the assembly:
22 |
23 | ```csharp
24 |
25 | services.AddNimbleMediator(cfg => {
26 | cfg.RegisterServicesFromAssembly(typeof(Startup).Assembly);
27 | });
28 |
29 | ```
30 |
31 | or from assemblies:
32 |
33 | ```csharp
34 |
35 | services.AddNimbleMediator(cfg => {
36 | cfg.RegisterServicesFromAssemblies(typeof(X).Assembly, typeof(Y).Assembly, typeof(Z).Assembly);
37 | });
38 |
39 | ```
40 |
41 | #### Set default publisher (optional)
42 | Set the default publisher implementation for notifications.
43 | It is ``ForeachAwaitRobustPublisher`` by default if you don't set it.
44 |
45 | ```csharp
46 |
47 | cfg.SetDefaultNotificationPublisher();
48 | or
49 | cfg.SetDefaultNotificationPublisher();
50 | or your custom publisher.
51 | ```
52 |
53 | #### Set default publisher's lifetime (optional)
54 | Set the default publisher's lifetime. It is ``Singleton`` by default if you don't set it.
55 |
56 | ```csharp
57 |
58 | cfg.SetDefaultNotificationPublisherLifetime(ServiceLifetime.Transient);
59 |
60 | ```
61 |
62 | or
63 |
64 | ```csharp
65 |
66 | cfg.SetDefaultNotificationPublisher(ServiceLifetime.Transient);
67 |
68 | ```
69 |
70 | #### Set a different publisher for a particular notification (optional)
71 |
72 | ```csharp
73 |
74 | cfg.SetNotificationPublisher();
75 |
76 | ```
77 |
78 | you can even provide lifetime here:
79 |
80 | ```csharp
81 |
82 | cfg.SetNotificationPublisher(ServiceLifetime.Singleton);
83 |
84 | ```
85 |
86 | ##### Default publishers
87 | 1. ``ForeachAwaitRobustPublisher``: Executes all handlers in a sequential manner. If any of the handlers throws an exception, it will be caught and will thrown after all handlers are executed. Ensures that all handlers are executed even if one of them throws an exception.
88 |
89 | 2. ``ForeachAwaitStopOnFirstExceptionPublisher``: Executes all handlers in a sequential manner. If any of the handlers throws an exception, will be caught and be thrown immediately. Stops executing handlers if one of them throws an exception.
90 |
91 | 3. ``TaskWhenAllPublisher``: Executes all handlers in a concurrent manner. If any of the handlers throws an exception, it will be caught and will thrown after all handlers are executed. Ensures that all handlers are executed even if one of them throws an exception.
92 |
93 | ##### Custom publishers
94 | You can implement your own publisher by implementing ``INotificationPublisher`` interface.
95 |
96 | ```csharp
97 |
98 | public class MyOwnCustomPublisher : INotificationPublisher
99 | {
100 | public async Task PublishAsync(TNotification notification, IEnumerable> handlers, CancellationToken cancellationToken)
101 | where TNotification : INotification
102 | {
103 | // Implement
104 | }
105 | }
106 |
107 | ```
108 |
109 | And set it as default publisher:
110 |
111 | ```csharp
112 |
113 | cfg.SetDefaultNotificationPublisher();
114 |
115 | ```
116 |
117 | Or set it just for a particular notification:
118 |
119 | ```csharp
120 |
121 | cfg.SetNotificationPublisher();
122 |
123 | ```
124 |
125 | #### Define Requests and Handlers
126 | Define your requests and handlers as you would with any other mediator library.
127 | Notice that ``ValueTask`` is used instead of ``Task`` to reduce memory allocations in case of synchronus execution based on some condition.
128 |
129 |
130 | ```csharp
131 |
132 | public class MyRequest : IRequest
133 | {
134 | public string Name { get; set; }
135 | }
136 |
137 | public class MyRequestHandler : IRequestHandler
138 | {
139 | public ValueTask Handle(Request1 request, CancellationToken cancellationToken)
140 | {
141 | // Do some work.
142 |
143 | if(someCondition)
144 | {
145 | return "NimbleMediator";
146 | }
147 |
148 | var result = await SomeAsyncTask();
149 |
150 | return result;
151 | }
152 | }
153 |
154 | ```
155 |
156 | #### Define Notifications and Handlers
157 | Define your notifications and handlers as you would with any other mediator library.
158 | ``ValueTask``'s are not used for Notifications due asynchronous nature of the notifications.
159 |
160 | ```csharp
161 |
162 | public class MyNotification : INotification
163 | {
164 | public string Name { get; set; }
165 | }
166 |
167 | public class MyNotificationHandler : INotificationHandler
168 | {
169 | public Task Handle(MyNotification notification, CancellationToken cancellationToken)
170 | {
171 |
172 | await SomeAsyncTask();
173 | }
174 | }
175 |
176 | ```
177 |
178 | ### Performance & Benchmarks
179 |
180 | NimbleMediator is directly depends DI container to get handlers instead of relying on runtime reflection. This approach provides a significant performance advantage over other mediator libraries.
181 |
182 | NimbleMediator is currently **3,5** times faster and uses **16x** less memory than MediatR in some cases.
183 |
184 | 
185 |
186 | 
187 |
188 | 
189 |
190 |
191 | ### Contributing
192 |
193 | Contributions are welcome! Please feel free to submit a Pull Request. See the [Contributing Guidelines](CONTRIBUTING.md) for more information.
194 |
195 | ### License
196 | All contents of this package are licensed under the [Apache License 2.0](LICENSE).
197 |
--------------------------------------------------------------------------------
/src/NimbleMediator/ServiceExtensions/NimbleMediatorConfig.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using Microsoft.Extensions.DependencyInjection;
3 | using Microsoft.Extensions.DependencyInjection.Extensions;
4 | using NimbleMediator.Contracts;
5 | using NimbleMediator.Implementations;
6 | using NimbleMediator.NotificationPublishers;
7 |
8 | namespace NimbleMediator.ServiceExtensions;
9 |
10 | ///
11 | /// Configuration for .
12 | ///
13 | public class NimbleMediatorConfig
14 | {
15 | private readonly IServiceCollection _services;
16 | private readonly Dictionary _publisherTypeMappings = new();
17 | private Type _defaultPublisherType = typeof(ForeachAwaitRobustPublisher);
18 | private ServiceLifetime _defaultPublisherLifetime = ServiceLifetime.Singleton;
19 | private ServiceLifetime _mediatorLifetime = ServiceLifetime.Scoped;
20 | private readonly HashSet _assemblies = new();
21 |
22 | public NimbleMediatorConfig(IServiceCollection services) => _services = services;
23 |
24 | ///
25 | /// Registers all requests, notifications and respective handlers from the given assembly.
26 | ///
27 | ///
28 | public void RegisterServicesFromAssembly(Assembly assembly) => _assemblies.Add(assembly);
29 |
30 | ///
31 | /// Registers all requests, notifications and respective handlers from the given assemblies.
32 | ///
33 | ///
34 | public void RegisterServicesFromAssemblies(params Assembly[] assemblies)
35 | {
36 | foreach (var assembly in assemblies)
37 | {
38 | RegisterServicesFromAssembly(assembly);
39 | }
40 | }
41 |
42 | ///
43 | /// Sets the lifetime of the mediator implementation and IMediator, ISender, IPublisher interfaces.
44 | ///
45 | ///
46 | public void SetMediatorLifetime(ServiceLifetime lifetime) => _mediatorLifetime = lifetime;
47 |
48 | ///
49 | /// Sets the default publisher type for notifications.
50 | ///
51 | ///
52 | public void SetDefaultNotificationPublisherLifetime(ServiceLifetime lifetime) => _defaultPublisherLifetime = lifetime;
53 |
54 | ///
55 | /// Sets the default publisher type for notifications.
56 | ///
57 | ///
58 | public void SetDefaultNotificationPublisher(ServiceLifetime? lifetime = null)
59 | where TNotificationPublisher : INotificationPublisher
60 | {
61 | var publisherType = typeof(TNotificationPublisher);
62 | _defaultPublisherType = publisherType;
63 |
64 | if (lifetime is not null)
65 | {
66 | SetDefaultNotificationPublisherLifetime(lifetime.Value);
67 | }
68 | }
69 |
70 | ///
71 | /// Sets the publisher type for the given notification type.
72 | ///
73 | ///
74 | ///
75 | ///
76 | public void SetNotificationPublisher(ServiceLifetime? lifetime = null)
77 | where TNotification : INotification
78 | where TNotificationPublisher : INotificationPublisher
79 | {
80 | var notificationType = typeof(TNotification);
81 | var publisherType = typeof(TNotificationPublisher);
82 |
83 | _publisherTypeMappings[notificationType] = publisherType;
84 |
85 | TryAdd(_services, publisherType, lifetime ?? _defaultPublisherLifetime);
86 | }
87 |
88 | ///
89 | /// The public Register methods are actually just adds assemblies to a hashset, not registering them immediately.
90 | /// This is needed for the config to not to be dependent on the order of the calls.
91 | /// This method is called internally to register all marked assemblies.
92 | ///
93 | internal void RegisterServicesInternal()
94 | {
95 | RegisterMediatorImplementationAndInterfaces(_services, _mediatorLifetime, _publisherTypeMappings);
96 |
97 | foreach (var assembly in _assemblies)
98 | {
99 | RegisterRequestsFromAssembly(assembly);
100 | RegisterNotificationsFromAssembly(assembly);
101 | }
102 |
103 | // Register default publisher type if not provided by user.
104 | TryAdd(_services, _defaultPublisherType, _defaultPublisherLifetime);
105 | }
106 |
107 | private static void RegisterMediatorImplementationAndInterfaces(IServiceCollection services, ServiceLifetime mediatorLifetime, Dictionary publisherTypeMappings)
108 | {
109 | services.Add(
110 | new ServiceDescriptor(typeof(Mediator),
111 | sp => new Mediator(sp, publisherTypeMappings),
112 | mediatorLifetime));
113 |
114 | services.Add(
115 | new ServiceDescriptor(typeof(IMediator),
116 | sp => sp.GetRequiredService(),
117 | mediatorLifetime));
118 |
119 | services.Add(
120 | new ServiceDescriptor(typeof(ISender),
121 | sp => sp.GetRequiredService(),
122 | mediatorLifetime));
123 |
124 | services.Add(
125 | new ServiceDescriptor(typeof(IPublisher),
126 | sp => sp.GetRequiredService(),
127 | mediatorLifetime));
128 | }
129 |
130 | private void RegisterRequestsFromAssembly(Assembly assembly)
131 | {
132 | foreach (var type in assembly.GetTypes())
133 | {
134 | foreach (var @interface in type.GetInterfaces())
135 | {
136 | if (@interface.IsGenericType
137 | && (@interface.GetGenericTypeDefinition() == typeof(IRequestHandler<,>)
138 | || @interface.GetGenericTypeDefinition() == typeof(IRequestHandler<>)))
139 | {
140 | _services.AddTransient(@interface, type);
141 | }
142 | }
143 | }
144 | }
145 |
146 | private void RegisterNotificationsFromAssembly(Assembly assembly)
147 | {
148 | foreach (var type in assembly.GetTypes())
149 | {
150 | foreach (var @interface in type.GetInterfaces())
151 | {
152 | if (@interface.IsGenericType && @interface.GetGenericTypeDefinition() == typeof(INotificationHandler<>))
153 | {
154 | var notificationType = @interface.GetGenericArguments()[0];
155 | var handlerType = type;
156 |
157 | _services.AddTransient(@interface, handlerType);
158 |
159 | _publisherTypeMappings.TryAdd(notificationType, _defaultPublisherType);
160 |
161 | }
162 | }
163 | }
164 | }
165 |
166 | private static void TryAdd(IServiceCollection services, Type type, ServiceLifetime lifetime)
167 | {
168 | switch (lifetime)
169 | {
170 | case ServiceLifetime.Singleton:
171 | services.TryAddSingleton(type);
172 | break;
173 |
174 | case ServiceLifetime.Scoped:
175 | services.TryAddScoped(type);
176 | break;
177 |
178 | case ServiceLifetime.Transient:
179 | services.TryAddTransient(type);
180 | break;
181 | }
182 | }
183 | }
--------------------------------------------------------------------------------
/src/tests/NimbleMediator.Tests/ServiceCollectionExtensionsTests.cs:
--------------------------------------------------------------------------------
1 | using System.Runtime.Serialization;
2 | using Microsoft.Extensions.DependencyInjection;
3 | using Microsoft.Extensions.DependencyInjection.Extensions;
4 | using NimbleMediator.Contracts;
5 | using NimbleMediator.NotificationPublishers;
6 | using NimbleMediator.ServiceExtensions;
7 | namespace NimbleMediator.Tests;
8 |
9 | public class ServiceCollectionExtensionsTests
10 | {
11 |
12 | public ServiceCollectionExtensionsTests()
13 | {
14 | var services = new ServiceCollection();
15 |
16 | services.AddNimbleMediator(config => config.RegisterServicesFromAssembly(typeof(MyRequestWithResponse).Assembly));
17 |
18 | _serviceProvider = services.BuildServiceProvider();
19 | }
20 |
21 | private readonly IServiceProvider _serviceProvider;
22 |
23 | [Fact]
24 | public void DI_Should_Resolve_ISender()
25 | {
26 | var sender = _serviceProvider.GetService();
27 |
28 | Assert.NotNull(sender);
29 | Assert.True(sender is ISender s);
30 | }
31 |
32 | [Fact]
33 | public void ISender_Should_Be_Scoped_By_Default()
34 | {
35 |
36 | var serviceCollection = new ServiceCollection();
37 | serviceCollection.AddNimbleMediator(config =>
38 | {
39 | config.RegisterServicesFromAssembly(typeof(MyRequestWithResponse).Assembly);
40 | });
41 |
42 | var sender = serviceCollection.FirstOrDefault(x => x.ServiceType == typeof(ISender));
43 |
44 | Assert.NotNull(sender);
45 | Assert.True(sender.Lifetime == ServiceLifetime.Scoped);
46 | }
47 |
48 | [Fact]
49 | public void DI_Should_Resolve_IPublisher()
50 | {
51 | var publisher = _serviceProvider.GetService();
52 |
53 | Assert.NotNull(publisher);
54 | Assert.True(publisher is IPublisher p);
55 | }
56 |
57 | [Fact]
58 | public void IPublisher_Should_Be_Scoped_By_Default()
59 | {
60 |
61 | var serviceCollection = new ServiceCollection();
62 | serviceCollection.AddNimbleMediator(config =>
63 | {
64 | config.RegisterServicesFromAssembly(typeof(MyRequestWithResponse).Assembly);
65 | });
66 |
67 | var publisher = serviceCollection.FirstOrDefault(x => x.ServiceType == typeof(IPublisher));
68 |
69 | Assert.NotNull(publisher);
70 | Assert.True(publisher.Lifetime == ServiceLifetime.Scoped);
71 | }
72 |
73 | [Fact]
74 | public void DI_Should_Resolve_IMediator()
75 | {
76 | var mediator = _serviceProvider.GetService();
77 |
78 | Assert.NotNull(mediator);
79 | Assert.True(mediator is IMediator m);
80 | }
81 |
82 | [Fact]
83 | public void IMediator_Should_Be_Scoped_By_Default()
84 | {
85 |
86 | var serviceCollection = new ServiceCollection();
87 | serviceCollection.AddNimbleMediator(config =>
88 | {
89 | config.RegisterServicesFromAssembly(typeof(MyRequestWithResponse).Assembly);
90 | });
91 |
92 | var mediator = serviceCollection.FirstOrDefault(x => x.ServiceType == typeof(IMediator));
93 |
94 | Assert.NotNull(mediator);
95 | Assert.True(mediator.Lifetime == ServiceLifetime.Scoped);
96 | }
97 |
98 | [Fact]
99 | public void DI_Should_Resolve_RequestHandler_TResponse()
100 | {
101 | var requestHandler = _serviceProvider.GetService>();
102 |
103 | Assert.NotNull(requestHandler);
104 | }
105 |
106 | [Fact]
107 | public void DI_Should_Resolve_RequestHandler()
108 | {
109 | var requestHandler = _serviceProvider.GetService>();
110 |
111 | Assert.NotNull(requestHandler);
112 | }
113 |
114 | [Fact]
115 | public void DI_Should_Resolve_ForeachAwaitRobustPublisher()
116 | {
117 |
118 | var services = new ServiceCollection();
119 |
120 | services.AddNimbleMediator(config =>
121 | {
122 | config.SetDefaultNotificationPublisher();
123 | config.RegisterServicesFromAssembly(typeof(MyRequestWithResponse).Assembly);
124 | });
125 |
126 | var serviceProvider = services.BuildServiceProvider();
127 | var notificationHandler = serviceProvider.GetService();
128 |
129 | Assert.NotNull(notificationHandler);
130 | Assert.True(notificationHandler is ForeachAwaitRobustPublisher publisher);
131 | }
132 |
133 | [Fact]
134 | public void DI_Should_Resolve_ForeachAwaitStopOnFirstExceptionPublisher()
135 | {
136 | var services = new ServiceCollection();
137 |
138 | services.AddNimbleMediator(config =>
139 | {
140 | config.SetDefaultNotificationPublisher();
141 | config.RegisterServicesFromAssembly(typeof(MyRequestWithResponse).Assembly);
142 | });
143 |
144 | var serviceProvider = services.BuildServiceProvider();
145 | var notificationHandler = serviceProvider.GetService();
146 |
147 | Assert.NotNull(notificationHandler);
148 | Assert.True(notificationHandler is ForeachAwaitStopOnFirstExceptionPublisher publisher);
149 | }
150 |
151 | [Fact]
152 | public void DI_Should_Resolve_TaskWhenAllPublisher()
153 | {
154 | var services = new ServiceCollection();
155 |
156 | services.AddNimbleMediator(config =>
157 | {
158 | config.SetDefaultNotificationPublisher();
159 | config.RegisterServicesFromAssembly(typeof(MyRequestWithResponse).Assembly);
160 | });
161 |
162 | var serviceProvider = services.BuildServiceProvider();
163 | var notificationHandler = serviceProvider.GetService();
164 |
165 | Assert.NotNull(notificationHandler);
166 | Assert.True(notificationHandler is TaskWhenAllPublisher publisher);
167 | }
168 |
169 | [Fact]
170 | public void Mediator_Should_Have_Singleton_If_Set_Explicitly()
171 | {
172 | var services = new ServiceCollection();
173 |
174 | services.AddNimbleMediator(config =>
175 | {
176 | config.SetMediatorLifetime(ServiceLifetime.Singleton);
177 | config.RegisterServicesFromAssembly(typeof(MyRequestWithResponse).Assembly);
178 | });
179 |
180 | var mediator = services.FirstOrDefault(x => x.ServiceType == typeof(IMediator));
181 |
182 | Assert.NotNull(mediator);
183 | Assert.True(mediator.Lifetime == ServiceLifetime.Singleton);
184 | }
185 |
186 | [Fact]
187 | public void Mediator_Should_Have_Scoped_If_Set_Explicitly()
188 | {
189 | var services = new ServiceCollection();
190 |
191 | services.AddNimbleMediator(config =>
192 | {
193 | config.SetMediatorLifetime(ServiceLifetime.Scoped);
194 | config.RegisterServicesFromAssembly(typeof(MyRequestWithResponse).Assembly);
195 | });
196 |
197 | var mediator = services.FirstOrDefault(x => x.ServiceType == typeof(IMediator));
198 |
199 | Assert.NotNull(mediator);
200 | Assert.True(mediator.Lifetime == ServiceLifetime.Scoped);
201 | }
202 |
203 | [Fact]
204 | public void Mediator_Should_Have_Transient_If_Set_Explicitly()
205 | {
206 | var services = new ServiceCollection();
207 |
208 | services.AddNimbleMediator(config =>
209 | {
210 | config.SetMediatorLifetime(ServiceLifetime.Transient);
211 | config.RegisterServicesFromAssembly(typeof(MyRequestWithResponse).Assembly);
212 | });
213 |
214 | var mediator = services.FirstOrDefault(x => x.ServiceType == typeof(IMediator));
215 |
216 | Assert.NotNull(mediator);
217 | Assert.True(mediator.Lifetime == ServiceLifetime.Transient);
218 | }
219 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 | ##
4 | ## Get latest from `dotnet new gitignore`
5 |
6 | # User-specific files
7 | *.rsuser
8 | *.suo
9 | *.user
10 | *.userosscache
11 | *.sln.docstates
12 |
13 | # User-specific files (MonoDevelop/Xamarin Studio)
14 | *.userprefs
15 |
16 | # Mono auto generated files
17 | mono_crash.*
18 |
19 | # Build results
20 | [Dd]ebug/
21 | [Dd]ebugPublic/
22 | [Rr]elease/
23 | [Rr]eleases/
24 | x64/
25 | x86/
26 | [Ww][Ii][Nn]32/
27 | [Aa][Rr][Mm]/
28 | [Aa][Rr][Mm]64/
29 | bld/
30 | [Bb]in/
31 | [Oo]bj/
32 | [Ll]og/
33 | [Ll]ogs/
34 |
35 | # Visual Studio 2015/2017 cache/options directory
36 | .vs/
37 | # Uncomment if you have tasks that create the project's static files in wwwroot
38 | #wwwroot/
39 |
40 | # Visual Studio 2017 auto generated files
41 | Generated\ Files/
42 |
43 | # MSTest test Results
44 | [Tt]est[Rr]esult*/
45 | [Bb]uild[Ll]og.*
46 |
47 | # NUnit
48 | *.VisualState.xml
49 | TestResult.xml
50 | nunit-*.xml
51 |
52 | # Build Results of an ATL Project
53 | [Dd]ebugPS/
54 | [Rr]eleasePS/
55 | dlldata.c
56 |
57 | # Benchmark Results
58 | BenchmarkDotNet.Artifacts/
59 |
60 | # .NET
61 | project.lock.json
62 | project.fragment.lock.json
63 | artifacts/
64 |
65 | # Tye
66 | .tye/
67 |
68 | # ASP.NET Scaffolding
69 | ScaffoldingReadMe.txt
70 |
71 | # StyleCop
72 | StyleCopReport.xml
73 |
74 | # Files built by Visual Studio
75 | *_i.c
76 | *_p.c
77 | *_h.h
78 | *.ilk
79 | *.meta
80 | *.obj
81 | *.iobj
82 | *.pch
83 | *.pdb
84 | *.ipdb
85 | *.pgc
86 | *.pgd
87 | *.rsp
88 | *.sbr
89 | *.tlb
90 | *.tli
91 | *.tlh
92 | *.tmp
93 | *.tmp_proj
94 | *_wpftmp.csproj
95 | *.log
96 | *.tlog
97 | *.vspscc
98 | *.vssscc
99 | .builds
100 | *.pidb
101 | *.svclog
102 | *.scc
103 |
104 | # Chutzpah Test files
105 | _Chutzpah*
106 |
107 | # Visual C++ cache files
108 | ipch/
109 | *.aps
110 | *.ncb
111 | *.opendb
112 | *.opensdf
113 | *.sdf
114 | *.cachefile
115 | *.VC.db
116 | *.VC.VC.opendb
117 |
118 | # Visual Studio profiler
119 | *.psess
120 | *.vsp
121 | *.vspx
122 | *.sap
123 |
124 | # Visual Studio Trace Files
125 | *.e2e
126 |
127 | # TFS 2012 Local Workspace
128 | $tf/
129 |
130 | # Guidance Automation Toolkit
131 | *.gpState
132 |
133 | # ReSharper is a .NET coding add-in
134 | _ReSharper*/
135 | *.[Rr]e[Ss]harper
136 | *.DotSettings.user
137 |
138 | # TeamCity is a build add-in
139 | _TeamCity*
140 |
141 | # DotCover is a Code Coverage Tool
142 | *.dotCover
143 |
144 | # AxoCover is a Code Coverage Tool
145 | .axoCover/*
146 | !.axoCover/settings.json
147 |
148 | # Coverlet is a free, cross platform Code Coverage Tool
149 | coverage*.json
150 | coverage*.xml
151 | coverage*.info
152 |
153 | # Visual Studio code coverage results
154 | *.coverage
155 | *.coveragexml
156 |
157 | # NCrunch
158 | _NCrunch_*
159 | .*crunch*.local.xml
160 | nCrunchTemp_*
161 |
162 | # MightyMoose
163 | *.mm.*
164 | AutoTest.Net/
165 |
166 | # Web workbench (sass)
167 | .sass-cache/
168 |
169 | # Installshield output folder
170 | [Ee]xpress/
171 |
172 | # DocProject is a documentation generator add-in
173 | DocProject/buildhelp/
174 | DocProject/Help/*.HxT
175 | DocProject/Help/*.HxC
176 | DocProject/Help/*.hhc
177 | DocProject/Help/*.hhk
178 | DocProject/Help/*.hhp
179 | DocProject/Help/Html2
180 | DocProject/Help/html
181 |
182 | # Click-Once directory
183 | publish/
184 |
185 | # Publish Web Output
186 | *.[Pp]ublish.xml
187 | *.azurePubxml
188 | # Note: Comment the next line if you want to checkin your web deploy settings,
189 | # but database connection strings (with potential passwords) will be unencrypted
190 | *.pubxml
191 | *.publishproj
192 |
193 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
194 | # checkin your Azure Web App publish settings, but sensitive information contained
195 | # in these scripts will be unencrypted
196 | PublishScripts/
197 |
198 | # NuGet Packages
199 | *.nupkg
200 | # NuGet Symbol Packages
201 | *.snupkg
202 | # The packages folder can be ignored because of Package Restore
203 | **/[Pp]ackages/*
204 | # except build/, which is used as an MSBuild target.
205 | !**/[Pp]ackages/build/
206 | # Uncomment if necessary however generally it will be regenerated when needed
207 | #!**/[Pp]ackages/repositories.config
208 | # NuGet v3's project.json files produces more ignorable files
209 | *.nuget.props
210 | *.nuget.targets
211 |
212 | # Microsoft Azure Build Output
213 | csx/
214 | *.build.csdef
215 |
216 | # Microsoft Azure Emulator
217 | ecf/
218 | rcf/
219 |
220 | # Windows Store app package directories and files
221 | AppPackages/
222 | BundleArtifacts/
223 | Package.StoreAssociation.xml
224 | _pkginfo.txt
225 | *.appx
226 | *.appxbundle
227 | *.appxupload
228 |
229 | # Visual Studio cache files
230 | # files ending in .cache can be ignored
231 | *.[Cc]ache
232 | # but keep track of directories ending in .cache
233 | !?*.[Cc]ache/
234 |
235 | # Others
236 | ClientBin/
237 | ~$*
238 | *~
239 | *.dbmdl
240 | *.dbproj.schemaview
241 | *.jfm
242 | *.pfx
243 | *.publishsettings
244 | orleans.codegen.cs
245 |
246 | # Including strong name files can present a security risk
247 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
248 | #*.snk
249 |
250 | # Since there are multiple workflows, uncomment next line to ignore bower_components
251 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
252 | #bower_components/
253 |
254 | # RIA/Silverlight projects
255 | Generated_Code/
256 |
257 | # Backup & report files from converting an old project file
258 | # to a newer Visual Studio version. Backup files are not needed,
259 | # because we have git ;-)
260 | _UpgradeReport_Files/
261 | Backup*/
262 | UpgradeLog*.XML
263 | UpgradeLog*.htm
264 | ServiceFabricBackup/
265 | *.rptproj.bak
266 |
267 | # SQL Server files
268 | *.mdf
269 | *.ldf
270 | *.ndf
271 |
272 | # Business Intelligence projects
273 | *.rdl.data
274 | *.bim.layout
275 | *.bim_*.settings
276 | *.rptproj.rsuser
277 | *- [Bb]ackup.rdl
278 | *- [Bb]ackup ([0-9]).rdl
279 | *- [Bb]ackup ([0-9][0-9]).rdl
280 |
281 | # Microsoft Fakes
282 | FakesAssemblies/
283 |
284 | # GhostDoc plugin setting file
285 | *.GhostDoc.xml
286 |
287 | # Node.js Tools for Visual Studio
288 | .ntvs_analysis.dat
289 | node_modules/
290 |
291 | # Visual Studio 6 build log
292 | *.plg
293 |
294 | # Visual Studio 6 workspace options file
295 | *.opt
296 |
297 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
298 | *.vbw
299 |
300 | # Visual Studio 6 auto-generated project file (contains which files were open etc.)
301 | *.vbp
302 |
303 | # Visual Studio 6 workspace and project file (working project files containing files to include in project)
304 | *.dsw
305 | *.dsp
306 |
307 | # Visual Studio 6 technical files
308 | *.ncb
309 | *.aps
310 |
311 | # Visual Studio LightSwitch build output
312 | **/*.HTMLClient/GeneratedArtifacts
313 | **/*.DesktopClient/GeneratedArtifacts
314 | **/*.DesktopClient/ModelManifest.xml
315 | **/*.Server/GeneratedArtifacts
316 | **/*.Server/ModelManifest.xml
317 | _Pvt_Extensions
318 |
319 | # Paket dependency manager
320 | .paket/paket.exe
321 | paket-files/
322 |
323 | # FAKE - F# Make
324 | .fake/
325 |
326 | # CodeRush personal settings
327 | .cr/personal
328 |
329 | # Python Tools for Visual Studio (PTVS)
330 | __pycache__/
331 | *.pyc
332 |
333 | # Cake - Uncomment if you are using it
334 | # tools/**
335 | # !tools/packages.config
336 |
337 | # Tabs Studio
338 | *.tss
339 |
340 | # Telerik's JustMock configuration file
341 | *.jmconfig
342 |
343 | # BizTalk build output
344 | *.btp.cs
345 | *.btm.cs
346 | *.odx.cs
347 | *.xsd.cs
348 |
349 | # OpenCover UI analysis results
350 | OpenCover/
351 |
352 | # Azure Stream Analytics local run output
353 | ASALocalRun/
354 |
355 | # MSBuild Binary and Structured Log
356 | *.binlog
357 |
358 | # NVidia Nsight GPU debugger configuration file
359 | *.nvuser
360 |
361 | # MFractors (Xamarin productivity tool) working folder
362 | .mfractor/
363 |
364 | # Local History for Visual Studio
365 | .localhistory/
366 |
367 | # Visual Studio History (VSHistory) files
368 | .vshistory/
369 |
370 | # BeatPulse healthcheck temp database
371 | healthchecksdb
372 |
373 | # Backup folder for Package Reference Convert tool in Visual Studio 2017
374 | MigrationBackup/
375 |
376 | # Ionide (cross platform F# VS Code tools) working folder
377 | .ionide/
378 |
379 | # Fody - auto-generated XML schema
380 | FodyWeavers.xsd
381 |
382 | # VS Code files for those working on multiple tools
383 | .vscode/*
384 | !.vscode/settings.json
385 | !.vscode/tasks.json
386 | !.vscode/launch.json
387 | !.vscode/extensions.json
388 | *.code-workspace
389 |
390 | # Local History for Visual Studio Code
391 | .history/
392 |
393 | # Windows Installer files from build outputs
394 | *.cab
395 | *.msi
396 | *.msix
397 | *.msm
398 | *.msp
399 |
400 | # JetBrains Rider
401 | *.sln.iml
402 |
403 | ##
404 | ## Visual studio for Mac
405 | ##
406 |
407 |
408 | # globs
409 | Makefile.in
410 | *.userprefs
411 | *.usertasks
412 | config.make
413 | config.status
414 | aclocal.m4
415 | install-sh
416 | autom4te.cache/
417 | *.tar.gz
418 | tarballs/
419 | test-results/
420 |
421 | # Mac bundle stuff
422 | *.dmg
423 | *.app
424 |
425 | # content below from: https://github.com/github/gitignore/blob/master/Global/macOS.gitignore
426 | # General
427 | .DS_Store
428 | .AppleDouble
429 | .LSOverride
430 |
431 | # Icon must end with two \r
432 | Icon
433 |
434 |
435 | # Thumbnails
436 | ._*
437 |
438 | # Files that might appear in the root of a volume
439 | .DocumentRevisions-V100
440 | .fseventsd
441 | .Spotlight-V100
442 | .TemporaryItems
443 | .Trashes
444 | .VolumeIcon.icns
445 | .com.apple.timemachine.donotpresent
446 |
447 | # Directories potentially created on remote AFP share
448 | .AppleDB
449 | .AppleDesktop
450 | Network Trash Folder
451 | Temporary Items
452 | .apdisk
453 |
454 | # content below from: https://github.com/github/gitignore/blob/master/Global/Windows.gitignore
455 | # Windows thumbnail cache files
456 | Thumbs.db
457 | ehthumbs.db
458 | ehthumbs_vista.db
459 |
460 | # Dump file
461 | *.stackdump
462 |
463 | # Folder config file
464 | [Dd]esktop.ini
465 |
466 | # Recycle Bin used on file shares
467 | $RECYCLE.BIN/
468 |
469 | # Windows Installer files
470 | *.cab
471 | *.msi
472 | *.msix
473 | *.msm
474 | *.msp
475 |
476 | # Windows shortcuts
477 | *.lnk
478 |
479 | # Vim temporary swap files
480 | *.swp
481 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 |
2 | [*.cs]
3 | #### Naming styles ####
4 |
5 | # Naming rules
6 |
7 | dotnet_naming_rule.private_or_internal_field_should_be_put___before_private.severity = error
8 | dotnet_naming_rule.private_or_internal_field_should_be_put___before_private.symbols = private_or_internal_field
9 | dotnet_naming_rule.private_or_internal_field_should_be_put___before_private.style = put___before_private
10 |
11 | dotnet_naming_rule.private_or_internal_static_field_should_be_put___before_private.severity = error
12 | dotnet_naming_rule.private_or_internal_static_field_should_be_put___before_private.symbols = private_or_internal_static_field
13 | dotnet_naming_rule.private_or_internal_static_field_should_be_put___before_private.style = put___before_private
14 |
15 | dotnet_naming_rule.interface_should_be_begins_with_i.severity = error
16 | dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface
17 | dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i
18 |
19 | dotnet_naming_rule.types_should_be_pascal_case.severity = warning
20 | dotnet_naming_rule.types_should_be_pascal_case.symbols = types
21 | dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case
22 |
23 | # Symbol specifications
24 |
25 | dotnet_naming_symbols.private_or_internal_field.applicable_kinds = field
26 | dotnet_naming_symbols.private_or_internal_field.applicable_accessibilities = internal, private, private_protected
27 | dotnet_naming_symbols.private_or_internal_field.required_modifiers =
28 |
29 | dotnet_naming_symbols.private_or_internal_static_field.applicable_kinds = field
30 | dotnet_naming_symbols.private_or_internal_static_field.applicable_accessibilities = internal, private, private_protected
31 | dotnet_naming_symbols.private_or_internal_static_field.required_modifiers = static
32 |
33 | dotnet_naming_symbols.interface.applicable_kinds = interface
34 | dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
35 | dotnet_naming_symbols.interface.required_modifiers =
36 |
37 | dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
38 | dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
39 | dotnet_naming_symbols.types.required_modifiers =
40 |
41 | # Naming styles
42 |
43 | dotnet_naming_style.put___before_private.required_prefix = _
44 | dotnet_naming_style.put___before_private.required_suffix =
45 | dotnet_naming_style.put___before_private.word_separator =
46 | dotnet_naming_style.put___before_private.capitalization = camel_case
47 |
48 | dotnet_naming_style.put___before_private.required_prefix = _
49 | dotnet_naming_style.put___before_private.required_suffix =
50 | dotnet_naming_style.put___before_private.word_separator =
51 | dotnet_naming_style.put___before_private.capitalization = camel_case
52 |
53 | dotnet_naming_style.begins_with_i.required_prefix = I
54 | dotnet_naming_style.begins_with_i.required_suffix =
55 | dotnet_naming_style.begins_with_i.word_separator =
56 | dotnet_naming_style.begins_with_i.capitalization = pascal_case
57 |
58 | dotnet_naming_style.pascal_case.required_prefix =
59 | dotnet_naming_style.pascal_case.required_suffix =
60 | dotnet_naming_style.pascal_case.word_separator =
61 | dotnet_naming_style.pascal_case.capitalization = pascal_case
62 | csharp_indent_labels = one_less_than_current
63 | csharp_using_directive_placement = outside_namespace:error
64 | csharp_prefer_simple_using_statement = true:suggestion
65 | csharp_prefer_braces = true:warning
66 | csharp_style_namespace_declarations = file_scoped:warning
67 | csharp_style_prefer_method_group_conversion = true:silent
68 | csharp_style_prefer_top_level_statements = true:silent
69 | csharp_style_expression_bodied_methods = when_on_single_line:suggestion
70 | csharp_style_expression_bodied_constructors = when_on_single_line:suggestion
71 | csharp_style_expression_bodied_operators = when_on_single_line:silent
72 | csharp_style_expression_bodied_properties = when_on_single_line:warning
73 | csharp_style_expression_bodied_indexers = when_on_single_line:warning
74 | csharp_style_expression_bodied_accessors = true:warning
75 | csharp_style_expression_bodied_lambdas = true:suggestion
76 | csharp_style_expression_bodied_local_functions = true:suggestion
77 | csharp_space_around_binary_operators = before_and_after
78 | csharp_style_throw_expression = true:warning
79 | csharp_style_prefer_null_check_over_type_check = true:warning
80 | csharp_prefer_simple_default_expression = true:warning
81 | csharp_style_prefer_local_over_anonymous_function = true:warning
82 | csharp_style_prefer_index_operator = true:warning
83 | csharp_style_prefer_range_operator = true:warning
84 | csharp_style_implicit_object_creation_when_type_is_apparent = true:warning
85 | csharp_style_prefer_tuple_swap = true:suggestion
86 | csharp_style_prefer_utf8_string_literals = true:suggestion
87 | csharp_style_inlined_variable_declaration = false:warning
88 | csharp_style_deconstructed_variable_declaration = true:warning
89 | csharp_style_unused_value_assignment_preference = discard_variable:suggestion
90 | csharp_style_unused_value_expression_statement_preference = discard_variable:none
91 | csharp_prefer_static_local_function = true:warning
92 | csharp_style_prefer_readonly_struct = true:warning
93 | csharp_style_prefer_readonly_struct_member = true:suggestion
94 | csharp_style_allow_embedded_statements_on_same_line_experimental = true:suggestion
95 | csharp_style_allow_blank_lines_between_consecutive_braces_experimental = true:silent
96 | csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true:silent
97 | csharp_style_allow_blank_line_after_token_in_conditional_expression_experimental = true:silent
98 | csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental = true:silent
99 | csharp_style_conditional_delegate_call = true:warning
100 | csharp_style_prefer_switch_expression = true:warning
101 | csharp_style_prefer_pattern_matching = true:warning
102 | csharp_style_pattern_matching_over_is_with_cast_check = true:warning
103 | csharp_style_pattern_matching_over_as_with_null_check = true:warning
104 | csharp_style_prefer_not_pattern = true:warning
105 | csharp_style_prefer_extended_property_pattern = true:suggestion
106 | csharp_style_var_for_built_in_types = false:silent
107 | csharp_style_var_when_type_is_apparent = true:silent
108 | csharp_style_var_elsewhere = false:silent
109 | dotnet_diagnostic.CA1001.severity = warning
110 | dotnet_diagnostic.CA1032.severity = suggestion
111 | dotnet_diagnostic.CA1507.severity = warning
112 | dotnet_diagnostic.CA2016.severity = warning
113 |
114 | # IDE0058: Expression value is never used
115 | dotnet_diagnostic.IDE0058.severity = none
116 | csharp_style_prefer_primary_constructors = true:suggestion
117 | dotnet_diagnostic.S2437.severity = warning
118 | dotnet_diagnostic.IDE0051.severity = warning
119 | dotnet_diagnostic.IDE0052.severity = warning
120 |
121 | [*.vb]
122 | #### Naming styles ####
123 |
124 | # Naming rules
125 |
126 | dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion
127 | dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface
128 | dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i
129 |
130 | dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion
131 | dotnet_naming_rule.types_should_be_pascal_case.symbols = types
132 | dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case
133 |
134 | # Symbol specifications
135 |
136 | dotnet_naming_symbols.interface.applicable_kinds = interface
137 | dotnet_naming_symbols.interface.applicable_accessibilities = public, friend, private, protected, protected_friend, private_protected
138 | dotnet_naming_symbols.interface.required_modifiers =
139 |
140 | dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
141 | dotnet_naming_symbols.types.applicable_accessibilities = public, friend, private, protected, protected_friend, private_protected
142 | dotnet_naming_symbols.types.required_modifiers =
143 |
144 | # Naming styles
145 |
146 | dotnet_naming_style.begins_with_i.required_prefix = I
147 | dotnet_naming_style.begins_with_i.required_suffix =
148 | dotnet_naming_style.begins_with_i.word_separator =
149 | dotnet_naming_style.begins_with_i.capitalization = pascal_case
150 |
151 | dotnet_naming_style.pascal_case.required_prefix =
152 | dotnet_naming_style.pascal_case.required_suffix =
153 | dotnet_naming_style.pascal_case.word_separator =
154 | dotnet_naming_style.pascal_case.capitalization = pascal_case
155 |
156 | [*.{cs,vb}]
157 | #### Naming styles ####
158 |
159 | # Naming rules
160 |
161 | dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = warning
162 | dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
163 | dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case
164 |
165 | # Symbol specifications
166 |
167 | dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
168 | dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
169 | dotnet_naming_symbols.non_field_members.required_modifiers =
170 |
171 | # Naming styles
172 |
173 | dotnet_naming_style.pascal_case.required_prefix =
174 | dotnet_naming_style.pascal_case.required_suffix =
175 | dotnet_naming_style.pascal_case.word_separator =
176 | dotnet_naming_style.pascal_case.capitalization = pascal_case
177 | dotnet_style_operator_placement_when_wrapping = beginning_of_line
178 | tab_width = 4
179 | indent_size = 4
180 | end_of_line = crlf
181 | dotnet_style_coalesce_expression = true:warning
182 | dotnet_style_null_propagation = true:warning
183 | dotnet_style_prefer_is_null_check_over_reference_equality_method = true:warning
184 | indent_style = space
185 | dotnet_style_prefer_auto_properties = true:silent
186 | dotnet_style_object_initializer = true:warning
187 | dotnet_style_collection_initializer = true:warning
188 | dotnet_style_prefer_simplified_boolean_expressions = true:warning
189 | dotnet_style_prefer_conditional_expression_over_assignment = true:warning
190 | dotnet_style_prefer_conditional_expression_over_return = true:suggestion
191 | dotnet_style_explicit_tuple_names = true:suggestion
192 | dotnet_style_prefer_inferred_tuple_names = false:suggestion
193 | dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion
194 | dotnet_style_prefer_compound_assignment = true:warning
195 | dotnet_style_prefer_simplified_interpolation = true:suggestion
196 | dotnet_style_namespace_match_folder = true:warning
197 | dotnet_style_readonly_field = true:warning
198 | dotnet_style_predefined_type_for_locals_parameters_members = true:silent
199 | dotnet_style_predefined_type_for_member_access = true:silent
200 | dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent
201 | dotnet_style_allow_multiple_blank_lines_experimental = true:silent
202 | dotnet_style_allow_statement_immediately_after_block_experimental = false:silent
203 | dotnet_code_quality_unused_parameters = all:warning
204 | dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent
205 | dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent
206 | dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent
207 | dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent
208 | dotnet_style_qualification_for_field = false:warning
209 | dotnet_style_qualification_for_property = false:warning
210 | dotnet_style_qualification_for_method = false:warning
211 | dotnet_style_qualification_for_event = false:warning
212 | dotnet_diagnostic.CA1031.severity = suggestion
213 | dotnet_diagnostic.CA1062.severity = suggestion
214 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------