Skip to content

Commit 92c7857

Browse files
committed
feat!: add background publisher
1 parent ac8ab49 commit 92c7857

20 files changed

+283
-111
lines changed

.github/workflows/publish.yaml

+2-3
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,16 @@ jobs:
1515
uses: actions/setup-dotnet@v1
1616
with:
1717
dotnet-version: |
18-
6.0.x
19-
7.0.x
2018
8.0.x
19+
9.0.x
2120
- name: Install dependencies
2221
run: dotnet restore
2322
- name: Build
2423
run: dotnet build --configuration Release --no-restore
2524
- name: Test Mediator.SourceGenerator
2625
run: dotnet test --no-restore --verbosity normal tests/Mediator.SourceGenerator.Tests/Mediator.SourceGenerator.Tests.csproj
2726
- name: Publish
28-
uses: GerardSmit/publish-nuget@v3.1.2
27+
uses: GerardSmit/publish-nuget@v4
2928
with:
3029
NUGET_KEY: ${{secrets.NUGET_API_KEY}}
3130
VERSION_FILE_PATH: src/Directory.Build.props

benchmarks/Benchmarks.csproj

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
<PropertyGroup>
44
<OutputType>Exe</OutputType>
5-
<TargetFramework>net6.0</TargetFramework>
5+
<TargetFramework>net8.0</TargetFramework>
66
<ImplicitUsings>enable</ImplicitUsings>
77
<Nullable>enable</Nullable>
88
</PropertyGroup>

examples/BlazorServer/BlazorServer.csproj

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Project Sdk="Microsoft.NET.Sdk.Web">
22

33
<PropertyGroup>
4-
<TargetFramework>net6.0</TargetFramework>
4+
<TargetFramework>net8.0</TargetFramework>
55
<Nullable>enable</Nullable>
66
<ImplicitUsings>enable</ImplicitUsings>
77
</PropertyGroup>

examples/Generics/Generics.csproj

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
<PropertyGroup>
44
<OutputType>Exe</OutputType>
5-
<TargetFramework>net6.0</TargetFramework>
5+
<TargetFramework>net8.0</TargetFramework>
66
<ImplicitUsings>enable</ImplicitUsings>
77
<Nullable>enable</Nullable>
88
<IsPackable>false</IsPackable>

examples/Simple/Simple.csproj

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
<PropertyGroup>
44
<OutputType>Exe</OutputType>
5-
<TargetFramework>net6.0</TargetFramework>
5+
<TargetFramework>net8.0</TargetFramework>
66
<ImplicitUsings>enable</ImplicitUsings>
77
<Nullable>enable</Nullable>
88
<IsPackable>false</IsPackable>

src/Directory.Build.props

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
<Project>
22

