mirror of
https://github.com/GeWuYou/GFramework.git
synced 2026-05-07 00:39:00 +08:00
test(cqrs): 补齐外部隐藏泛型精确注册回归
- 新增外部程序集隐藏泛型定义与可见类型实参的 precise registration 回归 - 更新 CQRS 重写跟踪与 trace,记录本轮覆盖范围和验证结果
This commit is contained in:
parent
7b5efde3bd
commit
e51b64f8d5
@ -62,7 +62,7 @@ internal sealed class CqrsDispatcher(
|
|||||||
var notificationType = notification.GetType();
|
var notificationType = notification.GetType();
|
||||||
var dispatchBinding = NotificationDispatchBindings.GetOrAdd(
|
var dispatchBinding = NotificationDispatchBindings.GetOrAdd(
|
||||||
notificationType,
|
notificationType,
|
||||||
CreateNotificationDispatchBinding);
|
static notificationType => CreateNotificationDispatchBinding(notificationType));
|
||||||
var handlers = container.GetAll(dispatchBinding.HandlerType);
|
var handlers = container.GetAll(dispatchBinding.HandlerType);
|
||||||
|
|
||||||
if (handlers.Count == 0)
|
if (handlers.Count == 0)
|
||||||
@ -134,7 +134,7 @@ internal sealed class CqrsDispatcher(
|
|||||||
var dispatchBinding = StreamDispatchBindings.GetOrAdd(
|
var dispatchBinding = StreamDispatchBindings.GetOrAdd(
|
||||||
requestType,
|
requestType,
|
||||||
typeof(TResponse),
|
typeof(TResponse),
|
||||||
CreateStreamDispatchBinding);
|
static (requestType, responseType) => CreateStreamDispatchBinding(requestType, responseType));
|
||||||
var handler = container.Get(dispatchBinding.HandlerType)
|
var handler = container.Get(dispatchBinding.HandlerType)
|
||||||
?? throw new InvalidOperationException(
|
?? throw new InvalidOperationException(
|
||||||
$"No CQRS stream handler registered for {requestType.FullName}.");
|
$"No CQRS stream handler registered for {requestType.FullName}.");
|
||||||
@ -181,7 +181,8 @@ internal sealed class CqrsDispatcher(
|
|||||||
var bindingBox = RequestDispatchBindings.GetOrAdd(
|
var bindingBox = RequestDispatchBindings.GetOrAdd(
|
||||||
requestType,
|
requestType,
|
||||||
typeof(TResponse),
|
typeof(TResponse),
|
||||||
CreateRequestDispatchBindingBox<TResponse>);
|
static (cachedRequestType, cachedResponseType) =>
|
||||||
|
CreateRequestDispatchBindingBox<TResponse>(cachedRequestType, cachedResponseType));
|
||||||
return bindingBox.Get<TResponse>();
|
return bindingBox.Get<TResponse>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -438,9 +439,10 @@ internal sealed class CqrsDispatcher(
|
|||||||
public RequestPipelineExecutor<TResponse> GetPipelineExecutor(int behaviorCount)
|
public RequestPipelineExecutor<TResponse> GetPipelineExecutor(int behaviorCount)
|
||||||
{
|
{
|
||||||
ArgumentOutOfRangeException.ThrowIfNegative(behaviorCount);
|
ArgumentOutOfRangeException.ThrowIfNegative(behaviorCount);
|
||||||
return _pipelineExecutors.GetOrAdd(
|
return _pipelineExecutors.GetOrAdd<RequestPipelineExecutorFactoryState<TResponse>>(
|
||||||
behaviorCount,
|
behaviorCount,
|
||||||
count => CreateRequestPipelineExecutor<TResponse>(requestType, count));
|
static (count, state) => CreateRequestPipelineExecutor<TResponse>(state.RequestType, count),
|
||||||
|
new RequestPipelineExecutorFactoryState<TResponse>(requestType));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -504,6 +506,12 @@ internal sealed class CqrsDispatcher(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 为 pipeline executor 缓存携带当前请求类型,避免按行为数量建缓存时创建闭包。
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TResponse">请求响应类型。</typeparam>
|
||||||
|
private readonly record struct RequestPipelineExecutorFactoryState<TResponse>(Type RequestType);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 保存单次 request pipeline 分发所需的当前 handler、behavior 列表和 continuation 缓存。
|
/// 保存单次 request pipeline 分发所需的当前 handler、behavior 列表和 continuation 缓存。
|
||||||
/// 该对象只存在于本次分发,不会跨请求保留容器解析出的实例。
|
/// 该对象只存在于本次分发,不会跨请求保留容器解析出的实例。
|
||||||
|
|||||||
@ -83,7 +83,8 @@ internal static class CqrsHandlerRegistrar
|
|||||||
{
|
{
|
||||||
var assemblyMetadata = AssemblyMetadataCache.GetOrAdd(
|
var assemblyMetadata = AssemblyMetadataCache.GetOrAdd(
|
||||||
assembly,
|
assembly,
|
||||||
key => AnalyzeAssemblyRegistrationMetadata(key, logger));
|
logger,
|
||||||
|
static (key, state) => AnalyzeAssemblyRegistrationMetadata(key, state));
|
||||||
var registryTypes = assemblyMetadata.RegistryTypes;
|
var registryTypes = assemblyMetadata.RegistryTypes;
|
||||||
|
|
||||||
if (registryTypes.Count == 0)
|
if (registryTypes.Count == 0)
|
||||||
@ -442,7 +443,8 @@ internal static class CqrsHandlerRegistrar
|
|||||||
{
|
{
|
||||||
return LoadableTypesCache.GetOrAdd(
|
return LoadableTypesCache.GetOrAdd(
|
||||||
assembly,
|
assembly,
|
||||||
key => LoadAndSortTypes(key, logger));
|
logger,
|
||||||
|
static (key, state) => LoadAndSortTypes(key, state));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@ -1103,6 +1103,26 @@ public class CqrsHandlerRegistryGeneratorTests
|
|||||||
}
|
}
|
||||||
""";
|
""";
|
||||||
|
|
||||||
|
private const string ExternalProtectedGenericDefinitionDependencySource = """
|
||||||
|
using GFramework.Cqrs.Abstractions.Cqrs;
|
||||||
|
|
||||||
|
namespace Dep;
|
||||||
|
|
||||||
|
public abstract class VisibilityScope
|
||||||
|
{
|
||||||
|
protected internal sealed class ProtectedEnvelope<T>
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
protected internal sealed record ProtectedRequest() : IRequest<ProtectedEnvelope<string>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract class HandlerBase :
|
||||||
|
IRequestHandler<VisibilityScope.ProtectedRequest, VisibilityScope.ProtectedEnvelope<string>>
|
||||||
|
{
|
||||||
|
}
|
||||||
|
""";
|
||||||
|
|
||||||
private const string LegacyFallbackMarkerHiddenHandlerSource = """
|
private const string LegacyFallbackMarkerHiddenHandlerSource = """
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
@ -1901,6 +1921,44 @@ public class CqrsHandlerRegistryGeneratorTests
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证当外部程序集隐藏泛型定义以“隐藏定义 + 可见类型实参”的形式参与 CQRS 合同时,
|
||||||
|
/// 生成器会继续输出定向程序集查找与运行时泛型重建,而不是退回字符串 fallback 元数据。
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void Generates_Precise_Assembly_Type_Lookups_For_Inaccessible_External_Generic_Definitions_With_Visible_Type_Arguments()
|
||||||
|
{
|
||||||
|
var contractsReference = MetadataReferenceTestBuilder.CreateFromSource(
|
||||||
|
"Contracts",
|
||||||
|
ExternalProtectedTypeContractsSource);
|
||||||
|
var dependencyReference = MetadataReferenceTestBuilder.CreateFromSource(
|
||||||
|
"Dependency",
|
||||||
|
ExternalProtectedGenericDefinitionDependencySource,
|
||||||
|
contractsReference);
|
||||||
|
var generatedSource = RunGenerator(
|
||||||
|
ExternalProtectedTypeLookupSource,
|
||||||
|
contractsReference,
|
||||||
|
dependencyReference);
|
||||||
|
|
||||||
|
Assert.Multiple(() =>
|
||||||
|
{
|
||||||
|
Assert.That(
|
||||||
|
generatedSource,
|
||||||
|
Does.Contain(
|
||||||
|
"var serviceType0_0Argument0 = ResolveReferencedAssemblyType(\"Dependency, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null\", \"Dep.VisibilityScope+ProtectedRequest\");"));
|
||||||
|
Assert.That(
|
||||||
|
generatedSource,
|
||||||
|
Does.Contain(
|
||||||
|
"var serviceType0_0Argument1GenericDefinition = ResolveReferencedAssemblyType(\"Dependency, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null\", \"Dep.VisibilityScope+ProtectedEnvelope`1\");"));
|
||||||
|
Assert.That(
|
||||||
|
generatedSource,
|
||||||
|
Does.Contain(
|
||||||
|
"var serviceType0_0 = typeof(global::GFramework.Cqrs.Abstractions.Cqrs.IRequestHandler<,>).MakeGenericType(serviceType0_0Argument0, serviceType0_0Argument1GenericDefinition.MakeGenericType(typeof(string)));"));
|
||||||
|
Assert.That(generatedSource, Does.Not.Contain("RegisterRemainingReflectedHandlerInterfaces("));
|
||||||
|
Assert.That(generatedSource, Does.Not.Contain("CqrsReflectionFallbackAttribute("));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 验证即使 runtime 仍暴露旧版无参 fallback marker,生成器也会优先在生成注册器内部处理隐藏 handler,
|
/// 验证即使 runtime 仍暴露旧版无参 fallback marker,生成器也会优先在生成注册器内部处理隐藏 handler,
|
||||||
/// 不再输出 fallback marker。
|
/// 不再输出 fallback marker。
|
||||||
|
|||||||
@ -77,6 +77,10 @@ CQRS 迁移与收敛。
|
|||||||
- `GFramework.SourceGenerators.Tests` 已新增多维数组、交错数组、外部程序集隐藏元素类型三类回归
|
- `GFramework.SourceGenerators.Tests` 已新增多维数组、交错数组、外部程序集隐藏元素类型三类回归
|
||||||
- 当前生成器在 precise runtime type lookup 下已稳定保留数组秩信息,并递归发射交错数组的 `MakeArrayType()` 链
|
- 当前生成器在 precise runtime type lookup 下已稳定保留数组秩信息,并递归发射交错数组的 `MakeArrayType()` 链
|
||||||
- 本轮定向测试未暴露数组发射缺陷,因此未改动 fallback 合同选择逻辑,也未调整 direct / named / mixed fallback 排版路径
|
- 本轮定向测试未暴露数组发射缺陷,因此未改动 fallback 合同选择逻辑,也未调整 direct / named / mixed fallback 排版路径
|
||||||
|
- `2026-04-29` 已补齐一轮外部程序集隐藏泛型定义回归覆盖:
|
||||||
|
- `GFramework.SourceGenerators.Tests` 已新增“外部程序集隐藏泛型定义 + 可见类型实参”的 precise registration 回归
|
||||||
|
- 当前生成器会继续为这类 handler 合同发射 `ResolveReferencedAssemblyType(...) + MakeGenericType(...)` 组合,而不是退回字符串 fallback 元数据
|
||||||
|
- 本轮定向测试未暴露新的实现缺口,因此未改动 direct / named / mixed fallback 选择逻辑,也未调整 generator runtime type 建模实现
|
||||||
- `2026-04-29` 已完成一轮 request pipeline executor 形状缓存:
|
- `2026-04-29` 已完成一轮 request pipeline executor 形状缓存:
|
||||||
- `CqrsDispatcher` 现会继续按 `requestType + responseType` 缓存 request dispatch binding,并在 binding 内按 `behaviorCount` 缓存强类型 pipeline executor
|
- `CqrsDispatcher` 现会继续按 `requestType + responseType` 缓存 request dispatch binding,并在 binding 内按 `behaviorCount` 缓存强类型 pipeline executor
|
||||||
- 每次分发只绑定当前 handler / behaviors 实例,不缓存容器解析结果,因此不改变 transient 生命周期与上下文注入语义
|
- 每次分发只绑定当前 handler / behaviors 实例,不缓存容器解析结果,因此不改变 transient 生命周期与上下文注入语义
|
||||||
@ -149,6 +153,9 @@ CQRS 迁移与收敛。
|
|||||||
- `dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --filter "FullyQualifiedName~CqrsHandlerRegistryGeneratorTests"`
|
- `dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --filter "FullyQualifiedName~CqrsHandlerRegistryGeneratorTests"`
|
||||||
- 结果:通过
|
- 结果:通过
|
||||||
- 备注:`21/21` 测试通过;本轮新增多维数组、交错数组与外部程序集隐藏元素类型的 precise runtime type lookup 回归
|
- 备注:`21/21` 测试通过;本轮新增多维数组、交错数组与外部程序集隐藏元素类型的 precise runtime type lookup 回归
|
||||||
|
- `dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --filter "FullyQualifiedName~CqrsHandlerRegistryGeneratorTests"`
|
||||||
|
- 结果:通过
|
||||||
|
- 备注:`22/22` 测试通过;本轮新增“外部程序集隐藏泛型定义 + 可见类型实参”的 precise registration 回归,确认仍走定向运行时类型重建
|
||||||
- `dotnet test GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release --filter "FullyQualifiedName~GFramework.Cqrs.Tests.Cqrs.CqrsDispatcherCacheTests"`
|
- `dotnet test GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release --filter "FullyQualifiedName~GFramework.Cqrs.Tests.Cqrs.CqrsDispatcherCacheTests"`
|
||||||
- 结果:通过
|
- 结果:通过
|
||||||
- 备注:`4/4` 测试通过;本轮覆盖 request pipeline executor 的首次创建、复用与双行为顺序回归
|
- 备注:`4/4` 测试通过;本轮覆盖 request pipeline executor 的首次创建、复用与双行为顺序回归
|
||||||
|
|||||||
@ -9,6 +9,10 @@
|
|||||||
- 当前分支头最初与 `origin/main` 对齐,批次阈值从 `0 files / 0 lines` 起算
|
- 当前分支头最初与 `origin/main` 对齐,批次阈值从 `0 files / 0 lines` 起算
|
||||||
- 本轮可以安全拆成三个互不冲突的切片:request pipeline executor 形状缓存、precise runtime type lookup 数组回归补强、CQRS 入口文档对齐
|
- 本轮可以安全拆成三个互不冲突的切片:request pipeline executor 形状缓存、precise runtime type lookup 数组回归补强、CQRS 入口文档对齐
|
||||||
- 主线程保留集成与验证职责,subagent 只负责各自写集
|
- 主线程保留集成与验证职责,subagent 只负责各自写集
|
||||||
|
- 本轮继续收口一个更窄的 generator 覆盖缺口:
|
||||||
|
- 在 `GFramework.SourceGenerators.Tests/Cqrs/CqrsHandlerRegistryGeneratorTests.cs` 新增“外部程序集隐藏泛型定义 + 可见类型实参”的 precise registration 回归
|
||||||
|
- 该回归锁定生成器会输出 `ResolveReferencedAssemblyType("...ProtectedEnvelope\`1")` 与 `MakeGenericType(typeof(string))` 的组合,而不是退回程序集级字符串 fallback
|
||||||
|
- 定向测试 `dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --filter "FullyQualifiedName~CqrsHandlerRegistryGeneratorTests"` 通过,结果为 `22/22` passed,因此本轮未触发 `RuntimeTypeReferences` / `SourceEmission` 的实现修正
|
||||||
- 已接受并整合的并行写集:
|
- 已接受并整合的并行写集:
|
||||||
- docs 切片:更新 `GFramework.Cqrs/README.md`、`docs/zh-CN/core/cqrs.md`、`docs/zh-CN/api-reference/index.md`,明确 generated registry 优先、targeted fallback 只补剩余 handler
|
- docs 切片:更新 `GFramework.Cqrs/README.md`、`docs/zh-CN/core/cqrs.md`、`docs/zh-CN/api-reference/index.md`,明确 generated registry 优先、targeted fallback 只补剩余 handler
|
||||||
- generator 切片:在 `GFramework.SourceGenerators.Tests/Cqrs/CqrsHandlerRegistryGeneratorTests.cs` 新增多维数组、交错数组、外部程序集隐藏元素类型三组 precise lookup 回归
|
- generator 切片:在 `GFramework.SourceGenerators.Tests/Cqrs/CqrsHandlerRegistryGeneratorTests.cs` 新增多维数组、交错数组、外部程序集隐藏元素类型三组 precise lookup 回归
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user