docs: 添加 CQRS 架构模式和游戏配置系统文档

- 新增 CQRS 核心概念、命令查询处理器实现指南
- 添加 CQRS 高级用法包括通知发布、管道行为和流式处理
- 提供 CQRS 最佳实践和常见问题解决方案
- 添加游戏配置系统完整接入模板和运行时读取示例
- 包含 YAML 配置文件和 JSON Schema 结构定义说明
- 提供 Godot 引擎配置桥接和热重载功能使用指南
- 添加架构模块集成和生成查询辅助功能文档
This commit is contained in:
GeWuYou 2026-04-16 20:36:15 +08:00
parent d8831733ff
commit 09f751a4f7
71 changed files with 485 additions and 671 deletions

View File

@ -73,9 +73,8 @@ Architecture 负责统一生命周期编排,核心阶段包括:
### CQRS ### CQRS
命令与查询分离,支持同步与异步执行。当前版本内建自有 CQRS runtime、行为管道和 handler 自动注册;公开 API 里仍保留少量历史 命令与查询分离,支持同步与异步执行。当前版本内建自有 CQRS runtime、行为管道和 handler 自动注册;历史 `Mediator`
`Mediator` 命名以兼容旧调用点,但这些别名已进入正式弃用周期:新代码应使用 `Cqrs` 命名入口,旧别名会继续兼容一段时间并计划在未来 兼容别名已从公开 API 移除,统一使用 `Cqrs` 命名入口。
major 版本中移除。
### EventBus ### EventBus

View File

@ -1,4 +1,3 @@
using System.ComponentModel;
using System.Reflection; using System.Reflection;
using GFramework.Core.Abstractions.Lifecycle; using GFramework.Core.Abstractions.Lifecycle;
using GFramework.Core.Abstractions.Model; using GFramework.Core.Abstractions.Model;
@ -82,20 +81,6 @@ public interface IArchitecture : IAsyncInitializable, IAsyncDestroyable, IInitia
void RegisterCqrsPipelineBehavior<TBehavior>() void RegisterCqrsPipelineBehavior<TBehavior>()
where TBehavior : class; where TBehavior : class;
/// <summary>
/// 注册 CQRS 请求管道行为。
/// 该成员保留旧名称以兼容历史调用点,内部行为与 <see cref="RegisterCqrsPipelineBehavior{TBehavior}" /> 一致。
/// 新代码不应继续依赖该别名;兼容层计划在未来的 major 版本中移除。
/// 既支持实现 <c>IPipelineBehavior&lt;,&gt;</c> 的开放泛型行为类型,
/// 也支持绑定到单一请求/响应对的封闭行为类型。
/// </summary>
/// <typeparam name="TBehavior">行为类型,必须是引用类型</typeparam>
[EditorBrowsable(EditorBrowsableState.Never)]
[Obsolete(
"Use RegisterCqrsPipelineBehavior<TBehavior>() instead. This compatibility alias will be removed in a future major version.")]
void RegisterMediatorBehavior<TBehavior>()
where TBehavior : class;
/// <summary> /// <summary>
/// 从指定程序集显式注册 CQRS 处理器。 /// 从指定程序集显式注册 CQRS 处理器。
/// 当处理器位于默认架构程序集之外的模块或扩展程序集中时,可在初始化阶段调用该入口接入对应程序集。 /// 当处理器位于默认架构程序集之外的模块或扩展程序集中时,可在初始化阶段调用该入口接入对应程序集。

View File

@ -1,5 +1,4 @@
using System.ComponentModel; using System.Reflection;
using System.Reflection;
using GFramework.Core.Abstractions.Rule; using GFramework.Core.Abstractions.Rule;
using GFramework.Core.Abstractions.Systems; using GFramework.Core.Abstractions.Systems;
@ -97,18 +96,6 @@ public interface IIocContainer : IContextAware
void RegisterCqrsPipelineBehavior<TBehavior>() void RegisterCqrsPipelineBehavior<TBehavior>()
where TBehavior : class; where TBehavior : class;
/// <summary>
/// 注册 CQRS 请求管道行为。
/// 该成员保留旧名称以兼容历史调用点,内部行为与 <see cref="RegisterCqrsPipelineBehavior{TBehavior}" /> 一致。
/// 新代码不应继续依赖该别名;兼容层计划在未来的 major 版本中移除。
/// </summary>
/// <typeparam name="TBehavior">行为类型,必须是引用类型</typeparam>
[EditorBrowsable(EditorBrowsableState.Never)]
[Obsolete(
"Use RegisterCqrsPipelineBehavior<TBehavior>() instead. This compatibility alias will be removed in a future major version.")]
void RegisterMediatorBehavior<TBehavior>()
where TBehavior : class;
/// <summary> /// <summary>
/// 从指定程序集显式注册 CQRS 处理器。 /// 从指定程序集显式注册 CQRS 处理器。
/// 该入口适用于处理器不位于默认架构程序集中的场景,例如扩展包、模块程序集或拆分后的业务程序集。 /// 该入口适用于处理器不位于默认架构程序集中的场景,例如扩展包、模块程序集或拆分后的业务程序集。

View File

@ -32,4 +32,4 @@ public sealed class PriorityAttribute : Attribute
/// 获取优先级值 /// 获取优先级值
/// </summary> /// </summary>
public int Value { get; } public int Value { get; }
} }

View File

@ -15,4 +15,4 @@ public sealed class GenerateEnumExtensionsAttribute : Attribute
/// 是否生成一个 IsIn(params T[]) 方法以简化多值判断(默认 true /// 是否生成一个 IsIn(params T[]) 方法以简化多值判断(默认 true
/// </summary> /// </summary>
public bool GenerateIsInMethod { get; set; } = true; public bool GenerateIsInMethod { get; set; } = true;
} }

View File

@ -15,4 +15,4 @@ global using System;
global using System.Collections.Generic; global using System.Collections.Generic;
global using System.Linq; global using System.Linq;
global using System.Threading; global using System.Threading;
global using System.Threading.Tasks; global using System.Threading.Tasks;

View File

@ -35,4 +35,4 @@ public sealed class LogAttribute : Attribute
/// <summary>访问修饰符</summary> /// <summary>访问修饰符</summary>
public string AccessModifier { get; set; } = "private"; public string AccessModifier { get; set; } = "private";
} }

View File

@ -6,4 +6,4 @@
[AttributeUsage(AttributeTargets.Class, Inherited = false)] [AttributeUsage(AttributeTargets.Class, Inherited = false)]
public sealed class ContextAwareAttribute : Attribute public sealed class ContextAwareAttribute : Attribute
{ {
} }

View File

