diff --git a/GFramework.Core.Abstractions/Architectures/IArchitectureContext.cs b/GFramework.Core.Abstractions/Architectures/IArchitectureContext.cs
index 0eb6a43f..c7b62e2f 100644
--- a/GFramework.Core.Abstractions/Architectures/IArchitectureContext.cs
+++ b/GFramework.Core.Abstractions/Architectures/IArchitectureContext.cs
@@ -15,7 +15,7 @@ namespace GFramework.Core.Abstractions.Architectures;
///
///
/// 旧的 GFramework.Core.Abstractions.Command 与 GFramework.Core.Abstractions.Query 契约会继续通过原有 Command/Query Executor 路径执行,以保证存量代码兼容。
-/// 新的 GFramework.Core.Abstractions.Cqrs 契约由内置 CQRS dispatcher 统一处理,支持 request pipeline、notification publish 与 stream request。
+/// 新的 GFramework.Cqrs.Abstractions.Cqrs 契约由内置 CQRS dispatcher 统一处理,支持 request pipeline、notification publish 与 stream request。
/// 新功能优先使用 、 与对应的 CQRS Command/Query 重载;迁移旧代码时可先保留旧入口,再逐步替换为 CQRS 请求模型。
///
public interface IArchitectureContext
@@ -175,7 +175,7 @@ public interface IArchitectureContext
/// 要发送的 CQRS 查询。
/// 查询结果。
///
- /// 这是迁移后的推荐查询入口。新查询应优先实现 GFramework.Core.Abstractions.Cqrs.Query.IQuery<TResponse>。
+ /// 这是迁移后的推荐查询入口。新查询应优先实现 GFramework.Cqrs.Abstractions.Cqrs.Query.IQuery<TResponse>。
///
TResponse SendQuery(GFramework.Cqrs.Abstractions.Cqrs.Query.IQuery query);
diff --git a/GFramework.Core.Abstractions/Logging/LoggerFactoryResolver.cs b/GFramework.Core.Abstractions/Logging/LoggerFactoryResolver.cs
new file mode 100644
index 00000000..fdffe5b2
--- /dev/null
+++ b/GFramework.Core.Abstractions/Logging/LoggerFactoryResolver.cs
@@ -0,0 +1,281 @@
+namespace GFramework.Core.Abstractions.Logging;
+
+///
+/// 提供全局日志工厂访问入口。
+///
+///
+/// 该类型位于抽象层,是为了让上层模块可以在不依赖 GFramework.Core 实现程序集的前提下
+/// 获取日志记录器。默认 provider 会优先通过反射解析 GFramework.Core 中的控制台实现,
+/// 若宿主未加载该程序集,则退回到静默 provider,避免抽象层形成实现层循环依赖。
+///
+public static class LoggerFactoryResolver
+{
+ private static readonly object ProviderLock = new();
+
+ private static string DefaultProviderTypeName =
+ "GFramework.Core.Logging.ConsoleLoggerFactoryProvider, GFramework.Core";
+
+ private static ILoggerFactoryProvider? _provider;
+
+ ///
+ /// 获取或设置当前日志工厂提供程序。
+ ///
+ ///
+ /// 读取与赋值都会通过同一把锁串行化,确保并发调用方观察到确定的 provider 引用。
+ /// 当调用方未显式赋值时,会在首次访问时尝试解析默认实现;若解析失败,则退回静默 provider。
+ ///
+ ///
+ /// 当赋值为 时抛出。
+ ///
+ public static ILoggerFactoryProvider Provider
+ {
+ get
+ {
+ lock (ProviderLock)
+ {
+ _provider ??= CreateDefaultProvider();
+ return _provider;
+ }
+ }
+ set
+ {
+ var provider = value ?? throw new ArgumentNullException(nameof(value));
+
+ lock (ProviderLock)
+ {
+ _provider = provider;
+ }
+ }
+ }
+
+ ///
+ /// 获取或设置新创建日志记录器的最小日志级别。
+ ///
+ ///
+ /// 该属性直接代理到当前 ,确保调用方调整级别后立即影响后续创建的日志器。
+ ///
+ public static LogLevel MinLevel
+ {
+ get => Provider.MinLevel;
+ set => Provider.MinLevel = value;
+ }
+
+ private static ILoggerFactoryProvider CreateDefaultProvider()
+ {
+ try
+ {
+ if (Type.GetType(DefaultProviderTypeName, throwOnError: false) is { } providerType &&
+ Activator.CreateInstance(providerType) is ILoggerFactoryProvider provider)
+ {
+ provider.MinLevel = LogLevel.Info;
+ return provider;
+ }
+ }
+ catch (Exception)
+ {
+ // The default provider is optional. Any load or activation failure must degrade to the silent provider so
+ // abstractions-only hosts can continue bootstrapping without the concrete logging assembly.
+ }
+
+ return new SilentLoggerFactoryProvider();
+ }
+
+ ///
+ /// 当宿主未提供默认日志实现时使用的静默 provider。
+ ///
+ private sealed class SilentLoggerFactoryProvider : ILoggerFactoryProvider
+ {
+ public LogLevel MinLevel { get; set; } = LogLevel.Info;
+
+ public ILogger CreateLogger(string name)
+ {
+ return new SilentLogger(name);
+ }
+ }
+
+ ///
+ /// 默认日志实现不可用时的 no-op 日志器。
+ ///
+ private sealed class SilentLogger(string name) : ILogger
+ {
+ public string Name()
+ {
+ return name;
+ }
+
+ public bool IsTraceEnabled()
+ {
+ return false;
+ }
+
+ public bool IsDebugEnabled()
+ {
+ return false;
+ }
+
+ public bool IsInfoEnabled()
+ {
+ return false;
+ }
+
+ public bool IsWarnEnabled()
+ {
+ return false;
+ }
+
+ public bool IsErrorEnabled()
+ {
+ return false;
+ }
+
+ public bool IsFatalEnabled()
+ {
+ return false;
+ }
+
+ public bool IsEnabledForLevel(LogLevel level)
+ {
+ return false;
+ }
+
+ public void Trace(string msg)
+ {
+ }
+
+ public void Trace(string format, object arg)
+ {
+ }
+
+ public void Trace(string format, object arg1, object arg2)
+ {
+ }
+
+ public void Trace(string format, params object[] arguments)
+ {
+ }
+
+ public void Trace(string msg, Exception t)
+ {
+ }
+
+ public void Debug(string msg)
+ {
+ }
+
+ public void Debug(string format, object arg)
+ {
+ }
+
+ public void Debug(string format, object arg1, object arg2)
+ {
+ }
+
+ public void Debug(string format, params object[] arguments)
+ {
+ }
+
+ public void Debug(string msg, Exception t)
+ {
+ }
+
+ public void Info(string msg)
+ {
+ }
+
+ public void Info(string format, object arg)
+ {
+ }
+
+ public void Info(string format, object arg1, object arg2)
+ {
+ }
+
+ public void Info(string format, params object[] arguments)
+ {
+ }
+
+ public void Info(string msg, Exception t)
+ {
+ }
+
+ public void Warn(string msg)
+ {
+ }
+
+ public void Warn(string format, object arg)
+ {
+ }
+
+ public void Warn(string format, object arg1, object arg2)
+ {
+ }
+
+ public void Warn(string format, params object[] arguments)
+ {
+ }
+
+ public void Warn(string msg, Exception t)
+ {
+ }
+
+ public void Error(string msg)
+ {
+ }
+
+ public void Error(string format, object arg)
+ {
+ }
+
+ public void Error(string format, object arg1, object arg2)
+ {
+ }
+
+ public void Error(string format, params object[] arguments)
+ {
+ }
+
+ public void Error(string msg, Exception t)
+ {
+ }
+
+ public void Fatal(string msg)
+ {
+ }
+
+ public void Fatal(string format, object arg)
+ {
+ }
+
+ public void Fatal(string format, object arg1, object arg2)
+ {
+ }
+
+ public void Fatal(string format, params object[] arguments)
+ {
+ }
+
+ public void Fatal(string msg, Exception t)
+ {
+ }
+
+ public void Log(LogLevel level, string message)
+ {
+ }
+
+ public void Log(LogLevel level, string format, object arg)
+ {
+ }
+
+ public void Log(LogLevel level, string format, object arg1, object arg2)
+ {
+ }
+
+ public void Log(LogLevel level, string format, params object[] arguments)
+ {
+ }
+
+ public void Log(LogLevel level, string message, Exception exception)
+ {
+ }
+ }
+}
diff --git a/GFramework.Core.Tests/Architectures/ArchitectureAdditionalCqrsHandlersTests.cs b/GFramework.Core.Tests/Architectures/ArchitectureAdditionalCqrsHandlersTests.cs
index 67255b41..ddec08c8 100644
--- a/GFramework.Core.Tests/Architectures/ArchitectureAdditionalCqrsHandlersTests.cs
+++ b/GFramework.Core.Tests/Architectures/ArchitectureAdditionalCqrsHandlersTests.cs
@@ -1,8 +1,8 @@
using System.Reflection;
-using GFramework.Core.Abstractions.Cqrs;
using GFramework.Core.Abstractions.Logging;
using GFramework.Core.Architectures;
using GFramework.Core.Logging;
+using GFramework.Cqrs;
using GFramework.Cqrs.Abstractions.Cqrs;
namespace GFramework.Core.Tests.Architectures;
diff --git a/GFramework.Core.Tests/Architectures/ArchitectureComponentRegistryBehaviorTests.cs b/GFramework.Core.Tests/Architectures/ArchitectureComponentRegistryBehaviorTests.cs
index 583c30c8..f6cbdafa 100644
--- a/GFramework.Core.Tests/Architectures/ArchitectureComponentRegistryBehaviorTests.cs
+++ b/GFramework.Core.Tests/Architectures/ArchitectureComponentRegistryBehaviorTests.cs
@@ -1,11 +1,11 @@
using GFramework.Core.Abstractions.Architectures;
using GFramework.Core.Abstractions.Enums;
+using GFramework.Core.Abstractions.Logging;
using GFramework.Core.Abstractions.Model;
using GFramework.Core.Abstractions.Systems;
using GFramework.Core.Abstractions.Utility;
using GFramework.Core.Architectures;
using GFramework.Core.Logging;
-using Microsoft.Extensions.DependencyInjection;
namespace GFramework.Core.Tests.Architectures;
@@ -714,4 +714,4 @@ public class ArchitectureComponentRegistryBehaviorTests
return _context;
}
}
-}
\ No newline at end of file
+}
diff --git a/GFramework.Core.Tests/Architectures/ArchitectureContextTests.cs b/GFramework.Core.Tests/Architectures/ArchitectureContextTests.cs
index 87978cd2..cdcde44d 100644
--- a/GFramework.Core.Tests/Architectures/ArchitectureContextTests.cs
+++ b/GFramework.Core.Tests/Architectures/ArchitectureContextTests.cs
@@ -5,6 +5,7 @@ using GFramework.Core.Abstractions.Cqrs;
using GFramework.Core.Abstractions.Enums;
using GFramework.Core.Abstractions.Environment;
using GFramework.Core.Abstractions.Ioc;
+using GFramework.Core.Abstractions.Logging;
using GFramework.Core.Abstractions.Model;
using GFramework.Core.Abstractions.Query;
using GFramework.Core.Abstractions.Systems;
diff --git a/GFramework.Core.Tests/Architectures/ArchitectureInitializationPipelineTests.cs b/GFramework.Core.Tests/Architectures/ArchitectureInitializationPipelineTests.cs
index 717e1b18..10945ad2 100644
--- a/GFramework.Core.Tests/Architectures/ArchitectureInitializationPipelineTests.cs
+++ b/GFramework.Core.Tests/Architectures/ArchitectureInitializationPipelineTests.cs
@@ -1,9 +1,9 @@
using GFramework.Core.Abstractions.Enums;
using GFramework.Core.Abstractions.Events;
+using GFramework.Core.Abstractions.Logging;
using GFramework.Core.Architectures;
using GFramework.Core.Environment;
using GFramework.Core.Logging;
-using Microsoft.Extensions.DependencyInjection;
namespace GFramework.Core.Tests.Architectures;
@@ -185,4 +185,4 @@ public class ArchitectureInitializationPipelineTests
private sealed class BootstrapMarker
{
}
-}
\ No newline at end of file
+}
diff --git a/GFramework.Core.Tests/Architectures/ArchitectureLifecycleBehaviorTests.cs b/GFramework.Core.Tests/Architectures/ArchitectureLifecycleBehaviorTests.cs
index b0ef7262..943e2bfd 100644
--- a/GFramework.Core.Tests/Architectures/ArchitectureLifecycleBehaviorTests.cs
+++ b/GFramework.Core.Tests/Architectures/ArchitectureLifecycleBehaviorTests.cs
@@ -2,12 +2,12 @@ using System.Reflection;
using GFramework.Core.Abstractions.Architectures;
using GFramework.Core.Abstractions.Enums;
using GFramework.Core.Abstractions.Lifecycle;
+using GFramework.Core.Abstractions.Logging;
using GFramework.Core.Abstractions.Model;
using GFramework.Core.Abstractions.Systems;
using GFramework.Core.Abstractions.Utility;
using GFramework.Core.Architectures;
using GFramework.Core.Logging;
-using Microsoft.Extensions.DependencyInjection;
namespace GFramework.Core.Tests.Architectures;
@@ -460,4 +460,4 @@ public class ArchitectureLifecycleBehaviorTests
return _context;
}
}
-}
\ No newline at end of file
+}
diff --git a/GFramework.Core.Tests/Architectures/ArchitectureModulesBehaviorTests.cs b/GFramework.Core.Tests/Architectures/ArchitectureModulesBehaviorTests.cs
index cfe0db79..493f3590 100644
--- a/GFramework.Core.Tests/Architectures/ArchitectureModulesBehaviorTests.cs
+++ b/GFramework.Core.Tests/Architectures/ArchitectureModulesBehaviorTests.cs
@@ -1,4 +1,5 @@
using GFramework.Core.Abstractions.Architectures;
+using GFramework.Core.Abstractions.Logging;
using GFramework.Core.Abstractions.Utility;
using GFramework.Core.Architectures;
using GFramework.Core.Logging;
diff --git a/GFramework.Core.Tests/Architectures/PriorityServiceTests.cs b/GFramework.Core.Tests/Architectures/PriorityServiceTests.cs
index 24fd2681..0499a4ab 100644
--- a/GFramework.Core.Tests/Architectures/PriorityServiceTests.cs
+++ b/GFramework.Core.Tests/Architectures/PriorityServiceTests.cs
@@ -1,5 +1,6 @@
using System.Reflection;
using GFramework.Core.Abstractions.Bases;
+using GFramework.Core.Abstractions.Logging;
using GFramework.Core.Abstractions.Model;
using GFramework.Core.Abstractions.Systems;
using GFramework.Core.Abstractions.Utility;
@@ -244,4 +245,4 @@ public class PriorityTestUtilityC : IPriorityTestUtility, IPrioritized
public int Priority => 30;
}
-#endregion
\ No newline at end of file
+#endregion
diff --git a/GFramework.Core.Tests/Cqrs/CqrsPublicNamespaceCompatibilityTests.cs b/GFramework.Core.Tests/Cqrs/CqrsPublicNamespaceCompatibilityTests.cs
new file mode 100644
index 00000000..64f69f02
--- /dev/null
+++ b/GFramework.Core.Tests/Cqrs/CqrsPublicNamespaceCompatibilityTests.cs
@@ -0,0 +1,76 @@
+using GFramework.Cqrs.Abstractions.Cqrs;
+using GFramework.Cqrs.Abstractions.Cqrs.Command;
+using GFramework.Cqrs.Abstractions.Cqrs.Notification;
+using GFramework.Cqrs.Abstractions.Cqrs.Query;
+using GFramework.Cqrs.Abstractions.Cqrs.Request;
+using GFramework.Cqrs.Command;
+using GFramework.Cqrs.Notification;
+using GFramework.Cqrs.Query;
+using GFramework.Cqrs.Request;
+
+namespace GFramework.Core.Tests.Cqrs;
+
+///
+/// 锁定 CQRS 基础消息类型在 runtime 拆分后的公开命名空间与程序集兼容性。
+///
+[TestFixture]
+public sealed class CqrsPublicNamespaceCompatibilityTests
+{
+ ///
+ /// 验证基础消息类型继续暴露在历史公开 CQRS 命名空间(GFramework.Cqrs.*),同时由独立 runtime 程序集承载实现。
+ ///
+ [Test]
+ public void Base_Message_Types_Should_Live_In_Cqrs_Namespaces_And_Runtime_Assembly()
+ {
+ Assert.Multiple(() =>
+ {
+ AssertLegacyType(typeof(CommandBase), "GFramework.Cqrs.Command");
+ AssertLegacyType(typeof(QueryBase), "GFramework.Cqrs.Query");
+ AssertLegacyType(typeof(RequestBase), "GFramework.Cqrs.Request");
+ AssertLegacyType(typeof(NotificationBase), "GFramework.Cqrs.Notification");
+ });
+ }
+
+ ///
+ /// 验证旧的 GFramework.Core 程序集限定名仍可解析到迁移后的 runtime 实现类型。
+ ///
+ [Test]
+ public void Type_Forwarding_Should_Resolve_Cqrs_Types_From_Core_Assembly()
+ {
+ Assert.Multiple(() =>
+ {
+ AssertForwardedType("GFramework.Cqrs.Command.CommandBase`2, GFramework.Core");
+ AssertForwardedType("GFramework.Cqrs.Query.QueryBase`2, GFramework.Core");
+ AssertForwardedType("GFramework.Cqrs.Request.RequestBase`2, GFramework.Core");
+ AssertForwardedType("GFramework.Cqrs.Notification.NotificationBase`1, GFramework.Core");
+ });
+ }
+
+ private static void AssertLegacyType(Type type, string expectedNamespace)
+ {
+ Assert.Multiple(() =>
+ {
+ Assert.That(type.Namespace, Is.EqualTo(expectedNamespace));
+ Assert.That(type.Assembly.GetName().Name, Is.EqualTo("GFramework.Cqrs"));
+ });
+ }
+
+ private static void AssertForwardedType(string assemblyQualifiedTypeName)
+ {
+ var resolvedType = Type.GetType(assemblyQualifiedTypeName, throwOnError: false);
+
+ Assert.Multiple(() =>
+ {
+ Assert.That(resolvedType, Is.Not.Null);
+ Assert.That(resolvedType!.Assembly.GetName().Name, Is.EqualTo("GFramework.Cqrs"));
+ });
+ }
+
+ private sealed record TestCommandInput : ICommandInput;
+
+ private sealed record TestQueryInput : IQueryInput;
+
+ private sealed record TestRequestInput : IRequestInput;
+
+ private sealed record TestNotificationInput : INotificationInput;
+}
diff --git a/GFramework.Core.Tests/Cqrs/MediatorCompatibilityDeprecationTests.cs b/GFramework.Core.Tests/Cqrs/MediatorCompatibilityDeprecationTests.cs
index b25806f0..a6cc927f 100644
--- a/GFramework.Core.Tests/Cqrs/MediatorCompatibilityDeprecationTests.cs
+++ b/GFramework.Core.Tests/Cqrs/MediatorCompatibilityDeprecationTests.cs
@@ -38,10 +38,10 @@ public class MediatorCompatibilityDeprecationTests
"Use GFramework.Core.Extensions.ContextAwareCqrsExtensions instead.");
AssertLegacyType(
typeof(ContextAwareMediatorCommandExtensions),
- "Use GFramework.Core.Extensions.ContextAwareCqrsCommandExtensions instead.");
+ "Use GFramework.Cqrs.Extensions.ContextAwareCqrsCommandExtensions instead.");
AssertLegacyType(
typeof(ContextAwareMediatorQueryExtensions),
- "Use GFramework.Core.Extensions.ContextAwareCqrsQueryExtensions instead.");
+ "Use GFramework.Cqrs.Extensions.ContextAwareCqrsQueryExtensions instead.");
AssertLegacyType(
typeof(MediatorCoroutineExtensions),
"Use GFramework.Core.Coroutine.Extensions.CqrsCoroutineExtensions instead.");
diff --git a/GFramework.Core.Tests/Ioc/MicrosoftDiContainerTests.cs b/GFramework.Core.Tests/Ioc/MicrosoftDiContainerTests.cs
index 0e03059e..4104ccb1 100644
--- a/GFramework.Core.Tests/Ioc/MicrosoftDiContainerTests.cs
+++ b/GFramework.Core.Tests/Ioc/MicrosoftDiContainerTests.cs
@@ -1,6 +1,7 @@
using System.Reflection;
using GFramework.Core.Abstractions.Bases;
using GFramework.Core.Abstractions.Cqrs;
+using GFramework.Core.Abstractions.Logging;
using GFramework.Core.Ioc;
using GFramework.Core.Logging;
using GFramework.Core.Tests.Cqrs;
diff --git a/GFramework.Core.Tests/Logging/LoggerFactoryTests.cs b/GFramework.Core.Tests/Logging/LoggerFactoryTests.cs
index 8ba7ac91..d175b32f 100644
--- a/GFramework.Core.Tests/Logging/LoggerFactoryTests.cs
+++ b/GFramework.Core.Tests/Logging/LoggerFactoryTests.cs
@@ -1,18 +1,19 @@
using System.IO;
+using System.Reflection;
using GFramework.Core.Abstractions.Logging;
using GFramework.Core.Logging;
-using NUnit.Framework;
namespace GFramework.Core.Tests.Logging;
///
-/// 测试LoggerFactory相关功能的测试类
+/// 测试 LoggerFactory 相关功能的测试类。
///
[TestFixture]
+[NonParallelizable]
public class LoggerFactoryTests
{
///
- /// 测试ConsoleLoggerFactory的GetLogger方法是否返回ConsoleLogger实例
+ /// 测试 ConsoleLoggerFactory 的 GetLogger 方法是否返回 ConsoleLogger 实例。
///
[Test]
public void ConsoleLoggerFactory_GetLogger_ShouldReturnConsoleLogger()
@@ -26,7 +27,7 @@ public class LoggerFactoryTests
}
///
- /// 测试ConsoleLoggerFactory使用不同名称获取不同的logger实例
+ /// 测试 ConsoleLoggerFactory 使用不同名称获取不同的 logger 实例。
///
[Test]
public void ConsoleLoggerFactory_GetLogger_WithDifferentNames_ShouldReturnDifferentLoggers()
@@ -40,7 +41,7 @@ public class LoggerFactoryTests
}
///
- /// 测试ConsoleLoggerFactory使用默认最小级别时的行为(默认为Info级别)
+ /// 测试 ConsoleLoggerFactory 使用默认最小级别时的行为。
///
[Test]
public void ConsoleLoggerFactory_GetLogger_WithDefaultMinLevel_ShouldUseInfo()
@@ -51,7 +52,6 @@ public class LoggerFactoryTests
var stringWriter = new StringWriter();
var testLogger = new ConsoleLogger("TestLogger", LogLevel.Info, stringWriter, false);
- // 验证Debug消息不会被记录,但Info消息会被记录
testLogger.Debug("Debug message");
testLogger.Info("Info message");
@@ -61,7 +61,7 @@ public class LoggerFactoryTests
}
///
- /// 测试ConsoleLoggerFactoryProvider创建logger时使用提供者的最小级别设置
+ /// 测试 ConsoleLoggerFactoryProvider 创建 logger 时使用提供者的最小级别设置。
///
[Test]
public void ConsoleLoggerFactoryProvider_CreateLogger_ShouldReturnLoggerWithProviderMinLevel()
@@ -72,7 +72,6 @@ public class LoggerFactoryTests
var stringWriter = new StringWriter();
var testLogger = new ConsoleLogger("TestLogger", LogLevel.Debug, stringWriter, false);
- // 验证Debug消息会被记录,但Trace消息不会被记录
testLogger.Debug("Debug message");
testLogger.Trace("Trace message");
@@ -82,7 +81,7 @@ public class LoggerFactoryTests
}
///
- /// 测试ConsoleLoggerFactoryProvider创建logger时使用提供的名称
+ /// 测试 ConsoleLoggerFactoryProvider 创建 logger 时使用提供的名称。
///
[Test]
public void ConsoleLoggerFactoryProvider_CreateLogger_ShouldUseProvidedName()
@@ -94,7 +93,7 @@ public class LoggerFactoryTests
}
///
- /// 测试LoggerFactoryResolver的Provider属性是否有默认值
+ /// 测试 LoggerFactoryResolver 的 Provider 属性是否有默认值。
///
[Test]
public void LoggerFactoryResolver_Provider_ShouldHaveDefaultValue()
@@ -104,7 +103,7 @@ public class LoggerFactoryTests
}
///
- /// 测试LoggerFactoryResolver的Provider属性可以被更改
+ /// 测试 LoggerFactoryResolver 的 Provider 属性可以被更改。
///
[Test]
public void LoggerFactoryResolver_Provider_CanBeChanged()
@@ -120,7 +119,7 @@ public class LoggerFactoryTests
}
///
- /// 测试LoggerFactoryResolver的MinLevel属性是否有默认值
+ /// 测试 LoggerFactoryResolver 的 MinLevel 属性是否有默认值。
///
[Test]
public void LoggerFactoryResolver_MinLevel_ShouldHaveDefaultValue()
@@ -129,7 +128,7 @@ public class LoggerFactoryTests
}
///
- /// 测试LoggerFactoryResolver的MinLevel属性可以被更改
+ /// 测试 LoggerFactoryResolver 的 MinLevel 属性可以被更改。
///
[Test]
public void LoggerFactoryResolver_MinLevel_CanBeChanged()
@@ -144,7 +143,7 @@ public class LoggerFactoryTests
}
///
- /// 测试ConsoleLoggerFactoryProvider的MinLevel属性是否有默认值
+ /// 测试 ConsoleLoggerFactoryProvider 的 MinLevel 属性是否有默认值。
///
[Test]
public void ConsoleLoggerFactoryProvider_MinLevel_ShouldHaveDefaultValue()
@@ -155,7 +154,7 @@ public class LoggerFactoryTests
}
///
- /// 测试ConsoleLoggerFactoryProvider的MinLevel属性可以被更改
+ /// 测试 ConsoleLoggerFactoryProvider 的 MinLevel 属性可以被更改。
///
[Test]
public void ConsoleLoggerFactoryProvider_MinLevel_CanBeChanged()
@@ -168,7 +167,7 @@ public class LoggerFactoryTests
}
///
- /// 测试LoggerFactoryResolver的Provider创建logger时使用提供者设置
+ /// 测试 LoggerFactoryResolver 的 Provider 创建 logger 时使用提供者设置。
///
[Test]
public void LoggerFactoryResolver_Provider_CreateLogger_ShouldUseProviderSettings()
@@ -183,7 +182,6 @@ public class LoggerFactoryTests
var stringWriter = new StringWriter();
var testLogger = new ConsoleLogger("TestLogger", LogLevel.Warning, stringWriter, false);
- // 验证Warn消息会被记录,但Info消息不会被记录
testLogger.Warn("Warn message");
testLogger.Info("Info message");
@@ -195,7 +193,7 @@ public class LoggerFactoryTests
}
///
- /// 测试LoggerFactoryResolver的MinLevel属性影响新创建的logger
+ /// 测试 LoggerFactoryResolver 的 MinLevel 属性影响新创建的 logger。
///
[Test]
public void LoggerFactoryResolver_MinLevel_AffectsNewLoggers()
@@ -210,7 +208,6 @@ public class LoggerFactoryTests
var stringWriter = new StringWriter();
var testLogger = new ConsoleLogger("TestLogger", LogLevel.Error, stringWriter, false);
- // 验证Error消息会被记录,但Warn消息不会被记录
testLogger.Error("Error message");
testLogger.Warn("Warn message");
@@ -222,7 +219,93 @@ public class LoggerFactoryTests
}
///
- /// 测试ConsoleLoggerFactory创建的多个logger实例是独立的
+ /// 验证默认 provider 激活失败时会回退到静默 provider。
+ ///
+ [Test]
+ public void
+ LoggerFactoryResolver_Provider_Should_Fall_Back_To_SilentProvider_When_DefaultProvider_Activation_Fails()
+ {
+ var originalProvider = LoggerFactoryResolver.Provider;
+ var originalTypeName = GetDefaultProviderTypeName();
+
+ try
+ {
+ ResetProvider();
+ SetDefaultProviderTypeName(typeof(ThrowingLoggerFactoryProvider).AssemblyQualifiedName!);
+
+ var provider = LoggerFactoryResolver.Provider;
+ var logger = provider.CreateLogger("Fallback");
+
+ Assert.Multiple(() =>
+ {
+ Assert.That(provider.GetType().Name, Is.EqualTo("SilentLoggerFactoryProvider"));
+ Assert.That(provider.MinLevel, Is.EqualTo(LogLevel.Info));
+ Assert.That(logger.IsEnabledForLevel(LogLevel.Error), Is.False);
+ });
+ }
+ finally
+ {
+ SetDefaultProviderTypeName(originalTypeName);
+ LoggerFactoryResolver.Provider = originalProvider;
+ }
+ }
+
+ ///
+ /// 验证并发首次访问默认 provider 时只会创建一个实例,并向所有调用方返回相同引用。
+ ///
+ [Test]
+ public async Task
+ LoggerFactoryResolver_Provider_Should_Create_A_Single_Default_Instance_When_Accessed_Concurrently()
+ {
+ var originalProvider = LoggerFactoryResolver.Provider;
+ var originalTypeName = GetDefaultProviderTypeName();
+
+ try
+ {
+ BlockingLoggerFactoryProvider.Reset();
+ ResetProvider();
+ SetDefaultProviderTypeName(typeof(BlockingLoggerFactoryProvider).AssemblyQualifiedName!);
+
+ var startGate = new ManualResetEventSlim(false);
+ var tasks = Enumerable.Range(0, 8)
+ .Select(_ => Task.Run(() =>
+ {
+ startGate.Wait();
+ return LoggerFactoryResolver.Provider;
+ }))
+ .ToArray();
+
+ startGate.Set();
+
+ Assert.That(
+ SpinWait.SpinUntil(
+ () => BlockingLoggerFactoryProvider.ConstructionCount >= 1,
+ TimeSpan.FromSeconds(2)),
+ Is.True,
+ "The test provider should start construction after concurrent access begins.");
+
+ BlockingLoggerFactoryProvider.ReleaseConstruction();
+
+ var providers = await Task.WhenAll(tasks);
+
+ Assert.Multiple(() =>
+ {
+ Assert.That(BlockingLoggerFactoryProvider.ConstructionCount, Is.EqualTo(1));
+ Assert.That(providers.Distinct().Count(), Is.EqualTo(1));
+ Assert.That(LoggerFactoryResolver.Provider, Is.SameAs(providers[0]));
+ });
+ }
+ finally
+ {
+ BlockingLoggerFactoryProvider.ReleaseConstruction();
+ BlockingLoggerFactoryProvider.Reset();
+ SetDefaultProviderTypeName(originalTypeName);
+ LoggerFactoryResolver.Provider = originalProvider;
+ }
+ }
+
+ ///
+ /// 测试 ConsoleLoggerFactory 创建的多个 logger 实例是独立的。
///
[Test]
public void ConsoleLoggerFactory_MultipleLoggers_ShouldBeIndependent()
@@ -236,7 +319,7 @@ public class LoggerFactoryTests
}
///
- /// 测试ConsoleLoggerFactoryProvider的MinLevel不会影响已创建的logger
+ /// 测试 ConsoleLoggerFactoryProvider 的 MinLevel 不会影响已创建的 logger。
///
[Test]
public void ConsoleLoggerFactoryProvider_MinLevel_DoesNotAffectCreatedLogger()
@@ -247,7 +330,6 @@ public class LoggerFactoryTests
var stringWriter = new StringWriter();
var testLogger = new ConsoleLogger("TestLogger", LogLevel.Error, stringWriter, false);
- // 验证Error和Fatal消息都会被记录
testLogger.Error("Error message");
testLogger.Fatal("Fatal message");
@@ -255,4 +337,114 @@ public class LoggerFactoryTests
Assert.That(output, Does.Contain("Error message"));
Assert.That(output, Does.Contain("Fatal message"));
}
-}
\ No newline at end of file
+
+ private static string GetDefaultProviderTypeName()
+ {
+ return (string)GetResolverField("DefaultProviderTypeName").GetValue(null)!;
+ }
+
+ private static void SetDefaultProviderTypeName(string typeName)
+ {
+ GetResolverField("DefaultProviderTypeName").SetValue(null, typeName);
+ }
+
+ private static void ResetProvider()
+ {
+ GetResolverField("_provider").SetValue(null, null);
+ }
+
+ private static FieldInfo GetResolverField(string fieldName)
+ {
+ return typeof(LoggerFactoryResolver).GetField(
+ fieldName,
+ BindingFlags.NonPublic | BindingFlags.Static)
+ ?? throw new InvalidOperationException(
+ $"Failed to locate LoggerFactoryResolver.{fieldName}.");
+ }
+
+ ///
+ /// 用于触发默认 provider 激活失败回退路径的测试桩。
+ ///
+ public sealed class ThrowingLoggerFactoryProvider : ILoggerFactoryProvider
+ {
+ ///
+ /// 初始化一个始终抛出异常的 provider。
+ ///
+ /// 始终抛出,用于覆盖回退路径。
+ public ThrowingLoggerFactoryProvider()
+ {
+ throw new InvalidOperationException("Simulated provider activation failure.");
+ }
+
+ ///
+ /// 获取或设置最小日志级别。
+ ///
+ public LogLevel MinLevel { get; set; } = LogLevel.Info;
+
+ ///
+ /// 创建日志器。
+ ///
+ /// 日志器名称。
+ /// 该测试桩永远不会成功创建日志器。
+ /// 始终抛出,因为该方法不应被调用。
+ public ILogger CreateLogger(string name)
+ {
+ throw new NotSupportedException();
+ }
+ }
+
+ ///
+ /// 用于验证并发首次初始化路径只创建单个 provider 实例的测试桩。
+ ///
+ public sealed class BlockingLoggerFactoryProvider : ILoggerFactoryProvider
+ {
+ private static int _constructionCount;
+ private static ManualResetEventSlim _constructionGate = new(false);
+
+ ///
+ /// 初始化一个会阻塞构造完成的 provider,用于放大并发首次访问竞争窗口。
+ ///
+ public BlockingLoggerFactoryProvider()
+ {
+ Interlocked.Increment(ref _constructionCount);
+ _constructionGate.Wait(TimeSpan.FromSeconds(5));
+ }
+
+ ///
+ /// 获取已经发生的构造次数。
+ ///
+ public static int ConstructionCount => Volatile.Read(ref _constructionCount);
+
+ ///
+ /// 获取或设置最小日志级别。
+ ///
+ public LogLevel MinLevel { get; set; } = LogLevel.Info;
+
+ ///
+ /// 创建测试日志器。
+ ///
+ /// 日志器名称。
+ /// 带有当前最小级别设置的控制台日志器。
+ public ILogger CreateLogger(string name)
+ {
+ return new ConsoleLogger(name, MinLevel, TextWriter.Null, false);
+ }
+
+ ///
+ /// 重置该测试桩的并发观测状态。
+ ///
+ public static void Reset()
+ {
+ _constructionGate = new ManualResetEventSlim(false);
+ Interlocked.Exchange(ref _constructionCount, 0);
+ }
+
+ ///
+ /// 释放当前被阻塞的 provider 构造过程。
+ ///
+ public static void ReleaseConstruction()
+ {
+ _constructionGate.Set();
+ }
+ }
+}
diff --git a/GFramework.Core.Tests/State/StateMachineSystemTests.cs b/GFramework.Core.Tests/State/StateMachineSystemTests.cs
index 0ea62de6..2c234238 100644
--- a/GFramework.Core.Tests/State/StateMachineSystemTests.cs
+++ b/GFramework.Core.Tests/State/StateMachineSystemTests.cs
@@ -1,5 +1,6 @@
using System.Reflection;
using GFramework.Core.Abstractions.Enums;
+using GFramework.Core.Abstractions.Logging;
using GFramework.Core.Abstractions.State;
using GFramework.Core.Abstractions.Systems;
using GFramework.Core.Architectures;
@@ -373,4 +374,4 @@ public class TestStateV5_2 : IState
}
}
-#endregion
\ No newline at end of file
+#endregion
diff --git a/GFramework.Core/Extensions/ContextAwareMediatorCommandExtensions.cs b/GFramework.Core/Extensions/ContextAwareMediatorCommandExtensions.cs
index 4ab0692b..24490239 100644
--- a/GFramework.Core/Extensions/ContextAwareMediatorCommandExtensions.cs
+++ b/GFramework.Core/Extensions/ContextAwareMediatorCommandExtensions.cs
@@ -1,6 +1,7 @@
using System.ComponentModel;
using GFramework.Core.Abstractions.Rule;
using GFramework.Cqrs.Abstractions.Cqrs.Command;
+using GFramework.Cqrs.Extensions;
namespace GFramework.Core.Extensions;
@@ -11,7 +12,7 @@ namespace GFramework.Core.Extensions;
///
[EditorBrowsable(EditorBrowsableState.Never)]
[Obsolete(
- "Use GFramework.Core.Extensions.ContextAwareCqrsCommandExtensions instead. This compatibility alias will be removed in a future major version.")]
+ "Use GFramework.Cqrs.Extensions.ContextAwareCqrsCommandExtensions instead. This compatibility alias will be removed in a future major version.")]
public static class ContextAwareMediatorCommandExtensions
{
///
diff --git a/GFramework.Core/Extensions/ContextAwareMediatorExtensions.cs b/GFramework.Core/Extensions/ContextAwareMediatorExtensions.cs
index f2294930..c7aec1b6 100644
--- a/GFramework.Core/Extensions/ContextAwareMediatorExtensions.cs
+++ b/GFramework.Core/Extensions/ContextAwareMediatorExtensions.cs
@@ -1,6 +1,7 @@
using System.ComponentModel;
using GFramework.Core.Abstractions.Rule;
using GFramework.Cqrs.Abstractions.Cqrs;
+using GFramework.Cqrs.Extensions;
namespace GFramework.Core.Extensions;
diff --git a/GFramework.Core/Extensions/ContextAwareMediatorQueryExtensions.cs b/GFramework.Core/Extensions/ContextAwareMediatorQueryExtensions.cs
index 61b02cb9..cf0b4513 100644
--- a/GFramework.Core/Extensions/ContextAwareMediatorQueryExtensions.cs
+++ b/GFramework.Core/Extensions/ContextAwareMediatorQueryExtensions.cs
@@ -1,6 +1,7 @@
using System.ComponentModel;
using GFramework.Core.Abstractions.Rule;
using GFramework.Cqrs.Abstractions.Cqrs.Query;
+using GFramework.Cqrs.Extensions;
namespace GFramework.Core.Extensions;
@@ -11,7 +12,7 @@ namespace GFramework.Core.Extensions;
///
[EditorBrowsable(EditorBrowsableState.Never)]
[Obsolete(
- "Use GFramework.Core.Extensions.ContextAwareCqrsQueryExtensions instead. This compatibility alias will be removed in a future major version.")]
+ "Use GFramework.Cqrs.Extensions.ContextAwareCqrsQueryExtensions instead. This compatibility alias will be removed in a future major version.")]
public static class ContextAwareMediatorQueryExtensions
{
///
diff --git a/GFramework.Core/GFramework.Core.csproj b/GFramework.Core/GFramework.Core.csproj
index f3e41eab..2535d4e9 100644
--- a/GFramework.Core/GFramework.Core.csproj
+++ b/GFramework.Core/GFramework.Core.csproj
@@ -10,6 +10,7 @@
+
diff --git a/GFramework.Core/Logging/LoggerFactoryResolver.cs b/GFramework.Core/Logging/LoggerFactoryResolver.cs
deleted file mode 100644
index c15378d1..00000000
--- a/GFramework.Core/Logging/LoggerFactoryResolver.cs
+++ /dev/null
@@ -1,26 +0,0 @@
-using GFramework.Core.Abstractions.Logging;
-
-namespace GFramework.Core.Logging;
-
-///
-/// 日志工厂提供程序解析器,用于管理和提供日志工厂提供程序实例
-///
-public static class LoggerFactoryResolver
-{
- ///
- /// 获取或设置当前的日志工厂提供程序
- ///
- ///
- /// 日志工厂提供程序实例,默认为控制台日志工厂提供程序
- ///
- public static ILoggerFactoryProvider Provider { get; set; }
- = new ConsoleLoggerFactoryProvider();
-
- ///
- /// 获取或设置日志记录的最小级别
- ///
- ///
- /// 日志级别枚举值,默认为Info级别
- ///
- public static LogLevel MinLevel { get; set; } = LogLevel.Info;
-}
\ No newline at end of file
diff --git a/GFramework.Core/Properties/TypeForwarders.cs b/GFramework.Core/Properties/TypeForwarders.cs
new file mode 100644
index 00000000..2c260462
--- /dev/null
+++ b/GFramework.Core/Properties/TypeForwarders.cs
@@ -0,0 +1,12 @@
+using System.Runtime.CompilerServices;
+using GFramework.Core.Abstractions.Logging;
+using GFramework.Cqrs.Command;
+using GFramework.Cqrs.Notification;
+using GFramework.Cqrs.Query;
+using GFramework.Cqrs.Request;
+
+[assembly: TypeForwardedTo(typeof(LoggerFactoryResolver))]
+[assembly: TypeForwardedTo(typeof(CommandBase<,>))]
+[assembly: TypeForwardedTo(typeof(QueryBase<,>))]
+[assembly: TypeForwardedTo(typeof(RequestBase<,>))]
+[assembly: TypeForwardedTo(typeof(NotificationBase<>))]
diff --git a/GFramework.Core/Services/Modules/CqrsRuntimeModule.cs b/GFramework.Core/Services/Modules/CqrsRuntimeModule.cs
index 5ca7a909..3fe37558 100644
--- a/GFramework.Core/Services/Modules/CqrsRuntimeModule.cs
+++ b/GFramework.Core/Services/Modules/CqrsRuntimeModule.cs
@@ -1,8 +1,8 @@
using GFramework.Core.Abstractions.Architectures;
using GFramework.Core.Abstractions.Cqrs;
using GFramework.Core.Abstractions.Ioc;
-using GFramework.Core.Cqrs.Internal;
-using GFramework.Core.Logging;
+using GFramework.Core.Abstractions.Logging;
+using GFramework.Cqrs;
using GFramework.Cqrs.Abstractions.Cqrs;
namespace GFramework.Core.Services.Modules;
@@ -37,11 +37,12 @@ public sealed class CqrsRuntimeModule : IServiceModule
{
ArgumentNullException.ThrowIfNull(container);
- var dispatcherLogger = LoggerFactoryResolver.Provider.CreateLogger(nameof(CqrsDispatcher));
- var registrarLogger = LoggerFactoryResolver.Provider.CreateLogger(nameof(DefaultCqrsHandlerRegistrar));
+ var dispatcherLogger = LoggerFactoryResolver.Provider.CreateLogger("CqrsDispatcher");
+ var registrarLogger = LoggerFactoryResolver.Provider.CreateLogger("DefaultCqrsHandlerRegistrar");
- container.Register(new CqrsDispatcher(container, dispatcherLogger));
- container.Register(new DefaultCqrsHandlerRegistrar(container, registrarLogger));
+ container.Register(CqrsRuntimeFactory.CreateRuntime(container, dispatcherLogger));
+ container.Register(
+ CqrsRuntimeFactory.CreateHandlerRegistrar(container, registrarLogger));
}
///
diff --git a/GFramework.Cqrs.Tests/Cqrs/AbstractCqrsHandlerContextTests.cs b/GFramework.Cqrs.Tests/Cqrs/AbstractCqrsHandlerContextTests.cs
new file mode 100644
index 00000000..58e86f89
--- /dev/null
+++ b/GFramework.Cqrs.Tests/Cqrs/AbstractCqrsHandlerContextTests.cs
@@ -0,0 +1,74 @@
+using GFramework.Core.Abstractions.Architectures;
+using GFramework.Core.Abstractions.Rule;
+using GFramework.Cqrs.Abstractions.Cqrs;
+using GFramework.Cqrs.Abstractions.Cqrs.Command;
+using GFramework.Cqrs.Cqrs.Command;
+
+namespace GFramework.Cqrs.Tests.Cqrs;
+
+///
+/// 验证 CQRS handler 基类在脱离 dispatcher 使用时会显式失败,并在注入上下文后保持可观察行为。
+///
+[TestFixture]
+internal sealed class AbstractCqrsHandlerContextTests
+{
+ ///
+ /// 验证新的轻量 handler 基类不会再偷偷回退到全局 GameContext。
+ ///
+ [Test]
+ public void GetContext_Should_Throw_When_Handler_Has_Not_Been_Initialized_By_Runtime()
+ {
+ var handler = new TestCommandHandler();
+
+ var exception = Assert.Throws(() => ((IContextAware)handler).GetContext());
+
+ Assert.That(
+ exception!.Message,
+ Does.Contain("has not been initialized").IgnoreCase);
+ }
+
+ ///
+ /// 验证 runtime 注入上下文后,派生 handler 可以继续访问 Context 并收到 OnContextReady 回调。
+ ///
+ [Test]
+ public async Task Handle_Should_Observe_Injected_Context_And_OnContextReady_Callback()
+ {
+ var handler = new TestCommandHandler();
+ var context = new Mock(MockBehavior.Strict).Object;
+
+ ((IContextAware)handler).SetContext(context);
+ await handler.Handle(new TestCommand(), CancellationToken.None);
+
+ Assert.Multiple(() =>
+ {
+ Assert.That(handler.OnContextReadyCallCount, Is.EqualTo(1));
+ Assert.That(handler.LastObservedContext, Is.SameAs(context));
+ });
+ }
+
+ ///
+ /// 用于验证上下文注入行为的最小 CQRS 命令。
+ ///
+ private sealed record TestCommand : ICommand;
+
+ ///
+ /// 暴露基类上下文访问与初始化回调的测试处理器。
+ ///
+ private sealed class TestCommandHandler : AbstractCommandHandler
+ {
+ public int OnContextReadyCallCount { get; private set; }
+
+ public IArchitectureContext? LastObservedContext { get; private set; }
+
+ protected override void OnContextReady()
+ {
+ OnContextReadyCallCount++;
+ }
+
+ public override ValueTask Handle(TestCommand command, CancellationToken cancellationToken)
+ {
+ LastObservedContext = Context;
+ return ValueTask.FromResult(Unit.Value);
+ }
+ }
+}
diff --git a/GFramework.Cqrs.Tests/Cqrs/CqrsHandlerRegistrarTests.cs b/GFramework.Cqrs.Tests/Cqrs/CqrsHandlerRegistrarTests.cs
index 360fe97b..3100ce84 100644
--- a/GFramework.Cqrs.Tests/Cqrs/CqrsHandlerRegistrarTests.cs
+++ b/GFramework.Cqrs.Tests/Cqrs/CqrsHandlerRegistrarTests.cs
@@ -1,4 +1,3 @@
-using GFramework.Core.Abstractions.Cqrs;
using GFramework.Core.Abstractions.Logging;
using GFramework.Core.Architectures;
using GFramework.Core.Ioc;
diff --git a/GFramework.Cqrs.Tests/Mediator/MediatorAdvancedFeaturesTests.cs b/GFramework.Cqrs.Tests/Mediator/MediatorAdvancedFeaturesTests.cs
index 257809f0..fe8f4413 100644
--- a/GFramework.Cqrs.Tests/Mediator/MediatorAdvancedFeaturesTests.cs
+++ b/GFramework.Cqrs.Tests/Mediator/MediatorAdvancedFeaturesTests.cs
@@ -1,3 +1,4 @@
+using GFramework.Core.Abstractions.Logging;
using GFramework.Core.Architectures;
using GFramework.Core.Ioc;
using GFramework.Core.Logging;
diff --git a/GFramework.Cqrs.Tests/Mediator/MediatorArchitectureIntegrationTests.cs b/GFramework.Cqrs.Tests/Mediator/MediatorArchitectureIntegrationTests.cs
index e403735e..728f005a 100644
--- a/GFramework.Cqrs.Tests/Mediator/MediatorArchitectureIntegrationTests.cs
+++ b/GFramework.Cqrs.Tests/Mediator/MediatorArchitectureIntegrationTests.cs
@@ -1,4 +1,5 @@
using GFramework.Core.Abstractions.Architectures;
+using GFramework.Core.Abstractions.Logging;
using GFramework.Core.Architectures;
using GFramework.Core.Command;
using GFramework.Core.Ioc;
diff --git a/GFramework.Cqrs.Tests/Mediator/MediatorComprehensiveTests.cs b/GFramework.Cqrs.Tests/Mediator/MediatorComprehensiveTests.cs
index b0b510d6..423b1c9b 100644
--- a/GFramework.Cqrs.Tests/Mediator/MediatorComprehensiveTests.cs
+++ b/GFramework.Cqrs.Tests/Mediator/MediatorComprehensiveTests.cs
@@ -1,5 +1,6 @@
using GFramework.Core.Abstractions.Architectures;
using GFramework.Core.Abstractions.Events;
+using GFramework.Core.Abstractions.Logging;
using GFramework.Core.Architectures;
using GFramework.Core.Command;
using GFramework.Core.Environment;
diff --git a/GFramework.Core/Cqrs/Command/CommandBase.cs b/GFramework.Cqrs/Command/CommandBase.cs
similarity index 53%
rename from GFramework.Core/Cqrs/Command/CommandBase.cs
rename to GFramework.Cqrs/Command/CommandBase.cs
index d8232608..486e4136 100644
--- a/GFramework.Core/Cqrs/Command/CommandBase.cs
+++ b/GFramework.Cqrs/Command/CommandBase.cs
@@ -13,19 +13,23 @@
using GFramework.Cqrs.Abstractions.Cqrs.Command;
-namespace GFramework.Core.Cqrs.Command;
+namespace GFramework.Cqrs.Command;
///
-/// 表示一个基础命令类,用于处理带有输入和响应的命令模式实现。
-/// 该类实现了 ICommand<TResponse> 接口,提供了通用的命令结构。
+/// 为携带输入模型的 CQRS 命令提供统一基类。
///
-/// 命令输入数据的类型
-/// 命令执行后返回结果的类型
-/// 命令执行所需的输入数据
-public abstract class CommandBase(TInput input) : ICommand where TInput : ICommandInput
+/// 命令输入类型。
+/// 命令响应类型。
+/// 命令执行所需的输入对象。
+///
+/// 该类型继续保留在历史公开命名空间中,以避免调用方因 runtime 程序集拆分而批量修改继承层次。
+/// 具体实现现由 GFramework.Cqrs 程序集承载,并通过 type forward 维持旧程序集兼容性。
+///
+public abstract class CommandBase(TInput input) : ICommand
+ where TInput : ICommandInput
{
///
- /// 获取命令的输入数据。
+ /// 获取命令执行时携带的输入对象。
///
public TInput Input => input;
}
diff --git a/GFramework.Core/Cqrs/Behaviors/LoggingBehavior.cs b/GFramework.Cqrs/Cqrs/Behaviors/LoggingBehavior.cs
similarity index 71%
rename from GFramework.Core/Cqrs/Behaviors/LoggingBehavior.cs
rename to GFramework.Cqrs/Cqrs/Behaviors/LoggingBehavior.cs
index 7230f53d..63896c92 100644
--- a/GFramework.Core/Cqrs/Behaviors/LoggingBehavior.cs
+++ b/GFramework.Cqrs/Cqrs/Behaviors/LoggingBehavior.cs
@@ -11,19 +11,20 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-using System.Diagnostics;
using GFramework.Core.Abstractions.Logging;
-using GFramework.Core.Logging;
using GFramework.Cqrs.Abstractions.Cqrs;
-namespace GFramework.Core.Cqrs.Behaviors;
+namespace GFramework.Cqrs.Cqrs.Behaviors;
///
-/// 日志记录行为类,用于在CQRS管道中记录请求处理的日志信息
-/// 实现IPipelineBehavior接口,为请求处理提供日志记录功能
+/// 在 CQRS 请求管道中记录请求开始、完成、取消与失败日志。
///
-/// 请求类型,必须实现IRequest接口
-/// 响应类型
+/// 请求类型。
+/// 响应类型。
+///
+/// 该行为已迁移到 GFramework.Cqrs.Cqrs.Behaviors 命名空间,
+/// 实现位于 GFramework.Cqrs 程序集,用于承载 CQRS runtime 细节并与旧层解耦。
+///
public sealed class LoggingBehavior : IPipelineBehavior
where TRequest : IRequest
{
@@ -31,13 +32,12 @@ public sealed class LoggingBehavior : IPipelineBehavior));
///
- /// 处理请求并记录日志
- /// 在请求处理前后记录调试信息,处理异常时记录错误日志
+ /// 执行日志包装后的下一段请求处理逻辑。
///
- /// 要处理的请求消息
- /// 下一个处理委托,用于继续管道执行
- /// 取消令牌,用于取消操作
- /// 处理结果的ValueTask
+ /// 当前请求消息。
+ /// 后续处理委托。
+ /// 取消令牌。
+ /// 请求处理结果。
public async ValueTask Handle(
TRequest message,
MessageHandlerDelegate next,
diff --git a/GFramework.Core/Cqrs/Behaviors/PerformanceBehavior.cs b/GFramework.Cqrs/Cqrs/Behaviors/PerformanceBehavior.cs
similarity index 61%
rename from GFramework.Core/Cqrs/Behaviors/PerformanceBehavior.cs
rename to GFramework.Cqrs/Cqrs/Behaviors/PerformanceBehavior.cs
index 35ab2978..1d13319d 100644
--- a/GFramework.Core/Cqrs/Behaviors/PerformanceBehavior.cs
+++ b/GFramework.Cqrs/Cqrs/Behaviors/PerformanceBehavior.cs
@@ -11,33 +11,34 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-using System.Diagnostics;
using GFramework.Core.Abstractions.Logging;
-using GFramework.Core.Logging;
using GFramework.Cqrs.Abstractions.Cqrs;
-namespace GFramework.Core.Cqrs.Behaviors;
+namespace GFramework.Cqrs.Cqrs.Behaviors;
///
-/// 性能监控行为类,用于监控CQRS请求的执行时间
-/// 实现IPipelineBehavior接口,检测并记录执行时间过长的请求
+/// 在 CQRS 请求管道中监控处理耗时,并对长耗时请求发出告警。
///
-/// 请求类型,必须实现IRequest接口
-/// 响应类型
+/// 请求类型。
+/// 响应类型。
+///
+/// 该行为保留现有公开命名空间以维持消费端兼容性,但实现已迁入 GFramework.Cqrs 程序集。
+///
public sealed class PerformanceBehavior : IPipelineBehavior
where TRequest : IRequest
{
+ private const double SlowRequestThresholdMilliseconds = 500;
+
private readonly ILogger _logger =
LoggerFactoryResolver.Provider.CreateLogger(nameof(PerformanceBehavior));
///
- /// 处理请求并监控执行时间
- /// 使用Stopwatch测量请求处理耗时,超过500ms时记录警告日志
+ /// 统计当前请求处理耗时,并在超阈值时记录警告日志。
///
- /// 要处理的请求消息
- /// 下一个处理委托,用于继续管道执行
- /// 取消令牌,用于取消操作
- /// 处理结果的ValueTask
+ /// 当前请求消息。
+ /// 后续处理委托。
+ /// 取消令牌。
+ /// 请求处理结果。
public async ValueTask Handle(
TRequest message,
MessageHandlerDelegate next,
@@ -53,11 +54,10 @@ public sealed class PerformanceBehavior : IPipelineBehavior
{
var elapsed = Stopwatch.GetElapsedTime(start);
- if (elapsed.TotalMilliseconds > 500)
+ if (elapsed.TotalMilliseconds > SlowRequestThresholdMilliseconds)
{
var requestName = typeof(TRequest).Name;
- _logger.Warn(
- $"Long Running Request: {requestName} ({elapsed.TotalMilliseconds:F2} ms)");
+ _logger.Warn($"Long Running Request: {requestName} ({elapsed.TotalMilliseconds:F2} ms)");
}
}
}
diff --git a/GFramework.Core/Cqrs/Command/AbstractCommandHandler.cs b/GFramework.Cqrs/Cqrs/Command/AbstractCommandHandler.cs
similarity index 83%
rename from GFramework.Core/Cqrs/Command/AbstractCommandHandler.cs
rename to GFramework.Cqrs/Cqrs/Command/AbstractCommandHandler.cs
index d7ebb117..825737bf 100644
--- a/GFramework.Core/Cqrs/Command/AbstractCommandHandler.cs
+++ b/GFramework.Cqrs/Cqrs/Command/AbstractCommandHandler.cs
@@ -11,19 +11,18 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-using GFramework.Core.Rule;
using GFramework.Cqrs.Abstractions.Cqrs;
using GFramework.Cqrs.Abstractions.Cqrs.Command;
-namespace GFramework.Core.Cqrs.Command;
+namespace GFramework.Cqrs.Cqrs.Command;
///
/// 抽象命令处理器基类
-/// 继承自 ContextAwareBase 并实现 IRequestHandler 接口,为具体的命令处理器提供基础功能。
+/// 继承自轻量 CQRS 上下文基类并实现 IRequestHandler 接口,为具体的命令处理器提供基础功能。
/// 框架会在每次分发前注入当前架构上下文,因此派生类可以通过 Context 访问架构级服务。
///
/// 命令类型
-public abstract class AbstractCommandHandler : ContextAwareBase, IRequestHandler
+public abstract class AbstractCommandHandler : CqrsContextAwareHandlerBase, IRequestHandler
where TCommand : ICommand
{
///
@@ -38,12 +37,13 @@ public abstract class AbstractCommandHandler : ContextAwareBase, IRequ
///
/// 抽象命令处理器基类(带返回值版本)
-/// 继承自 ContextAwareBase 并实现 IRequestHandler 接口,为具体的命令处理器提供基础功能。
+/// 继承自轻量 CQRS 上下文基类并实现 IRequestHandler 接口,为具体的命令处理器提供基础功能。
/// 支持泛型命令和结果类型,框架会在每次分发前注入当前架构上下文。
///
/// 命令类型,必须实现ICommand接口
/// 命令执行结果类型
-public abstract class AbstractCommandHandler : ContextAwareBase, IRequestHandler
+public abstract class AbstractCommandHandler : CqrsContextAwareHandlerBase,
+ IRequestHandler
where TCommand : ICommand
{
///
diff --git a/GFramework.Core/Cqrs/Command/AbstractStreamCommandHandler.cs b/GFramework.Cqrs/Cqrs/Command/AbstractStreamCommandHandler.cs
similarity index 92%
rename from GFramework.Core/Cqrs/Command/AbstractStreamCommandHandler.cs
rename to GFramework.Cqrs/Cqrs/Command/AbstractStreamCommandHandler.cs
index 223a9cc5..cb4ccb6b 100644
--- a/GFramework.Core/Cqrs/Command/AbstractStreamCommandHandler.cs
+++ b/GFramework.Cqrs/Cqrs/Command/AbstractStreamCommandHandler.cs
@@ -11,15 +11,14 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-using GFramework.Core.Rule;
using GFramework.Cqrs.Abstractions.Cqrs;
using GFramework.Cqrs.Abstractions.Cqrs.Command;
-namespace GFramework.Core.Cqrs.Command;
+namespace GFramework.Cqrs.Cqrs.Command;
///
/// 抽象流式命令处理器基类。
-/// 继承自 并实现 ,
+/// 继承自轻量 CQRS 上下文基类并实现 ,
/// 为具体的流式命令处理器提供基础功能。
///
/// 流式命令类型,必须实现 。
@@ -32,7 +31,7 @@ namespace GFramework.Core.Cqrs.Command;
/// 传入 的取消令牌同时约束流的创建与后续枚举,
/// 派生类应在启动阶段和每次生成响应前尊重取消请求,避免在调用方停止枚举后继续执行后台工作。
///
-public abstract class AbstractStreamCommandHandler : ContextAwareBase,
+public abstract class AbstractStreamCommandHandler : CqrsContextAwareHandlerBase,
IStreamRequestHandler
where TCommand : IStreamCommand
{
diff --git a/GFramework.Cqrs/Cqrs/CqrsContextAwareHandlerBase.cs b/GFramework.Cqrs/Cqrs/CqrsContextAwareHandlerBase.cs
new file mode 100644
index 00000000..73e25ff1
--- /dev/null
+++ b/GFramework.Cqrs/Cqrs/CqrsContextAwareHandlerBase.cs
@@ -0,0 +1,59 @@
+using GFramework.Core.Abstractions.Architectures;
+using GFramework.Core.Abstractions.Rule;
+
+namespace GFramework.Cqrs.Cqrs;
+
+///
+/// 为 CQRS 处理器提供最小化的上下文感知基类实现。
+///
+///
+/// 该基类只承接 CQRS runtime 在分发前注入的 ,
+/// 不再像 ContextAwareBase 那样回退到 GameContext 全局查找。
+/// 这样可以让 GFramework.Cqrs 保持对 GFramework.Core 运行时实现的零依赖,
+/// 同时在处理器被错误地脱离 dispatcher 使用时以显式异常快速失败。
+///
+public abstract class CqrsContextAwareHandlerBase : IContextAware
+{
+ private IArchitectureContext? _context;
+
+ ///
+ /// 获取当前分发周期内已注入的架构上下文。
+ ///
+ ///
+ /// 当前处理器尚未被 CQRS runtime 注入上下文。
+ ///
+ protected IArchitectureContext Context => _context ?? throw new InvalidOperationException(
+ "The CQRS handler context has not been initialized. Ensure the handler is invoked through the CQRS runtime.");
+
+ ///
+ /// 由 runtime 在分发前注入当前架构上下文。
+ ///
+ /// 当前架构上下文。
+ void IContextAware.SetContext(IArchitectureContext context)
+ {
+ ArgumentNullException.ThrowIfNull(context);
+
+ _context = context;
+ OnContextReady();
+ }
+
+ ///
+ /// 获取当前处理器实例已绑定的架构上下文。
+ ///
+ /// 当前分发周期内的架构上下文。
+ IArchitectureContext IContextAware.GetContext()
+ {
+ return Context;
+ }
+
+ ///
+ /// 当上下文注入完成后执行额外初始化。
+ ///
+ ///
+ /// 该钩子保留与旧 ContextAwareBase 相近的扩展点,
+ /// 便于处理器在迁移后继续承接分发前的派生类初始化逻辑。
+ ///
+ protected virtual void OnContextReady()
+ {
+ }
+}
diff --git a/GFramework.Core/Cqrs/Notification/AbstractNotificationHandler.cs b/GFramework.Cqrs/Cqrs/Notification/AbstractNotificationHandler.cs
similarity index 85%
rename from GFramework.Core/Cqrs/Notification/AbstractNotificationHandler.cs
rename to GFramework.Cqrs/Cqrs/Notification/AbstractNotificationHandler.cs
index da6f5281..16246d16 100644
--- a/GFramework.Core/Cqrs/Notification/AbstractNotificationHandler.cs
+++ b/GFramework.Cqrs/Cqrs/Notification/AbstractNotificationHandler.cs
@@ -11,18 +11,18 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-using GFramework.Core.Rule;
using GFramework.Cqrs.Abstractions.Cqrs;
-namespace GFramework.Core.Cqrs.Notification;
+namespace GFramework.Cqrs.Cqrs.Notification;
///
/// 抽象通知处理器基类
-/// 继承自ContextAwareBase并实现INotificationHandler接口,为具体的通知处理器提供基础功能
+/// 继承自轻量 CQRS 上下文基类并实现INotificationHandler接口,为具体的通知处理器提供基础功能
/// 用于处理CQRS模式中的通知消息,支持异步处理
///
/// 通知类型,必须实现INotification接口
-public abstract class AbstractNotificationHandler : ContextAwareBase, INotificationHandler
+public abstract class AbstractNotificationHandler : CqrsContextAwareHandlerBase,
+ INotificationHandler
where TNotification : INotification
{
///
diff --git a/GFramework.Core/Cqrs/Query/AbstractQueryHandler.cs b/GFramework.Cqrs/Cqrs/Query/AbstractQueryHandler.cs
similarity index 83%
rename from GFramework.Core/Cqrs/Query/AbstractQueryHandler.cs
rename to GFramework.Cqrs/Cqrs/Query/AbstractQueryHandler.cs
index 85c86425..5096f4b7 100644
--- a/GFramework.Core/Cqrs/Query/AbstractQueryHandler.cs
+++ b/GFramework.Cqrs/Cqrs/Query/AbstractQueryHandler.cs
@@ -11,20 +11,20 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-using GFramework.Core.Rule;
using GFramework.Cqrs.Abstractions.Cqrs;
using GFramework.Cqrs.Abstractions.Cqrs.Query;
-namespace GFramework.Core.Cqrs.Query;
+namespace GFramework.Cqrs.Cqrs.Query;
///
/// 抽象查询处理器基类
-/// 继承自 ContextAwareBase 并实现 IRequestHandler 接口,为具体的查询处理器提供基础功能。
+/// 继承自轻量 CQRS 上下文基类并实现 IRequestHandler 接口,为具体的查询处理器提供基础功能。
/// 框架会在每次分发前注入当前架构上下文,因此派生类可以通过 Context 访问架构级服务。
///
/// 查询类型,必须实现IQuery接口
/// 查询结果类型
-public abstract class AbstractQueryHandler : ContextAwareBase, IRequestHandler
+public abstract class AbstractQueryHandler : CqrsContextAwareHandlerBase,
+ IRequestHandler
where TQuery : IQuery
{
///
diff --git a/GFramework.Core/Cqrs/Query/AbstractStreamQueryHandler.cs b/GFramework.Cqrs/Cqrs/Query/AbstractStreamQueryHandler.cs
similarity index 50%
rename from GFramework.Core/Cqrs/Query/AbstractStreamQueryHandler.cs
rename to GFramework.Cqrs/Cqrs/Query/AbstractStreamQueryHandler.cs
index 9695dc42..f532f80d 100644
--- a/GFramework.Core/Cqrs/Query/AbstractStreamQueryHandler.cs
+++ b/GFramework.Cqrs/Cqrs/Query/AbstractStreamQueryHandler.cs
@@ -11,29 +11,30 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-using GFramework.Core.Rule;
using GFramework.Cqrs.Abstractions.Cqrs;
using GFramework.Cqrs.Abstractions.Cqrs.Query;
-namespace GFramework.Core.Cqrs.Query;
+namespace GFramework.Cqrs.Cqrs.Query;
///
-/// 抽象流式查询处理器基类
-/// 继承自ContextAwareBase并实现IStreamQueryHandler接口,为具体的流式查询处理器提供基础功能
-/// 支持流式处理查询并产生异步可枚举的响应序列,适用于大数据量或实时数据查询场景
+/// 为流式查询处理器提供共享的 CQRS 上下文访问基类。
///
-/// 流式查询类型,必须实现IStreamQuery接口
-/// 流式查询响应元素类型
-public abstract class AbstractStreamQueryHandler : ContextAwareBase,
+/// 流式查询类型,必须实现 。
+/// 流式查询响应元素类型。
+///
+/// 该基类复用 的上下文注入能力,并实现
+/// 契约,让派生类只需聚焦于结果流的生成逻辑。
+/// 适用于需要逐步产出大量结果或长生命周期响应流的查询场景。
+///
+public abstract class AbstractStreamQueryHandler : CqrsContextAwareHandlerBase,
IStreamRequestHandler
where TQuery : IStreamQuery
{
///
- /// 处理流式查询并返回异步可枚举的响应序列
- /// 由具体的流式查询处理器子类实现流式查询处理逻辑
+ /// 处理流式查询并返回异步可枚举的响应序列。
///
- /// 要处理的流式查询对象
- /// 取消令牌,用于取消流式查询操作
- /// 异步可枚举的响应序列,每个元素类型为TResponse
+ /// 要处理的流式查询对象。
+ /// 用于停止结果流生成的取消令牌。
+ /// 按需生成的异步响应序列。
public abstract IAsyncEnumerable Handle(TQuery query, CancellationToken cancellationToken);
}
diff --git a/GFramework.Core/Cqrs/Request/AbstractRequestHandler.cs b/GFramework.Cqrs/Cqrs/Request/AbstractRequestHandler.cs
similarity index 86%
rename from GFramework.Core/Cqrs/Request/AbstractRequestHandler.cs
rename to GFramework.Cqrs/Cqrs/Request/AbstractRequestHandler.cs
index f26c7cfa..8343576b 100644
--- a/GFramework.Core/Cqrs/Request/AbstractRequestHandler.cs
+++ b/GFramework.Cqrs/Cqrs/Request/AbstractRequestHandler.cs
@@ -11,17 +11,16 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-using GFramework.Core.Rule;
using GFramework.Cqrs.Abstractions.Cqrs;
-namespace GFramework.Core.Cqrs.Request;
+namespace GFramework.Cqrs.Cqrs.Request;
///
/// 抽象请求处理器基类,用于处理不返回具体响应的请求
-/// 继承自ContextAwareBase并实现IRequestHandler接口
+/// 继承自轻量 CQRS 上下文基类并实现IRequestHandler接口
///
/// 请求类型,必须实现IRequest[Unit]接口
-public abstract class AbstractRequestHandler : ContextAwareBase, IRequestHandler
+public abstract class AbstractRequestHandler : CqrsContextAwareHandlerBase, IRequestHandler
where TRequest : IRequest
{
///
@@ -35,11 +34,11 @@ public abstract class AbstractRequestHandler : ContextAwareBase, IRequ
///
/// 抽象请求处理器基类,用于处理需要返回具体响应的请求
-/// 继承自ContextAwareBase并实现IRequestHandler接口
+/// 继承自轻量 CQRS 上下文基类并实现IRequestHandler接口
///
/// 请求类型,必须实现IRequest[TResponse]接口
/// 响应类型
-public abstract class AbstractRequestHandler : ContextAwareBase,
+public abstract class AbstractRequestHandler : CqrsContextAwareHandlerBase,
IRequestHandler where TRequest : IRequest
{
///
diff --git a/GFramework.Core/Cqrs/Request/AbstractStreamRequestHandler.cs b/GFramework.Cqrs/Cqrs/Request/AbstractStreamRequestHandler.cs
similarity index 88%
rename from GFramework.Core/Cqrs/Request/AbstractStreamRequestHandler.cs
rename to GFramework.Cqrs/Cqrs/Request/AbstractStreamRequestHandler.cs
index 2cbf438d..42b968f4 100644
--- a/GFramework.Core/Cqrs/Request/AbstractStreamRequestHandler.cs
+++ b/GFramework.Cqrs/Cqrs/Request/AbstractStreamRequestHandler.cs
@@ -11,19 +11,18 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-using GFramework.Core.Rule;
using GFramework.Cqrs.Abstractions.Cqrs;
-namespace GFramework.Core.Cqrs.Request;
+namespace GFramework.Cqrs.Cqrs.Request;
///
/// 抽象流式请求处理器基类
-/// 继承自ContextAwareBase并实现IStreamRequestHandler接口,为具体的流式请求处理器提供基础功能
+/// 继承自轻量 CQRS 上下文基类并实现IStreamRequestHandler接口,为具体的流式请求处理器提供基础功能
/// 支持流式处理请求并产生异步可枚举的响应序列,适用于需要逐步返回结果的请求处理场景
///
/// 流式请求类型,必须实现IStreamRequest接口
/// 流式请求响应元素类型
-public abstract class AbstractStreamRequestHandler : ContextAwareBase,
+public abstract class AbstractStreamRequestHandler : CqrsContextAwareHandlerBase,
IStreamRequestHandler
where TRequest : IStreamRequest
{
diff --git a/GFramework.Core.Abstractions/Cqrs/CqrsHandlerRegistryAttribute.cs b/GFramework.Cqrs/CqrsHandlerRegistryAttribute.cs
similarity index 94%
rename from GFramework.Core.Abstractions/Cqrs/CqrsHandlerRegistryAttribute.cs
rename to GFramework.Cqrs/CqrsHandlerRegistryAttribute.cs
index e073fe5c..a64875f3 100644
--- a/GFramework.Core.Abstractions/Cqrs/CqrsHandlerRegistryAttribute.cs
+++ b/GFramework.Cqrs/CqrsHandlerRegistryAttribute.cs
@@ -1,4 +1,4 @@
-namespace GFramework.Core.Abstractions.Cqrs;
+namespace GFramework.Cqrs;
///
/// 声明程序集内可供运行时直接调用的 CQRS 处理器注册器类型。
diff --git a/GFramework.Cqrs/CqrsRuntimeFactory.cs b/GFramework.Cqrs/CqrsRuntimeFactory.cs
new file mode 100644
index 00000000..0a0f86ce
--- /dev/null
+++ b/GFramework.Cqrs/CqrsRuntimeFactory.cs
@@ -0,0 +1,51 @@
+using GFramework.Core.Abstractions.Cqrs;
+using GFramework.Core.Abstractions.Ioc;
+using GFramework.Core.Abstractions.Logging;
+using GFramework.Cqrs.Abstractions.Cqrs;
+using GFramework.Cqrs.Internal;
+
+namespace GFramework.Cqrs;
+
+///
+/// 提供 CQRS runtime 默认实现的跨程序集创建入口。
+///
+///
+/// 需要在不暴露内部实现细节的前提下接入默认 CQRS runtime,
+/// 因此通过该工厂返回抽象接口,而不是直接公开内部 dispatcher / registrar 类型。
+///
+public static class CqrsRuntimeFactory
+{
+ ///
+ /// 创建默认 CQRS runtime 分发器。
+ ///
+ /// 目标依赖注入容器。
+ /// 用于 runtime 诊断的日志器。
+ /// 默认 CQRS runtime。
+ ///
+ /// 或 为 。
+ ///
+ public static ICqrsRuntime CreateRuntime(IIocContainer container, ILogger logger)
+ {
+ ArgumentNullException.ThrowIfNull(container);
+ ArgumentNullException.ThrowIfNull(logger);
+
+ return new CqrsDispatcher(container, logger);
+ }
+
+ ///
+ /// 创建默认 CQRS 处理器注册器。
+ ///
+ /// 目标依赖注入容器。
+ /// 用于注册阶段诊断的日志器。
+ /// 默认 CQRS handler registrar。
+ ///
+ /// 或 为 。
+ ///
+ public static ICqrsHandlerRegistrar CreateHandlerRegistrar(IIocContainer container, ILogger logger)
+ {
+ ArgumentNullException.ThrowIfNull(container);
+ ArgumentNullException.ThrowIfNull(logger);
+
+ return new DefaultCqrsHandlerRegistrar(container, logger);
+ }
+}
diff --git a/GFramework.Core/Extensions/ContextAwareCqrsCommandExtensions.cs b/GFramework.Cqrs/Extensions/ContextAwareCqrsCommandExtensions.cs
similarity index 98%
rename from GFramework.Core/Extensions/ContextAwareCqrsCommandExtensions.cs
rename to GFramework.Cqrs/Extensions/ContextAwareCqrsCommandExtensions.cs
index f99fae25..b3c2bde8 100644
--- a/GFramework.Core/Extensions/ContextAwareCqrsCommandExtensions.cs
+++ b/GFramework.Cqrs/Extensions/ContextAwareCqrsCommandExtensions.cs
@@ -1,7 +1,7 @@
using GFramework.Core.Abstractions.Rule;
using GFramework.Cqrs.Abstractions.Cqrs.Command;
-namespace GFramework.Core.Extensions;
+namespace GFramework.Cqrs.Extensions;
///
/// 提供对 接口的 CQRS 命令扩展方法。
diff --git a/GFramework.Core/Extensions/ContextAwareCqrsExtensions.cs b/GFramework.Cqrs/Extensions/ContextAwareCqrsExtensions.cs
similarity index 99%
rename from GFramework.Core/Extensions/ContextAwareCqrsExtensions.cs
rename to GFramework.Cqrs/Extensions/ContextAwareCqrsExtensions.cs
index 6db156f5..ef5eb247 100644
--- a/GFramework.Core/Extensions/ContextAwareCqrsExtensions.cs
+++ b/GFramework.Cqrs/Extensions/ContextAwareCqrsExtensions.cs
@@ -1,7 +1,7 @@
using GFramework.Core.Abstractions.Rule;
using GFramework.Cqrs.Abstractions.Cqrs;
-namespace GFramework.Core.Extensions;
+namespace GFramework.Cqrs.Extensions;
///
/// 提供对 接口的 CQRS 统一扩展方法。
diff --git a/GFramework.Core/Extensions/ContextAwareCqrsQueryExtensions.cs b/GFramework.Cqrs/Extensions/ContextAwareCqrsQueryExtensions.cs
similarity index 98%
rename from GFramework.Core/Extensions/ContextAwareCqrsQueryExtensions.cs
rename to GFramework.Cqrs/Extensions/ContextAwareCqrsQueryExtensions.cs
index 40ec0f7d..21c1e952 100644
--- a/GFramework.Core/Extensions/ContextAwareCqrsQueryExtensions.cs
+++ b/GFramework.Cqrs/Extensions/ContextAwareCqrsQueryExtensions.cs
@@ -1,7 +1,7 @@
using GFramework.Core.Abstractions.Rule;
using GFramework.Cqrs.Abstractions.Cqrs.Query;
-namespace GFramework.Core.Extensions;
+namespace GFramework.Cqrs.Extensions;
///
/// 提供对 接口的 CQRS 查询扩展方法。
diff --git a/GFramework.Cqrs/GFramework.Cqrs.csproj b/GFramework.Cqrs/GFramework.Cqrs.csproj
index 9f002283..86323d6e 100644
--- a/GFramework.Cqrs/GFramework.Cqrs.csproj
+++ b/GFramework.Cqrs/GFramework.Cqrs.csproj
@@ -11,6 +11,7 @@
+
diff --git a/GFramework.Cqrs/GlobalUsings.cs b/GFramework.Cqrs/GlobalUsings.cs
new file mode 100644
index 00000000..97f2d13a
--- /dev/null
+++ b/GFramework.Cqrs/GlobalUsings.cs
@@ -0,0 +1,7 @@
+global using System;
+global using System.Collections.Generic;
+global using System.Linq;
+global using System.Threading;
+global using System.Threading.Tasks;
+global using Microsoft.Extensions.DependencyInjection;
+global using System.Diagnostics;
diff --git a/GFramework.Core.Abstractions/Cqrs/ICqrsHandlerRegistry.cs b/GFramework.Cqrs/ICqrsHandlerRegistry.cs
similarity index 95%
rename from GFramework.Core.Abstractions/Cqrs/ICqrsHandlerRegistry.cs
rename to GFramework.Cqrs/ICqrsHandlerRegistry.cs
index 91af6be7..db3775de 100644
--- a/GFramework.Core.Abstractions/Cqrs/ICqrsHandlerRegistry.cs
+++ b/GFramework.Cqrs/ICqrsHandlerRegistry.cs
@@ -1,6 +1,6 @@
using GFramework.Core.Abstractions.Logging;
-namespace GFramework.Core.Abstractions.Cqrs;
+namespace GFramework.Cqrs;
///
/// 定义由源码生成器产出的 CQRS 处理器注册器契约。
diff --git a/GFramework.Core/Cqrs/Internal/CqrsDispatcher.cs b/GFramework.Cqrs/Internal/CqrsDispatcher.cs
similarity index 99%
rename from GFramework.Core/Cqrs/Internal/CqrsDispatcher.cs
rename to GFramework.Cqrs/Internal/CqrsDispatcher.cs
index 0cae34c4..dafea402 100644
--- a/GFramework.Core/Cqrs/Internal/CqrsDispatcher.cs
+++ b/GFramework.Cqrs/Internal/CqrsDispatcher.cs
@@ -7,7 +7,7 @@ using GFramework.Core.Abstractions.Logging;
using GFramework.Core.Abstractions.Rule;
using GFramework.Cqrs.Abstractions.Cqrs;
-namespace GFramework.Core.Cqrs.Internal;
+namespace GFramework.Cqrs.Internal;
///
/// GFramework 自有 CQRS 运行时分发器。
diff --git a/GFramework.Core/Cqrs/Internal/CqrsHandlerRegistrar.cs b/GFramework.Cqrs/Internal/CqrsHandlerRegistrar.cs
similarity index 99%
rename from GFramework.Core/Cqrs/Internal/CqrsHandlerRegistrar.cs
rename to GFramework.Cqrs/Internal/CqrsHandlerRegistrar.cs
index 65b1cbcf..85a95509 100644
--- a/GFramework.Core/Cqrs/Internal/CqrsHandlerRegistrar.cs
+++ b/GFramework.Cqrs/Internal/CqrsHandlerRegistrar.cs
@@ -1,10 +1,9 @@
using System.Reflection;
-using GFramework.Core.Abstractions.Cqrs;
using GFramework.Core.Abstractions.Ioc;
using GFramework.Core.Abstractions.Logging;
using GFramework.Cqrs.Abstractions.Cqrs;
-namespace GFramework.Core.Cqrs.Internal;
+namespace GFramework.Cqrs.Internal;
///
/// 在架构初始化期间扫描并注册 CQRS 处理器。
diff --git a/GFramework.Core/Cqrs/Internal/DefaultCqrsHandlerRegistrar.cs b/GFramework.Cqrs/Internal/DefaultCqrsHandlerRegistrar.cs
similarity index 96%
rename from GFramework.Core/Cqrs/Internal/DefaultCqrsHandlerRegistrar.cs
rename to GFramework.Cqrs/Internal/DefaultCqrsHandlerRegistrar.cs
index ddf0c06a..70d33562 100644
--- a/GFramework.Core/Cqrs/Internal/DefaultCqrsHandlerRegistrar.cs
+++ b/GFramework.Cqrs/Internal/DefaultCqrsHandlerRegistrar.cs
@@ -3,7 +3,7 @@ using GFramework.Core.Abstractions.Ioc;
using GFramework.Core.Abstractions.Logging;
using GFramework.Cqrs.Abstractions.Cqrs;
-namespace GFramework.Core.Cqrs.Internal;
+namespace GFramework.Cqrs.Internal;
///
/// 默认的 CQRS 处理器注册器实现。
diff --git a/GFramework.Core/Cqrs/Notification/NotificationBase.cs b/GFramework.Cqrs/Notification/NotificationBase.cs
similarity index 55%
rename from GFramework.Core/Cqrs/Notification/NotificationBase.cs
rename to GFramework.Cqrs/Notification/NotificationBase.cs
index 05db2de7..653a22f2 100644
--- a/GFramework.Core/Cqrs/Notification/NotificationBase.cs
+++ b/GFramework.Cqrs/Notification/NotificationBase.cs
@@ -14,18 +14,22 @@
using GFramework.Cqrs.Abstractions.Cqrs;
using GFramework.Cqrs.Abstractions.Cqrs.Notification;
-namespace GFramework.Core.Cqrs.Notification;
+namespace GFramework.Cqrs.Notification;
///
-/// 表示一个基础通知类,用于处理带有输入的通知模式实现。
-/// 该类实现了 INotification 接口,提供了通用的通知结构。
+/// 为携带输入模型的 CQRS 通知提供统一基类。
///
-/// 通知输入数据的类型,必须实现 INotificationInput 接口
-/// 通知执行所需的输入数据
-public abstract class NotificationBase(TInput input) : INotification where TInput : INotificationInput
+/// 通知输入类型,必须实现 。
+/// 通知广播时携带的输入对象。
+///
+/// 该类型继续保留在历史公开命名空间中,以避免调用方因 runtime 程序集拆分而批量修改继承层次。
+/// 具体实现现由 GFramework.Cqrs 程序集承载,并通过 type forward 维持旧程序集兼容性。
+///
+public abstract class NotificationBase(TInput input) : INotification
+ where TInput : INotificationInput
{
///
- /// 获取通知的输入数据。
+ /// 获取通知广播时携带的输入对象。
///
public TInput Input => input;
}
diff --git a/GFramework.Core/Cqrs/Query/QueryBase.cs b/GFramework.Cqrs/Query/QueryBase.cs
similarity index 52%
rename from GFramework.Core/Cqrs/Query/QueryBase.cs
rename to GFramework.Cqrs/Query/QueryBase.cs
index 759b8df1..4da82bf3 100644
--- a/GFramework.Core/Cqrs/Query/QueryBase.cs
+++ b/GFramework.Cqrs/Query/QueryBase.cs
@@ -13,19 +13,23 @@
using GFramework.Cqrs.Abstractions.Cqrs.Query;
-namespace GFramework.Core.Cqrs.Query;
+namespace GFramework.Cqrs.Query;
///
-/// 表示一个基础查询类,用于处理带有输入和响应的查询模式实现。
-/// 该类实现 IQuery<TResponse> 接口,提供了通用的查询结构。
+/// 为携带输入模型的 CQRS 查询提供统一基类。
///
-/// 查询输入数据的类型,必须实现 IQueryInput 接口
-/// 查询执行后返回结果的类型
-/// 查询执行所需的输入数据
-public abstract class QueryBase(TInput input) : IQuery where TInput : IQueryInput
+/// 查询输入类型,必须实现 。
+/// 查询响应类型。
+/// 查询执行所需的输入对象。
+///
+/// 该类型继续保留在历史公开命名空间中,以避免调用方因 runtime 程序集拆分而批量修改继承层次。
+/// 具体实现现由 GFramework.Cqrs 程序集承载,并通过 type forward 维持旧程序集兼容性。
+///
+public abstract class QueryBase(TInput input) : IQuery
+ where TInput : IQueryInput
{
///
- /// 获取查询的输入数据。
+ /// 获取查询执行时携带的输入对象。
///
public TInput Input => input;
}
diff --git a/GFramework.Core/Cqrs/Request/RequestBase.cs b/GFramework.Cqrs/Request/RequestBase.cs
similarity index 53%
rename from GFramework.Core/Cqrs/Request/RequestBase.cs
rename to GFramework.Cqrs/Request/RequestBase.cs
index 5ff18a04..08ceabe4 100644
--- a/GFramework.Core/Cqrs/Request/RequestBase.cs
+++ b/GFramework.Cqrs/Request/RequestBase.cs
@@ -14,19 +14,23 @@
using GFramework.Cqrs.Abstractions.Cqrs;
using GFramework.Cqrs.Abstractions.Cqrs.Request;
-namespace GFramework.Core.Cqrs.Request;
+namespace GFramework.Cqrs.Request;
///
-/// 表示一个基础请求类,用于处理带有输入和响应的请求模式实现。
-/// 该类实现了 IRequest<TResponse> 接口,提供了通用的请求结构。
+/// 为携带输入模型的通用 CQRS 请求提供统一基类。
///
-/// 请求输入数据的类型,必须实现 IRequestInput 接口
-/// 请求执行后返回结果的类型
-/// 请求执行所需的输入数据
-public abstract class RequestBase(TInput input) : IRequest where TInput : IRequestInput
+/// 请求输入类型,必须实现 。
+/// 请求响应类型。
+/// 请求执行所需的输入对象。
+///
+/// 该类型继续保留在历史公开命名空间中,以避免调用方因 runtime 程序集拆分而批量修改继承层次。
+/// 具体实现现由 GFramework.Cqrs 程序集承载,并通过 type forward 维持旧程序集兼容性。
+///
+public abstract class RequestBase(TInput input) : IRequest
+ where TInput : IRequestInput
{
///
- /// 获取请求的输入数据。
+ /// 获取请求执行时携带的输入对象。
///
public TInput Input => input;
}
diff --git a/GFramework.Godot/Coroutine/ContextAwareCoroutineExtensions.cs b/GFramework.Godot/Coroutine/ContextAwareCoroutineExtensions.cs
index 6805d2e7..15ee4561 100644
--- a/GFramework.Godot/Coroutine/ContextAwareCoroutineExtensions.cs
+++ b/GFramework.Godot/Coroutine/ContextAwareCoroutineExtensions.cs
@@ -1,10 +1,10 @@
using GFramework.Core.Abstractions.Rule;
using GFramework.Core.Coroutine;
using GFramework.Core.Coroutine.Extensions;
-using GFramework.Core.Extensions;
using GFramework.Cqrs.Abstractions.Cqrs;
using GFramework.Cqrs.Abstractions.Cqrs.Command;
using GFramework.Cqrs.Abstractions.Cqrs.Query;
+using GFramework.Cqrs.Extensions;
namespace GFramework.Godot.Coroutine;
diff --git a/GFramework.SourceGenerators.Common/Constants/PathContests.cs b/GFramework.SourceGenerators.Common/Constants/PathContests.cs
index 3facd8b6..a9416fbe 100644
--- a/GFramework.SourceGenerators.Common/Constants/PathContests.cs
+++ b/GFramework.SourceGenerators.Common/Constants/PathContests.cs
@@ -15,6 +15,11 @@ public static class PathContests
///
public const string CoreNamespace = $"{BaseNamespace}.Core";
+ ///
+ /// GFramework CQRS runtime 命名空间
+ ///
+ public const string CqrsNamespace = $"{BaseNamespace}.Cqrs";
+
///
/// GFramework Godot模块命名空间
///
@@ -45,4 +50,9 @@ public static class PathContests
/// GFramework核心抽象层命名空间
///
public const string CoreAbstractionsNamespace = $"{CoreNamespace}.Abstractions";
-}
\ No newline at end of file
+
+ ///
+ /// GFramework CQRS 抽象层命名空间
+ ///
+ public const string CqrsAbstractionsNamespace = $"{CqrsNamespace}.Abstractions";
+}
diff --git a/GFramework.SourceGenerators.Tests/Cqrs/CqrsHandlerRegistryGeneratorTests.cs b/GFramework.SourceGenerators.Tests/Cqrs/CqrsHandlerRegistryGeneratorTests.cs
index e2b7ffb1..0392ac8a 100644
--- a/GFramework.SourceGenerators.Tests/Cqrs/CqrsHandlerRegistryGeneratorTests.cs
+++ b/GFramework.SourceGenerators.Tests/Cqrs/CqrsHandlerRegistryGeneratorTests.cs
@@ -38,7 +38,7 @@ public class CqrsHandlerRegistryGeneratorTests
}
}
- namespace GFramework.Core.Abstractions.Cqrs
+ namespace GFramework.Cqrs.Abstractions.Cqrs
{
public interface IRequest { }
public interface INotification { }
@@ -47,7 +47,10 @@ public class CqrsHandlerRegistryGeneratorTests
public interface IRequestHandler where TRequest : IRequest { }
public interface INotificationHandler where TNotification : INotification { }
public interface IStreamRequestHandler where TRequest : IStreamRequest { }
+ }
+ namespace GFramework.Cqrs
+ {
public interface ICqrsHandlerRegistry
{
void Register(Microsoft.Extensions.DependencyInjection.IServiceCollection services, GFramework.Core.Abstractions.Logging.ILogger logger);
@@ -62,7 +65,7 @@ public class CqrsHandlerRegistryGeneratorTests
namespace TestApp
{
- using GFramework.Core.Abstractions.Cqrs;
+ using GFramework.Cqrs.Abstractions.Cqrs;
public sealed record PingQuery() : IRequest;
public sealed record DomainEvent() : INotification;
@@ -78,11 +81,11 @@ public class CqrsHandlerRegistryGeneratorTests
//
#nullable enable
- [assembly: global::GFramework.Core.Abstractions.Cqrs.CqrsHandlerRegistryAttribute(typeof(global::GFramework.Generated.Cqrs.__GFrameworkGeneratedCqrsHandlerRegistry))]
+ [assembly: global::GFramework.Cqrs.CqrsHandlerRegistryAttribute(typeof(global::GFramework.Generated.Cqrs.__GFrameworkGeneratedCqrsHandlerRegistry))]
namespace GFramework.Generated.Cqrs;
- internal sealed class __GFrameworkGeneratedCqrsHandlerRegistry : global::GFramework.Core.Abstractions.Cqrs.ICqrsHandlerRegistry
+ internal sealed class __GFrameworkGeneratedCqrsHandlerRegistry : global::GFramework.Cqrs.ICqrsHandlerRegistry
{
public void Register(global::Microsoft.Extensions.DependencyInjection.IServiceCollection services, global::GFramework.Core.Abstractions.Logging.ILogger logger)
{
@@ -93,19 +96,19 @@ public class CqrsHandlerRegistryGeneratorTests
global::Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddTransient(
services,
- typeof(global::GFramework.Core.Abstractions.Cqrs.IRequestHandler),
+ typeof(global::GFramework.Cqrs.Abstractions.Cqrs.IRequestHandler),
typeof(global::TestApp.AlphaQueryHandler));
- logger.Debug("Registered CQRS handler TestApp.AlphaQueryHandler as GFramework.Core.Abstractions.Cqrs.IRequestHandler.");
+ logger.Debug("Registered CQRS handler TestApp.AlphaQueryHandler as GFramework.Cqrs.Abstractions.Cqrs.IRequestHandler.");
global::Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddTransient(
services,
- typeof(global::GFramework.Core.Abstractions.Cqrs.IStreamRequestHandler),
+ typeof(global::GFramework.Cqrs.Abstractions.Cqrs.IStreamRequestHandler),
typeof(global::TestApp.StreamHandler));
- logger.Debug("Registered CQRS handler TestApp.StreamHandler as GFramework.Core.Abstractions.Cqrs.IStreamRequestHandler.");
+ logger.Debug("Registered CQRS handler TestApp.StreamHandler as GFramework.Cqrs.Abstractions.Cqrs.IStreamRequestHandler.");
global::Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddTransient(
services,
- typeof(global::GFramework.Core.Abstractions.Cqrs.INotificationHandler),
+ typeof(global::GFramework.Cqrs.Abstractions.Cqrs.INotificationHandler),
typeof(global::TestApp.ZetaNotificationHandler));
- logger.Debug("Registered CQRS handler TestApp.ZetaNotificationHandler as GFramework.Core.Abstractions.Cqrs.INotificationHandler.");
+ logger.Debug("Registered CQRS handler TestApp.ZetaNotificationHandler as GFramework.Cqrs.Abstractions.Cqrs.INotificationHandler.");
}
}
@@ -143,7 +146,7 @@ public class CqrsHandlerRegistryGeneratorTests
}
}
- namespace GFramework.Core.Abstractions.Cqrs
+ namespace GFramework.Cqrs.Abstractions.Cqrs
{
public interface IRequest { }
public interface INotification { }
@@ -152,7 +155,10 @@ public class CqrsHandlerRegistryGeneratorTests
public interface IRequestHandler where TRequest : IRequest { }
public interface INotificationHandler where TNotification : INotification { }
public interface IStreamRequestHandler where TRequest : IStreamRequest { }
+ }
+ namespace GFramework.Cqrs
+ {
public interface ICqrsHandlerRegistry
{
void Register(Microsoft.Extensions.DependencyInjection.IServiceCollection services, GFramework.Core.Abstractions.Logging.ILogger logger);
@@ -167,7 +173,7 @@ public class CqrsHandlerRegistryGeneratorTests
namespace TestApp
{
- using GFramework.Core.Abstractions.Cqrs;
+ using GFramework.Cqrs.Abstractions.Cqrs;
public sealed record VisibleRequest() : IRequest;
diff --git a/GFramework.SourceGenerators/Cqrs/CqrsHandlerRegistryGenerator.cs b/GFramework.SourceGenerators/Cqrs/CqrsHandlerRegistryGenerator.cs
index 39c3aadb..65aad69a 100644
--- a/GFramework.SourceGenerators/Cqrs/CqrsHandlerRegistryGenerator.cs
+++ b/GFramework.SourceGenerators/Cqrs/CqrsHandlerRegistryGenerator.cs
@@ -8,13 +8,17 @@ namespace GFramework.SourceGenerators.Cqrs;
[Generator]
public sealed class CqrsHandlerRegistryGenerator : IIncrementalGenerator
{
- private const string CqrsNamespace = $"{PathContests.CoreAbstractionsNamespace}.Cqrs";
+ private const string CqrsContractsNamespace = $"{PathContests.CqrsAbstractionsNamespace}.Cqrs";
+ private const string CqrsRuntimeNamespace = PathContests.CqrsNamespace;
private const string LoggingNamespace = $"{PathContests.CoreAbstractionsNamespace}.Logging";
- private const string IRequestHandlerMetadataName = $"{CqrsNamespace}.IRequestHandler`2";
- private const string INotificationHandlerMetadataName = $"{CqrsNamespace}.INotificationHandler`1";
- private const string IStreamRequestHandlerMetadataName = $"{CqrsNamespace}.IStreamRequestHandler`2";
- private const string ICqrsHandlerRegistryMetadataName = $"{CqrsNamespace}.ICqrsHandlerRegistry";
- private const string CqrsHandlerRegistryAttributeMetadataName = $"{CqrsNamespace}.CqrsHandlerRegistryAttribute";
+ private const string IRequestHandlerMetadataName = $"{CqrsContractsNamespace}.IRequestHandler`2";
+ private const string INotificationHandlerMetadataName = $"{CqrsContractsNamespace}.INotificationHandler`1";
+ private const string IStreamRequestHandlerMetadataName = $"{CqrsContractsNamespace}.IStreamRequestHandler`2";
+ private const string ICqrsHandlerRegistryMetadataName = $"{CqrsRuntimeNamespace}.ICqrsHandlerRegistry";
+
+ private const string CqrsHandlerRegistryAttributeMetadataName =
+ $"{CqrsRuntimeNamespace}.CqrsHandlerRegistryAttribute";
+
private const string ILoggerMetadataName = $"{LoggingNamespace}.ILogger";
private const string IServiceCollectionMetadataName = "Microsoft.Extensions.DependencyInjection.IServiceCollection";
private const string GeneratedNamespace = "GFramework.Generated.Cqrs";
@@ -273,7 +277,7 @@ public sealed class CqrsHandlerRegistryGenerator : IIncrementalGenerator
builder.AppendLine("#nullable enable");
builder.AppendLine();
builder.Append("[assembly: global::");
- builder.Append(CqrsNamespace);
+ builder.Append(CqrsRuntimeNamespace);
builder.Append(".CqrsHandlerRegistryAttribute(typeof(global::");
builder.Append(GeneratedNamespace);
builder.Append('.');
@@ -287,7 +291,7 @@ public sealed class CqrsHandlerRegistryGenerator : IIncrementalGenerator
builder.Append("internal sealed class ");
builder.Append(GeneratedTypeName);
builder.Append(" : global::");
- builder.Append(CqrsNamespace);
+ builder.Append(CqrsRuntimeNamespace);
builder.AppendLine(".ICqrsHandlerRegistry");
builder.AppendLine("{");
builder.Append(
diff --git a/GFramework.Tests.Common/CqrsTestRuntime.cs b/GFramework.Tests.Common/CqrsTestRuntime.cs
index 57d4effd..e7c971db 100644
--- a/GFramework.Tests.Common/CqrsTestRuntime.cs
+++ b/GFramework.Tests.Common/CqrsTestRuntime.cs
@@ -4,10 +4,10 @@ using System.Reflection;
using GFramework.Core.Abstractions.Cqrs;
using GFramework.Core.Abstractions.Ioc;
using GFramework.Core.Abstractions.Logging;
-using GFramework.Core.Architectures;
using GFramework.Core.Ioc;
-using GFramework.Core.Logging;
+using GFramework.Cqrs;
using GFramework.Cqrs.Abstractions.Cqrs;
+using GFramework.Cqrs.Command;
namespace GFramework.Tests.Common;
@@ -20,9 +20,11 @@ namespace GFramework.Tests.Common;
///
public static class CqrsTestRuntime
{
- private static readonly Type CqrsHandlerRegistrarType = typeof(ArchitectureContext).Assembly
+ private static readonly Assembly CqrsRuntimeAssembly = typeof(CommandBase<,>).Assembly;
+
+ private static readonly Type CqrsHandlerRegistrarType = CqrsRuntimeAssembly
.GetType(
- "GFramework.Core.Cqrs.Internal.CqrsHandlerRegistrar",
+ "GFramework.Cqrs.Internal.CqrsHandlerRegistrar",
throwOnError: true)!;
private static readonly MethodInfo RegisterHandlersMethod = CqrsHandlerRegistrarType
@@ -40,47 +42,11 @@ public static class CqrsTestRuntime
?? throw new InvalidOperationException(
"Failed to locate CqrsHandlerRegistrar.RegisterHandlers.");
- private static readonly Type CqrsDispatcherType = typeof(ArchitectureContext).Assembly
- .GetType(
- "GFramework.Core.Cqrs.Internal.CqrsDispatcher",
- throwOnError: true)!;
-
- private static readonly ConstructorInfo CqrsDispatcherConstructor = CqrsDispatcherType.GetConstructor(
- BindingFlags.Instance |
- BindingFlags.Public |
- BindingFlags.NonPublic,
- binder: null,
- [
- typeof(IIocContainer),
- typeof(ILogger)
- ],
- modifiers: null)
- ?? throw new InvalidOperationException(
- "Failed to locate CqrsDispatcher constructor.");
-
- private static readonly Type DefaultCqrsHandlerRegistrarType = typeof(ArchitectureContext).Assembly
- .GetType(
- "GFramework.Core.Cqrs.Internal.DefaultCqrsHandlerRegistrar",
- throwOnError: true)!;
-
- private static readonly ConstructorInfo DefaultCqrsHandlerRegistrarConstructor =
- DefaultCqrsHandlerRegistrarType.GetConstructor(
- BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic,
- binder: null,
- [
- typeof(IIocContainer),
- typeof(ILogger)
- ],
- modifiers: null)
- ?? throw new InvalidOperationException(
- "Failed to locate DefaultCqrsHandlerRegistrar constructor.");
-
///
/// 为裸测试容器补齐默认 CQRS runtime seam。
///
/// 目标测试容器。
/// 为 。
- /// 反射调用底层 CQRS runtime 或注册器构造函数失败时抛出。
///
/// 这使仅使用 的测试环境也能观察与生产路径一致的 runtime 行为,
/// 而无需完整启动服务模块管理器。
@@ -92,16 +58,15 @@ public static class CqrsTestRuntime
if (container.Get() is null)
{
- var runtimeLogger = LoggerFactoryResolver.Provider.CreateLogger(CqrsDispatcherType.Name);
- var runtime = (ICqrsRuntime)CqrsDispatcherConstructor.Invoke([container, runtimeLogger]);
+ var runtimeLogger = LoggerFactoryResolver.Provider.CreateLogger("CqrsDispatcher");
+ var runtime = CqrsRuntimeFactory.CreateRuntime(container, runtimeLogger);
container.Register(runtime);
}
if (container.Get() is null)
{
- var registrarLogger = LoggerFactoryResolver.Provider.CreateLogger(DefaultCqrsHandlerRegistrarType.Name);
- var registrar =
- (ICqrsHandlerRegistrar)DefaultCqrsHandlerRegistrarConstructor.Invoke([container, registrarLogger]);
+ var registrarLogger = LoggerFactoryResolver.Provider.CreateLogger("DefaultCqrsHandlerRegistrar");
+ var registrar = CqrsRuntimeFactory.CreateHandlerRegistrar(container, registrarLogger);
container.Register(registrar);
}
}
diff --git a/GFramework.Tests.Common/GFramework.Tests.Common.csproj b/GFramework.Tests.Common/GFramework.Tests.Common.csproj
index 51f8fba9..ddd8c02c 100644
--- a/GFramework.Tests.Common/GFramework.Tests.Common.csproj
+++ b/GFramework.Tests.Common/GFramework.Tests.Common.csproj
@@ -10,6 +10,7 @@
+