Skip to content

Commit 0ba427b

Browse files
authored
Merge pull request #28 from KuraiAndras/feature/fast-mediatr
Add Fast mediatr
2 parents d89b430 + fa6cb2f commit 0ba427b

File tree

18 files changed

+424
-11
lines changed

18 files changed

+424
-11
lines changed

.github/workflows/publish.yml

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,28 +17,28 @@ jobs:
1717
with:
1818
PROJECT_FILE_PATH: Injecter/Injecter.csproj
1919
TAG_COMMIT: "false"
20-
NUGET_KEY: ${{secrets.NUGET_API_KEY}} # nuget.org API key
20+
NUGET_KEY: ${{secrets.NUGET_API_KEY}}
2121

2222
- name: Publish Injecter.Avalonia to nuget.org
2323
uses: rohith/publish-nuget@v2
2424
with:
2525
PROJECT_FILE_PATH: Injecter.Avalonia/Injecter.Avalonia.csproj
2626
TAG_COMMIT: "false"
27-
NUGET_KEY: ${{secrets.NUGET_API_KEY}} # nuget.org API key
27+
NUGET_KEY: ${{secrets.NUGET_API_KEY}}
2828

2929
- name: Publish Injecter.Wpf to nuget.org
3030
uses: rohith/publish-nuget@v2
3131
with:
3232
PROJECT_FILE_PATH: Injecter.Wpf/Injecter.Wpf.csproj
3333
TAG_COMMIT: "false"
34-
NUGET_KEY: ${{secrets.NUGET_API_KEY}} # nuget.org API key
34+
NUGET_KEY: ${{secrets.NUGET_API_KEY}}
3535

3636
- name: Publish Injecter.Xamarin.Forms to nuget.org
3737
uses: rohith/publish-nuget@v2
3838
with:
3939
PROJECT_FILE_PATH: Injecter.Xamarin.Forms/Injecter.Xamarin.Forms.csproj
4040
TAG_COMMIT: "false"
41-
NUGET_KEY: ${{secrets.NUGET_API_KEY}} # nuget.org API key
41+
NUGET_KEY: ${{secrets.NUGET_API_KEY}}
4242

4343
- name: Publish Injecter.Unity to openupm
4444
uses: Klemensas/action-autotag@stable
@@ -51,4 +51,11 @@ jobs:
5151
with:
5252
PROJECT_FILE_PATH: Injecter.Uwp/Injecter.Uwp.csproj
5353
TAG_COMMIT: "false"
54-
NUGET_KEY: ${{secrets.NUGET_API_KEY}} # nuget.org API key
54+
NUGET_KEY: ${{secrets.NUGET_API_KEY}}
55+
56+
- name: Publish Injecter.FastMediatR to nuget.org
57+
uses: rohith/publish-nuget@v2
58+
with:
59+
PROJECT_FILE_PATH: Injecter.FastMediatR/Injecter.FastMediatR.csproj
60+
TAG_COMMIT: "false"
61+
NUGET_KEY: ${{secrets.NUGET_API_KEY}}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
[*.cs]
2+
3+
# CA1062: Validate arguments of public methods
4+
dotnet_diagnostic.CA1062.severity = none