@ -6,4 +6,4 @@ namespace GFramework.SourceGenerators.Abstractions.Rule;
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)] [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
public sealed class GetAllAttribute : Attribute public sealed class GetAllAttribute : Attribute
{ {
} }

View File

@ -6,4 +6,4 @@ namespace GFramework.SourceGenerators.Abstractions.Rule;
[AttributeUsage(AttributeTargets.Field, Inherited = false, AllowMultiple = false)] [AttributeUsage(AttributeTargets.Field, Inherited = false, AllowMultiple = false)]
public sealed class GetModelAttribute : Attribute public sealed class GetModelAttribute : Attribute
{ {
} }

View File

@ -6,4 +6,4 @@ namespace GFramework.SourceGenerators.Abstractions.Rule;
[AttributeUsage(AttributeTargets.Field, Inherited = false, AllowMultiple = false)] [AttributeUsage(AttributeTargets.Field, Inherited = false, AllowMultiple = false)]
public sealed class GetModelsAttribute : Attribute public sealed class GetModelsAttribute : Attribute
{ {
} }

View File

@ -6,4 +6,4 @@ namespace GFramework.SourceGenerators.Abstractions.Rule;
[AttributeUsage(AttributeTargets.Field, Inherited = false, AllowMultiple = false)] [AttributeUsage(AttributeTargets.Field, Inherited = false, AllowMultiple = false)]
public sealed class GetServiceAttribute : Attribute public sealed class GetServiceAttribute : Attribute
{ {
} }

View File

@ -6,4 +6,4 @@ namespace GFramework.SourceGenerators.Abstractions.Rule;
[AttributeUsage(AttributeTargets.Field, Inherited = false, AllowMultiple = false)] [AttributeUsage(AttributeTargets.Field, Inherited = false, AllowMultiple = false)]
public sealed class GetServicesAttribute : Attribute public sealed class GetServicesAttribute : Attribute
{ {
} }

View File

@ -6,4 +6,4 @@ namespace GFramework.SourceGenerators.Abstractions.Rule;
[AttributeUsage(AttributeTargets.Field, Inherited = false, AllowMultiple = false)] [AttributeUsage(AttributeTargets.Field, Inherited = false, AllowMultiple = false)]
public sealed class GetSystemAttribute : Attribute public sealed class GetSystemAttribute : Attribute
{ {
} }

View File

@ -6,4 +6,4 @@ namespace GFramework.SourceGenerators.Abstractions.Rule;
[AttributeUsage(AttributeTargets.Field, Inherited = false, AllowMultiple = false)] [AttributeUsage(AttributeTargets.Field, Inherited = false, AllowMultiple = false)]
public sealed class GetSystemsAttribute : Attribute public sealed class GetSystemsAttribute : Attribute
{ {
} }

View File

@ -6,4 +6,4 @@ namespace GFramework.SourceGenerators.Abstractions.Rule;
[AttributeUsage(AttributeTargets.Field, Inherited = false, AllowMultiple = false)] [AttributeUsage(AttributeTargets.Field, Inherited = false, AllowMultiple = false)]
public sealed class GetUtilitiesAttribute : Attribute public sealed class GetUtilitiesAttribute : Attribute
{ {
} }

View File

@ -6,4 +6,4 @@ namespace GFramework.SourceGenerators.Abstractions.Rule;
[AttributeUsage(AttributeTargets.Field, Inherited = false, AllowMultiple = false)] [AttributeUsage(AttributeTargets.Field, Inherited = false, AllowMultiple = false)]
public sealed class GetUtilityAttribute : Attribute public sealed class GetUtilityAttribute : Attribute
{ {
} }

View File

@ -18,16 +18,6 @@
GF_ContextRegistration_001 | GFramework.SourceGenerators.rule | Warning | ContextRegistrationDiagnostics GF_ContextRegistration_001 | GFramework.SourceGenerators.rule | Warning | ContextRegistrationDiagnostics
GF_ContextRegistration_002 | GFramework.SourceGenerators.rule | Warning | ContextRegistrationDiagnostics GF_ContextRegistration_002 | GFramework.SourceGenerators.rule | Warning | ContextRegistrationDiagnostics
GF_ContextRegistration_003 | GFramework.SourceGenerators.rule | Warning | ContextRegistrationDiagnostics GF_ContextRegistration_003 | GFramework.SourceGenerators.rule | Warning | ContextRegistrationDiagnostics
GF_ConfigSchema_001 | GFramework.SourceGenerators.Config | Error | ConfigSchemaDiagnostics
GF_ConfigSchema_002 | GFramework.SourceGenerators.Config | Error | ConfigSchemaDiagnostics
GF_ConfigSchema_003 | GFramework.SourceGenerators.Config | Error | ConfigSchemaDiagnostics
GF_ConfigSchema_004 | GFramework.SourceGenerators.Config | Error | ConfigSchemaDiagnostics
GF_ConfigSchema_005 | GFramework.SourceGenerators.Config | Error | ConfigSchemaDiagnostics
GF_ConfigSchema_006 | GFramework.SourceGenerators.Config | Error | ConfigSchemaDiagnostics
GF_ConfigSchema_007 | GFramework.SourceGenerators.Config | Error | ConfigSchemaDiagnostics
GF_ConfigSchema_008 | GFramework.SourceGenerators.Config | Error | ConfigSchemaDiagnostics
GF_ConfigSchema_009 | GFramework.SourceGenerators.Config | Error | ConfigSchemaDiagnostics
GF_ConfigSchema_010 | GFramework.SourceGenerators.Config | Error | ConfigSchemaDiagnostics
GF_AutoModule_001 | GFramework.SourceGenerators.Architecture | Error | AutoRegisterModuleDiagnostics GF_AutoModule_001 | GFramework.SourceGenerators.Architecture | Error | AutoRegisterModuleDiagnostics
GF_AutoModule_002 | GFramework.SourceGenerators.Architecture | Error | AutoRegisterModuleDiagnostics GF_AutoModule_002 | GFramework.SourceGenerators.Architecture | Error | AutoRegisterModuleDiagnostics
GF_AutoModule_003 | GFramework.SourceGenerators.Architecture | Error | AutoRegisterModuleDiagnostics GF_AutoModule_003 | GFramework.SourceGenerators.Architecture | Error | AutoRegisterModuleDiagnostics

View File

@ -1,4 +1,3 @@
using System.Collections.Immutable;
using GFramework.SourceGenerators.Common.Constants; using GFramework.SourceGenerators.Common.Constants;
using GFramework.SourceGenerators.Diagnostics; using GFramework.SourceGenerators.Diagnostics;
using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Diagnostics;
@ -12,6 +11,17 @@ namespace GFramework.SourceGenerators.Analyzers;
[DiagnosticAnalyzer(LanguageNames.CSharp)] [DiagnosticAnalyzer(LanguageNames.CSharp)]
public sealed class ContextRegistrationAnalyzer : DiagnosticAnalyzer public sealed class ContextRegistrationAnalyzer : DiagnosticAnalyzer
{ {
private static readonly IReadOnlyDictionary<string, ComponentKind> _contextAwareBindingNames =
new Dictionary<string, ComponentKind>(StringComparer.Ordinal)
{
["GetModel"] = ComponentKind.Model,
["GetModels"] = ComponentKind.Model,
["GetSystem"] = ComponentKind.System,
["GetSystems"] = ComponentKind.System,
["GetUtility"] = ComponentKind.Utility,
["GetUtilities"] = ComponentKind.Utility
};
/// <summary> /// <summary>
/// 当前分析器支持的诊断规则。 /// 当前分析器支持的诊断规则。
/// </summary> /// </summary>
@ -58,10 +68,12 @@ public sealed class ContextRegistrationAnalyzer : DiagnosticAnalyzer
if (context.Node is not VariableDeclaratorSyntax variableDeclarator) if (context.Node is not VariableDeclaratorSyntax variableDeclarator)
return; return;
if (context.SemanticModel.GetDeclaredSymbol(variableDeclarator, context.CancellationToken) is not IFieldSymbol fieldSymbol) if (context.SemanticModel.GetDeclaredSymbol(variableDeclarator, context.CancellationToken) is not IFieldSymbol
fieldSymbol)
return; return;
if (!TryCreateBindingRequest(fieldSymbol, variableDeclarator.Identifier.GetLocation(), symbols, out var request)) if (!TryCreateBindingRequest(fieldSymbol, variableDeclarator.Identifier.GetLocation(), symbols,
out var request))
return; return;
ReportMissingRegistration(context, registrationIndex, request); ReportMissingRegistration(context, registrationIndex, request);
@ -272,17 +284,6 @@ public sealed class ContextRegistrationAnalyzer : DiagnosticAnalyzer
return namedType.TypeArguments[0] as INamedTypeSymbol; return namedType.TypeArguments[0] as INamedTypeSymbol;
} }
private static readonly IReadOnlyDictionary<string, ComponentKind> _contextAwareBindingNames =
new Dictionary<string, ComponentKind>(StringComparer.Ordinal)
{
["GetModel"] = ComponentKind.Model,
["GetModels"] = ComponentKind.Model,
["GetSystem"] = ComponentKind.System,
["GetSystems"] = ComponentKind.System,
["GetUtility"] = ComponentKind.Utility,
["GetUtilities"] = ComponentKind.Utility
};
private enum ComponentKind private enum ComponentKind
{ {
Model, Model,
@ -368,22 +369,35 @@ public sealed class ContextRegistrationAnalyzer : DiagnosticAnalyzer
{ {
return new SymbolCache( return new SymbolCache(
compilation.GetTypeByMetadataName($"{PathContests.CoreNamespace}.Architectures.Architecture"), compilation.GetTypeByMetadataName($"{PathContests.CoreNamespace}.Architectures.Architecture"),
compilation.GetTypeByMetadataName($"{PathContests.CoreAbstractionsNamespace}.Architectures.IArchitecture"), compilation.GetTypeByMetadataName(
compilation.GetTypeByMetadataName($"{PathContests.CoreAbstractionsNamespace}.Architectures.IArchitectureModule"), $"{PathContests.CoreAbstractionsNamespace}.Architectures.IArchitecture"),
compilation.GetTypeByMetadataName($"{PathContests.CoreAbstractionsNamespace}.Architectures.IArchitectureContext"), compilation.GetTypeByMetadataName(
$"{PathContests.CoreAbstractionsNamespace}.Architectures.IArchitectureModule"),
compilation.GetTypeByMetadataName(
$"{PathContests.CoreAbstractionsNamespace}.Architectures.IArchitectureContext"),
compilation.GetTypeByMetadataName("System.Collections.Generic.IReadOnlyList`1"), compilation.GetTypeByMetadataName("System.Collections.Generic.IReadOnlyList`1"),
compilation.GetTypeByMetadataName($"{PathContests.CoreNamespace}.Extensions.ContextAwareServiceExtensions"), compilation.GetTypeByMetadataName(
compilation.GetTypeByMetadataName($"{PathContests.SourceGeneratorsAbstractionsPath}.Rule.GetModelAttribute"), $"{PathContests.CoreNamespace}.Extensions.ContextAwareServiceExtensions"),
compilation.GetTypeByMetadataName($"{PathContests.SourceGeneratorsAbstractionsPath}.Rule.GetModelsAttribute"), compilation.GetTypeByMetadataName(
compilation.GetTypeByMetadataName($"{PathContests.SourceGeneratorsAbstractionsPath}.Rule.GetSystemAttribute"), $"{PathContests.SourceGeneratorsAbstractionsPath}.Rule.GetModelAttribute"),
compilation.GetTypeByMetadataName($"{PathContests.SourceGeneratorsAbstractionsPath}.Rule.GetSystemsAttribute"), compilation.GetTypeByMetadataName(
compilation.GetTypeByMetadataName($"{PathContests.SourceGeneratorsAbstractionsPath}.Rule.GetUtilityAttribute"), $"{PathContests.SourceGeneratorsAbstractionsPath}.Rule.GetModelsAttribute"),
compilation.GetTypeByMetadataName($"{PathContests.SourceGeneratorsAbstractionsPath}.Rule.GetUtilitiesAttribute")); compilation.GetTypeByMetadataName(
$"{PathContests.SourceGeneratorsAbstractionsPath}.Rule.GetSystemAttribute"),
compilation.GetTypeByMetadataName(
$"{PathContests.SourceGeneratorsAbstractionsPath}.Rule.GetSystemsAttribute"),
compilation.GetTypeByMetadataName(
$"{PathContests.SourceGeneratorsAbstractionsPath}.Rule.GetUtilityAttribute"),
compilation.GetTypeByMetadataName(
$"{PathContests.SourceGeneratorsAbstractionsPath}.Rule.GetUtilitiesAttribute"));
} }
} }
private sealed class RegistrationIndex private sealed class RegistrationIndex
{ {
private readonly Compilation _compilation;
private readonly IReadOnlyDictionary<INamedTypeSymbol, ArchitectureRegistrationData> _registrations;
private RegistrationIndex( private RegistrationIndex(
Compilation compilation, Compilation compilation,
IReadOnlyDictionary<INamedTypeSymbol, ArchitectureRegistrationData> registrations) IReadOnlyDictionary<INamedTypeSymbol, ArchitectureRegistrationData> registrations)
@ -392,14 +406,12 @@ public sealed class ContextRegistrationAnalyzer : DiagnosticAnalyzer
_registrations = registrations; _registrations = registrations;
} }
private readonly Compilation _compilation;
private readonly IReadOnlyDictionary<INamedTypeSymbol, ArchitectureRegistrationData> _registrations;
public static RegistrationIndex Build( public static RegistrationIndex Build(
Compilation compilation, Compilation compilation,
SymbolCache symbols) SymbolCache symbols)
{ {
var registrations = new Dictionary<INamedTypeSymbol, ArchitectureRegistrationData>(SymbolEqualityComparer.Default); var registrations =
new Dictionary<INamedTypeSymbol, ArchitectureRegistrationData>(SymbolEqualityComparer.Default);
foreach (var type in SymbolHelpers.EnumerateNamedTypes(compilation.Assembly.GlobalNamespace)) foreach (var type in SymbolHelpers.EnumerateNamedTypes(compilation.Assembly.GlobalNamespace))
{ {
@ -690,7 +702,8 @@ public sealed class ContextRegistrationAnalyzer : DiagnosticAnalyzer
return helperMethod.DeclaringSyntaxReferences.Length > 0; return helperMethod.DeclaringSyntaxReferences.Length > 0;
} }
helperMethod = SymbolHelpers.ResolveHierarchyMethodImplementation(targetMethod, concreteType) ?? targetMethod; helperMethod = SymbolHelpers.ResolveHierarchyMethodImplementation(targetMethod, concreteType) ??
targetMethod;
return helperMethod.DeclaringSyntaxReferences.Length > 0; return helperMethod.DeclaringSyntaxReferences.Length > 0;
} }
} }
@ -813,9 +826,12 @@ public sealed class ContextRegistrationAnalyzer : DiagnosticAnalyzer
if (SymbolEqualityComparer.Default.Equals(candidate.OriginalDefinition, method.OriginalDefinition)) if (SymbolEqualityComparer.Default.Equals(candidate.OriginalDefinition, method.OriginalDefinition))
return candidate; return candidate;
for (var overridden = candidate.OverriddenMethod; overridden != null; overridden = overridden.OverriddenMethod) for (var overridden = candidate.OverriddenMethod;
overridden != null;
overridden = overridden.OverriddenMethod)
{ {
if (SymbolEqualityComparer.Default.Equals(overridden.OriginalDefinition, method.OriginalDefinition)) if (SymbolEqualityComparer.Default.Equals(overridden.OriginalDefinition,
method.OriginalDefinition))
return candidate; return candidate;
} }
} }

View File

@ -1,6 +1,4 @@
using System.Collections.Immutable;
using GFramework.SourceGenerators.Diagnostics; using GFramework.SourceGenerators.Diagnostics;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Operations; using Microsoft.CodeAnalysis.Operations;
@ -111,4 +109,4 @@ public sealed class PriorityUsageAnalyzer : DiagnosticAnalyzer
{ {
return type.AllInterfaces.Any(i => SymbolEqualityComparer.Default.Equals(i, interfaceType)); return type.AllInterfaces.Any(i => SymbolEqualityComparer.Default.Equals(i, interfaceType));
} }
} }

View File

@ -1,10 +1,6 @@
using System.Text;
using GFramework.SourceGenerators.Common.Constants; using GFramework.SourceGenerators.Common.Constants;
using GFramework.SourceGenerators.Common.Generator; using GFramework.SourceGenerators.Common.Generator;
using GFramework.SourceGenerators.Diagnostics; using GFramework.SourceGenerators.Diagnostics;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
namespace GFramework.SourceGenerators.Bases; namespace GFramework.SourceGenerators.Bases;
@ -150,4 +146,4 @@ public sealed class PriorityGenerator : MetadataAttributeClassGeneratorBase
return $"{metadataName}.Priority.g.cs"; return $"{metadataName}.Priority.g.cs";
} }
} }

View File

@ -1,6 +1,4 @@
using Microsoft.CodeAnalysis; namespace GFramework.SourceGenerators.Diagnostics;
namespace GFramework.SourceGenerators.Diagnostics;
/// <summary> /// <summary>
/// 提供与上下文感知相关的诊断规则定义 /// 提供与上下文感知相关的诊断规则定义
@ -18,4 +16,4 @@ public static class ContextAwareDiagnostic
DiagnosticSeverity.Error, DiagnosticSeverity.Error,
true true
); );
} }

View File

@ -96,4 +96,4 @@ public static class ContextGetDiagnostics
SourceGeneratorsRuleCategory, SourceGeneratorsRuleCategory,
DiagnosticSeverity.Error, DiagnosticSeverity.Error,
true); true);
} }

View File

@ -1,6 +1,4 @@
using Microsoft.CodeAnalysis; namespace GFramework.SourceGenerators.Diagnostics;
namespace GFramework.SourceGenerators.Diagnostics;
/// <summary> /// <summary>
/// 提供诊断描述符的静态类用于GFramework日志生成器的编译时检查 /// 提供诊断描述符的静态类用于GFramework日志生成器的编译时检查
@ -18,4 +16,4 @@ internal static class LoggerDiagnostics
"GFramework.Godot.Logging", "GFramework.Godot.Logging",
DiagnosticSeverity.Warning, DiagnosticSeverity.Warning,
true); true);
} }

View File

@ -1,5 +1,3 @@
using Microsoft.CodeAnalysis;
namespace GFramework.SourceGenerators.Diagnostics; namespace GFramework.SourceGenerators.Diagnostics;
/// <summary> /// <summary>
@ -86,4 +84,4 @@ internal static class PriorityDiagnostic
isEnabledByDefault: true, isEnabledByDefault: true,
description: "当获取实现了 IPrioritized 接口的服务时,应使用 GetAllByPriority 方法以确保按优先级排序." description: "当获取实现了 IPrioritized 接口的服务时,应使用 GetAllByPriority 方法以确保按优先级排序."
); );
} }

View File

@ -1,9 +1,6 @@
using System.Text; using GFramework.SourceGenerators.Common.Constants;
using GFramework.SourceGenerators.Common.Constants;
using GFramework.SourceGenerators.Common.Diagnostics; using GFramework.SourceGenerators.Common.Diagnostics;
using GFramework.SourceGenerators.Common.Generator; using GFramework.SourceGenerators.Common.Generator;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
namespace GFramework.SourceGenerators.Enums; namespace GFramework.SourceGenerators.Enums;
@ -110,4 +107,4 @@ public sealed class EnumExtensionsGenerator : AttributeEnumGeneratorBase
{ {
return $"{symbol.Name}.EnumExtensions.g.cs"; return $"{symbol.Name}.EnumExtensions.g.cs";
} }
} }

View File

