diff --git a/GFramework.Core.Abstractions/Logging/LoggerFactoryResolver.cs b/GFramework.Core.Abstractions/Logging/LoggerFactoryResolver.cs
index a87a18da..fdffe5b2 100644
--- a/GFramework.Core.Abstractions/Logging/LoggerFactoryResolver.cs
+++ b/GFramework.Core.Abstractions/Logging/LoggerFactoryResolver.cs
@@ -10,19 +10,42 @@ namespace GFramework.Core.Abstractions.Logging;
///
public static class LoggerFactoryResolver
{
- private const string DefaultProviderTypeName =
+ 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 => field ??= CreateDefaultProvider();
- set => field = value ?? throw new ArgumentNullException(nameof(value));
+ get
+ {
+ lock (ProviderLock)
+ {
+ _provider ??= CreateDefaultProvider();
+ return _provider;
+ }
+ }
+ set
+ {
+ var provider = value ?? throw new ArgumentNullException(nameof(value));
+
+ lock (ProviderLock)
+ {
+ _provider = provider;
+ }
+ }
}
///
@@ -39,11 +62,19 @@ public static class LoggerFactoryResolver
private static ILoggerFactoryProvider CreateDefaultProvider()
{
- if (Type.GetType(DefaultProviderTypeName, throwOnError: false) is { } providerType &&
- Activator.CreateInstance(providerType) is ILoggerFactoryProvider provider)
+ try
{
- provider.MinLevel = LogLevel.Info;
- return provider;
+ 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();
diff --git a/GFramework.Core.Tests/Cqrs/CqrsPublicNamespaceCompatibilityTests.cs b/GFramework.Core.Tests/Cqrs/CqrsPublicNamespaceCompatibilityTests.cs
new file mode 100644
index 00000000..2f02dbb7
--- /dev/null
+++ b/GFramework.Core.Tests/Cqrs/CqrsPublicNamespaceCompatibilityTests.cs
@@ -0,0 +1,76 @@
+using GFramework.Core.Cqrs.Command;
+using GFramework.Core.Cqrs.Notification;
+using GFramework.Core.Cqrs.Query;
+using GFramework.Core.Cqrs.Request;
+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;
+
+namespace GFramework.Core.Tests.Cqrs;
+
+///
+/// 锁定 CQRS 基础消息类型在 runtime 拆分后的公开命名空间与程序集兼容性。
+///
+[TestFixture]
+public sealed class CqrsPublicNamespaceCompatibilityTests
+{
+ ///
+ /// 验证基础消息类型继续暴露在历史 Core.Cqrs 命名空间,同时由独立 runtime 程序集承载实现。
+ ///
+ [Test]
+ public void Base_Message_Types_Should_Remain_In_Legacy_Namespaces_While_Living_In_Runtime_Assembly()
+ {
+ Assert.Multiple(() =>
+ {
+ AssertLegacyType(typeof(CommandBase), "GFramework.Core.Cqrs.Command");
+ AssertLegacyType(typeof(QueryBase), "GFramework.Core.Cqrs.Query");
+ AssertLegacyType(typeof(RequestBase), "GFramework.Core.Cqrs.Request");
+ AssertLegacyType(typeof(NotificationBase), "GFramework.Core.Cqrs.Notification");
+ });
+ }
+
+ ///
+ /// 验证旧的 GFramework.Core 程序集限定名仍可解析到迁移后的 runtime 实现类型。
+ ///
+ [Test]
+ public void GFramework_Core_Assembly_Should_Forward_Legacy_Base_Types_To_Runtime_Assembly()
+ {
+ Assert.Multiple(() =>
+ {
+ AssertForwardedType("GFramework.Core.Cqrs.Command.CommandBase`2, GFramework.Core");
+ AssertForwardedType("GFramework.Core.Cqrs.Query.QueryBase`2, GFramework.Core");
+ AssertForwardedType("GFramework.Core.Cqrs.Request.RequestBase`2, GFramework.Core");
+ AssertForwardedType("GFramework.Core.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: true);
+
+ 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/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/Extensions/ContextAwareMediatorCommandExtensions.cs b/GFramework.Core/Extensions/ContextAwareMediatorCommandExtensions.cs
index d001cb71..24490239 100644
--- a/GFramework.Core/Extensions/ContextAwareMediatorCommandExtensions.cs
+++ b/GFramework.Core/Extensions/ContextAwareMediatorCommandExtensions.cs
@@ -12,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/ContextAwareMediatorQueryExtensions.cs b/GFramework.Core/Extensions/ContextAwareMediatorQueryExtensions.cs
index 4eb1b2c7..cf0b4513 100644
--- a/GFramework.Core/Extensions/ContextAwareMediatorQueryExtensions.cs
+++ b/GFramework.Core/Extensions/ContextAwareMediatorQueryExtensions.cs
@@ -12,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/Properties/TypeForwarders.cs b/GFramework.Core/Properties/TypeForwarders.cs
index a71d9af2..a27d2bf4 100644
--- a/GFramework.Core/Properties/TypeForwarders.cs
+++ b/GFramework.Core/Properties/TypeForwarders.cs
@@ -1,4 +1,12 @@
using System.Runtime.CompilerServices;
using GFramework.Core.Abstractions.Logging;
+using GFramework.Core.Cqrs.Command;
+using GFramework.Core.Cqrs.Notification;
+using GFramework.Core.Cqrs.Query;
+using GFramework.Core.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.Cqrs/Command/CommandBase.cs b/GFramework.Cqrs/Command/CommandBase.cs
index 57703c47..01351332 100644
--- a/GFramework.Cqrs/Command/CommandBase.cs
+++ b/GFramework.Cqrs/Command/CommandBase.cs
@@ -13,19 +13,23 @@
using GFramework.Cqrs.Abstractions.Cqrs.Command;
-namespace GFramework.Cqrs.Command;
+namespace GFramework.Core.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.Cqrs/Cqrs/Behaviors/LoggingBehavior.cs b/GFramework.Cqrs/Cqrs/Behaviors/LoggingBehavior.cs
index ccd9f0bf..63896c92 100644
--- a/GFramework.Cqrs/Cqrs/Behaviors/LoggingBehavior.cs
+++ b/GFramework.Cqrs/Cqrs/Behaviors/LoggingBehavior.cs
@@ -22,8 +22,8 @@ namespace GFramework.Cqrs.Cqrs.Behaviors;
/// 请求类型。
/// 响应类型。
///
-/// 该行为保留在 GFramework.Core.Cqrs.Behaviors 命名空间以兼容现有调用点,
-/// 但实现已迁入 GFramework.Cqrs 程序集,避免继续由 GFramework.Core 承载 CQRS runtime 细节。
+/// 该行为已迁移到 GFramework.Cqrs.Cqrs.Behaviors 命名空间,
+/// 实现位于 GFramework.Cqrs 程序集,用于承载 CQRS runtime 细节并与旧层解耦。
///
public sealed class LoggingBehavior : IPipelineBehavior
where TRequest : IRequest
diff --git a/GFramework.Cqrs/Cqrs/Query/AbstractStreamQueryHandler.cs b/GFramework.Cqrs/Cqrs/Query/AbstractStreamQueryHandler.cs
index 7d301009..f532f80d 100644
--- a/GFramework.Cqrs/Cqrs/Query/AbstractStreamQueryHandler.cs
+++ b/GFramework.Cqrs/Cqrs/Query/AbstractStreamQueryHandler.cs
@@ -17,22 +17,24 @@ using GFramework.Cqrs.Abstractions.Cqrs.Query;
namespace GFramework.Cqrs.Cqrs.Query;
///
-/// 抽象流式查询处理器基类
-/// 继承自轻量 CQRS 上下文基类并实现IStreamQueryHandler接口,为具体的流式查询处理器提供基础功能
-/// 支持流式处理查询并产生异步可枚举的响应序列,适用于大数据量或实时数据查询场景
+/// 为流式查询处理器提供共享的 CQRS 上下文访问基类。
///
-/// 流式查询类型,必须实现IStreamQuery接口
-/// 流式查询响应元素类型
+/// 流式查询类型,必须实现 。
+/// 流式查询响应元素类型。
+///
+/// 该基类复用 的上下文注入能力,并实现
+/// 契约,让派生类只需聚焦于结果流的生成逻辑。
+/// 适用于需要逐步产出大量结果或长生命周期响应流的查询场景。
+///
public abstract class AbstractStreamQueryHandler : CqrsContextAwareHandlerBase,
IStreamRequestHandler
where TQuery : IStreamQuery
{
///
- /// 处理流式查询并返回异步可枚举的响应序列
- /// 由具体的流式查询处理器子类实现流式查询处理逻辑
+ /// 处理流式查询并返回异步可枚举的响应序列。
///
- /// 要处理的流式查询对象
- /// 取消令牌,用于取消流式查询操作
- /// 异步可枚举的响应序列,每个元素类型为TResponse
+ /// 要处理的流式查询对象。
+ /// 用于停止结果流生成的取消令牌。
+ /// 按需生成的异步响应序列。
public abstract IAsyncEnumerable Handle(TQuery query, CancellationToken cancellationToken);
}
diff --git a/GFramework.Cqrs/CqrsRuntimeFactory.cs b/GFramework.Cqrs/CqrsRuntimeFactory.cs
index 45dbb07a..0a0f86ce 100644
--- a/GFramework.Cqrs/CqrsRuntimeFactory.cs
+++ b/GFramework.Cqrs/CqrsRuntimeFactory.cs
@@ -21,6 +21,9 @@ public static class CqrsRuntimeFactory
/// 目标依赖注入容器。
/// 用于 runtime 诊断的日志器。
/// 默认 CQRS runtime。
+ ///
+ /// 或 为 。
+ ///
public static ICqrsRuntime CreateRuntime(IIocContainer container, ILogger logger)
{
ArgumentNullException.ThrowIfNull(container);
@@ -35,6 +38,9 @@ public static class CqrsRuntimeFactory
/// 目标依赖注入容器。
/// 用于注册阶段诊断的日志器。
/// 默认 CQRS handler registrar。
+ ///
+ /// 或 为 。
+ ///
public static ICqrsHandlerRegistrar CreateHandlerRegistrar(IIocContainer container, ILogger logger)
{
ArgumentNullException.ThrowIfNull(container);
diff --git a/GFramework.Cqrs/Notification/NotificationBase.cs b/GFramework.Cqrs/Notification/NotificationBase.cs
index 06390406..93daea8f 100644
--- a/GFramework.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.Cqrs.Notification;
+namespace GFramework.Core.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.Cqrs/Query/QueryBase.cs b/GFramework.Cqrs/Query/QueryBase.cs
index d0f491b1..9d15e028 100644
--- a/GFramework.Cqrs/Query/QueryBase.cs
+++ b/GFramework.Cqrs/Query/QueryBase.cs
@@ -13,19 +13,23 @@
using GFramework.Cqrs.Abstractions.Cqrs.Query;
-namespace GFramework.Cqrs.Query;
+namespace GFramework.Core.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.Cqrs/Request/RequestBase.cs b/GFramework.Cqrs/Request/RequestBase.cs
index 8c534c86..40d7a44d 100644
--- a/GFramework.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.Cqrs.Request;
+namespace GFramework.Core.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.Tests.Common/CqrsTestRuntime.cs b/GFramework.Tests.Common/CqrsTestRuntime.cs
index f044bbc8..ad02120e 100644
--- a/GFramework.Tests.Common/CqrsTestRuntime.cs
+++ b/GFramework.Tests.Common/CqrsTestRuntime.cs
@@ -4,11 +4,10 @@ using System.Reflection;
using GFramework.Core.Abstractions.Cqrs;
using GFramework.Core.Abstractions.Ioc;
using GFramework.Core.Abstractions.Logging;
+using GFramework.Core.Cqrs.Command;
using GFramework.Core.Ioc;
-using GFramework.Core.Logging;
using GFramework.Cqrs;
using GFramework.Cqrs.Abstractions.Cqrs;
-using GFramework.Cqrs.Command;
namespace GFramework.Tests.Common;
@@ -48,7 +47,6 @@ public static class CqrsTestRuntime
///
/// 目标测试容器。
/// 为 。
- /// 反射调用底层 CQRS runtime 或注册器构造函数失败时抛出。
///
/// 这使仅使用 的测试环境也能观察与生产路径一致的 runtime 行为,
/// 而无需完整启动服务模块管理器。