Skip to content

Commit 61fc560

Browse files
authored
Merge pull request #11 from A-Programmer/feature/mediatR
Add Basic MediatR features and some tests
2 parents 88f40f3 + 3f831d7 commit 61fc560

39 files changed

+1155
-10
lines changed

.github/workflows/publish-nuget.yml

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,7 @@ jobs:
2929
run: dotnet build KSFramework.sln --configuration Release --no-restore
3030

3131
- name: Pack NuGet
32-
run: dotnet pack src/KSFramework/KSFramework.csproj \
33-
--configuration Release \
34-
-p:PackageVersion=${{ env.PACKAGE_VERSION }} \
35-
--no-build -o out
32+
run: dotnet pack src/KSFramework/KSFramework.csproj --configuration Release -p:PackageVersion=${{ env.PACKAGE_VERSION }} --no-build -o out
3633

3734
- name: Push to NuGet
38-
run: dotnet nuget push out/*.nupkg \
39-
--api-key ${{ secrets.NUGET_API_KEY }} \
40-
--source https://api.nuget.org/v3/index.json
35+
run: dotnet nuget push out/*.nupkg --api-key ${{ secrets.NUGET_API_KEY }} --source https://api.nuget.org/v3/index.json

KSFramework.sln

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,18 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{827E0CD3-B72
77
EndProject
88
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KSFramework", "src\KSFramework\KSFramework.csproj", "{B335BD79-05C0-4F0E-A2DA-FC72DF07A18C}"
99
EndProject
10+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Samples", "Samples", "{5D20AA90-6969-D8BD-9DCD-8634F4692FDA}"
11+
EndProject
12+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "MediatorSampleApp", "MediatorSampleApp", "{B36C9166-6687-1700-0084-D88BD073518A}"
13+
EndProject
14+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mediator", "Samples\MediatorSampleApp\Mediator.csproj", "{D849CD75-73AE-4F1A-80EA-0FC596C80723}"
15+
EndProject
16+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{0AB3BF05-4346-4AA6-1389-037BE0695223}"
17+
EndProject
18+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "KSFramework", "KSFramework", "{6F746C7E-99E8-D040-B792-CDA2514E83CE}"
19+
EndProject
20+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KSFramework.UnitTests", "tests\KSFramework\KSFramework.UnitTests\KSFramework.UnitTests.csproj", "{48D1B7B2-D99B-4554-BBFB-95E75720B8E5}"
21+
EndProject
1022
Global
1123
GlobalSection(SolutionConfigurationPlatforms) = preSolution
1224
Debug|Any CPU = Debug|Any CPU
@@ -29,11 +41,39 @@ Global
2941
{B335BD79-05C0-4F0E-A2DA-FC72DF07A18C}.Release|x64.Build.0 = Release|Any CPU
3042
{B335BD79-05C0-4F0E-A2DA-FC72DF07A18C}.Release|x86.ActiveCfg = Release|Any CPU
3143
{B335BD79-05C0-4F0E-A2DA-FC72DF07A18C}.Release|x86.Build.0 = Release|Any CPU
44+
{D849CD75-73AE-4F1A-80EA-0FC596C80723}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
45+
{D849CD75-73AE-4F1A-80EA-0FC596C80723}.Debug|Any CPU.Build.0 = Debug|Any CPU
46+
{D849CD75-73AE-4F1A-80EA-0FC596C80723}.Debug|x64.ActiveCfg = Debug|Any CPU
47+
{D849CD75-73AE-4F1A-80EA-0FC596C80723}.Debug|x64.Build.0 = Debug|Any CPU
48+
{D849CD75-73AE-4F1A-80EA-0FC596C80723}.Debug|x86.ActiveCfg = Debug|Any CPU
49+
{D849CD75-73AE-4F1A-80EA-0FC596C80723}.Debug|x86.Build.0 = Debug|Any CPU
50+
{D849CD75-73AE-4F1A-80EA-0FC596C80723}.Release|Any CPU.ActiveCfg = Release|Any CPU
51+
{D849CD75-73AE-4F1A-80EA-0FC596C80723}.Release|Any CPU.Build.0 = Release|Any CPU
52+
{D849CD75-73AE-4F1A-80EA-0FC596C80723}.Release|x64.ActiveCfg = Release|Any CPU
53+
{D849CD75-73AE-4F1A-80EA-0FC596C80723}.Release|x64.Build.0 = Release|Any CPU
54+
{D849CD75-73AE-4F1A-80EA-0FC596C80723}.Release|x86.ActiveCfg = Release|Any CPU
55+
{D849CD75-73AE-4F1A-80EA-0FC596C80723}.Release|x86.Build.0 = Release|Any CPU
56+
{48D1B7B2-D99B-4554-BBFB-95E75720B8E5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
57+
{48D1B7B2-D99B-4554-BBFB-95E75720B8E5}.Debug|Any CPU.Build.0 = Debug|Any CPU
58+
{48D1B7B2-D99B-4554-BBFB-95E75720B8E5}.Debug|x64.ActiveCfg = Debug|Any CPU
59+
{48D1B7B2-D99B-4554-BBFB-95E75720B8E5}.Debug|x64.Build.0 = Debug|Any CPU
60+
{48D1B7B2-D99B-4554-BBFB-95E75720B8E5}.Debug|x86.ActiveCfg = Debug|Any CPU
61+
{48D1B7B2-D99B-4554-BBFB-95E75720B8E5}.Debug|x86.Build.0 = Debug|Any CPU
62+
{48D1B7B2-D99B-4554-BBFB-95E75720B8E5}.Release|Any CPU.ActiveCfg = Release|Any CPU
63+
{48D1B7B2-D99B-4554-BBFB-95E75720B8E5}.Release|Any CPU.Build.0 = Release|Any CPU
64+
{48D1B7B2-D99B-4554-BBFB-95E75720B8E5}.Release|x64.ActiveCfg = Release|Any CPU
65+
{48D1B7B2-D99B-4554-BBFB-95E75720B8E5}.Release|x64.Build.0 = Release|Any CPU
66+
{48D1B7B2-D99B-4554-BBFB-95E75720B8E5}.Release|x86.ActiveCfg = Release|Any CPU
67+
{48D1B7B2-D99B-4554-BBFB-95E75720B8E5}.Release|x86.Build.0 = Release|Any CPU
3268
EndGlobalSection
3369
GlobalSection(SolutionProperties) = preSolution
3470
HideSolutionNode = FALSE
3571
EndGlobalSection
3672
GlobalSection(NestedProjects) = preSolution
3773
{B335BD79-05C0-4F0E-A2DA-FC72DF07A18C} = {827E0CD3-B72D-47B6-A68D-7590B98EB39B}
74+
{B36C9166-6687-1700-0084-D88BD073518A} = {5D20AA90-6969-D8BD-9DCD-8634F4692FDA}
75+
{D849CD75-73AE-4F1A-80EA-0FC596C80723} = {B36C9166-6687-1700-0084-D88BD073518A}
76+
{6F746C7E-99E8-D040-B792-CDA2514E83CE} = {0AB3BF05-4346-4AA6-1389-037BE0695223}
77+
{48D1B7B2-D99B-4554-BBFB-95E75720B8E5} = {6F746C7E-99E8-D040-B792-CDA2514E83CE}
3878
EndGlobalSection
3979
EndGlobal
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>Exe</OutputType>
5+
<TargetFramework>net10.0</TargetFramework>
6+
<ImplicitUsings>enable</ImplicitUsings>
7+
<Nullable>enable</Nullable>
8+
</PropertyGroup>
9+
10+
<ItemGroup>
11+
<ProjectReference Include="..\..\src\KSFramework\KSFramework.csproj" />
12+
</ItemGroup>
13+
14+
</Project>
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
using FluentValidation;
2+
using KSFramework.Messaging;
3+
using KSFramework.Messaging.Abstraction;
4+
using KSFramework.Messaging.Behaviors;
5+
using KSFramework.Messaging.Configuration;
6+
using KSFramework.Messaging.Samples;
7+
using Microsoft.Extensions.DependencyInjection;
8+
9+
var services = new ServiceCollection();
10+
11+
// فقط یکبار mediator رو ثبت کن
12+
services.AddScoped<IMediator, Mediator>();
13+
services.AddScoped<ISender>(sp => sp.GetRequiredService<IMediator>());
14+
15+
services.AddLogging();
16+
17+
services.AddValidatorsFromAssembly(typeof(Program).Assembly);
18+
services.AddMessaging(typeof(MultiplyByTwoHandler).Assembly);
19+
20+
// اگر رفتارهای pipeline داری، ثبت کن
21+
services.AddScoped(typeof(IPipelineBehavior<,>), typeof(RequestProcessorBehavior<,>));
22+
services.AddMessaging(typeof(ExceptionHandlingBehavior<,>).Assembly, typeof(MultiplyByTwoRequest).Assembly);
23+
24+
var provider = services.BuildServiceProvider();
25+
26+
var mediator = provider.GetRequiredService<IMediator>();
27+
28+
var result = await mediator.Send(new MultiplyByTwoRequest(5));
29+
Console.WriteLine($"Result: {result}");

src/KSFramework/Domain/IDomainEvent.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using MediatR;
1+
using KSFramework.Messaging.Abstraction;
22

33
namespace KSFramework.Domain;
44
public interface IDomainEvent : INotification

src/KSFramework/KSFramework.csproj

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,10 @@
2828
</ItemGroup>
2929

3030
<ItemGroup>
31-
<PackageReference Include="MediatR" Version="12.5.0" />
32-
<PackageReference Include="FluentValidation" Version="12.0.0" />
31+
<!-- TODO: To be removed-->
32+
<!-- <PackageReference Include="MediatR" Version="12.5.0" />-->
33+
<PackageReference Include="FluentValidation" Version="12.0" />
34+
<PackageReference Include="FluentValidation.DependencyInjectionExtensions" Version="12.0" />
3335
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.3.0" />
3436
<PackageReference Include="Microsoft.AspNetCore.Mvc.TagHelpers" Version="2.3.0" />
3537
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.5" />
@@ -38,6 +40,7 @@
3840
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.5" />
3941
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
4042
<PackageReference Include="Pluralize.NET" Version="1.0.2" />
43+
<PackageReference Include="Scrutor" Version="6.0.1" />
4144
</ItemGroup>
4245

4346
</Project>
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
namespace KSFramework.Messaging.Abstraction;
2+
3+
/// <summary>
4+
/// Represents a mediator that supports sending requests and publishing notifications.
5+
/// </summary>
6+
public interface IMediator : ISender
7+
{
8+
/// <summary>
9+
/// Publishes a notification to all registered handlers.
10+
/// </summary>
11+
/// <typeparam name="TNotification">The notification type.</typeparam>
12+
/// <param name="notification">The notification instance.</param>
13+
/// <param name="cancellationToken">Cancellation token.</param>
14+
/// <returns>A task representing the asynchronous operation.</returns>
15+
Task Publish<TNotification>(TNotification notification, CancellationToken cancellationToken = default)
16+
where TNotification : INotification;
17+
18+
/// <summary>
19+
/// Sends a request without knowing the response type at compile time.
20+
/// </summary>
21+
/// <param name="request">The request to send.</param>
22+
/// <param name="cancellationToken">Cancellation token.</param>
23+
/// <returns>A task that represents the response.</returns>
24+
Task<object?> Send(object request, CancellationToken cancellationToken = default);
25+
26+
/// <summary>
27+
/// Publishes a notification without knowing the notification type at compile time.
28+
/// </summary>
29+
/// <param name="notification">The notification instance.</param>
30+
/// <param name="cancellationToken">Cancellation token.</param>
31+
/// <returns>A task representing the asynchronous operation.</returns>
32+
Task Publish(object notification, CancellationToken cancellationToken = default);
33+
34+
/// <summary>
35+
/// Creates a stream of responses for a given stream request.
36+
/// </summary>
37+
/// <typeparam name="TResponse">Type of the streamed response.</typeparam>
38+
/// <param name="request">The stream request instance.</param>
39+
/// <param name="cancellationToken">Cancellation token.</param>
40+
/// <returns>An asynchronous stream of responses.</returns>
41+
IAsyncEnumerable<TResponse> CreateStream<TResponse>(
42+
IStreamRequest<TResponse> request,
43+
CancellationToken cancellationToken = default);
44+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
namespace KSFramework.Messaging.Abstraction;
2+
3+
/// <summary>
4+
/// Marker interface to represent a notification message.
5+
/// </summary>
6+
public interface INotification
7+
{
8+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
namespace KSFramework.Messaging.Abstraction;
2+
3+
/// <summary>
4+
/// Delegate that represents the next notification handler or behavior in the pipeline.
5+
/// </summary>
6+
public delegate Task NotificationHandlerDelegate();
7+
8+
/// <summary>
9+
/// Interface for notification pipeline behaviors.
10+
/// </summary>
11+
/// <typeparam name="TNotification">Type of the notification.</typeparam>
12+
public interface INotificationBehavior<TNotification> where TNotification : INotification
13+
{
14+
Task Handle(TNotification notification, CancellationToken cancellationToken, NotificationHandlerDelegate next);
15+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
namespace KSFramework.Messaging.Abstraction;
2+
3+
/// <summary>
4+
/// Defines a handler for a specific type of notification.
5+
/// </summary>
6+
/// <typeparam name="TNotification">The notification type.</typeparam>
7+
public interface INotificationHandler<TNotification> where TNotification : INotification
8+
{
9+
/// <summary>
10+
/// Handles the notification asynchronously.
11+
/// </summary>
12+
/// <param name="notification">The notification instance.</param>
13+
Task Handle(TNotification notification, CancellationToken cancellationToken);
14+
}

0 commit comments

Comments
 (0)