@ -30,7 +30,7 @@
<!-- Generator 编译期引用 SourceGenerators.Abstractions / Common / Core.Abstractions但不打包 --> <!-- Generator 编译期引用 SourceGenerators.Abstractions / Common / Core.Abstractions但不打包 -->
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\$(AssemblyName).Abstractions\$(AssemblyName).Abstractions.csproj" PrivateAssets="all"/> <ProjectReference Include="..\$(AssemblyName).Abstractions\$(AssemblyName).Abstractions.csproj" PrivateAssets="all"/>
<ProjectReference Include="..\$(AssemblyName).Common\$(AssemblyName).Common.csproj" PrivateAssets="all"/> <ProjectReference Include="..\GFramework.SourceGenerators.Common\GFramework.SourceGenerators.Common.csproj" PrivateAssets="all"/>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
@ -63,6 +63,6 @@
<None Include="$(OutputPath)\$(AssemblyName).Abstractions.xml" Pack="true" PackagePath="lib\netstandard2.0" Visible="true"/> <None Include="$(OutputPath)\$(AssemblyName).Abstractions.xml" Pack="true" PackagePath="lib\netstandard2.0" Visible="true"/>
<None Include="$(OutputPath)\$(AssemblyName).Common.dll" Pack="true" PackagePath="lib\netstandard2.0" Visible="true"/> <None Include="$(OutputPath)\$(AssemblyName).Common.dll" Pack="true" PackagePath="lib\netstandard2.0" Visible="true"/>
<None Include="$(OutputPath)\$(AssemblyName).Common.xml" Pack="true" PackagePath="lib\netstandard2.0" Visible="true"/> <None Include="$(OutputPath)\$(AssemblyName).Common.xml" Pack="true" PackagePath="lib\netstandard2.0" Visible="true"/>
<None Include="GeWuYou.$(AssemblyName).targets" Pack="true" PackagePath="build" Visible="false"/> <None Include="GeWuYou.GFramework.Core.SourceGenerators.targets" Pack="true" PackagePath="build" Visible="false"/>
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Analyzer Include="$(MSBuildThisFileDirectory)../analyzers/dotnet/cs/GFramework.Core.SourceGenerators.dll"
Condition="Exists('$(MSBuildThisFileDirectory)../analyzers/dotnet/cs/GFramework.Core.SourceGenerators.dll')"/>
<Analyzer Include="$(MSBuildThisFileDirectory)../analyzers/dotnet/cs/GFramework.Core.SourceGenerators.Abstractions.dll"
Condition="Exists('$(MSBuildThisFileDirectory)../analyzers/dotnet/cs/GFramework.Core.SourceGenerators.Abstractions.dll')"/>
<Analyzer Include="$(MSBuildThisFileDirectory)../analyzers/dotnet/cs/GFramework.SourceGenerators.Common.dll"
Condition="Exists('$(MSBuildThisFileDirectory)../analyzers/dotnet/cs/GFramework.SourceGenerators.Common.dll')"/>
</ItemGroup>
<Target Name="EnsureGFrameworkCoreAnalyzers" BeforeTargets="CoreCompile">
<Message Text="Loading GFramework.Core source generators" Importance="high"/>
</Target>
</Project>

View File

@ -24,4 +24,4 @@ global using Microsoft.CodeAnalysis.Text;
global using System.Globalization; global using System.Globalization;
global using System.IO; global using System.IO;
global using System.Text; global using System.Text;
global using System.Text.Json; global using System.Text.Json;

View File

@ -1,10 +1,7 @@
using System.Text; using GFramework.SourceGenerators.Abstractions.Logging;
using GFramework.SourceGenerators.Abstractions.Logging;
using GFramework.SourceGenerators.Common.Constants; using GFramework.SourceGenerators.Common.Constants;
using GFramework.SourceGenerators.Common.Extensions; using GFramework.SourceGenerators.Common.Extensions;
using GFramework.SourceGenerators.Common.Generator; using GFramework.SourceGenerators.Common.Generator;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
namespace GFramework.SourceGenerators.Logging; namespace GFramework.SourceGenerators.Logging;
@ -97,4 +94,4 @@ public sealed class LoggerGenerator : TypeAttributeClassGeneratorBase
{ {
return $"{symbol.Name}.Logger.g.cs"; return $"{symbol.Name}.Logger.g.cs";
} }
} }

View File

@ -1,11 +1,7 @@
using System.Text; using GFramework.SourceGenerators.Common.Constants;
using GFramework.SourceGenerators.Common.Constants;
using GFramework.SourceGenerators.Common.Diagnostics; using GFramework.SourceGenerators.Common.Diagnostics;
using GFramework.SourceGenerators.Common.Generator; using GFramework.SourceGenerators.Common.Generator;
using GFramework.SourceGenerators.Diagnostics; using GFramework.SourceGenerators.Diagnostics;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
namespace GFramework.SourceGenerators.Rule; namespace GFramework.SourceGenerators.Rule;
@ -251,4 +247,4 @@ public sealed class ContextAwareGenerator : MetadataAttributeClassGeneratorBase
break; break;
} }
} }
} }

View File

@ -1,4 +1,3 @@
using System.Text;
using GFramework.SourceGenerators.Common.Constants; using GFramework.SourceGenerators.Common.Constants;
using GFramework.SourceGenerators.Common.Diagnostics; using GFramework.SourceGenerators.Common.Diagnostics;
using GFramework.SourceGenerators.Common.Extensions; using GFramework.SourceGenerators.Common.Extensions;
@ -894,4 +893,4 @@ public sealed class ContextGetGenerator : IIncrementalGenerator
public List<FieldCandidateInfo> FieldCandidates { get; } = []; public List<FieldCandidateInfo> FieldCandidates { get; } = [];
public ClassDeclarationSyntax? GetAllDeclaration { get; set; } public ClassDeclarationSyntax? GetAllDeclaration { get; set; }
} }
} }

View File

@ -78,28 +78,6 @@ public class ArchitectureModulesBehaviorTests
await architecture.DestroyAsync(); await architecture.DestroyAsync();
} }
/// <summary>
/// 验证兼容别名 <c>RegisterMediatorBehavior</c> 仍会把 CQRS 行为接入请求管道。
/// </summary>
[Test]
public async Task RegisterMediatorBehavior_Should_Apply_Pipeline_Behavior_To_Request()
{
var architecture = new ModuleTestArchitecture(target =>
target.RegisterMediatorBehavior<TrackingPipelineBehavior<ModuleBehaviorRequest, string>>());
await architecture.InitializeAsync();
var response = await architecture.Context.SendRequestAsync(new ModuleBehaviorRequest());
Assert.Multiple(() =>
{
Assert.That(response, Is.EqualTo("handled"));
Assert.That(TrackingPipelineBehavior<ModuleBehaviorRequest, string>.InvocationCount, Is.EqualTo(1));
});
await architecture.DestroyAsync();
}
/// <summary> /// <summary>
/// 用于测试模块行为的最小架构实现。 /// 用于测试模块行为的最小架构实现。
/// </summary> /// </summary>

View File

@ -206,12 +206,6 @@ public class TestArchitectureWithRegistry : IArchitecture
throw new NotImplementedException(); throw new NotImplementedException();
} }
[Obsolete("Use RegisterCqrsPipelineBehavior<TBehavior>() instead.")]
public void RegisterMediatorBehavior<TBehavior>() where TBehavior : class
{
RegisterCqrsPipelineBehavior<TBehavior>();
}
public IArchitectureModule InstallModule(IArchitectureModule module) public IArchitectureModule InstallModule(IArchitectureModule module)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
@ -357,12 +351,6 @@ public class TestArchitectureWithoutRegistry : IArchitecture
throw new NotImplementedException(); throw new NotImplementedException();
} }
[Obsolete("Use RegisterCqrsPipelineBehavior<TBehavior>() instead.")]
public void RegisterMediatorBehavior<TBehavior>() where TBehavior : class
{
RegisterCqrsPipelineBehavior<TBehavior>();
}
public IArchitectureModule InstallModule(IArchitectureModule module) public IArchitectureModule InstallModule(IArchitectureModule module)
{ {
throw new NotImplementedException(); throw new NotImplementedException();

View File

@ -1,99 +0,0 @@
using System.ComponentModel;
using System.Reflection;
using GFramework.Core.Abstractions.Architectures;
using GFramework.Core.Abstractions.Ioc;
using GFramework.Core.Architectures;
using GFramework.Core.Coroutine.Extensions;
using GFramework.Core.Ioc;
namespace GFramework.Core.Tests.Cqrs;
/// <summary>
/// 锁定历史 Mediator 兼容入口的正式弃用策略。
/// 这些测试确保旧 API 不仅保留行为兼容,还会通过编译期提示和 IntelliSense 隐藏引导调用方迁移到新的 CQRS 命名。
/// </summary>
[TestFixture]
public class MediatorCompatibilityDeprecationTests
{
/// <summary>
/// 验证公开兼容方法仍可用,但已被显式标记为未来移除的旧别名。
/// </summary>
[Test]
public void Legacy_Public_Methods_Should_Be_Obsolete_And_Hidden_From_Editor_Browsing()
{
AssertLegacyMethod(typeof(IArchitecture), nameof(IArchitecture.RegisterMediatorBehavior));
AssertLegacyMethod(typeof(IIocContainer), nameof(IIocContainer.RegisterMediatorBehavior));
AssertLegacyMethod(typeof(Architecture), nameof(Architecture.RegisterMediatorBehavior));
AssertLegacyMethod(typeof(MicrosoftDiContainer), nameof(MicrosoftDiContainer.RegisterMediatorBehavior));
}
/// <summary>
/// 验证历史扩展类型会把迁移目标写入弃用说明,并从 IntelliSense 主路径隐藏。
/// </summary>
[Test]
public void Legacy_Extension_Types_Should_Be_Obsolete_And_Hidden_From_Editor_Browsing()
{
AssertLegacyType(
typeof(ContextAwareMediatorExtensions),
"Use GFramework.Core.Extensions.ContextAwareCqrsExtensions instead.");
AssertLegacyType(
typeof(ContextAwareMediatorCommandExtensions),
"Use GFramework.Cqrs.Extensions.ContextAwareCqrsCommandExtensions instead.");
AssertLegacyType(
typeof(ContextAwareMediatorQueryExtensions),
"Use GFramework.Cqrs.Extensions.ContextAwareCqrsQueryExtensions instead.");
AssertLegacyType(
typeof(MediatorCoroutineExtensions),
"Use GFramework.Core.Coroutine.Extensions.CqrsCoroutineExtensions instead.");
}
/// <summary>
/// 断言方法级兼容 API 具备统一的弃用元数据。
/// </summary>
/// <param name="declaringType">声明该方法的类型。</param>
/// <param name="methodName">方法名称。</param>
private static void AssertLegacyMethod(Type declaringType, string methodName)
{
var method = declaringType
.GetMethods(BindingFlags.Public | BindingFlags.Instance)
.Single(candidate => candidate.Name == methodName);
var obsoleteAttribute = method.GetCustomAttribute<ObsoleteAttribute>();
var editorBrowsableAttribute = method.GetCustomAttribute<EditorBrowsableAttribute>();
Assert.Multiple(() =>
{
Assert.That(obsoleteAttribute, Is.Not.Null);
Assert.That(
obsoleteAttribute!.Message,
Does.Contain("Use RegisterCqrsPipelineBehavior<TBehavior>() instead."));
Assert.That(
obsoleteAttribute.Message,
Does.Contain("removed in a future major version"));
Assert.That(editorBrowsableAttribute, Is.Not.Null);
Assert.That(editorBrowsableAttribute!.State, Is.EqualTo(EditorBrowsableState.Never));
});
}
/// <summary>
/// 断言类型级兼容扩展具备统一的弃用元数据。
/// </summary>
/// <param name="type">兼容扩展类型。</param>
/// <param name="expectedReplacementHint">期望的迁移提示。</param>
private static void AssertLegacyType(Type type, string expectedReplacementHint)
{
var obsoleteAttribute = type.GetCustomAttribute<ObsoleteAttribute>();
var editorBrowsableAttribute = type.GetCustomAttribute<EditorBrowsableAttribute>();
Assert.Multiple(() =>
{
Assert.That(obsoleteAttribute, Is.Not.Null);
Assert.That(obsoleteAttribute!.Message, Does.Contain(expectedReplacementHint));
Assert.That(
obsoleteAttribute.Message,
Does.Contain("removed in a future major version"));
Assert.That(editorBrowsableAttribute, Is.Not.Null);
Assert.That(editorBrowsableAttribute!.State, Is.EqualTo(EditorBrowsableState.Never));
});
}
}

View File

@ -21,9 +21,11 @@
<ProjectReference Include="..\GFramework.Tests.Common\GFramework.Tests.Common.csproj"/> <ProjectReference Include="..\GFramework.Tests.Common\GFramework.Tests.Common.csproj"/>
<ProjectReference Include="..\GFramework.Core.Abstractions\GFramework.Core.Abstractions.csproj"/> <ProjectReference Include="..\GFramework.Core.Abstractions\GFramework.Core.Abstractions.csproj"/>
<ProjectReference Include="..\GFramework.Core\GFramework.Core.csproj"/> <ProjectReference Include="..\GFramework.Core\GFramework.Core.csproj"/>
<ProjectReference Include="..\GFramework.SourceGenerators.Abstractions\GFramework.SourceGenerators.Abstractions.csproj"/> <ProjectReference Include="..\GFramework.Core.SourceGenerators.Abstractions\GFramework.Core.SourceGenerators.Abstractions.csproj"/>
<ProjectReference Include="..\GFramework.SourceGenerators.Common\GFramework.SourceGenerators.Common.csproj"/> <ProjectReference Include="..\GFramework.SourceGenerators.Common\GFramework.SourceGenerators.Common.csproj"
<ProjectReference Include="..\GFramework.SourceGenerators\GFramework.SourceGenerators.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false"/> OutputItemType="Analyzer"
ReferenceOutputAssembly="false"/>
<ProjectReference Include="..\GFramework.Core.SourceGenerators\GFramework.Core.SourceGenerators.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false"/>
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -1,4 +1,3 @@
using System.ComponentModel;
using System.Reflection; using System.Reflection;
using GFramework.Core.Abstractions.Architectures; using GFramework.Core.Abstractions.Architectures;
using GFramework.Core.Abstractions.Enums; using GFramework.Core.Abstractions.Enums;
@ -8,7 +7,6 @@ using GFramework.Core.Abstractions.Model;
using GFramework.Core.Abstractions.Systems; using GFramework.Core.Abstractions.Systems;
using GFramework.Core.Abstractions.Utility; using GFramework.Core.Abstractions.Utility;
using GFramework.Core.Environment; using GFramework.Core.Environment;
using GFramework.Core.Logging;
namespace GFramework.Core.Architectures; namespace GFramework.Core.Architectures;
@ -156,20 +154,6 @@ public abstract class Architecture : IArchitecture
_modules.RegisterCqrsPipelineBehavior<TBehavior>(); _modules.RegisterCqrsPipelineBehavior<TBehavior>();
} }
/// <summary>
/// 注册 CQRS 请求管道行为。
/// 该成员保留旧名称以兼容历史调用点,内部行为与 <see cref="RegisterCqrsPipelineBehavior{TBehavior}" /> 一致。
/// 新代码不应继续依赖该别名;兼容层计划在未来的 major 版本中移除。
/// </summary>
/// <typeparam name="TBehavior">行为类型,必须是引用类型</typeparam>
[EditorBrowsable(EditorBrowsableState.Never)]
[Obsolete(
"Use RegisterCqrsPipelineBehavior<TBehavior>() instead. This compatibility alias will be removed in a future major version.")]
public void RegisterMediatorBehavior<TBehavior>() where TBehavior : class
{
RegisterCqrsPipelineBehavior<TBehavior>();
}
/// <summary> /// <summary>
/// 从指定程序集显式注册 CQRS 处理器。 /// 从指定程序集显式注册 CQRS 处理器。
/// 该入口适用于把拆分到其他模块或扩展包程序集中的 handlers 接入当前架构。 /// 该入口适用于把拆分到其他模块或扩展包程序集中的 handlers 接入当前架构。

