refactor(core): 替换Mediator集成实现为通用服务配置机制

- 移除专用的RegisterMediator方法,替换为ExecuteServicesHook通用服务配置方法
- 从架构配置中移除Mediator特定配置选项,改为通用服务配置委托
- 在架构基类中添加Configurator属性支持,允许子类提供自定义服务配置
- 更新测试代码适配新的服务配置方式,通过ExecuteServicesHook注册Mediator
- 移除过时的测试组件和相关验证逻辑
- 删除Mediator.SourceGenerator包引用,保留运行时依赖
- 添加WaitUntilReadyAsync方法的详细文档注释
This commit is contained in:
GeWuYou 2026-02-14 15:49:07 +08:00 committed by gewuyou
parent a61c796e4d
commit e755c5c7f8
12 changed files with 1346 additions and 208 deletions

View File

@ -26,9 +26,5 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
<PackageReference Include="Mediator.Abstractions" Version="3.0.1"/>
<PackageReference Include="Mediator.SourceGenerator" Version="3.0.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
</Project>

View File

@ -1,6 +1,7 @@
using GFramework.Core.Abstractions.model;
using GFramework.Core.Abstractions.system;
using GFramework.Core.Abstractions.utility;
using Microsoft.Extensions.DependencyInjection;
namespace GFramework.Core.Abstractions.architecture;
@ -15,6 +16,14 @@ public interface IArchitecture : IAsyncInitializable
/// </summary>
IArchitectureContext Context { get; }
/// <summary>
/// 获取或设置用于配置服务集合的委托
/// </summary>
/// <value>
/// 一个可为空的委托用于配置IServiceCollection实例
/// </value>
Action<IServiceCollection>? Configurator { get; }
/// <summary>
/// 初始化方法,用于执行对象的初始化操作
/// </summary>

View File

@ -1,5 +1,4 @@
using GFramework.Core.Abstractions.properties;
using Mediator;
namespace GFramework.Core.Abstractions.architecture;
@ -17,11 +16,4 @@ public interface IArchitectureConfiguration
/// 获取或设置架构选项,包含架构相关的配置参数
/// </summary>
ArchitectureProperties ArchitectureProperties { get; set; }
/// <summary>
/// 获取或设置Mediator配置委托
/// 用于自定义Mediator框架的配置选项
/// </summary>
/// <returns>配置Mediator选项的委托函数可为null</returns>
Action<MediatorOptions>? Configurator { get; set; }
}

View File

@ -1,6 +1,5 @@
using GFramework.Core.Abstractions.rule;
using GFramework.Core.Abstractions.system;
using Mediator;
using Microsoft.Extensions.DependencyInjection;
namespace GFramework.Core.Abstractions.ioc;
@ -80,12 +79,12 @@ public interface IIocContainer : IContextAware
void RegisterMediatorBehavior<TBehavior>()
where TBehavior : class;
/// <summary>
/// 注册并配置Mediator框架
/// 提供自定义配置选项来调整Mediator的行为
/// 配置服务
/// </summary>
/// <param name="configurator">可选的配置委托函数用于自定义Mediator选项</param>
void RegisterMediator(Action<MediatorOptions>? configurator = null);
/// <param name="configurator">服务配置委托</param>
void ExecuteServicesHook(Action<IServiceCollection>? configurator = null);
#endregion

View File