33
<PropertyGroup>
4-
<Version>1.0.0-alpha.9</Version>
5-
<PackageVersion>1.0.0-alpha.9</PackageVersion>
4+
<Version>1.0.0-alpha.10</Version>
5+
<PackageVersion>1.0.0-alpha.10</PackageVersion>
66
<Authors>Zapto</Authors>
77
<RepositoryUrl>https://github.com/zapto-dev/Mediator</RepositoryUrl>
8-
<Copyright>Copyright © 2023 Zapto</Copyright>
8+
<Copyright>Copyright © 2025 Zapto</Copyright>
99
<PackageTags>zapto, mediator</PackageTags>
1010
<PackageProjectUrl>https://github.com/zapto-dev/Mediator</PackageProjectUrl>
1111
<PackageLicenseUrl>https://github.com/zapto-dev/Mediator/blob/main/LICENSE</PackageLicenseUrl>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
using System;
2+
using System.Threading;
3+
using System.Threading.Tasks;
4+
using MediatR;
5+
using Microsoft.Extensions.DependencyInjection;
6+
7+
namespace Zapto.Mediator;
8+
9+
internal class DefaultBackgroundPublisher : IBackgroundPublisher
10+
{
11+
private readonly IServiceScopeFactory _scopeFactory;
12+
13+
public DefaultBackgroundPublisher(IServiceScopeFactory scopeFactory)
14+
{
15+
_scopeFactory = scopeFactory;
16+
}
17+
18+
/// <inheritdoc />
19+
public ValueTask Publish(object notification, CancellationToken cancellationToken = default)
20+
{
21+
_ = Task.Run(async () =>
22+
{
23+
using var scope = _scopeFactory.CreateScope();
24+
var mediator = scope.ServiceProvider.GetRequiredService<IPublisher>();
25+
26+
await mediator.Publish(notification, cancellationToken);
27+
}, cancellationToken);
28+
29+
return default;
30+
}
31+
32+
/// <inheritdoc />
33+
public ValueTask Publish(MediatorNamespace ns, object notification, CancellationToken cancellationToken = default)
34+
{
35+
_ = Task.Run(async () =>
36+
{
37+
using var scope = _scopeFactory.CreateScope();
38+
var mediator = scope.ServiceProvider.GetRequiredService<IPublisher>();
39+
40+
await mediator.Publish(ns, notification, cancellationToken);
41+
}, cancellationToken);
42+
43+
return default;
44+
}
45+
46+
/// <inheritdoc />
47+
public ValueTask Publish<TNotification>(TNotification notification, CancellationToken cancellationToken = default) where TNotification : INotification
48+
{
49+
_ = Task.Run(async () =>
50+
{
51+
using var scope = _scopeFactory.CreateScope();
52+
var mediator = scope.ServiceProvider.GetRequiredService<IPublisher>();
53+
54+
await mediator.Publish(notification, cancellationToken);
55+
}, cancellationToken);
56+
57+
return default;
58+
}
59+
60+
/// <inheritdoc />
61+
public ValueTask Publish<TNotification>(MediatorNamespace ns, TNotification notification,
62+
CancellationToken cancellationToken = default) where TNotification : INotification
63+
{
64+
_ = Task.Run(async () =>
65+
{
66+
using var scope = _scopeFactory.CreateScope();
67+
var mediator = scope.ServiceProvider.GetRequiredService<IPublisher>();
68+
69+
await mediator.Publish(ns, notification, cancellationToken);
70+
}, cancellationToken);
71+
72+
return default;
73+
}
74+
}

src/Mediator.DependencyInjection/Extensions/ServiceExtensions.cs