View File

@ -1,4 +1,3 @@
using System.ComponentModel;
using System.Reflection; using System.Reflection;
using GFramework.Core.Abstractions.Architectures; using GFramework.Core.Abstractions.Architectures;
using GFramework.Core.Abstractions.Logging; using GFramework.Core.Abstractions.Logging;
@ -25,20 +24,6 @@ internal sealed class ArchitectureModules(
services.Container.RegisterCqrsPipelineBehavior<TBehavior>(); services.Container.RegisterCqrsPipelineBehavior<TBehavior>();
} }
/// <summary>
/// 注册 CQRS 请求管道行为。
/// 该成员保留旧名称以兼容历史调用点,内部行为与 <see cref="RegisterCqrsPipelineBehavior{TBehavior}" /> 一致。
/// 新代码不应继续依赖该别名;兼容层计划在未来的 major 版本中移除。
/// </summary>
/// <typeparam name="TBehavior">行为类型,必须是引用类型</typeparam>
[EditorBrowsable(EditorBrowsableState.Never)]
[Obsolete(
"Use RegisterCqrsPipelineBehavior<TBehavior>() instead. This compatibility alias will be removed in a future major version.")]
public void RegisterMediatorBehavior<TBehavior>() where TBehavior : class
{
RegisterCqrsPipelineBehavior<TBehavior>();
}
/// <summary> /// <summary>
/// 从指定程序集显式注册 CQRS 处理器。 /// 从指定程序集显式注册 CQRS 处理器。
/// 该入口用于把默认架构程序集之外的扩展处理器接入当前架构容器。 /// 该入口用于把默认架构程序集之外的扩展处理器接入当前架构容器。

View File

@ -1,47 +0,0 @@
// Copyright (c) 2026 GeWuYou
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
using System.ComponentModel;
using GFramework.Core.Abstractions.Coroutine;
using GFramework.Core.Abstractions.Rule;
using GFramework.Cqrs.Abstractions.Cqrs;
namespace GFramework.Core.Coroutine.Extensions;
/// <summary>
/// 提供 CQRS 命令与协程集成的扩展方法。
/// 该类型保留旧名称以兼容历史调用点;新代码应改用 <see cref="CqrsCoroutineExtensions" />。
/// 兼容层计划在未来的 major 版本中移除,因此不会继续承载新能力。
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
[Obsolete(
"Use GFramework.Core.Coroutine.Extensions.CqrsCoroutineExtensions instead. This compatibility alias will be removed in a future major version.")]
public static class MediatorCoroutineExtensions
{
/// <summary>
/// 以协程方式发送无返回值 CQRS 命令并处理可能的异常。
/// </summary>
/// <typeparam name="TCommand">命令的类型。</typeparam>
/// <param name="contextAware">上下文感知对象,用于获取服务</param>
/// <param name="command">要发送的命令对象</param>
/// <param name="onError">发生异常时的回调处理函数</param>
/// <returns>协程枚举器,用于协程执行</returns>
public static IEnumerator<IYieldInstruction> SendCommandCoroutine<TCommand>(
this IContextAware contextAware,
TCommand command,
Action<Exception>? onError = null)
where TCommand : IRequest<Unit>
{
return CqrsCoroutineExtensions.SendCommandCoroutine(contextAware, command, onError);
}
}

View File

@ -1,49 +0,0 @@
using System.ComponentModel;
using GFramework.Core.Abstractions.Rule;
using GFramework.Cqrs.Abstractions.Cqrs.Command;
using GFramework.Cqrs.Extensions;
namespace GFramework.Core.Extensions;
/// <summary>
/// 提供对 <see cref="IContextAware" /> 接口的 CQRS 命令扩展方法。
/// 该类型保留旧名称以兼容历史调用点;新代码应改用 <see cref="ContextAwareCqrsCommandExtensions" />。
/// 兼容层计划在未来的 major 版本中移除,因此不会继续承载新能力。
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
[Obsolete(
"Use GFramework.Cqrs.Extensions.ContextAwareCqrsCommandExtensions instead. This compatibility alias will be removed in a future major version.")]
public static class ContextAwareMediatorCommandExtensions
{
/// <summary>
/// 发送命令的同步版本(不推荐,仅用于兼容性)
/// </summary>
/// <typeparam name="TResponse">命令响应类型</typeparam>
/// <param name="contextAware">实现 IContextAware 接口的对象</param>
/// <param name="command">要发送的命令对象</param>
/// <returns>命令执行结果</returns>
/// <exception cref="ArgumentNullException">当 contextAware 或 command 为 null 时抛出</exception>
public static TResponse SendCommand<TResponse>(this IContextAware contextAware,
ICommand<TResponse> command)
{
return ContextAwareCqrsCommandExtensions.SendCommand(contextAware, command);
}
/// <summary>
/// 异步发送命令并返回结果
/// </summary>
/// <typeparam name="TResponse">命令响应类型</typeparam>
/// <param name="contextAware">实现 IContextAware 接口的对象</param>
/// <param name="command">要发送的命令对象</param>
/// <param name="cancellationToken">取消令牌,用于取消操作</param>
/// <returns>包含命令执行结果的ValueTask</returns>
/// <exception cref="ArgumentNullException">当 contextAware 或 command 为 null 时抛出</exception>
public static ValueTask<TResponse> SendCommandAsync<TResponse>(this IContextAware contextAware,
ICommand<TResponse> command, CancellationToken cancellationToken = default)
{
return ContextAwareCqrsCommandExtensions.SendCommandAsync(
contextAware,
command,
cancellationToken);
}
}

View File

@ -1,123 +0,0 @@
using System.ComponentModel;
using GFramework.Core.Abstractions.Rule;
using GFramework.Cqrs.Abstractions.Cqrs;
using GFramework.Cqrs.Extensions;
namespace GFramework.Core.Extensions;
/// <summary>
/// 提供对 <see cref="IContextAware" /> 接口的 CQRS 统一接口扩展方法。
/// 该类型保留旧名称以兼容历史调用点;新代码应改用 <see cref="ContextAwareCqrsExtensions" />。
/// 兼容层计划在未来的 major 版本中移除,因此不会继续承载新能力。
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
[Obsolete(
"Use GFramework.Core.Extensions.ContextAwareCqrsExtensions instead. This compatibility alias will be removed in a future major version.")]
public static class ContextAwareMediatorExtensions
{
/// <summary>
/// 发送请求(统一处理 Command/Query
/// </summary>
/// <typeparam name="TResponse">响应类型</typeparam>
/// <param name="contextAware">实现 IContextAware 接口的对象</param>
/// <param name="request">要发送的请求</param>
/// <param name="cancellationToken">取消令牌</param>
/// <returns>请求结果</returns>
/// <exception cref="ArgumentNullException">当 contextAware 或 request 为 null 时抛出</exception>
public static ValueTask<TResponse> SendRequestAsync<TResponse>(this IContextAware contextAware,
IRequest<TResponse> request, CancellationToken cancellationToken = default)
{
return ContextAwareCqrsExtensions.SendRequestAsync(
contextAware,
request,
cancellationToken);
}
/// <summary>
/// 发送请求(同步版本,不推荐)
/// </summary>
/// <typeparam name="TResponse">响应类型</typeparam>
/// <param name="contextAware">实现 IContextAware 接口的对象</param>
/// <param name="request">要发送的请求</param>
/// <returns>请求结果</returns>
/// <exception cref="ArgumentNullException">当 contextAware 或 request 为 null 时抛出</exception>
public static TResponse SendRequest<TResponse>(this IContextAware contextAware,
IRequest<TResponse> request)
{
return ContextAwareCqrsExtensions.SendRequest(contextAware, request);
}
/// <summary>
/// 发布通知(一对多事件)
/// </summary>
/// <typeparam name="TNotification">通知类型</typeparam>
/// <param name="contextAware">实现 IContextAware 接口的对象</param>
/// <param name="notification">要发布的通知</param>
/// <param name="cancellationToken">取消令牌</param>
/// <returns>异步任务</returns>
/// <exception cref="ArgumentNullException">当 contextAware 或 notification 为 null 时抛出</exception>
public static ValueTask PublishAsync<TNotification>(this IContextAware contextAware,
TNotification notification, CancellationToken cancellationToken = default)
where TNotification : INotification
{
return ContextAwareCqrsExtensions.PublishAsync(
contextAware,
notification,
cancellationToken);
}
/// <summary>
/// 创建流式请求(用于大数据集)
/// </summary>
/// <typeparam name="TResponse">响应类型</typeparam>
/// <param name="contextAware">实现 IContextAware 接口的对象</param>
/// <param name="request">流式请求</param>
/// <param name="cancellationToken">取消令牌</param>
/// <returns>异步响应流</returns>
/// <exception cref="ArgumentNullException">当 contextAware 或 request 为 null 时抛出</exception>
public static IAsyncEnumerable<TResponse> CreateStream<TResponse>(this IContextAware contextAware,
IStreamRequest<TResponse> request, CancellationToken cancellationToken = default)
{
return ContextAwareCqrsExtensions.CreateStream(
contextAware,
request,
cancellationToken);
}
/// <summary>
/// 发送命令(无返回值)
/// </summary>
/// <typeparam name="TCommand">命令类型</typeparam>
/// <param name="contextAware">实现 IContextAware 接口的对象</param>
/// <param name="command">要发送的命令</param>
/// <param name="cancellationToken">取消令牌</param>
/// <returns>异步任务</returns>
/// <exception cref="ArgumentNullException">当 contextAware 或 command 为 null 时抛出</exception>
public static ValueTask SendAsync<TCommand>(this IContextAware contextAware, TCommand command,
CancellationToken cancellationToken = default)
where TCommand : IRequest<Unit>
{
return ContextAwareCqrsExtensions.SendAsync(
contextAware,
command,
cancellationToken);
}
/// <summary>
/// 发送命令(有返回值)
/// </summary>
/// <typeparam name="TResponse">响应类型</typeparam>
/// <param name="contextAware">实现 IContextAware 接口的对象</param>
/// <param name="command">要发送的命令</param>
/// <param name="cancellationToken">取消令牌</param>
/// <returns>命令执行结果</returns>
/// <exception cref="ArgumentNullException">当 contextAware 或 command 为 null 时抛出</exception>
public static ValueTask<TResponse> SendAsync<TResponse>(this IContextAware contextAware,
IRequest<TResponse> command, CancellationToken cancellationToken = default)
{
return ContextAwareCqrsExtensions.SendAsync(
contextAware,
command,
cancellationToken);
}
}

View File

@ -1,48 +0,0 @@
using System.ComponentModel;
using GFramework.Core.Abstractions.Rule;
using GFramework.Cqrs.Abstractions.Cqrs.Query;
using GFramework.Cqrs.Extensions;
namespace GFramework.Core.Extensions;
/// <summary>
/// 提供对 <see cref="IContextAware" /> 接口的 CQRS 查询扩展方法。
/// 该类型保留旧名称以兼容历史调用点;新代码应改用 <see cref="ContextAwareCqrsQueryExtensions" />。
/// 兼容层计划在未来的 major 版本中移除,因此不会继续承载新能力。
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
[Obsolete(
"Use GFramework.Cqrs.Extensions.ContextAwareCqrsQueryExtensions instead. This compatibility alias will be removed in a future major version.")]
public static class ContextAwareMediatorQueryExtensions
{
/// <summary>
/// 发送查询的同步版本(不推荐,仅用于兼容性)
/// </summary>
/// <typeparam name="TResponse">查询响应类型</typeparam>
/// <param name="contextAware">实现 IContextAware 接口的对象</param>
/// <param name="query">要发送的查询对象</param>
/// <returns>查询结果</returns>
/// <exception cref="ArgumentNullException">当 contextAware 或 query 为 null 时抛出</exception>
public static TResponse SendQuery<TResponse>(this IContextAware contextAware, IQuery<TResponse> query)
{
return ContextAwareCqrsQueryExtensions.SendQuery(contextAware, query);
}
/// <summary>
/// 异步发送查询并返回结果
/// </summary>
/// <typeparam name="TResponse">查询响应类型</typeparam>
/// <param name="contextAware">实现 IContextAware 接口的对象</param>
/// <param name="query">要发送的查询对象</param>
/// <param name="cancellationToken">取消令牌,用于取消操作</param>
/// <returns>包含查询结果的ValueTask</returns>
/// <exception cref="ArgumentNullException">当 contextAware 或 query 为 null 时抛出</exception>
public static ValueTask<TResponse> SendQueryAsync<TResponse>(this IContextAware contextAware,
IQuery<TResponse> query, CancellationToken cancellationToken = default)
{
return ContextAwareCqrsQueryExtensions.SendQueryAsync(
contextAware,
query,
cancellationToken);
}
}