@ -1,11 +1,6 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using GFramework.Core.Abstractions.architecture;
using GFramework.Core.Abstractions.enums;
using GFramework.Core.Abstractions.events;
using GFramework.Core.Abstractions.model;
using GFramework.Core.Abstractions.system;
using GFramework.Core.Abstractions.utility;
using GFramework.Core.architecture;
using GFramework.Core.command;
using GFramework.Core.environment;
@ -16,6 +11,7 @@ using GFramework.Core.query;
using Mediator;
using Microsoft.Extensions.DependencyInjection;
using NUnit.Framework;
// ✅ Mediator 库的命名空间
// ✅ 使用 global using 或别名来区分
@ -50,28 +46,10 @@ public class MediatorComprehensiveTests
_container.RegisterPlurality(_environment);
// ✅ 注册 Mediator
_container.RegisterMediator(options => { options.ServiceLifetime = ServiceLifetime.Singleton; });
// ✅ 手动注册 Mediator Handlers
_container.Services.AddSingleton<IRequestHandler<TestRequest, int>, TestRequestHandler>();
_container.Services.AddSingleton<IRequestHandler<TestCommand, Unit>, TestCommandHandler>();
_container.Services.AddSingleton<IRequestHandler<TestCommandWithResult, int>, TestCommandWithResultHandler>();
_container.Services.AddSingleton<IRequestHandler<TestQuery, string>, TestQueryHandler>();
_container.Services.AddSingleton<INotificationHandler<TestNotification>, TestNotificationHandler>();
_container.Services.AddSingleton<IStreamRequestHandler<TestStreamRequest, int>, TestStreamRequestHandler>();
// 注册测试组件Legacy
_testSystem = new TestSystem();
_testModel = new TestModel();
_testUtility = new TestUtility();
_testCommand = new TestTraditionalCommand();
_testQuery = new TestTraditionalQuery { Result = 999 };
_container.RegisterPlurality(_testSystem);
_container.RegisterPlurality(_testModel);
_container.RegisterPlurality(_testUtility);
_container.RegisterPlurality(_testCommand);
_container.RegisterPlurality(_testQuery);
_container.ExecuteServicesHook(configurator =>
{
configurator.AddMediator(options => { options.ServiceLifetime = ServiceLifetime.Singleton; });
});
// ✅ Freeze 容器
_container.Freeze();
@ -89,11 +67,6 @@ public class MediatorComprehensiveTests
_queryBus = null;
_asyncQueryBus = null;
_environment = null;
_testSystem = null;
_testModel = null;
_testUtility = null;
_testCommand = null;
_testQuery = null;
}
private ArchitectureContext? _context;
@ -103,11 +76,6 @@ public class MediatorComprehensiveTests
private QueryExecutor? _queryBus;
private AsyncQueryExecutor? _asyncQueryBus;
private DefaultEnvironment? _environment;
private TestSystem? _testSystem;
private TestModel? _testModel;
private TestUtility? _testUtility;
private TestTraditionalCommand? _testCommand;
private TestTraditionalQuery? _testQuery;
[Test]
public async Task SendRequestAsync_Should_ReturnResult_When_Request_IsValid()
@ -200,32 +168,6 @@ public class MediatorComprehensiveTests
Assert.That(TestNotificationHandler.LastReceivedMessage, Is.EqualTo("test event"));
}
[Test]
public async Task Mediator_And_CommandExecutor_Should_Coexist()
{
// 使用传统方式Legacy
_context!.SendCommand(_testCommand!);
Assert.That(_testCommand!.Executed, Is.True);
// 使用 Mediator 方式
var mediatorCommand = new TestCommandWithResult { ResultValue = 123 };
var result = await _context.SendAsync(mediatorCommand);
Assert.That(result, Is.EqualTo(123));
}
[Test]
public async Task Mediator_And_QueryExecutor_Should_Coexist()
{
// 使用传统方式Legacy
var traditionalResult = _context!.SendQuery(_testQuery!);
Assert.That(traditionalResult, Is.EqualTo(999));
// 使用 Mediator 方式
var mediatorQuery = new TestQuery { QueryResult = "mediator result" };
var mediatorResult = await _context.QueryAsync(mediatorQuery);
Assert.That(mediatorResult, Is.EqualTo("mediator result"));
}
[Test]
public void GetService_Should_Use_Cache()
{
@ -237,19 +179,6 @@ public class MediatorComprehensiveTests
Assert.That(secondResult, Is.SameAs(firstResult));
}
[Test]
public void Architecture_Component_Getters_Should_Work()
{
var system = _context!.GetSystem<TestSystem>();
var model = _context.GetModel<TestModel>();
var utility = _context.GetUtility<TestUtility>();
var environment = _context.GetEnvironment();
Assert.That(system, Is.SameAs(_testSystem));
Assert.That(model, Is.SameAs(_testModel));
Assert.That(utility, Is.SameAs(_testUtility));
Assert.That(environment, Is.SameAs(_environment));
}
[Test]
public void Unregistered_Mediator_Should_Throw_InvalidOperationException()
@ -363,81 +292,4 @@ public sealed class TestStreamRequestHandler : IStreamRequestHandler<TestStreamR
}
}
#endregion
#region Test Classes - Legacy CQRS ()
public class TestSystem : ISystem
{
private IArchitectureContext _context = null!;
public int Id { get; init; }
public void SetContext(IArchitectureContext context) => _context = context;
public IArchitectureContext GetContext() => _context;
public void Init()
{
}
public void Destroy()
{
}
public void OnArchitecturePhase(ArchitecturePhase phase)
{
}
}
public class TestModel : IModel
{
private IArchitectureContext _context = null!;
public int Id { get; init; }
public void SetContext(IArchitectureContext context) => _context = context;
public IArchitectureContext GetContext() => _context;
public void Init()
{
}
public void OnArchitecturePhase(ArchitecturePhase phase)
{
}
public void Destroy()
{
}
}
public class TestUtility : IUtility
{
private IArchitectureContext _context = null!;
public int Id { get; init; }
public void SetContext(IArchitectureContext context) => _context = context;
public IArchitectureContext GetContext() => _context;
}
// ✅ 使用你框架的 ICommand
public class TestTraditionalCommand : ICommand
{
private IArchitectureContext _context = null!;
public bool Executed { get; private set; }
public void Execute() => Executed = true;
public void SetContext(IArchitectureContext context) => _context = context;
public IArchitectureContext GetContext() => _context;
}
// ✅ 使用你框架的 IQuery
public class TestTraditionalQuery : IQuery<int>
{
private IArchitectureContext _context = null!;
public int Result { get; init; }
public int Do() => Result;
public void SetContext(IArchitectureContext context) => _context = context;
public IArchitectureContext GetContext() => _context;
}
#endregion

