更新框架
This commit is contained in:
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();
|
||||
|
||||
@@ -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>
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
|
||||
+17
-5
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);同步基类本身不检查取消。
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user