View File

@ -1,4 +1,3 @@
using System.ComponentModel;
using System.Reflection; using System.Reflection;
using GFramework.Core.Abstractions.Bases; using GFramework.Core.Abstractions.Bases;
using GFramework.Core.Abstractions.Ioc; using GFramework.Core.Abstractions.Ioc;
@ -367,20 +366,6 @@ public class MicrosoftDiContainer(IServiceCollection? serviceCollection = null)
} }
} }
/// <summary>
/// 注册 CQRS 请求管道行为。
/// 该成员保留旧名称以兼容历史调用点,内部行为与 <see cref="RegisterCqrsPipelineBehavior{TBehavior}" /> 一致。
/// 新代码不应继续依赖该别名;兼容层计划在未来的 major 版本中移除。
/// </summary>
/// <typeparam name="TBehavior">行为类型,必须是引用类型</typeparam>
[EditorBrowsable(EditorBrowsableState.Never)]
[Obsolete(
"Use RegisterCqrsPipelineBehavior<TBehavior>() instead. This compatibility alias will be removed in a future major version.")]
public void RegisterMediatorBehavior<TBehavior>() where TBehavior : class
{
RegisterCqrsPipelineBehavior<TBehavior>();
}
/// <summary> /// <summary>
/// 从指定程序集显式注册 CQRS 处理器。 /// 从指定程序集显式注册 CQRS 处理器。
/// </summary> /// </summary>

View File