View File

@ -636,8 +636,13 @@ public abstract class Architecture(
// 为服务设置上下文
Services.SetContext(_context);
// 添加 Mediator
Container.RegisterMediator(Configuration.Configurator);
if (Configurator is null)
{
_logger.Debug("Mediator-based cqrs will not take effect without the service setter configured!");
}
// 执行服务钩子
Container.ExecuteServicesHook(Configurator);
// === 用户 Init ===
_logger.Debug("Calling user Init()");
Init();
@ -660,11 +665,23 @@ public abstract class Architecture(
/// <summary>
/// 等待架构初始化完成Ready 阶段)
/// 如果架构已经处于就绪状态,则立即返回已完成的任务;
/// 否则返回一个任务,该任务将在架构进入就绪状态时完成。
/// </summary>
/// <returns>表示等待操作的Task对象</returns>
public Task WaitUntilReadyAsync()
{
return IsReady ? Task.CompletedTask : _readyTcs.Task;
}
/// <summary>
/// 获取用于配置服务集合的委托
/// 默认实现返回null子类可以重写此属性以提供自定义配置逻辑
/// </summary>
/// <value>
/// 一个可为空的Action委托用于配置IServiceCollection实例
/// </value>
public virtual Action<IServiceCollection>? Configurator => null;
#endregion
}

View File

@ -2,8 +2,6 @@
using GFramework.Core.Abstractions.logging;
using GFramework.Core.Abstractions.properties;
using GFramework.Core.logging;
using Mediator;
using Microsoft.Extensions.DependencyInjection;
namespace GFramework.Core.architecture;
@ -34,17 +32,4 @@ public sealed class ArchitectureConfiguration : IArchitectureConfiguration
AllowLateRegistration = false,
StrictPhaseValidation = true
};
/// <summary>
/// 获取或设置Mediator配置委托
/// 用于自定义Mediator框架的配置选项
/// </summary>
/// <returns>配置Mediator选项的委托函数可为null</returns>
public Action<MediatorOptions>? Configurator { get; set; } = options =>
{
options.Namespace = "GFramework.Core.Mediator";
options.ServiceLifetime = ServiceLifetime.Singleton;
options.GenerateTypesAsInternal = true;
options.NotificationPublisherType = typeof(ForeachAwaitPublisher);
};
}

View File

@ -266,29 +266,12 @@ public class MicrosoftDiContainer(IServiceCollection? serviceCollection = null)
}
/// <summary>
/// 注册 Mediator基于 Source Generator
/// 配置服务
/// </summary>
/// <param name="configurator">可选的配置委托</param>
public void RegisterMediator(Action<MediatorOptions>? configurator = null)
/// <param name="configurator">服务配置委托</param>
public void ExecuteServicesHook(Action<IServiceCollection>? configurator = null)
{
_lock.EnterWriteLock();
try
{
ThrowIfFrozen();
// 添加 Mediator
Services.AddMediator(options =>
{
// 用户自定义配置
configurator?.Invoke(options);
});
_logger.Info("Mediator registered with Source Generator");
}
finally
{
_lock.ExitWriteLock();
}
configurator?.Invoke(Services);
}
#endregion

View File

@ -0,0 +1,38 @@
// <auto-generated>
// Generated by the Mediator source generator.
// </auto-generated>
namespace Mediator
{
/// <summary>
/// Represents an assembly reference.
/// This is used to specify the types or assemblies to scan for Mediator handlers.
/// </summary>
[global::System.CodeDom.Compiler.GeneratedCode("Mediator.SourceGenerator", "3.0.0.0")]
public sealed class AssemblyReference
{
/// <summary>
/// The assembly reference.
/// </summary>
public global::System.Reflection.Assembly Assembly { get; }
private AssemblyReference(global::System.Reflection.Assembly assembly)
{
Assembly = assembly;
}
/// <summary>
/// Creates a new instance of <see cref="AssemblyReference" /> from the specified type.
/// </summary>
/// <param name="type">The type</param>
/// <returns>A new instance of <see cref="AssemblyReference" /></returns>
public static implicit operator AssemblyReference(global::System.Type type) => new AssemblyReference(type.Assembly);
/// <summary>
/// Creates a new instance of <see cref="AssemblyReference" /> from the specified assembly.
/// </summary>
/// <param name="assembly">The assembly</param>
/// <returns>A new instance of <see cref="AssemblyReference" /></returns>
public static implicit operator AssemblyReference(global::System.Reflection.Assembly assembly) => new AssemblyReference(assembly);
}
}