+2
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ public static IMediatorBuilder AddMediator(this IServiceCollection services)
1111
{
1212
var builder = new MediatorBuilder(services);
1313

14+
services.TryAddTransient<IBackgroundPublisher, DefaultBackgroundPublisher>();
15+
1416
services.TryAddTransient<IMediator, ServiceProviderMediator>();
1517
services.TryAddTransient<ISender, ServiceProviderMediator>();
1618
services.TryAddTransient<IPublisher, ServiceProviderMediator>();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
using System.Threading.Tasks;
2+
3+
namespace Zapto.Mediator;
4+
5+
internal static class TaskExtensions
6+
{
7+
public static ValueTask<T> AsValueTask<T>(this Task<T> task)
8+
{
9+
return new ValueTask<T>(task);
10+
}
11+
12+
public static ValueTask AsValueTask(this Task task)
13+
{
14+
return new ValueTask(task);
15+
}
16+
}

src/Mediator.DependencyInjection/Mediator.DependencyInjection.csproj

+6-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<TargetFrameworks>netstandard2.0;netstandard2.1</TargetFrameworks>
4+
<TargetFrameworks>netstandard2.0;netstandard2.1;net8.0;net9.0</TargetFrameworks>
55
<AssemblyName>Zapto.Mediator.DependencyInjection</AssemblyName>
66
<RootNamespace>Zapto.Mediator</RootNamespace>
77
<LangVersion>10</LangVersion>
@@ -13,7 +13,11 @@
1313
</PropertyGroup>
1414

1515
<ItemGroup>
16-
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="6.0.0" />
16+
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.0" />
17+
</ItemGroup>
18+
19+
<ItemGroup Condition="'$(TargetFramework)' == 'net9.0'">
20+
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.0" />
1721
</ItemGroup>
1822

1923
<ItemGroup>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
using System;
2+
using System.Linq;
3+
using System.Reflection;
4+
using System.Threading;
5+
using System.Threading.Tasks;
6+
using MediatR;
7+
using Microsoft.Extensions.DependencyInjection;
8+
using Zapto.Mediator.Wrappers;
9+
10+
namespace Zapto.Mediator;
11+
12+
public class PublisherBase : IPublisherBase
13+
{
14+
private readonly IServiceProvider _provider;
15+
16+
public PublisherBase(IServiceProvider provider)
17+
{
18+
_provider = provider;
19+
}
20+
21+
/// <inheritdoc />
22+
public ValueTask Publish(object notification, CancellationToken cancellationToken = default)
23+
{
24+
return NotificationWrapper.Get(notification.GetType()).Handle(notification, cancellationToken, this);
25+
}
26+
27+
public ValueTask Publish(MediatorNamespace ns, object notification, CancellationToken cancellationToken = default)
28+
{
29+
return NotificationWrapper.Get(notification.GetType()).Handle(ns, notification, cancellationToken, this);
30+
}
31+
32+
/// <inheritdoc />
33+
public async ValueTask Publish<TNotification>(TNotification notification, CancellationToken cancellationToken = default)
34+
where TNotification : INotification
35+
{
36+
var didHandle = false;
37+
38+
foreach (var handler in _provider.GetServices<INotificationHandler<TNotification>>())
39+
{
40+
await handler.Handle(_provider, notification, cancellationToken);
41+
didHandle = true;
42+
}
43+
44+
var didGenericHandle = await _provider
45+
.GetRequiredService<GenericNotificationHandler<TNotification>>()
46+
.Handle(_provider, notification, cancellationToken);
47+
48+
if (!didHandle && !didGenericHandle && _provider.GetService<IDefaultNotificationHandler>() is { } defaultHandler)
49+
{
50+
await defaultHandler.Handle(_provider, notification, cancellationToken);
51+
}
52+
}
53+
54+
/// <inheritdoc />
55+
public async ValueTask Publish<TNotification>(MediatorNamespace ns, TNotification notification, CancellationToken cancellationToken = default)
56+
where TNotification : INotification
57+
{
58+
var services = _provider
59+
.GetServices<INamespaceNotificationHandler<TNotification>>()
60+
.Where(i => i.Namespace == ns);
61+
62+
foreach (var factory in services)
63+
{
64+
await factory.GetHandler(_provider).Handle(_provider, notification, cancellationToken);
65+
}
66+
}
67+
68+
/// <inheritdoc />
69+
public IDisposable RegisterNotificationHandler(object handler, Func<Func<Task>, Task>? invokeAsync = null, bool queue = true)
70+
{
71+
if (queue)
72+
{
73+
if (invokeAsync is { } invoker)
74+
{
75+
invokeAsync = cb =>
76+
{
77+
_ = Task.Run(() => invoker(cb));
78+
return Task.CompletedTask;
79+
};
80+
}
81+
else
82+
{
83+
invokeAsync = Task.Run;
84+
}
85+
}
86+
87+
return (IDisposable) typeof(NotificationAttributeHandler<>).MakeGenericType(handler.GetType())
88+
.GetMethod(nameof(NotificationAttributeHandler<object>.RegisterHandlers), BindingFlags.Static | BindingFlags.Public)!
89+
.Invoke(null, new[] { _provider, handler, invokeAsync });
90+
}
91+
}

src/Mediator.DependencyInjection/ServiceProviderMediator.cs

+5-74
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
using System.Diagnostics;
44
using System.Linq;
55
using System.Linq.Expressions;
6-
using System.Reflection;
76
using System.Threading;
87
using System.Threading.Tasks;
98
using MediatR;
@@ -12,11 +11,14 @@
1211

1312
namespace Zapto.Mediator;
1413

15-
public class ServiceProviderMediator : IMediator
14+
public class ServiceProviderMediator : PublisherBase, IMediator
1615
{
16+
private IBackgroundPublisher? _background;
1717
private readonly IServiceProvider _provider;
1818

19-
public ServiceProviderMediator(IServiceProvider provider)
19+
public IBackgroundPublisher Background => _background ??= _provider.GetRequiredService<IBackgroundPublisher>();
20+
21+
public ServiceProviderMediator(IServiceProvider provider) : base(provider)
2022
{
2123
_provider = provider;
2224
}
@@ -226,75 +228,4 @@ CancellationToken cancellationToken
226228

227229
return next();
228230
}
229-
230-
/// <inheritdoc />
231-
public ValueTask Publish(object notification, CancellationToken cancellationToken = default)
232-
{
233-
return NotificationWrapper.Get(notification.GetType()).Handle(notification, cancellationToken, this);
234-
}
235-
236-
public ValueTask Publish(MediatorNamespace ns, object notification, CancellationToken cancellationToken = default)
237-
{
238-
return NotificationWrapper.Get(notification.GetType()).Handle(ns, notification, cancellationToken, this);
239-
}
240-
241-
/// <inheritdoc />
242-
public async ValueTask Publish<TNotification>(TNotification notification, CancellationToken cancellationToken = default)
243-
where TNotification : INotification
244-
{
245-
var didHandle = false;
246-
247-
foreach (var handler in _provider.GetServices<INotificationHandler<TNotification>>())
248-
{
249-
await handler.Handle(_provider, notification, cancellationToken);
250-
didHandle = true;
251-
}
252-
253-
var didGenericHandle = await _provider
254-
.GetRequiredService<GenericNotificationHandler<TNotification>>()
255-
.Handle(_provider, notification, cancellationToken);
256-
257-
if (!didHandle && !didGenericHandle && _provider.GetService<IDefaultNotificationHandler>() is { } defaultHandler)
258-
{
259-
await defaultHandler.Handle(_provider, notification, cancellationToken);
260-
}
261-
}
262-
263-
/// <inheritdoc />
264-
public async ValueTask Publish<TNotification>(MediatorNamespace ns, TNotification notification, CancellationToken cancellationToken = default)
265-
where TNotification : INotification
266-
{
267-
var services = _provider
268-
.GetServices<INamespaceNotificationHandler<TNotification>>()
269-
.Where(i => i.Namespace == ns);
270-
271-
foreach (var factory in services)
272-
{
273-
await factory.GetHandler(_provider).Handle(_provider, notification, cancellationToken);
274-
}
275-
}
276-
277-
/// <inheritdoc />
278-
public IDisposable RegisterNotificationHandler(object handler, Func<Func<Task>, Task>? invokeAsync = null, bool queue = true)
279-
{
280-
if (queue)
281-
{
282-
if (invokeAsync is { } invoker)
283-
{
284-
invokeAsync = cb =>
285-
{
286-
_ = Task.Run(() => invoker(cb));
287-
return Task.CompletedTask;
288-
};
289-
}
290-
else
291-
{
292-
invokeAsync = Task.Run;
293-
}
294-
}
295-
296-
return (IDisposable) typeof(NotificationAttributeHandler<>).MakeGenericType(handler.GetType())
297-
.GetMethod(nameof(NotificationAttributeHandler<object>.RegisterHandlers), BindingFlags.Static | BindingFlags.Public)!
298-
.Invoke(null, new[] { _provider, handler, invokeAsync });
299-
}
300231
}

0 commit comments

Comments
 (0)