@ -337,10 +337,17 @@ public sealed class CqrsHandlerRegistryGenerator : IIncrementalGenerator
return true; return true;
} }
if (type is INamedTypeSymbol namedType && if (type is INamedTypeSymbol namedType)
SymbolEqualityComparer.Default.Equals(namedType.ContainingAssembly, compilation.Assembly))
{ {
runtimeTypeReference = RuntimeTypeReferenceSpec.FromReflectionLookup( if (SymbolEqualityComparer.Default.Equals(namedType.ContainingAssembly, compilation.Assembly))
{
runtimeTypeReference = RuntimeTypeReferenceSpec.FromReflectionLookup(
GetReflectionTypeMetadataName(namedType));
return true;
}
runtimeTypeReference = RuntimeTypeReferenceSpec.FromExternalReflectionLookup(
namedType.ContainingAssembly.Identity.Name,
GetReflectionTypeMetadataName(namedType)); GetReflectionTypeMetadataName(namedType));
return true; return true;
} }
@ -388,8 +395,10 @@ public sealed class CqrsHandlerRegistryGenerator : IIncrementalGenerator
return true; return true;
} }
genericTypeDefinitionReference = null; genericTypeDefinitionReference = RuntimeTypeReferenceSpec.FromExternalReflectionLookup(
return false; genericTypeDefinition.ContainingAssembly.Identity.Name,
GetReflectionTypeMetadataName(genericTypeDefinition));
return true;
} }
private static bool CanReferenceFromGeneratedRegistry(Compilation compilation, ITypeSymbol type) private static bool CanReferenceFromGeneratedRegistry(Compilation compilation, ITypeSymbol type)
@ -492,6 +501,9 @@ public sealed class CqrsHandlerRegistryGenerator : IIncrementalGenerator
!registration.PreciseReflectedRegistrations.IsDefaultOrEmpty); !registration.PreciseReflectedRegistrations.IsDefaultOrEmpty);
var hasRuntimeInterfaceDiscovery = registrations.Any(static registration => var hasRuntimeInterfaceDiscovery = registrations.Any(static registration =>
registration.RequiresRuntimeInterfaceDiscovery); registration.RequiresRuntimeInterfaceDiscovery);
var hasExternalAssemblyTypeLookups = registrations.Any(static registration =>
registration.PreciseReflectedRegistrations.Any(static preciseRegistration =>
preciseRegistration.ServiceTypeArguments.Any(ContainsExternalAssemblyTypeLookup)));
var builder = new StringBuilder(); var builder = new StringBuilder();
builder.AppendLine("// <auto-generated />"); builder.AppendLine("// <auto-generated />");
builder.AppendLine("#nullable enable"); builder.AppendLine("#nullable enable");
@ -556,10 +568,13 @@ public sealed class CqrsHandlerRegistryGenerator : IIncrementalGenerator
builder.AppendLine(" }"); builder.AppendLine(" }");
if (hasRuntimeInterfaceDiscovery) if (hasRuntimeInterfaceDiscovery || hasExternalAssemblyTypeLookups)
{ {
builder.AppendLine(); builder.AppendLine();
AppendReflectionHelpers(builder); AppendReflectionHelpers(
builder,
hasRuntimeInterfaceDiscovery,
hasExternalAssemblyTypeLookups);
} }
builder.AppendLine("}"); builder.AppendLine("}");
@ -910,14 +925,68 @@ public sealed class CqrsHandlerRegistryGenerator : IIncrementalGenerator
builder.Append(indent); builder.Append(indent);
builder.Append("var "); builder.Append("var ");
builder.Append(variableBaseName); builder.Append(variableBaseName);
builder.Append(" = registryAssembly.GetType(\""); if (string.IsNullOrWhiteSpace(runtimeTypeReference.ReflectionAssemblyName))
builder.Append(EscapeStringLiteral(reflectionTypeMetadataName)); {
builder.AppendLine("\", throwOnError: false, ignoreCase: false);"); builder.Append(" = registryAssembly.GetType(\"");
builder.Append(EscapeStringLiteral(reflectionTypeMetadataName));
builder.AppendLine("\", throwOnError: false, ignoreCase: false);");
}
else
{
builder.Append(" = ResolveReferencedAssemblyType(\"");
builder.Append(EscapeStringLiteral(runtimeTypeReference.ReflectionAssemblyName!));
builder.Append("\", \"");
builder.Append(EscapeStringLiteral(reflectionTypeMetadataName));
builder.AppendLine("\");");
}
return variableBaseName; return variableBaseName;
} }
private static void AppendReflectionHelpers(StringBuilder builder) private static void AppendReflectionHelpers(
StringBuilder builder,
bool includeRuntimeInterfaceDiscoveryHelpers,
bool includeExternalAssemblyTypeLookupHelpers)
{ {
if (includeExternalAssemblyTypeLookupHelpers)
{
builder.AppendLine(
" private static global::System.Type? ResolveReferencedAssemblyType(string assemblyName, string typeMetadataName)");
builder.AppendLine(" {");
builder.AppendLine(" var assembly = ResolveReferencedAssembly(assemblyName);");
builder.AppendLine(
" return assembly?.GetType(typeMetadataName, throwOnError: false, ignoreCase: false);");
builder.AppendLine(" }");
builder.AppendLine();
builder.AppendLine(
" private static global::System.Reflection.Assembly? ResolveReferencedAssembly(string assemblyName)");
builder.AppendLine(" {");
builder.AppendLine(
" foreach (var assembly in global::System.AppDomain.CurrentDomain.GetAssemblies())");
builder.AppendLine(" {");
builder.AppendLine(
" if (global::System.StringComparer.Ordinal.Equals(assembly.GetName().Name, assemblyName))");
builder.AppendLine(" return assembly;");
builder.AppendLine(" }");
builder.AppendLine();
builder.AppendLine(" try");
builder.AppendLine(" {");
builder.AppendLine(
" return global::System.Reflection.Assembly.Load(new global::System.Reflection.AssemblyName(assemblyName));");
builder.AppendLine(" }");
builder.AppendLine(" catch");
builder.AppendLine(" {");
builder.AppendLine(" return null;");
builder.AppendLine(" }");
builder.AppendLine(" }");
}
if (!includeRuntimeInterfaceDiscoveryHelpers)
return;
if (includeExternalAssemblyTypeLookupHelpers)
builder.AppendLine();
// Emit the runtime helper methods only when at least one handler still needs implementation-scoped // Emit the runtime helper methods only when at least one handler still needs implementation-scoped
// interface discovery after all direct / precise registrations have been emitted. // interface discovery after all direct / precise registrations have been emitted.
builder.AppendLine( builder.AppendLine(
@ -1038,6 +1107,32 @@ public sealed class CqrsHandlerRegistryGenerator : IIncrementalGenerator
.Replace("\r", "\\r"); .Replace("\r", "\\r");
} }
private static bool ContainsExternalAssemblyTypeLookup(RuntimeTypeReferenceSpec runtimeTypeReference)
{
if (!string.IsNullOrWhiteSpace(runtimeTypeReference.ReflectionAssemblyName))
return true;
if (runtimeTypeReference.ArrayElementTypeReference is not null &&
ContainsExternalAssemblyTypeLookup(runtimeTypeReference.ArrayElementTypeReference))
{
return true;
}
if (runtimeTypeReference.GenericTypeDefinitionReference is not null &&
ContainsExternalAssemblyTypeLookup(runtimeTypeReference.GenericTypeDefinitionReference))
{
return true;
}
foreach (var genericTypeArgument in runtimeTypeReference.GenericTypeArguments)
{
if (ContainsExternalAssemblyTypeLookup(genericTypeArgument))
return true;
}
return false;
}
private readonly record struct HandlerRegistrationSpec( private readonly record struct HandlerRegistrationSpec(
string HandlerInterfaceDisplayName, string HandlerInterfaceDisplayName,
string ImplementationTypeDisplayName, string ImplementationTypeDisplayName,
@ -1058,6 +1153,7 @@ public sealed class CqrsHandlerRegistryGenerator : IIncrementalGenerator
private sealed record RuntimeTypeReferenceSpec( private sealed record RuntimeTypeReferenceSpec(
string? TypeDisplayName, string? TypeDisplayName,
string? ReflectionTypeMetadataName, string? ReflectionTypeMetadataName,
string? ReflectionAssemblyName,
RuntimeTypeReferenceSpec? ArrayElementTypeReference, RuntimeTypeReferenceSpec? ArrayElementTypeReference,
int ArrayRank, int ArrayRank,
RuntimeTypeReferenceSpec? GenericTypeDefinitionReference, RuntimeTypeReferenceSpec? GenericTypeDefinitionReference,
@ -1065,19 +1161,28 @@ public sealed class CqrsHandlerRegistryGenerator : IIncrementalGenerator
{ {
public static RuntimeTypeReferenceSpec FromDirectReference(string typeDisplayName) public static RuntimeTypeReferenceSpec FromDirectReference(string typeDisplayName)
{ {
return new RuntimeTypeReferenceSpec(typeDisplayName, null, null, 0, null, return new RuntimeTypeReferenceSpec(typeDisplayName, null, null, null, 0, null,
ImmutableArray<RuntimeTypeReferenceSpec>.Empty); ImmutableArray<RuntimeTypeReferenceSpec>.Empty);
} }
public static RuntimeTypeReferenceSpec FromReflectionLookup(string reflectionTypeMetadataName) public static RuntimeTypeReferenceSpec FromReflectionLookup(string reflectionTypeMetadataName)
{ {
return new RuntimeTypeReferenceSpec(null, reflectionTypeMetadataName, null, 0, null, return new RuntimeTypeReferenceSpec(null, reflectionTypeMetadataName, null, null, 0, null,
ImmutableArray<RuntimeTypeReferenceSpec>.Empty);
}
public static RuntimeTypeReferenceSpec FromExternalReflectionLookup(
string reflectionAssemblyName,
string reflectionTypeMetadataName)
{
return new RuntimeTypeReferenceSpec(null, reflectionTypeMetadataName, reflectionAssemblyName, null, 0,
null,
ImmutableArray<RuntimeTypeReferenceSpec>.Empty); ImmutableArray<RuntimeTypeReferenceSpec>.Empty);
} }
public static RuntimeTypeReferenceSpec FromArray(RuntimeTypeReferenceSpec elementTypeReference, int arrayRank) public static RuntimeTypeReferenceSpec FromArray(RuntimeTypeReferenceSpec elementTypeReference, int arrayRank)
{ {
return new RuntimeTypeReferenceSpec(null, null, elementTypeReference, arrayRank, null, return new RuntimeTypeReferenceSpec(null, null, null, elementTypeReference, arrayRank, null,
ImmutableArray<RuntimeTypeReferenceSpec>.Empty); ImmutableArray<RuntimeTypeReferenceSpec>.Empty);
} }
@ -1085,7 +1190,7 @@ public sealed class CqrsHandlerRegistryGenerator : IIncrementalGenerator
RuntimeTypeReferenceSpec genericTypeDefinitionReference, RuntimeTypeReferenceSpec genericTypeDefinitionReference,
ImmutableArray<RuntimeTypeReferenceSpec> genericTypeArguments) ImmutableArray<RuntimeTypeReferenceSpec> genericTypeArguments)
{ {
return new RuntimeTypeReferenceSpec(null, null, null, 0, genericTypeDefinitionReference, return new RuntimeTypeReferenceSpec(null, null, null, null, 0, genericTypeDefinitionReference,
genericTypeArguments); genericTypeArguments);
} }
} }

View File

@ -0,0 +1,49 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<PackageId>GeWuYou.$(AssemblyName)</PackageId>
<TargetFramework>netstandard2.0</TargetFramework>
<IsRoslynAnalyzer>true</IsRoslynAnalyzer>
<IncludeBuildOutput>false</IncludeBuildOutput>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
<CompilerGeneratedFilesOutputPath>Generated</CompilerGeneratedFilesOutputPath>
<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.14.0" PrivateAssets="all"/>
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="4.14.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
<PackageReference Include="System.Text.Json" Version="8.0.6" PrivateAssets="all"/>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\GFramework.SourceGenerators.Common\GFramework.SourceGenerators.Common.csproj" PrivateAssets="all"/>
</ItemGroup>
<ItemGroup>
<Compile Include="..\GFramework.SourceGenerators.Common\Internals\IsExternalInit.cs"
Link="Internals\IsExternalInit.cs"/>
</ItemGroup>
<ItemGroup>
<None Include="$(OutputPath)\$(AssemblyName).dll"
Pack="true"
PackagePath="analyzers/dotnet/cs"
Visible="false"/>
<None Include="$(OutputPath)\GFramework.SourceGenerators.Common.dll"
Pack="true"
PackagePath="analyzers/dotnet/cs"
Visible="false"/>
</ItemGroup>
<ItemGroup>
<None Include="$(OutputPath)\GFramework.SourceGenerators.Common.dll" Pack="true" PackagePath="lib\netstandard2.0" Visible="true"/>
<None Include="$(OutputPath)\GFramework.SourceGenerators.Common.xml" Pack="true" PackagePath="lib\netstandard2.0" Visible="true"/>
</ItemGroup>
</Project>

View File

@ -0,0 +1,13 @@
global using System;
global using System.Collections.Generic;
global using System.Collections.Immutable;
global using System.IO;
global using System.Linq;
global using System.Text;
global using System.Text.Json;
global using System.Threading;
global using System.Threading.Tasks;
global using Microsoft.CodeAnalysis;
global using Microsoft.CodeAnalysis.CSharp;
global using Microsoft.CodeAnalysis.CSharp.Syntax;
global using Microsoft.CodeAnalysis.Text;

View File

@ -0,0 +1,2 @@
; Shipped analyzer release
; Intentionally empty until the split game source-generator package ships a stable analyzer release.

View File

@ -0,0 +1,17 @@
; Unshipped analyzer release
; https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md
### New Rules
Rule ID | Category | Severity | Notes
---------------------|------------------------------------|----------|-------------------------
GF_ConfigSchema_001 | GFramework.SourceGenerators.Config | Error | ConfigSchemaDiagnostics
GF_ConfigSchema_002 | GFramework.SourceGenerators.Config | Error | ConfigSchemaDiagnostics
GF_ConfigSchema_003 | GFramework.SourceGenerators.Config | Error | ConfigSchemaDiagnostics
GF_ConfigSchema_004 | GFramework.SourceGenerators.Config | Error | ConfigSchemaDiagnostics
GF_ConfigSchema_005 | GFramework.SourceGenerators.Config | Error | ConfigSchemaDiagnostics
GF_ConfigSchema_006 | GFramework.SourceGenerators.Config | Error | ConfigSchemaDiagnostics
GF_ConfigSchema_007 | GFramework.SourceGenerators.Config | Error | ConfigSchemaDiagnostics
GF_ConfigSchema_008 | GFramework.SourceGenerators.Config | Error | ConfigSchemaDiagnostics
GF_ConfigSchema_009 | GFramework.SourceGenerators.Config | Error | ConfigSchemaDiagnostics
GF_ConfigSchema_010 | GFramework.SourceGenerators.Config | Error | ConfigSchemaDiagnostics

View File

@ -31,7 +31,8 @@ public sealed class SchemaConfigGenerator : IIncrementalGenerator
private const string LookupIndexReferencePropertyMessage = private const string LookupIndexReferencePropertyMessage =
"Reference properties are excluded from generated lookup indexes because they already carry cross-table semantics."; "Reference properties are excluded from generated lookup indexes because they already carry cross-table semantics.";
private const string SupportedStringFormatNames = "'date', 'date-time', 'duration', 'email', 'time', 'uri', and 'uuid'"; private const string SupportedStringFormatNames =
"'date', 'date-time', 'duration', 'email', 'time', 'uri', and 'uuid'";
/// <inheritdoc /> /// <inheritdoc />
public void Initialize(IncrementalGeneratorInitializationContext context) public void Initialize(IncrementalGeneratorInitializationContext context)
@ -3183,7 +3184,8 @@ public sealed class SchemaConfigGenerator : IIncrementalGenerator
var targets = dependency.Value var targets = dependency.Value
.EnumerateArray() .EnumerateArray()
.Where(static item => item.ValueKind == JsonValueKind.String && !string.IsNullOrWhiteSpace(item.GetString())) .Where(static item =>
item.ValueKind == JsonValueKind.String && !string.IsNullOrWhiteSpace(item.GetString()))
.Select(static item => item.GetString()!) .Select(static item => item.GetString()!)
.Distinct(StringComparer.Ordinal) .Distinct(StringComparer.Ordinal)
.ToArray(); .ToArray();

View File

@ -0,0 +1,55 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<PackageId>GeWuYou.$(AssemblyName)</PackageId>
<TargetFramework>netstandard2.0</TargetFramework>
<IsRoslynAnalyzer>true</IsRoslynAnalyzer>
<IncludeBuildOutput>false</IncludeBuildOutput>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
<CompilerGeneratedFilesOutputPath>Generated</CompilerGeneratedFilesOutputPath>
<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.14.0" PrivateAssets="all"/>
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="4.14.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
<PackageReference Include="System.Text.Json" Version="8.0.6" PrivateAssets="all"/>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\GFramework.SourceGenerators.Common\GFramework.SourceGenerators.Common.csproj" PrivateAssets="all"/>
</ItemGroup>
<ItemGroup>
<Compile Include="..\GFramework.SourceGenerators.Common\Internals\IsExternalInit.cs"
Link="Internals\IsExternalInit.cs"/>
</ItemGroup>
<ItemGroup>
<AdditionalFiles Include="AnalyzerReleases.Shipped.md"/>
<AdditionalFiles Include="AnalyzerReleases.Unshipped.md"/>
</ItemGroup>
<ItemGroup>
<None Include="$(OutputPath)\$(AssemblyName).dll"
Pack="true"
PackagePath="analyzers/dotnet/cs"
Visible="false"/>
<None Include="$(OutputPath)\GFramework.SourceGenerators.Common.dll"
Pack="true"
PackagePath="analyzers/dotnet/cs"
Visible="false"/>
</ItemGroup>
<ItemGroup>
<None Include="$(OutputPath)\GFramework.SourceGenerators.Common.dll" Pack="true" PackagePath="lib\netstandard2.0" Visible="true"/>
<None Include="$(OutputPath)\GFramework.SourceGenerators.Common.xml" Pack="true" PackagePath="lib\netstandard2.0" Visible="true"/>
<None Include="GeWuYou.GFramework.Game.SourceGenerators.targets" Pack="true" PackagePath="build" Visible="false"/>
</ItemGroup>
</Project>

View File

@ -1,8 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- This file is automatically generated by the NuGet package -->
<!-- It ensures that the source generators are properly registered during build -->
<PropertyGroup> <PropertyGroup>
<!-- <!--
消费项目默认从 schemas/ 目录收集配置 schema。 消费项目默认从 schemas/ 目录收集配置 schema。
@ -12,15 +9,8 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<!-- <Analyzer Include="$(MSBuildThisFileDirectory)../analyzers/dotnet/cs/GFramework.Game.SourceGenerators.dll"
仅在 NuGet 打包布局存在时自动注入 analyzer。 Condition="Exists('$(MSBuildThisFileDirectory)../analyzers/dotnet/cs/GFramework.Game.SourceGenerators.dll')"/>
仓库内项目引用场景会通过 ProjectReference(OutputItemType=Analyzer) 提供生成器,
因此这里需要避免对不存在的打包路径做无效引用。
-->
<Analyzer Include="$(MSBuildThisFileDirectory)../analyzers/dotnet/cs/GFramework.SourceGenerators.dll"
Condition="Exists('$(MSBuildThisFileDirectory)../analyzers/dotnet/cs/GFramework.SourceGenerators.dll')"/>
<Analyzer Include="$(MSBuildThisFileDirectory)../analyzers/dotnet/cs/GFramework.SourceGenerators.Abstractions.dll"
Condition="Exists('$(MSBuildThisFileDirectory)../analyzers/dotnet/cs/GFramework.SourceGenerators.Abstractions.dll')"/>
<Analyzer Include="$(MSBuildThisFileDirectory)../analyzers/dotnet/cs/GFramework.SourceGenerators.Common.dll" <Analyzer Include="$(MSBuildThisFileDirectory)../analyzers/dotnet/cs/GFramework.SourceGenerators.Common.dll"
Condition="Exists('$(MSBuildThisFileDirectory)../analyzers/dotnet/cs/GFramework.SourceGenerators.Common.dll')"/> Condition="Exists('$(MSBuildThisFileDirectory)../analyzers/dotnet/cs/GFramework.SourceGenerators.Common.dll')"/>
</ItemGroup> </ItemGroup>
@ -33,8 +23,7 @@
<AdditionalFiles Include="$(MSBuildProjectDirectory)/$(GFrameworkConfigSchemaDirectory)/**/*.schema.json"/> <AdditionalFiles Include="$(MSBuildProjectDirectory)/$(GFrameworkConfigSchemaDirectory)/**/*.schema.json"/>
</ItemGroup> </ItemGroup>
<!-- Ensure the analyzers are loaded --> <Target Name="EnsureGFrameworkGameAnalyzers" BeforeTargets="CoreCompile">
<Target Name="EnsureGFrameworkAnalyzers" BeforeTargets="CoreCompile"> <Message Text="Loading GFramework.Game source generators" Importance="high"/>
<Message Text="Loading GFramework source generators" Importance="high"/>
</Target> </Target>
</Project> </Project>

View File

@ -0,0 +1,14 @@
global using System;
global using System.Collections.Generic;
global using System.Collections.Immutable;
global using System.Globalization;
global using System.IO;
global using System.Linq;
global using System.Text;
global using System.Text.Json;
global using System.Threading;
global using System.Threading.Tasks;
global using Microsoft.CodeAnalysis;
global using Microsoft.CodeAnalysis.CSharp;
global using Microsoft.CodeAnalysis.CSharp.Syntax;
global using Microsoft.CodeAnalysis.Text;

View File

@ -20,13 +20,14 @@
<ProjectReference Include="..\GFramework.Game\GFramework.Game.csproj"/> <ProjectReference Include="..\GFramework.Game\GFramework.Game.csproj"/>
<ProjectReference Include="..\GFramework.Core\GFramework.Core.csproj"/> <ProjectReference Include="..\GFramework.Core\GFramework.Core.csproj"/>
<ProjectReference Include="..\GFramework.Godot\GFramework.Godot.csproj"/> <ProjectReference Include="..\GFramework.Godot\GFramework.Godot.csproj"/>
<ProjectReference Include="..\GFramework.SourceGenerators.Abstractions\GFramework.SourceGenerators.Abstractions.csproj" <ProjectReference Include="..\GFramework.Core.SourceGenerators.Abstractions\GFramework.Core.SourceGenerators.Abstractions.csproj"/>
OutputItemType="Analyzer"
ReferenceOutputAssembly="false"/>
<ProjectReference Include="..\GFramework.SourceGenerators.Common\GFramework.SourceGenerators.Common.csproj" <ProjectReference Include="..\GFramework.SourceGenerators.Common\GFramework.SourceGenerators.Common.csproj"
OutputItemType="Analyzer" OutputItemType="Analyzer"
ReferenceOutputAssembly="false"/> ReferenceOutputAssembly="false"/>
<ProjectReference Include="..\GFramework.SourceGenerators\GFramework.SourceGenerators.csproj" <ProjectReference Include="..\GFramework.Core.SourceGenerators\GFramework.Core.SourceGenerators.csproj"
OutputItemType="Analyzer"
ReferenceOutputAssembly="false"/>
<ProjectReference Include="..\GFramework.Game.SourceGenerators\GFramework.Game.SourceGenerators.csproj"
OutputItemType="Analyzer" OutputItemType="Analyzer"
ReferenceOutputAssembly="false"/> ReferenceOutputAssembly="false"/>
</ItemGroup> </ItemGroup>
@ -35,6 +36,6 @@
通过仓库内的 targets 复用消费者默认约定,确保测试项目与真实消费项目一样 通过仓库内的 targets 复用消费者默认约定,确保测试项目与真实消费项目一样
自动拾取 schemas/**/*.schema.json 作为 Source Generator 的 AdditionalFiles。 自动拾取 schemas/**/*.schema.json 作为 Source Generator 的 AdditionalFiles。
--> -->
<Import Project="..\GFramework.SourceGenerators\GeWuYou.GFramework.SourceGenerators.targets"/> <Import Project="..\GFramework.Game.SourceGenerators\GeWuYou.GFramework.Game.SourceGenerators.targets"/>
</Project> </Project>

View File

@ -827,10 +827,10 @@ public class CqrsHandlerRegistryGeneratorTests
/// <summary> /// <summary>
/// 验证当外部基类暴露的 handler interface 含有生成注册器顶层上下文不可直接引用的 protected 类型时, /// 验证当外部基类暴露的 handler interface 含有生成注册器顶层上下文不可直接引用的 protected 类型时,
/// 生成器会保留已知直注册,并只对剩余未知接口做本地 interface discovery /// 生成器会输出定向程序集查找,而不是继续退回 implementation 级接口发现
/// </summary> /// </summary>
[Test] [Test]
public void Generates_Partial_Runtime_Interface_Discovery_For_Inaccessible_External_Protected_Types() public void Generates_Precise_Assembly_Type_Lookups_For_Inaccessible_External_Protected_Types()
{ {
const string contractsSource = """ const string contractsSource = """
namespace GFramework.Cqrs.Abstractions.Cqrs namespace GFramework.Cqrs.Abstractions.Cqrs
@ -931,30 +931,28 @@ public class CqrsHandlerRegistryGeneratorTests
Assert.That( Assert.That(
generatedSource, generatedSource,
Does.Contain( Does.Contain(
"var knownServiceTypes0 = new global::System.Collections.Generic.HashSet<global::System.Type>();")); "ResolveReferencedAssemblyType(\"Dependency\", \"Dep.VisibilityScope+ProtectedRequest\")"));
Assert.That( Assert.That(
generatedSource, generatedSource,
Does.Contain( Does.Contain(
"// Remaining runtime interface discovery target: GFramework.Cqrs.Abstractions.Cqrs.IRequestHandler<Dep.VisibilityScope.ProtectedRequest, Dep.VisibilityScope.ProtectedResponse[]>")); "ResolveReferencedAssemblyType(\"Dependency\", \"Dep.VisibilityScope+ProtectedResponse\")"));
Assert.That( Assert.That(
generatedSource, generatedSource,
Does.Contain( Does.Contain(
"knownServiceTypes0.Add(typeof(global::GFramework.Cqrs.Abstractions.Cqrs.IRequestHandler<global::Dep.VisibleRequest, string>));")); "typeof(global::GFramework.Cqrs.Abstractions.Cqrs.IRequestHandler<global::Dep.VisibleRequest, string>)"));
Assert.That( Assert.That(
generatedSource, generatedSource,
Does.Contain( Does.Contain("ResolveReferencedAssembly(string assemblyName)"));
"RegisterRemainingReflectedHandlerInterfaces(services, logger, implementationType0, knownServiceTypes0);"));
Assert.That( Assert.That(
generatedSource, generatedSource,
Does.Contain("if (knownServiceTypes.Contains(handlerInterface))")); Does.Not.Contain("knownServiceTypes0"));
Assert.That( Assert.That(
generatedSource, generatedSource,
Does.Contain( Does.Not.Contain("RegisterRemainingReflectedHandlerInterfaces"));
"Registered CQRS handler TestApp.DerivedHandler as GFramework.Cqrs.Abstractions.Cqrs.IRequestHandler<Dep.VisibleRequest, string>."));
Assert.That( Assert.That(
generatedSource, generatedSource,
Does.Not.Contain( Does.Not.Contain(
"typeof(global::GFramework.Cqrs.Abstractions.Cqrs.IRequestHandler<global::Dep.VisibilityScope.ProtectedRequest")); "// Remaining runtime interface discovery target:"));
}); });
} }