View File

@ -0,0 +1,55 @@
// <auto-generated>
// Generated by the Mediator source generator.
// </auto-generated>
namespace Mediator
{
/// <summary>
/// Provide options for the Mediator source generator.
/// </summary>
[global::System.CodeDom.Compiler.GeneratedCode("Mediator.SourceGenerator", "3.0.0.0")]
public sealed class MediatorOptions
{
/// <summary>
/// The namespace in which the Mediator implementation is generated.
/// By default, the namespace is "Mediator".
/// </summary>
public string Namespace { get; set; } = "Mediator";
/// <summary>
/// Wether or not generated types should be <c>internal</c> (they are public by default).
/// </summary>
public bool GenerateTypesAsInternal { get; set; } = false;
/// <summary>
/// The <see cref="global::Mediator.INotificationPublisher" /> type to use when publishing notifications.
/// By default, the type is <see cref="global::Mediator.ForeachAwaitPublisher" />.
/// </summary>
public global::System.Type NotificationPublisherType { get; set; } = typeof(global::Mediator.ForeachAwaitPublisher);
/// <summary>
/// The default lifetime of the services registered in the DI container by the Mediator source generator.
/// By default, the lifetime is <see cref="global::Microsoft.Extensions.DependencyInjection.ServiceLifetime.Singleton" />.
/// </summary>
public global::Microsoft.Extensions.DependencyInjection.ServiceLifetime ServiceLifetime { get; set; } =
global::Microsoft.Extensions.DependencyInjection.ServiceLifetime.Singleton;
/// <summary>
/// The collection of assemblies to scan for Mediator handlers.
/// By default, the collection is empty, in which case the source generator will scan all assemblies through references from the source generated project.
/// </summary>
public global::System.Collections.Generic.IReadOnlyList<global::Mediator.AssemblyReference> Assemblies { get; set; } = new global::Mediator.AssemblyReference[0];
/// <summary>
/// The collection of types of pipeline behaviors to register in DI.
/// When the type is an unconstructed generic type, the source generator will register all the constructed types of the generic type (open generics that is supported during AoT).
/// </summary>
public global::System.Collections.Generic.IReadOnlyList<global::System.Type> PipelineBehaviors { get; set; } = new global::System.Type[0];
/// <summary>
/// The collection of types of streaming pipeline behaviors to register in DI.
/// When the type is an unconstructed generic type, the source generator will register all the constructed types of the generic type (open generics that is supported during AoT).
/// </summary>
public global::System.Collections.Generic.IReadOnlyList<global::System.Type> StreamPipelineBehaviors { get; set; } = new global::System.Type[0];
}
}

View File

@ -0,0 +1,33 @@
// <auto-generated>
// Generated by the Mediator source generator.
// </auto-generated>
namespace Mediator
{
/// <summary>
/// Provide options for the Mediator source generator.
/// </summary>
[global::System.AttributeUsage(global::System.AttributeTargets.Assembly, AllowMultiple = false)]
[global::System.CodeDom.Compiler.GeneratedCode("Mediator.SourceGenerator", "3.0.0.0")]
public sealed class MediatorOptionsAttribute : global::System.Attribute
{
/// <summary>
/// The namespace in which the Mediator implementation is generated.
/// By default, the namespace is "Mediator".
/// </summary>
public string Namespace { get; set; } = "Mediator";
/// <summary>
/// The <see cref="global::Mediator.INotificationPublisher" /> type to use when publishing notifications.
/// By default, the type is <see cref="global::Mediator.ForeachAwaitPublisher" />.
/// </summary>
public global::System.Type NotificationPublisherType { get; set; } = typeof(global::Mediator.ForeachAwaitPublisher);
/// <summary>
/// The default lifetime of the services registered in the DI container by the Mediator source generator.
/// By default, the lifetime is <see cref="global::Microsoft.Extensions.DependencyInjection.ServiceLifetime.Singleton" />.
/// </summary>
public global::Microsoft.Extensions.DependencyInjection.ServiceLifetime ServiceLifetime { get; set; } =
global::Microsoft.Extensions.DependencyInjection.ServiceLifetime.Singleton;
}
}