更新框架

This commit is contained in:
chfei committed 2026-10-27 14:35:03 +08:00
1 parent ef50fa0620
commit 84b28f9672
72 files changed
+2222 -282

No files matched your search

@@ -1,85 +0,0 @@
using Autofac;
using MAX.Core.DependencyInjection;
using MAX.Core.Helpers;
using MAX.Core.Infrastructure;
using MAX.Core.Reflections;
using System;
using System.Collections.Generic;
using System.Linq;
namespace MAX.Core.Autofac;
public static class AutofaceMiddlewareExtensions
{
public static Bootstrap UseAutofac(this Bootstrap engine)
{
engine.UseMiddleware<AutofaceMiddleware>();
return engine;
}
}
internal class AutofaceMiddleware : IBootstrap
{
private ContainerBuilder _builder;
public void Build()
{
IocManager.Container = _builder.Build();
}
public void Use()
{
_builder = new ContainerBuilder();
IocManager.ContainerBuilder = _builder;
RegisterDependencies(_builder);
}
/// <summary>
/// 注册IOC
/// </summary>
/// <param name="builder"></param>
/// <param name="cnetConfig"></param>
private void RegisterDependencies(ContainerBuilder builder)
{
#region 注册所有IOC接口
IEnumerable<IDependencyRegistrar> ListDependency = ReflectionHelper.GetInstancesByInterface<IDependencyRegistrar>(AppTypeFinder.Assemblies.ToArray());
foreach (IDependencyRegistrar item in ListDependency)
{
item.Register(builder);
}
//ISingletonDependency
Type singtype = typeof(ISingletonDependency);
builder.RegisterAssemblyTypes(AppTypeFinder.Assemblies.ToArray())
.Where(a =>
singtype.IsAssignableFrom(a)
&& a.IsClass
&& !a.IsAbstract
&& !typeof(INoDependency).IsAssignableFrom(a)
)
.AsImplementedInterfaces()
.AsSelf()
.PropertiesAutowired(new CustomPropertySelector())
.SingleInstance();
//ITransientDependency
Type transienttype = typeof(ITransientDependency);
builder.RegisterAssemblyTypes(AppTypeFinder.Assemblies.ToArray())
.Where(a =>
transienttype.IsAssignableFrom(a)
&& a.IsClass
&& !a.IsAbstract
&& !typeof(INoDependency).IsAssignableFrom(a)
)
.AsImplementedInterfaces()
.AsSelf()
.PropertiesAutowired(new CustomPropertySelector())
.InstancePerLifetimeScope();
#endregion
}
}
@@ -0,0 +1,15 @@
using Autofac;
namespace MAX.Core.Infrastructure;
/// <summary>
/// 将通知发布器的服务解析委托给当前 Autofac 作用域,无须引入额外适配包。
/// </summary>
internal sealed class AutofacNotificationServiceProvider(ILifetimeScope scope) : IServiceProvider
{
public object? GetService(Type serviceType)
{
ArgumentNullException.ThrowIfNull(serviceType);
return scope.ResolveOptional(serviceType);
}
}
@@ -1,32 +0,0 @@
using System.Collections.Generic;
namespace MAX.Core.Infrastructure;
public class Bootstrap//Bootstrap
{
public Bootstrap()
{
}
private readonly List<IBootstrap> _listUse = [];
public static Bootstrap Create()
{
return new Bootstrap();
}
public void UseMiddleware<T>() where T : IBootstrap, new()
{
T t = new();
t.Use();
_listUse.Add(t);
}
public void Builder()
{
foreach (IBootstrap item in _listUse)
{
item.Build();
}
}
}
@@ -1,7 +0,0 @@
namespace MAX.Core.Infrastructure;
public interface IBootstrap
{
void Use();
void Build();
}
@@ -1,18 +1,15 @@
using MAX.Core.Autofac;
using MediatR;
using Microsoft.Win32;
using MAX.Core.Autofac;
using MAX.Notification;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace MAX.Core.Infrastructure;
public class MediatorManager : Mediator
public class MediatorManager : NotificationPublisher
{
private readonly DynamicNotificationHandlerRegistry _registry;
@@ -24,11 +21,11 @@ public class MediatorManager : Mediator
}
}
public MediatorManager(ServiceFactory serviceFactory) : base(serviceFactory)
public MediatorManager(IServiceProvider serviceProvider) : base(serviceProvider)
{
_registry = new DynamicNotificationHandlerRegistry();
}
protected override async Task PublishCore(IEnumerable<Func<INotification, CancellationToken, Task>> allHandlers, INotification notification, CancellationToken cancellationToken)
protected override async Task PublishCore(IEnumerable<NotificationHandlerExecutor> allHandlers, INotification notification, CancellationToken cancellationToken)
{
await base.PublishCore(allHandlers, notification, cancellationToken);
@@ -47,7 +44,7 @@ class DynamicNotificationHandlerRegistry
public DynamicNotificationHandlerRegistry()
{
}
// Key: notification type, Value: 已注册的 handler 调用委托列表
// 按通知类型保存动态注册的处理器调用委托。
private readonly ConcurrentDictionary<Type, List<Func<INotification, CancellationToken, Task>>>
_handlers = new();
@@ -82,7 +79,7 @@ class DynamicNotificationHandlerRegistry
}
}
/// <summary>Publish 时由 PluginAwareMediator 调用</summary>
/// <summary>发布通知时调用动态处理器。</summary>
public async Task Publish(INotification notification, CancellationToken cancellationToken)
{
var notificationType = notification.GetType();
+4 -1
View File
@@ -8,7 +8,10 @@
<ItemGroup>
<PackageReference Include="Autofac" Version="8.3.0" />
<PackageReference Include="MediatR" Version="9.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\MAX.Notification\MAX.Notification.csproj" />
</ItemGroup>
</Project>
+8 -21
View File
@@ -1,4 +1,4 @@
/***
/***
* _ooOoo_
* o8888888o
* 88" . "88
@@ -20,7 +20,7 @@
*
* .............................................
* 佛祖保佑 永无BUG
* 快加工资 不改需求
* 快加工资 不改需求
*/
@@ -28,12 +28,8 @@ using Autofac;
using MAX.Core.DependencyInjection;
using MAX.Core.Infrastructure;
using MAX.Core.Reflections;
using MediatR;
using System;
using System.Collections.Generic;
using MAX.Notification;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
namespace MAX.Core;
@@ -50,10 +46,13 @@ public class Startup : IStartup
{
builder.RegisterInstance(LazyServiceProvider.Instance).As<ILazyServiceProvider>().AsSelf().SingleInstance();
#region 注册MediatR
#region 注册通知服务
Assembly[] assemblies = AppTypeFinder.Assemblies.ToArray();
builder.RegisterType<MediatorManager>().As<IMediator>().AsSelf().SingleInstance();
builder.Register(ctx => new MediatorManager(
new AutofacNotificationServiceProvider(ctx.Resolve<ILifetimeScope>())))
.As<IPublisher>().AsSelf().InstancePerLifetimeScope();
builder.RegisterAssemblyTypes(assemblies)
.AsClosedTypesOf(typeof(INotificationHandler<>))
@@ -61,18 +60,6 @@ public class Startup : IStartup
.AsSelf()
.InstancePerLifetimeScope();
builder.RegisterAssemblyTypes(assemblies)
.AsClosedTypesOf(typeof(IRequestHandler<>))
.AsImplementedInterfaces()
.AsSelf()
.InstancePerLifetimeScope();
builder.Register<ServiceFactory>(ctx =>
{
var c = ctx.Resolve<IComponentContext>();
return t => c.Resolve(t);
}).SingleInstance();
#endregion
}
}
@@ -0,0 +1,17 @@
using MAX.Core.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MAX.Framework.Interfaces
{
public interface IFolderConfig : ISingletonDependency
{
string ConfigFolder { get; }
string DataFolder { get; }
string LoggingFolder { get; }
string CacheFolder { get; }
}
}
+26 -12
View File
@@ -1,28 +1,42 @@
using MAX.Core.Infrastructure;
using MAX.Core.DependencyInjection;
using MAX.Core.Infrastructure;
using MAX.Core.Logger;
using MAX.Framework.Interfaces;
using NLog;
using NLog.Config;
using NLog.Targets;
using System;
using LLogLevel = MAX.Core.Logger.LogLevel;
using NLogLevel = NLog.LogLevel;
namespace MAX.Framework.Logger;
public class NLogger : SingleInstance<NLogger>, ILoggerFacade
public class NLogger : ILoggerFacade, ISingletonDependency
{
private readonly ILogger _log;
private NLogger()
public static ILoggerFacade Instance => LazyServiceProvider.Instance.LazyGetService<ILoggerFacade>();
public NLogger(IFolderConfig folderConfig)
{
//Target target = new FileTarget("log1")
//{
// FileName = $"{FolderConfig.LoggingFolder}/{DateTime.Now:yyyy-MM-dd}.log",
//};
//LoggingConfiguration config = new();
//config.AddRule(NLogLevel.Debug, NLogLevel.Fatal, target);
//LogManager.Configuration = config;
ArgumentNullException.ThrowIfNull(folderConfig);
ArgumentException.ThrowIfNullOrWhiteSpace(folderConfig.LoggingFolder);
lock (ConfigurationLock)
{
// 使用进程上下文,让 XML 配置及其自动重载共用宿主的目录约定。
GlobalDiagnosticsContext.Set("LoggingFolder", folderConfig.LoggingFolder);
if (LogManager.Configuration == null)
{
var target = new FileTarget("application")
{
FileName = System.IO.Path.Combine(folderConfig.LoggingFolder, "${shortdate}.log"),
};
var config = new LoggingConfiguration();
config.AddRule(NLogLevel.Debug, NLogLevel.Fatal, target);
LogManager.Configuration = config;
}
}
_log = LogManager.GetLogger("GlobalLogger");
}
private static readonly object ConfigurationLock = new();
public bool IsDebugEnabled { get; private set; } = true;
@@ -150,12 +150,18 @@ public class AutofacContainerExtension : IContainerExtension<IContainer>, IConta
public IContainerRegistry RegisterSingleton(Type type, Func<object> factoryMethod)
{
throw new NotImplementedException();
ArgumentNullException.ThrowIfNull(type);
ArgumentNullException.ThrowIfNull(factoryMethod);
Builder.Register(_ => factoryMethod()).As(type).SingleInstance();
return this;
}
public IContainerRegistry RegisterSingleton(Type type, Func<IContainerProvider, object> factoryMethod)
{
throw new NotImplementedException();
ArgumentNullException.ThrowIfNull(type);
ArgumentNullException.ThrowIfNull(factoryMethod);
Builder.Register(_ => factoryMethod(this)).As(type).SingleInstance();
return this;
}
public object Resolve(Type type)
@@ -192,12 +198,18 @@ public class AutofacContainerExtension : IContainerExtension<IContainer>, IConta
public IContainerRegistry Register(Type type, Func<object> factoryMethod)
{
throw new NotImplementedException();
ArgumentNullException.ThrowIfNull(type);
ArgumentNullException.ThrowIfNull(factoryMethod);
Builder.Register(_ => factoryMethod()).As(type);
return this;
}
public IContainerRegistry Register(Type type, Func<IContainerProvider, object> factoryMethod)
{
throw new NotImplementedException();
ArgumentNullException.ThrowIfNull(type);
ArgumentNullException.ThrowIfNull(factoryMethod);
Builder.Register(_ => factoryMethod(this)).As(type);
return this;
}
public IContainerRegistry RegisterMany(Type type, params Type[] serviceTypes)
{
@@ -320,4 +332,4 @@ public class AutofacContainerExtension : IContainerExtension<IContainer>, IConta
}
}
}
}
+1 -1
View File
@@ -24,7 +24,7 @@ public class Startup : IStartup
public void Register(ContainerBuilder builder)
{
builder.RegisterInstance(NLogger.Instance).As<ILoggerFacade>().SingleInstance();
//builder.RegisterInstance(NLogger.Instance).As<ILoggerFacade>().SingleInstance();
#region 注册所有IOC接口
@@ -0,0 +1,7 @@
// 摘自 MediatR 14.2.0,许可声明与修改记录见 LICENSE.md 和 UPSTREAM.md。
namespace MAX.Notification;
/// <summary>
/// 标记可发布的通知。
/// </summary>
public interface INotification { }
@@ -0,0 +1,41 @@
// 摘自 MediatR 14.2.0,许可声明与修改记录见 LICENSE.md 和 UPSTREAM.md。
using System.Threading;
using System.Threading.Tasks;
namespace MAX.Notification;
/// <summary>
/// 定义通知处理器。
/// </summary>
/// <typeparam name="TNotification">要处理的通知类型。</typeparam>
public interface INotificationHandler<in TNotification>
where TNotification : INotification
{
/// <summary>
/// 处理通知。
/// </summary>
/// <param name="notification">要处理的通知。</param>
/// <param name="cancellationToken">取消令牌。</param>
Task Handle(TNotification notification, CancellationToken cancellationToken);
}
/// <summary>
/// 同步通知处理器的基类。
/// </summary>
/// <typeparam name="TNotification">通知类型。</typeparam>
public abstract class NotificationHandler<TNotification> : INotificationHandler<TNotification>
where TNotification : INotification
{
Task INotificationHandler<TNotification>.Handle(TNotification notification, CancellationToken cancellationToken)
{
Handle(notification);
return Task.CompletedTask;
}
/// <summary>
/// 在派生类中重写此方法,实现同步通知处理逻辑。
/// </summary>
/// <param name="notification">要处理的通知。</param>
protected abstract void Handle(TNotification notification);
}
@@ -0,0 +1,12 @@
// 摘自 MediatR 14.2.0,许可声明与修改记录见 LICENSE.md 和 UPSTREAM.md。
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Threading;
namespace MAX.Notification;
public interface INotificationPublisher
{
Task Publish(IEnumerable<NotificationHandlerExecutor> handlerExecutors, INotification notification,
CancellationToken cancellationToken);
}
@@ -0,0 +1,28 @@
// 摘自 MediatR 14.2.0,许可声明与修改记录见 LICENSE.md 和 UPSTREAM.md。
using System.Threading;
using System.Threading.Tasks;
namespace MAX.Notification;
/// <summary>
/// 发布通知或事件,交由多个处理器处理。
/// </summary>
public interface IPublisher
{
/// <summary>
/// 异步向多个处理器分发通知。
/// </summary>
/// <param name="notification">通知对象。</param>
/// <param name="cancellationToken">可选的取消令牌。</param>
/// <returns>表示本次通知发布操作的任务。</returns>
Task Publish(object notification, CancellationToken cancellationToken = default);
/// <summary>
/// 异步向多个处理器分发通知。
/// </summary>
/// <param name="notification">通知对象。</param>
/// <param name="cancellationToken">可选的取消令牌。</param>
/// <returns>表示本次通知发布操作的任务。</returns>
Task Publish<TNotification>(TNotification notification, CancellationToken cancellationToken = default)
where TNotification : INotification;
}
+10
View File
@@ -0,0 +1,10 @@
By accessing code under the [Lucky Penny Software GitHub Organization](https://github.com/LuckyPennySoftware) (Lucky Penny Software) here, you are agreeing to the following licensing terms.
If you do not agree to these terms, do not access Lucky Penny Software code.
Your license to Lucky Penny Software source code and/or binaries is governed by the Reciprocal Public License 1.5 (RPL1.5) license as described here:
https://opensource.org/license/rpl-1-5/
If you do not wish to release the source of software you build using Lucky Penny Software source code and/or binaries under the terms above, you may use Lucky Penny Software source code and/or binaries under the License Agreement described here:
https://luckypennysoftware.com/license
@@ -0,0 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Remove="PolySharp" />
<None Update="LICENSE.md" Pack="true" PackagePath="" />
<None Update="UPSTREAM.md" Pack="true" PackagePath="" />
</ItemGroup>
</Project>
@@ -0,0 +1,8 @@
// 摘自 MediatR 14.2.0,许可声明与修改记录见 LICENSE.md 和 UPSTREAM.md。
using System;
using System.Threading;
using System.Threading.Tasks;
namespace MAX.Notification;
public record NotificationHandlerExecutor(object HandlerInstance, Func<INotification, CancellationToken, Task> HandlerCallback);
@@ -0,0 +1,67 @@
// 摘自 MediatR 14.2.0 的 Mediator.cs,仅保留通知功能。
// 许可声明与修改记录见 LICENSE.md 和 UPSTREAM.md。
using System.Collections.Concurrent;
using MAX.Notification.NotificationPublishers;
using MAX.Notification.Wrappers;
namespace MAX.Notification;
public class NotificationPublisher : IPublisher
{
private readonly IServiceProvider _serviceProvider;
private readonly INotificationPublisher _publisher;
private static readonly ConcurrentDictionary<Type, NotificationHandlerWrapper> _notificationHandlers = new();
public NotificationPublisher(IServiceProvider serviceProvider)
: this(serviceProvider, new ForeachAwaitPublisher()) { }
public NotificationPublisher(IServiceProvider serviceProvider, INotificationPublisher publisher)
{
ArgumentNullException.ThrowIfNull(serviceProvider);
ArgumentNullException.ThrowIfNull(publisher);
_serviceProvider = serviceProvider;
_publisher = publisher;
}
public Task Publish<TNotification>(TNotification notification, CancellationToken cancellationToken = default)
where TNotification : INotification
{
if (notification == null)
{
throw new ArgumentNullException(nameof(notification));
}
return PublishNotification(notification, cancellationToken);
}
public Task Publish(object notification, CancellationToken cancellationToken = default) =>
notification switch
{
null => throw new ArgumentNullException(nameof(notification)),
INotification instance => PublishNotification(instance, cancellationToken),
_ => throw new ArgumentException($"{nameof(notification)} does not implement ${nameof(INotification)}")
};
/// <summary>
/// 在派生类中重写此方法可控制任务的等待方式。默认调用 <see cref="INotificationPublisher"/>。
/// </summary>
/// <param name="handlerExecutors">封装各通知处理器实例及其调用委托的执行器集合。</param>
/// <param name="notification">要发布的通知。</param>
/// <param name="cancellationToken">取消令牌。</param>
/// <returns>表示本次处理器分发操作的任务。</returns>
protected virtual Task PublishCore(IEnumerable<NotificationHandlerExecutor> handlerExecutors, INotification notification, CancellationToken cancellationToken)
=> _publisher.Publish(handlerExecutors, notification, cancellationToken);
private Task PublishNotification(INotification notification, CancellationToken cancellationToken = default)
{
var handler = _notificationHandlers.GetOrAdd(notification.GetType(), static notificationType =>
{
var wrapperType = typeof(NotificationHandlerWrapperImpl<>).MakeGenericType(notificationType);
var wrapper = Activator.CreateInstance(wrapperType) ?? throw new InvalidOperationException($"Could not create wrapper for type {notificationType}");
return (NotificationHandlerWrapper)wrapper;
});
return handler.Handle(notification, _serviceProvider, PublishCore, cancellationToken);
}
}
@@ -0,0 +1,25 @@
// 摘自 MediatR 14.2.0,许可声明与修改记录见 LICENSE.md 和 UPSTREAM.md。
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace MAX.Notification.NotificationPublishers;
/// <summary>
/// 在循环中依次调用并等待各通知处理器:
/// <code>
/// foreach (var handler in handlers) {
/// await handler(notification, cancellationToken);
/// }
/// </code>
/// </summary>
public class ForeachAwaitPublisher : INotificationPublisher
{
public async Task Publish(IEnumerable<NotificationHandlerExecutor> handlerExecutors, INotification notification, CancellationToken cancellationToken)
{
foreach (var handler in handlerExecutors)
{
await handler.HandlerCallback(notification, cancellationToken).ConfigureAwait(false);
}
}
}
@@ -0,0 +1,29 @@
// 摘自 MediatR 14.2.0,许可声明与修改记录见 LICENSE.md 和 UPSTREAM.md。
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace MAX.Notification.NotificationPublishers;
/// <summary>
/// 使用 Task.WhenAll 等待通知处理器任务集合:
/// <code>
/// var tasks = handlers
/// .Select(handler => handler.Handle(notification, cancellationToken))
/// .ToList();
///
/// return Task.WhenAll(tasks);
/// </code>
/// </summary>
public class TaskWhenAllPublisher : INotificationPublisher
{
public Task Publish(IEnumerable<NotificationHandlerExecutor> handlerExecutors, INotification notification, CancellationToken cancellationToken)
{
var tasks = handlerExecutors
.Select(handler => handler.HandlerCallback(notification, cancellationToken))
.ToArray();
return Task.WhenAll(tasks);
}
}
+67
View File
@@ -0,0 +1,67 @@
# MAX.Notification
从本地 MediatR 14.2.0 源码抽取的通知组件,仅依赖 .NET 8,不引用任何第三方 NuGet 包。原许可声明完整保存在 LICENSE.md;源码来源和修改记录见 UPSTREAM.md。不包含请求响应、管道、流、许可证检测或容器扫描功能。移除第三方依赖不改变原源码的许可条件。
## 定义与使用
```csharp
using MAX.Notification;
public sealed record ProductChanged(int ProductId) : INotification;
public sealed class RefreshCacheHandler : INotificationHandler<ProductChanged>
{
public Task Handle(ProductChanged notification, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
// 更新缓存,或返回真正的异步业务操作。
return Task.CompletedTask;
}
}
```
发布器通过标准 `IServiceProvider.GetService(typeof(IEnumerable<INotificationHandler<T>>))` 获取处理器。调用方可自行实现此接口;组件不拥有、不释放容器及处理器。无容器实现可参考测试项目中的 TestServices。
宿主已经使用 Microsoft DI 时,可以在宿主中注册(以下扩展方法来自宿主自己的 DI 包,本项目无须引用):
```csharp
services.AddTransient<INotificationHandler<ProductChanged>, RefreshCacheHandler>();
services.AddScoped<IPublisher>(sp => new NotificationPublisher(sp));
// 在有效作用域内获取 IPublisher 后:
await publisher.Publish(new ProductChanged(42), cancellationToken);
```
宿主使用 Autofac 时:
```csharp
builder.RegisterType<RefreshCacheHandler>()
.As<INotificationHandler<ProductChanged>>();
builder.Register(ctx => new NotificationPublisher(
new AutofacServiceProvider(ctx.Resolve<ILifetimeScope>())))
.As<IPublisher>().InstancePerLifetimeScope();
```
上述 Autofac 示例需要宿主已有 Autofac.Extensions.DependencyInjection,或者宿主自己实现 IServiceProvider 适配器。不要把捕获短期作用域的发布器注册为单例。
## 行为与边界
- 默认 ForeachAwaitPublisher:按容器返回顺序逐个等待;Handler 抛异常或返回取消任务后停止,已完成的操作不会回滚。可实现 INotificationPublisher 自定义分发策略。
- 并发策略:构造时传入 new MAX.Notification.NotificationPublishers.TaskWhenAllPublisher()。使用原版 Task.WhenAll 实现;Handler 同步抛异常会中断后续枚举,已启动任务不会由此次调用统一等待。返回失败 Task 则会继续启动其他 Handler 并等待全部完成。没有 Task.Run,不保证运行在后台线程,不应并发共享非线程安全资源。
- 按通知实际运行时类型解析;不自动查找基类通知处理器,是否支持逆变解析取决于容器。
- 一次发布内相同处理器实际类型只执行第一个实例;不同类型各执行一次。这也会合并同类型但不同配置的实例。
- 无处理器时成功返回。每次发布重新解析,只缓存类型适配器,不缓存处理器或页面实例。
- 取消行为与原版一致:只传递原始 CancellationToken,发布器不主动检查取消。Handler 应自行检查或把令牌传入异步操作;没有 Handler 时,已取消的令牌不会使发布失败。
- 无消息去重、持久化、重试、事务、跨进程通信或 UI 线程调度。重复 Publish 仍会重复执行。
- 页面关闭不会自动影响 DI 中的处理器;业务 Handler 应与页面生命周期解耦。
- 使用运行时泛型反射构建适配器;当前未提供 NativeAOT / trimming 支持保证。
## 验证
```powershell
dotnet build Source/Core/MAX.Notification/MAX.Notification.csproj -c Release
dotnet run --project Tests/MAX.Notification.SmokeTests/MAX.Notification.SmokeTests.csproj -c Release
```
测试项目同样不依赖第三方包,覆盖类型分发、重复处理器、重新解析、空处理器、输入校验、异常、顺序/并发及取消。
支持继承 NotificationHandler<TNotification> 实现同步处理器,覆写 protected Handle(notification);同步基类本身不检查取消。
+25
View File
@@ -0,0 +1,25 @@
# 源码来源与修改记录
来源:用户提供的 `D:\Sources\MediatR-14.2.0`。
上游项目:https://github.com/LuckyPennySoftware/MediatR
LICENSE.md 为原目录同名文件的原样副本,声明 RPL 1.5 或商业许可。移除依赖不改变原许可要求。
## 直接抽取
- src/MediatR.Contracts/INotification.cs
- src/MediatR/INotificationHandler.cs(含同步 NotificationHandler 基类)
- src/MediatR/IPublisher.cs
- src/MediatR/INotificationPublisher.cs
- src/MediatR/NotificationHandlerExecutor.cs
- src/MediatR/NotificationPublishers/ForeachAwaitPublisher.cs
- src/MediatR/NotificationPublishers/TaskWhenAllPublisher.cs
- src/MediatR/Wrappers/NotificationHandlerWrapper.cs
以上代码注释已翻译为中文,许可声明保留原文;命名空间由 MediatR 改为 MAX.Notification,并添加来源说明。Wrapper 移除 Microsoft.Extensions.DependencyInjection,GetServices<T>() 改为标准 IServiceProvider.GetService(typeof(IEnumerable<T>)),空结果使用空集合,错误返回类型抛出转换异常。保留原处理器类型去重逻辑。
NotificationPublisher.cs 从 src/MediatR/Mediator.cs 抽取两个 Publish 重载、PublishCore、PublishNotification、通知缓存及相关字段。入口类改名,保留默认和自定义发布策略构造方式,增加构造参数 null 校验。去除请求响应、流以及第三方许可证检测调用,保留许可声明。
项目配置、使用说明和测试为新增内容。库不依赖第三方 NuGet 包,也排除全局 PolySharp 引用。
保留原版行为:只传递 CancellationToken,不主动检查取消;TaskWhenAllPublisher 枚举期间遇到同步异常时停止启动后续处理器。
@@ -0,0 +1,32 @@
// 摘自 MediatR 14.2.0,许可声明与修改记录见 LICENSE.md 和 UPSTREAM.md。
namespace MAX.Notification.Wrappers;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
public abstract class NotificationHandlerWrapper
{
public abstract Task Handle(INotification notification, IServiceProvider serviceFactory,
Func<IEnumerable<NotificationHandlerExecutor>, INotification, CancellationToken, Task> publish,
CancellationToken cancellationToken);
}
public class NotificationHandlerWrapperImpl<TNotification> : NotificationHandlerWrapper
where TNotification : INotification
{
public override Task Handle(INotification notification, IServiceProvider serviceFactory,
Func<IEnumerable<NotificationHandlerExecutor>, INotification, CancellationToken, Task> publish,
CancellationToken cancellationToken)
{
var handlers = ((IEnumerable<INotificationHandler<TNotification>>?)serviceFactory.GetService(typeof(IEnumerable<INotificationHandler<TNotification>>)) ?? Array.Empty<INotificationHandler<TNotification>>())
.GroupBy(static x => x.GetType())
.Select(static g => g.First())
.Select(static x => new NotificationHandlerExecutor(x, (theNotification, theToken) => x.Handle((TNotification)theNotification, theToken)));
return publish(handlers, notification, cancellationToken);
}
}