View File

@ -23,7 +23,9 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\GFramework.SourceGenerators\GFramework.SourceGenerators.csproj"/> <ProjectReference Include="..\GFramework.Core.SourceGenerators\GFramework.Core.SourceGenerators.csproj"/>
<ProjectReference Include="..\GFramework.Cqrs.SourceGenerators\GFramework.Cqrs.SourceGenerators.csproj"/>
<ProjectReference Include="..\GFramework.Game.SourceGenerators\GFramework.Game.SourceGenerators.csproj"/>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -29,21 +29,25 @@
<None Remove="GFramework.Core\**"/> <None Remove="GFramework.Core\**"/>
<None Remove="GFramework.Game\**"/> <None Remove="GFramework.Game\**"/>
<None Remove="GFramework.Godot\**"/> <None Remove="GFramework.Godot\**"/>
<None Update="GFramework.SourceGenerators\logging\README.md"> <None Update="GFramework.Core.SourceGenerators\logging\README.md">
<Link>GFramework.SorceGenerators\logging\README.md</Link> <Link>GFramework.SorceGenerators\logging\README.md</Link>
</None> </None>
<None Update="GFramework.SourceGenerators\README.md"> <None Update="GFramework.Core.SourceGenerators\README.md">
<Link>GFramework.SorceGenerators\README.md</Link> <Link>GFramework.SorceGenerators\README.md</Link>
</None> </None>
<None Update="GFramework.SourceGenerators\AnalyzerReleases.Shipped.md"> <None Update="GFramework.Core.SourceGenerators\AnalyzerReleases.Shipped.md">
<Link>GFramework.SorceGenerators\AnalyzerReleases.Shipped.md</Link> <Link>GFramework.SorceGenerators\AnalyzerReleases.Shipped.md</Link>
</None> </None>
<None Update="GFramework.SourceGenerators\AnalyzerReleases.Unshipped.md"> <None Update="GFramework.Core.SourceGenerators\AnalyzerReleases.Unshipped.md">
<Link>GFramework.SorceGenerators\AnalyzerReleases.Unshipped.md</Link> <Link>GFramework.SorceGenerators\AnalyzerReleases.Unshipped.md</Link>
</None> </None>
<None Remove="GFramework.Godot.SourceGenerators\**"/> <None Remove="GFramework.Godot.SourceGenerators\**"/>
<None Remove="GFramework.SorceGenerators\**"/> <None Remove="GFramework.SorceGenerators\**"/>
<None Remove="GFramework.SourceGenerators\**"/> <None Remove="GFramework.SourceGenerators\**"/>
<None Remove="GFramework.Core.SourceGenerators\**"/>
<None Remove="GFramework.Core.SourceGenerators.Abstractions\**"/>
<None Remove="GFramework.Cqrs.SourceGenerators\**"/>
<None Remove="GFramework.Game.SourceGenerators\**"/>
<None Remove="GFramework.SourceGenerators.Common\**"/> <None Remove="GFramework.SourceGenerators.Common\**"/>
<None Remove="GFramework.SourceGenerators.Tests\**"/> <None Remove="GFramework.SourceGenerators.Tests\**"/>
<None Remove="GFramework.Godot.SourceGenerators.Tests\**"/> <None Remove="GFramework.Godot.SourceGenerators.Tests\**"/>
@ -77,18 +81,22 @@
<Compile Remove="GFramework.Core\**"/> <Compile Remove="GFramework.Core\**"/>
<Compile Remove="GFramework.Game\**"/> <Compile Remove="GFramework.Game\**"/>
<Compile Remove="GFramework.Godot\**"/> <Compile Remove="GFramework.Godot\**"/>
<Compile Update="GFramework.SourceGenerators\enums\EnumExtensionsGenerator.cs"> <Compile Update="GFramework.Core.SourceGenerators\enums\EnumExtensionsGenerator.cs">
<Link>GFramework.SorceGenerators\enums\EnumExtensionsGenerator.cs</Link> <Link>GFramework.SorceGenerators\enums\EnumExtensionsGenerator.cs</Link>
</Compile> </Compile>
<Compile Update="GFramework.SourceGenerators\logging\Diagnostic.cs"> <Compile Update="GFramework.Core.SourceGenerators\logging\Diagnostic.cs">
<Link>GFramework.SorceGenerators\logging\Diagnostic.cs</Link> <Link>GFramework.SorceGenerators\logging\Diagnostic.cs</Link>
</Compile> </Compile>
<Compile Update="GFramework.SourceGenerators\logging\LoggerGenerator.cs"> <Compile Update="GFramework.Core.SourceGenerators\logging\LoggerGenerator.cs">
<Link>GFramework.SorceGenerators\logging\LoggerGenerator.cs</Link> <Link>GFramework.SorceGenerators\logging\LoggerGenerator.cs</Link>
</Compile> </Compile>
<Compile Remove="GFramework.Godot.SourceGenerators\**"/> <Compile Remove="GFramework.Godot.SourceGenerators\**"/>
<Compile Remove="GFramework.SorceGenerators\**"/> <Compile Remove="GFramework.SorceGenerators\**"/>
<Compile Remove="GFramework.SourceGenerators\**"/> <Compile Remove="GFramework.SourceGenerators\**"/>
<Compile Remove="GFramework.Core.SourceGenerators\**"/>
<Compile Remove="GFramework.Core.SourceGenerators.Abstractions\**"/>
<Compile Remove="GFramework.Cqrs.SourceGenerators\**"/>
<Compile Remove="GFramework.Game.SourceGenerators\**"/>
<Compile Remove="GFramework.SourceGenerators.Common\**"/> <Compile Remove="GFramework.SourceGenerators.Common\**"/>
<Compile Remove="GFramework.SourceGenerators.Tests\**"/> <Compile Remove="GFramework.SourceGenerators.Tests\**"/>
<Compile Remove="GFramework.Godot.SourceGenerators.Tests\**"/> <Compile Remove="GFramework.Godot.SourceGenerators.Tests\**"/>
@ -120,6 +128,10 @@
<EmbeddedResource Remove="GFramework.Godot.SourceGenerators\**"/> <EmbeddedResource Remove="GFramework.Godot.SourceGenerators\**"/>
<EmbeddedResource Remove="GFramework.SorceGenerators\**"/> <EmbeddedResource Remove="GFramework.SorceGenerators\**"/>
<EmbeddedResource Remove="GFramework.SourceGenerators\**"/> <EmbeddedResource Remove="GFramework.SourceGenerators\**"/>
<EmbeddedResource Remove="GFramework.Core.SourceGenerators\**"/>
<EmbeddedResource Remove="GFramework.Core.SourceGenerators.Abstractions\**"/>
<EmbeddedResource Remove="GFramework.Cqrs.SourceGenerators\**"/>
<EmbeddedResource Remove="GFramework.Game.SourceGenerators\**"/>
<EmbeddedResource Remove="GFramework.SourceGenerators.Common\**"/> <EmbeddedResource Remove="GFramework.SourceGenerators.Common\**"/>
<EmbeddedResource Remove="GFramework.SourceGenerators.Tests\**"/> <EmbeddedResource Remove="GFramework.SourceGenerators.Tests\**"/>
<EmbeddedResource Remove="GFramework.Godot.SourceGenerators.Tests\**"/> <EmbeddedResource Remove="GFramework.Godot.SourceGenerators.Tests\**"/>

View File