Injecter.FastMediatR.Tests/Add.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
using MediatR;
2+
3+
namespace Injecter.FastMediatR.Tests
4+
{
5+
public sealed class Add : IRequest<int>
6+
{
7+
public int A { get; set; }
8+
9+
public int B { get; set; }
10+
}
11+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
using System.Threading;
2+
using System.Threading.Tasks;
3+
4+
namespace Injecter.FastMediatR.Tests
5+
{
6+
public sealed class AddHandler : ISyncHandler<Add, int>
7+
{
8+
public Task<int> Handle(Add request, CancellationToken cancellationToken) => Task.FromResult(HandleSync(request));
9+
10+
public int HandleSync(Add request) => request.A + request.B;
11+
}
12+
}
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
using MediatR;
2+
using Microsoft.Extensions.DependencyInjection;
3+
using System;
4+
using System.Diagnostics;
5+
using Xunit;
6+
7+
namespace Injecter.FastMediatR.Tests
8+
{
9+
public sealed class FastMediatRTests : IDisposable
10+
{
11+
private readonly IServiceProvider _serviceProvider;
12+
13+
public FastMediatRTests() =>
14+
_serviceProvider = new ServiceCollection()
15+
.AddFastMediatR()
16+
.AddMediatR(typeof(FastMediatRTests).Assembly)
17+
.BuildServiceProvider();
18+
19+
[Fact]
20+
public void FastMediatRWorks()
21+
{
22+
// Arrange
23+
var fastMediator = _serviceProvider.GetRequiredService<IFastMediator>();
24+
var request = new Add { A = 1, B = 1 };
25+
26+
// Act
27+
var result = fastMediator.SendSync(request);
28+
29+
// Assert
30+
Assert.Equal(2, result);
31+
}
32+
33+
[Fact]
34+
public void SendNoResponseWorks()
35+
{
36+
// Arrange
37+
var fastMediator = _serviceProvider.GetRequiredService<IFastMediator>();
38+
var request = new NoResponse();
39+
40+
// Act
41+
fastMediator.SendSync(request);
42+
43+
// Assert
44+
Assert.True(true);
45+
}
46+
47+
[Theory]
48+
[InlineData(10)]
49+
[InlineData(100)]
50+
[InlineData(1000)]
51+
[InlineData(10000)]
52+
public void FastMediatRMightBeFasterThanSyncing(int iterationCount)
53+
{
54+
// Arrange
55+
var fastMediator = _serviceProvider.GetRequiredService<IFastMediator>();
56+
var mediator = _serviceProvider.GetRequiredService<IMediator>();
57+
var request = new Add { A = 1, B = 1 };
58+
59+
// Act
60+
var stopwatch = Stopwatch.StartNew();
61+
62+
for (var i = 0; i < iterationCount; i++)
63+
{
64+
mediator.Send(request).GetAwaiter().GetResult();
65+
}
66+
67+
stopwatch.Stop();
68+
69+
var mediatorTime = stopwatch.Elapsed;
70+
71+
stopwatch.Restart();
72+
73+
for (var i = 0; i < iterationCount; i++)
74+
{
75+
fastMediator.SendSync(request);
76+
}
77+
78+
stopwatch.Stop();
79+
var fastMediatorTime = stopwatch.Elapsed;
80+
81+
// Assert
82+
Assert.True(mediatorTime > fastMediatorTime);
83+
}
84+
85+
public void Dispose()
86+
{
87+
if (_serviceProvider is IDisposable d) d.Dispose();
88+
}
89+
}
90+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>netcoreapp3.1</TargetFramework>
5+
6+
<IsPackable>false</IsPackable>
7+
</PropertyGroup>
8+
9+
<ItemGroup>
10+
<PackageReference Include="coverlet.collector" Version="1.2.1">
11+
<PrivateAssets>all</PrivateAssets>
12+
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
13+
</PackageReference>
14+
<PackageReference Include="coverlet.msbuild" Version="2.8.1">
15+
<PrivateAssets>all</PrivateAssets>
16+
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
17+
</PackageReference>
18+
<PackageReference Include="MediatR.Extensions.Microsoft.DependencyInjection" Version="8.0.0" />
19+
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.1.3" />
20+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.6.1" />
21+
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.8">
22+
<PrivateAssets>all</PrivateAssets>
23+
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
24+
</PackageReference>
25+
<PackageReference Include="Roslynator.Analyzers" Version="2.3.0">
26+
<PrivateAssets>all</PrivateAssets>
27+
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
28+
</PackageReference>
29+
<PackageReference Include="SonarAnalyzer.CSharp" Version="8.6.0.16497">
30+
<PrivateAssets>all</PrivateAssets>
31+
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
32+
</PackageReference>
33+
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118">
34+
<PrivateAssets>all</PrivateAssets>
35+
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
36+
</PackageReference>
37+
<PackageReference Include="xunit" Version="2.4.1" />
38+
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1">
39+
<PrivateAssets>all</PrivateAssets>
40+
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
41+
</PackageReference>
42+
</ItemGroup>
43+
44+
<ItemGroup>
45+
<ProjectReference Include="..\Injecter.FastMediatR\Injecter.FastMediatR.csproj" />
46+
</ItemGroup>
47+
48+
</Project>
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
using MediatR;
2+
3+
namespace Injecter.FastMediatR.Tests
4+
{
5+
public sealed class NoResponse : IRequest
6+
{
7+
}
8+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
using MediatR;
2+
using System.Threading;
3+
using System.Threading.Tasks;
4+
5+
namespace Injecter.FastMediatR.Tests
6+
{
7+
public sealed class NoResponseHandler : ISyncHandler<NoResponse, Unit>
8+
{
9+
public Task<Unit> Handle(NoResponse request, CancellationToken cancellationToken) => Task.FromResult(HandleSync(request));
10+
11+
public Unit HandleSync(NoResponse request) => Unit.Value;
12+
}
13+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
using MediatR;
2+
using System;
3+
using System.Collections.Generic;
4+
using System.Reflection;
5+
6+
namespace Injecter.FastMediatR
7+
{
8+
#pragma warning disable CA1062 // Validate arguments of public methods
9+
public sealed class DefaultFastMediator : IFastMediator
10+
{
11+
private const string HandleMethodName = nameof(ISyncHandler<IRequest, Unit>.HandleSync);
12+
13+
private readonly IServiceProvider _serviceProvider;
14+
15+
private readonly Type _openHandlerType = typeof(ISyncHandler<,>);
16+
private readonly Type _openRegularHandlerType = typeof(IRequestHandler<,>);
17+
private readonly Dictionary<Type, (object concreteHandler, MethodInfo methodInfo)> _handlers = new Dictionary<Type, (object concreteHandler, MethodInfo methodInfo)>();
18+
19+
public DefaultFastMediator(IServiceProvider serviceProvider) => _serviceProvider = serviceProvider;
20+
21+
public void Dispose()
22+
{
23+
if (_serviceProvider is IDisposable d) d.Dispose();
24+
_handlers.Clear();
25+
}
26+
27+
public void SendSync(IRequest request) => SendSyncInternal(request);
28+
29+
public TResponse SendSync<TResponse>(IRequest<TResponse> request) => SendSyncInternal(request);
30+
31+
private TResponse SendSyncInternal<TResponse>(IRequest<TResponse> request)
32+
{
33+
var requestType = request.GetType();
34+
var responseType = typeof(TResponse);
35+
36+
var handlerType = _openHandlerType.MakeGenericType(requestType, responseType);
37+
38+
if (!_handlers.TryGetValue(handlerType, out var handler))
39+
{
40+
var regularHandlerType = _openRegularHandlerType.MakeGenericType(requestType, responseType);
41+
42+
handler.methodInfo = handlerType.GetMethod(HandleMethodName, BindingFlags.Instance | BindingFlags.Public);
43+
handler.concreteHandler = _serviceProvider.GetService(regularHandlerType);
44+
_handlers.Add(handlerType, handler);
45+
}
46+
47+
// ReSharper disable once PossibleNullReferenceException
48+
return (TResponse)handler.methodInfo.Invoke(handler.concreteHandler, new object[] { request });
49+
}
50+
}
51+
#pragma warning restore CA1062 // Validate arguments of public methods
52+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
using MediatR;
2+
using System;
3+
4+
namespace Injecter.FastMediatR
5+
{
6+
public interface IFastMediator : IDisposable
7+
{
8+
void SendSync(IRequest request);
9+
10+
TResponse SendSync<TResponse>(IRequest<TResponse> request);
11+
}
12+
}

0 commit comments

Comments
 (0)