@ -2,10 +2,6 @@
Microsoft Visual Studio Solution File, Format Version 12.00 Microsoft Visual Studio Solution File, Format Version 12.00
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GFramework", "GFramework.csproj", "{9BEDDD6C-DF8B-4E71-9C75-F44EC669ABBD}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GFramework", "GFramework.csproj", "{9BEDDD6C-DF8B-4E71-9C75-F44EC669ABBD}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GFramework.SourceGenerators", "GFramework.SourceGenerators\GFramework.SourceGenerators.csproj", "{E9D51809-0351-4B83-B85B-B5F469AAB3B8}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GFramework.SourceGenerators.Abstractions", "GFramework.SourceGenerators.Abstractions\GFramework.SourceGenerators.Abstractions.csproj", "{84C5C3C9-5620-4924-BA04-92F813F2B70F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GFramework.Core", "GFramework.Core\GFramework.Core.csproj", "{A6D5854D-79EA-487A-9ED9-396E6A1F8031}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GFramework.Core", "GFramework.Core\GFramework.Core.csproj", "{A6D5854D-79EA-487A-9ED9-396E6A1F8031}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GFramework.Godot", "GFramework.Godot\GFramework.Godot.csproj", "{FC56D81A-3A3B-4B49-B318-363DFA0D8206}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GFramework.Godot", "GFramework.Godot\GFramework.Godot.csproj", "{FC56D81A-3A3B-4B49-B318-363DFA0D8206}"
@ -46,6 +42,14 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GFramework.Cqrs.Tests", "GF
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GFramework.Tests.Common", "GFramework.Tests.Common\GFramework.Tests.Common.csproj", "{1100EE3E-A12D-4DE5-ABA8-591D3126570B}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GFramework.Tests.Common", "GFramework.Tests.Common\GFramework.Tests.Common.csproj", "{1100EE3E-A12D-4DE5-ABA8-591D3126570B}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GFramework.Core.SourceGenerators", "GFramework.Core.SourceGenerators\GFramework.Core.SourceGenerators.csproj", "{2E8A4BB6-DA58-484F-ACC5-A8F2FA885B36}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GFramework.Core.SourceGenerators.Abstractions", "GFramework.Core.SourceGenerators.Abstractions\GFramework.Core.SourceGenerators.Abstractions.csproj", "{8858F489-4EDD-41F1-9A74-1CA1CB287EB4}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GFramework.Cqrs.SourceGenerators", "GFramework.Cqrs.SourceGenerators\GFramework.Cqrs.SourceGenerators.csproj", "{3FDCD803-604F-48D9-B2A8-2EC621E8D598}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GFramework.Game.SourceGenerators", "GFramework.Game.SourceGenerators\GFramework.Game.SourceGenerators.csproj", "{9D3AADF0-55E6-4F80-B9C5-875F63E170D8}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@ -68,30 +72,6 @@ Global
{9BEDDD6C-DF8B-4E71-9C75-F44EC669ABBD}.Release|x64.Build.0 = Release|Any CPU {9BEDDD6C-DF8B-4E71-9C75-F44EC669ABBD}.Release|x64.Build.0 = Release|Any CPU
{9BEDDD6C-DF8B-4E71-9C75-F44EC669ABBD}.Release|x86.ActiveCfg = Release|Any CPU {9BEDDD6C-DF8B-4E71-9C75-F44EC669ABBD}.Release|x86.ActiveCfg = Release|Any CPU
{9BEDDD6C-DF8B-4E71-9C75-F44EC669ABBD}.Release|x86.Build.0 = Release|Any CPU {9BEDDD6C-DF8B-4E71-9C75-F44EC669ABBD}.Release|x86.Build.0 = Release|Any CPU
{E9D51809-0351-4B83-B85B-B5F469AAB3B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E9D51809-0351-4B83-B85B-B5F469AAB3B8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E9D51809-0351-4B83-B85B-B5F469AAB3B8}.Debug|x64.ActiveCfg = Debug|Any CPU
{E9D51809-0351-4B83-B85B-B5F469AAB3B8}.Debug|x64.Build.0 = Debug|Any CPU
{E9D51809-0351-4B83-B85B-B5F469AAB3B8}.Debug|x86.ActiveCfg = Debug|Any CPU
{E9D51809-0351-4B83-B85B-B5F469AAB3B8}.Debug|x86.Build.0 = Debug|Any CPU
{E9D51809-0351-4B83-B85B-B5F469AAB3B8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E9D51809-0351-4B83-B85B-B5F469AAB3B8}.Release|Any CPU.Build.0 = Release|Any CPU
{E9D51809-0351-4B83-B85B-B5F469AAB3B8}.Release|x64.ActiveCfg = Release|Any CPU
{E9D51809-0351-4B83-B85B-B5F469AAB3B8}.Release|x64.Build.0 = Release|Any CPU
{E9D51809-0351-4B83-B85B-B5F469AAB3B8}.Release|x86.ActiveCfg = Release|Any CPU
{E9D51809-0351-4B83-B85B-B5F469AAB3B8}.Release|x86.Build.0 = Release|Any CPU
{84C5C3C9-5620-4924-BA04-92F813F2B70F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{84C5C3C9-5620-4924-BA04-92F813F2B70F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{84C5C3C9-5620-4924-BA04-92F813F2B70F}.Debug|x64.ActiveCfg = Debug|Any CPU
{84C5C3C9-5620-4924-BA04-92F813F2B70F}.Debug|x64.Build.0 = Debug|Any CPU
{84C5C3C9-5620-4924-BA04-92F813F2B70F}.Debug|x86.ActiveCfg = Debug|Any CPU
{84C5C3C9-5620-4924-BA04-92F813F2B70F}.Debug|x86.Build.0 = Debug|Any CPU
{84C5C3C9-5620-4924-BA04-92F813F2B70F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{84C5C3C9-5620-4924-BA04-92F813F2B70F}.Release|Any CPU.Build.0 = Release|Any CPU
{84C5C3C9-5620-4924-BA04-92F813F2B70F}.Release|x64.ActiveCfg = Release|Any CPU
{84C5C3C9-5620-4924-BA04-92F813F2B70F}.Release|x64.Build.0 = Release|Any CPU
{84C5C3C9-5620-4924-BA04-92F813F2B70F}.Release|x86.ActiveCfg = Release|Any CPU
{84C5C3C9-5620-4924-BA04-92F813F2B70F}.Release|x86.Build.0 = Release|Any CPU
{A6D5854D-79EA-487A-9ED9-396E6A1F8031}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A6D5854D-79EA-487A-9ED9-396E6A1F8031}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A6D5854D-79EA-487A-9ED9-396E6A1F8031}.Debug|Any CPU.Build.0 = Debug|Any CPU {A6D5854D-79EA-487A-9ED9-396E6A1F8031}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A6D5854D-79EA-487A-9ED9-396E6A1F8031}.Debug|x64.ActiveCfg = Debug|Any CPU {A6D5854D-79EA-487A-9ED9-396E6A1F8031}.Debug|x64.ActiveCfg = Debug|Any CPU
@ -332,6 +312,54 @@ Global
{1100EE3E-A12D-4DE5-ABA8-591D3126570B}.Release|x64.Build.0 = Release|Any CPU {1100EE3E-A12D-4DE5-ABA8-591D3126570B}.Release|x64.Build.0 = Release|Any CPU
{1100EE3E-A12D-4DE5-ABA8-591D3126570B}.Release|x86.ActiveCfg = Release|Any CPU {1100EE3E-A12D-4DE5-ABA8-591D3126570B}.Release|x86.ActiveCfg = Release|Any CPU
{1100EE3E-A12D-4DE5-ABA8-591D3126570B}.Release|x86.Build.0 = Release|Any CPU {1100EE3E-A12D-4DE5-ABA8-591D3126570B}.Release|x86.Build.0 = Release|Any CPU
{2E8A4BB6-DA58-484F-ACC5-A8F2FA885B36}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2E8A4BB6-DA58-484F-ACC5-A8F2FA885B36}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2E8A4BB6-DA58-484F-ACC5-A8F2FA885B36}.Debug|x64.ActiveCfg = Debug|Any CPU
{2E8A4BB6-DA58-484F-ACC5-A8F2FA885B36}.Debug|x64.Build.0 = Debug|Any CPU
{2E8A4BB6-DA58-484F-ACC5-A8F2FA885B36}.Debug|x86.ActiveCfg = Debug|Any CPU
{2E8A4BB6-DA58-484F-ACC5-A8F2FA885B36}.Debug|x86.Build.0 = Debug|Any CPU
{2E8A4BB6-DA58-484F-ACC5-A8F2FA885B36}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2E8A4BB6-DA58-484F-ACC5-A8F2FA885B36}.Release|Any CPU.Build.0 = Release|Any CPU
{2E8A4BB6-DA58-484F-ACC5-A8F2FA885B36}.Release|x64.ActiveCfg = Release|Any CPU
{2E8A4BB6-DA58-484F-ACC5-A8F2FA885B36}.Release|x64.Build.0 = Release|Any CPU
{2E8A4BB6-DA58-484F-ACC5-A8F2FA885B36}.Release|x86.ActiveCfg = Release|Any CPU
{2E8A4BB6-DA58-484F-ACC5-A8F2FA885B36}.Release|x86.Build.0 = Release|Any CPU
{8858F489-4EDD-41F1-9A74-1CA1CB287EB4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8858F489-4EDD-41F1-9A74-1CA1CB287EB4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8858F489-4EDD-41F1-9A74-1CA1CB287EB4}.Debug|x64.ActiveCfg = Debug|Any CPU
{8858F489-4EDD-41F1-9A74-1CA1CB287EB4}.Debug|x64.Build.0 = Debug|Any CPU
{8858F489-4EDD-41F1-9A74-1CA1CB287EB4}.Debug|x86.ActiveCfg = Debug|Any CPU
{8858F489-4EDD-41F1-9A74-1CA1CB287EB4}.Debug|x86.Build.0 = Debug|Any CPU
{8858F489-4EDD-41F1-9A74-1CA1CB287EB4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8858F489-4EDD-41F1-9A74-1CA1CB287EB4}.Release|Any CPU.Build.0 = Release|Any CPU
{8858F489-4EDD-41F1-9A74-1CA1CB287EB4}.Release|x64.ActiveCfg = Release|Any CPU
{8858F489-4EDD-41F1-9A74-1CA1CB287EB4}.Release|x64.Build.0 = Release|Any CPU
{8858F489-4EDD-41F1-9A74-1CA1CB287EB4}.Release|x86.ActiveCfg = Release|Any CPU
{8858F489-4EDD-41F1-9A74-1CA1CB287EB4}.Release|x86.Build.0 = Release|Any CPU
{3FDCD803-604F-48D9-B2A8-2EC621E8D598}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3FDCD803-604F-48D9-B2A8-2EC621E8D598}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3FDCD803-604F-48D9-B2A8-2EC621E8D598}.Debug|x64.ActiveCfg = Debug|Any CPU
{3FDCD803-604F-48D9-B2A8-2EC621E8D598}.Debug|x64.Build.0 = Debug|Any CPU
{3FDCD803-604F-48D9-B2A8-2EC621E8D598}.Debug|x86.ActiveCfg = Debug|Any CPU
{3FDCD803-604F-48D9-B2A8-2EC621E8D598}.Debug|x86.Build.0 = Debug|Any CPU
{3FDCD803-604F-48D9-B2A8-2EC621E8D598}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3FDCD803-604F-48D9-B2A8-2EC621E8D598}.Release|Any CPU.Build.0 = Release|Any CPU
{3FDCD803-604F-48D9-B2A8-2EC621E8D598}.Release|x64.ActiveCfg = Release|Any CPU
{3FDCD803-604F-48D9-B2A8-2EC621E8D598}.Release|x64.Build.0 = Release|Any CPU
{3FDCD803-604F-48D9-B2A8-2EC621E8D598}.Release|x86.ActiveCfg = Release|Any CPU
{3FDCD803-604F-48D9-B2A8-2EC621E8D598}.Release|x86.Build.0 = Release|Any CPU
{9D3AADF0-55E6-4F80-B9C5-875F63E170D8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9D3AADF0-55E6-4F80-B9C5-875F63E170D8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9D3AADF0-55E6-4F80-B9C5-875F63E170D8}.Debug|x64.ActiveCfg = Debug|Any CPU
{9D3AADF0-55E6-4F80-B9C5-875F63E170D8}.Debug|x64.Build.0 = Debug|Any CPU
{9D3AADF0-55E6-4F80-B9C5-875F63E170D8}.Debug|x86.ActiveCfg = Debug|Any CPU
{9D3AADF0-55E6-4F80-B9C5-875F63E170D8}.Debug|x86.Build.0 = Debug|Any CPU
{9D3AADF0-55E6-4F80-B9C5-875F63E170D8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9D3AADF0-55E6-4F80-B9C5-875F63E170D8}.Release|Any CPU.Build.0 = Release|Any CPU
{9D3AADF0-55E6-4F80-B9C5-875F63E170D8}.Release|x64.ActiveCfg = Release|Any CPU
{9D3AADF0-55E6-4F80-B9C5-875F63E170D8}.Release|x64.Build.0 = Release|Any CPU
{9D3AADF0-55E6-4F80-B9C5-875F63E170D8}.Release|x86.ActiveCfg = Release|Any CPU
{9D3AADF0-55E6-4F80-B9C5-875F63E170D8}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE

View File

@ -246,9 +246,8 @@ public class GameArchitecture : Architecture
优先使用程序集级生成注册器,失败时自动回退到反射扫描;如果同一程序集已经由默认路径或其他模块接入,框架会自动去重,避免重复注册 优先使用程序集级生成注册器,失败时自动回退到反射扫描;如果同一程序集已经由默认路径或其他模块接入,框架会自动去重,避免重复注册
handler。 handler。
`RegisterCqrsPipelineBehavior<TBehavior>()` 是推荐入口;旧的 `RegisterMediatorBehavior<TBehavior>()` `RegisterCqrsPipelineBehavior<TBehavior>()` 是唯一保留的公开入口;旧的 `Mediator` 兼容别名与扩展已移除,不再继续维护。
仅作为兼容名称保留,当前已标记为 `Obsolete` 并从 IntelliSense 主路径隐藏,计划在未来 major 版本中移除。 当前接口支持两种形式:
`ContextAwareMediator*Extensions``MediatorCoroutineExtensions` 也遵循同样的弃用节奏。当前接口支持两种形式:
- 开放泛型行为,例如 `LoggingBehavior<,>`,用于匹配所有请求 - 开放泛型行为,例如 `LoggingBehavior<,>`,用于匹配所有请求
- 封闭行为类型,例如某个只服务于单一请求的 `SpecialBehavior` - 封闭行为类型,例如某个只服务于单一请求的 `SpecialBehavior`

View File

@ -153,26 +153,29 @@ GameProject/
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\GFramework.Game\GFramework.Game.csproj" /> <ProjectReference Include="..\GFramework.Game\GFramework.Game.csproj" />
<ProjectReference Include="..\GFramework.SourceGenerators.Abstractions\GFramework.SourceGenerators.Abstractions.csproj" <ProjectReference Include="..\GFramework.Core.SourceGenerators.Abstractions\GFramework.Core.SourceGenerators.Abstractions.csproj" />
OutputItemType="Analyzer"
ReferenceOutputAssembly="false" />
<ProjectReference Include="..\GFramework.SourceGenerators.Common\GFramework.SourceGenerators.Common.csproj" <ProjectReference Include="..\GFramework.SourceGenerators.Common\GFramework.SourceGenerators.Common.csproj"
OutputItemType="Analyzer" OutputItemType="Analyzer"
ReferenceOutputAssembly="false" /> ReferenceOutputAssembly="false" />
<ProjectReference Include="..\GFramework.SourceGenerators\GFramework.SourceGenerators.csproj" <ProjectReference Include="..\GFramework.Core.SourceGenerators\GFramework.Core.SourceGenerators.csproj"
OutputItemType="Analyzer"
ReferenceOutputAssembly="false" />
<ProjectReference Include="..\GFramework.Game.SourceGenerators\GFramework.Game.SourceGenerators.csproj"
OutputItemType="Analyzer" OutputItemType="Analyzer"
ReferenceOutputAssembly="false" /> ReferenceOutputAssembly="false" />
</ItemGroup> </ItemGroup>
<Import Project="..\GFramework.SourceGenerators\GeWuYou.GFramework.SourceGenerators.targets" /> <Import Project="..\GFramework.Game.SourceGenerators\GeWuYou.GFramework.Game.SourceGenerators.targets" />
</Project> </Project>
``` ```
这段配置的作用: 这段配置的作用:
- `GFramework.Game` 提供运行时 `YamlConfigLoader``ConfigRegistry``GameConfigBootstrap``GameConfigModule` 和只读表实现 - `GFramework.Game` 提供运行时 `YamlConfigLoader``ConfigRegistry``GameConfigBootstrap``GameConfigModule` 和只读表实现
- 三个 `ProjectReference(... OutputItemType="Analyzer")` 把生成器接进当前消费者项目 - `GFramework.Core.SourceGenerators.Abstractions` 提供 `Core` 侧 source-generator attributes
- `GeWuYou.GFramework.SourceGenerators.targets` 自动把 `schemas/**/*.schema.json` 加入 `AdditionalFiles` - `GFramework.SourceGenerators.Common``GFramework.Core.SourceGenerators``GFramework.Game.SourceGenerators`
共同把生成器接进当前消费者项目
- `GeWuYou.GFramework.Game.SourceGenerators.targets` 自动把 `schemas/**/*.schema.json` 加入 `AdditionalFiles`
如果你使用打包后的 NuGet而不是仓库内项目引用原则保持不变 如果你使用打包后的 NuGet而不是仓库内项目引用原则保持不变