mirror of
https://github.com/GeWuYou/GFramework.git
synced 2026-05-08 01:24:31 +08:00
Merge pull request #235 from GeWuYou/refactor/cqrs-and-config-system
refactor: 移除 Mediator 兼容性 API 并重组源生成器项目结构
This commit is contained in:
commit
1d6ff223d5
@ -73,9 +73,8 @@ Architecture 负责统一生命周期编排,核心阶段包括:
|
||||
|
||||
### CQRS
|
||||
|
||||
命令与查询分离,支持同步与异步执行。当前版本内建自有 CQRS runtime、行为管道和 handler 自动注册;公开 API 里仍保留少量历史
|
||||
`Mediator` 命名以兼容旧调用点,但这些别名已进入正式弃用周期:新代码应使用 `Cqrs` 命名入口,旧别名会继续兼容一段时间并计划在未来
|
||||
major 版本中移除。
|
||||
命令与查询分离,支持同步与异步执行。当前版本内建自有 CQRS runtime、行为管道和 handler 自动注册;历史 `Mediator`
|
||||
兼容别名已从公开 API 移除,统一使用 `Cqrs` 命名入口。
|
||||
|
||||
### EventBus
|
||||
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
using System.ComponentModel;
|
||||
using System.Reflection;
|
||||
using GFramework.Core.Abstractions.Lifecycle;
|
||||
using GFramework.Core.Abstractions.Model;
|
||||
@ -82,20 +81,6 @@ public interface IArchitecture : IAsyncInitializable, IAsyncDestroyable, IInitia
|
||||
void RegisterCqrsPipelineBehavior<TBehavior>()
|
||||
where TBehavior : class;
|
||||
|
||||
/// <summary>
|
||||
/// 注册 CQRS 请求管道行为。
|
||||
/// 该成员保留旧名称以兼容历史调用点,内部行为与 <see cref="RegisterCqrsPipelineBehavior{TBehavior}" /> 一致。
|
||||
/// 新代码不应继续依赖该别名;兼容层计划在未来的 major 版本中移除。
|
||||
/// 既支持实现 <c>IPipelineBehavior<,></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>
|
||||
/// 从指定程序集显式注册 CQRS 处理器。
|
||||
/// 当处理器位于默认架构程序集之外的模块或扩展程序集中时,可在初始化阶段调用该入口接入对应程序集。
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
using System.ComponentModel;
|
||||
using System.Reflection;
|
||||
using System.Reflection;
|
||||
using GFramework.Core.Abstractions.Rule;
|
||||
using GFramework.Core.Abstractions.Systems;
|
||||
|
||||
@ -97,18 +96,6 @@ public interface IIocContainer : IContextAware
|
||||
void RegisterCqrsPipelineBehavior<TBehavior>()
|
||||
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>
|
||||
/// 从指定程序集显式注册 CQRS 处理器。
|
||||
/// 该入口适用于处理器不位于默认架构程序集中的场景,例如扩展包、模块程序集或拆分后的业务程序集。
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
namespace GFramework.SourceGenerators.Abstractions.Architectures;
|
||||
namespace GFramework.Core.SourceGenerators.Abstractions.Architectures;
|
||||
|
||||
/// <summary>
|
||||
/// 标记架构模块类型,Source Generator 会根据注册特性生成 <c>Install</c> 方法。
|
||||
@ -1,4 +1,4 @@
|
||||
namespace GFramework.SourceGenerators.Abstractions.Architectures;
|
||||
namespace GFramework.Core.SourceGenerators.Abstractions.Architectures;
|
||||
|
||||
/// <summary>
|
||||
/// 声明架构模块需要自动注册的模型类型。
|
||||
@ -1,4 +1,4 @@
|
||||
namespace GFramework.SourceGenerators.Abstractions.Architectures;
|
||||
namespace GFramework.Core.SourceGenerators.Abstractions.Architectures;
|
||||
|
||||
/// <summary>
|
||||
/// 声明架构模块需要自动注册的系统类型。
|
||||
@ -1,4 +1,4 @@
|
||||
namespace GFramework.SourceGenerators.Abstractions.Architectures;
|
||||
namespace GFramework.Core.SourceGenerators.Abstractions.Architectures;
|
||||
|
||||
/// <summary>
|
||||
/// 声明架构模块需要自动注册的工具类型。
|
||||
@ -1,4 +1,4 @@
|
||||
namespace GFramework.SourceGenerators.Abstractions.Bases;
|
||||
namespace GFramework.Core.SourceGenerators.Abstractions.Bases;
|
||||
|
||||
/// <summary>
|
||||
/// 标记类的优先级,自动生成 <c>GFramework.Core.Abstractions.Bases.IPrioritized</c> 接口实现。
|
||||
@ -32,4 +32,4 @@ public sealed class PriorityAttribute : Attribute
|
||||
/// 获取优先级值
|
||||
/// </summary>
|
||||
public int Value { get; }
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
namespace GFramework.SourceGenerators.Abstractions.Enums;
|
||||
namespace GFramework.Core.SourceGenerators.Abstractions.Enums;
|
||||
|
||||
/// <summary>
|
||||
/// 标注在 enum 上,Source Generator 会为该 enum 生成扩展方法。
|
||||
@ -15,4 +15,4 @@ public sealed class GenerateEnumExtensionsAttribute : Attribute
|
||||
/// 是否生成一个 IsIn(params T[]) 方法以简化多值判断(默认 true)。
|
||||
/// </summary>
|
||||
public bool GenerateIsInMethod { get; set; } = true;
|
||||
}
|
||||
}
|
||||
@ -12,7 +12,7 @@
|
||||
|
||||
<!-- 引入必要的命名空间 -->
|
||||
<ItemGroup>
|
||||
<Using Include="GFramework.SourceGenerators.Abstractions"/>
|
||||
<Using Include="GFramework.Core.SourceGenerators.Abstractions"/>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Update="Meziantou.Analyzer" Version="3.0.46">
|
||||
@ -15,4 +15,4 @@ global using System;
|
||||
global using System.Collections.Generic;
|
||||
global using System.Linq;
|
||||
global using System.Threading;
|
||||
global using System.Threading.Tasks;
|
||||
global using System.Threading.Tasks;
|
||||
@ -1,5 +1,5 @@
|
||||
#nullable enable
|
||||
namespace GFramework.SourceGenerators.Abstractions.Logging;
|
||||
namespace GFramework.Core.SourceGenerators.Abstractions.Logging;
|
||||
|
||||
/// <summary>
|
||||
/// 标注在类上,Source Generator 会为该类自动生成一个日志记录器字段。
|
||||
@ -35,4 +35,4 @@ public sealed class LogAttribute : Attribute
|
||||
|
||||
/// <summary>访问修饰符</summary>
|
||||
public string AccessModifier { get; set; } = "private";
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
namespace GFramework.SourceGenerators.Abstractions.Rule;
|
||||
namespace GFramework.Core.SourceGenerators.Abstractions.Rule;
|
||||
|
||||
/// <summary>
|
||||
/// 标记该类需要自动实现 IContextAware
|
||||
@ -6,4 +6,4 @@
|
||||
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
|
||||
public sealed class ContextAwareAttribute : Attribute
|
||||
{
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
namespace GFramework.SourceGenerators.Abstractions.Rule;
|
||||
namespace GFramework.Core.SourceGenerators.Abstractions.Rule;
|
||||
|
||||
/// <summary>
|
||||
/// 标记类需要自动推断并注入上下文相关字段。
|
||||
@ -6,4 +6,4 @@ namespace GFramework.SourceGenerators.Abstractions.Rule;
|
||||
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
|
||||
public sealed class GetAllAttribute : Attribute
|
||||
{
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
namespace GFramework.SourceGenerators.Abstractions.Rule;
|
||||
namespace GFramework.Core.SourceGenerators.Abstractions.Rule;
|
||||
|
||||
/// <summary>
|
||||
/// 标记字段需要自动注入单个模型实例。
|
||||
@ -6,4 +6,4 @@ namespace GFramework.SourceGenerators.Abstractions.Rule;
|
||||
[AttributeUsage(AttributeTargets.Field, Inherited = false, AllowMultiple = false)]
|
||||
public sealed class GetModelAttribute : Attribute
|
||||
{
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
namespace GFramework.SourceGenerators.Abstractions.Rule;
|
||||
namespace GFramework.Core.SourceGenerators.Abstractions.Rule;
|
||||
|
||||
/// <summary>
|
||||
/// 标记字段需要自动注入模型集合。
|
||||
@ -6,4 +6,4 @@ namespace GFramework.SourceGenerators.Abstractions.Rule;
|
||||
[AttributeUsage(AttributeTargets.Field, Inherited = false, AllowMultiple = false)]
|
||||
public sealed class GetModelsAttribute : Attribute
|
||||
{
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
namespace GFramework.SourceGenerators.Abstractions.Rule;
|
||||
namespace GFramework.Core.SourceGenerators.Abstractions.Rule;
|
||||
|
||||
/// <summary>
|
||||
/// 标记字段需要自动注入单个服务实例。
|
||||
@ -6,4 +6,4 @@ namespace GFramework.SourceGenerators.Abstractions.Rule;
|
||||
[AttributeUsage(AttributeTargets.Field, Inherited = false, AllowMultiple = false)]
|
||||
public sealed class GetServiceAttribute : Attribute
|
||||
{
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
namespace GFramework.SourceGenerators.Abstractions.Rule;
|
||||
namespace GFramework.Core.SourceGenerators.Abstractions.Rule;
|
||||
|
||||
/// <summary>
|
||||
/// 标记字段需要自动注入服务集合。
|
||||
@ -6,4 +6,4 @@ namespace GFramework.SourceGenerators.Abstractions.Rule;
|
||||
[AttributeUsage(AttributeTargets.Field, Inherited = false, AllowMultiple = false)]
|
||||
public sealed class GetServicesAttribute : Attribute
|
||||
{
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
namespace GFramework.SourceGenerators.Abstractions.Rule;
|
||||
namespace GFramework.Core.SourceGenerators.Abstractions.Rule;
|
||||
|
||||
/// <summary>
|
||||
/// 标记字段需要自动注入单个系统实例。
|
||||
@ -6,4 +6,4 @@ namespace GFramework.SourceGenerators.Abstractions.Rule;
|
||||
[AttributeUsage(AttributeTargets.Field, Inherited = false, AllowMultiple = false)]
|
||||
public sealed class GetSystemAttribute : Attribute
|
||||
{
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,23 @@
|
||||
namespace GFramework.Core.SourceGenerators.Abstractions.Rule;
|
||||
|
||||
/// <summary>
|
||||
/// 标记字段需要自动注入系统集合。
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Source Generator 会为标记字段生成从当前架构上下文收集系统实例的注入代码,用于避免在组件内部重复书写
|
||||
/// <c>GetSystems()</c> 一类的样板访问逻辑。
|
||||
/// 被标记字段应声明为可承载多个系统实例的类型,例如 <c>IEnumerable<ISystem></c> 或兼容集合接口。
|
||||
/// </remarks>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// public partial class CombatPanel : IContextAware
|
||||
/// {
|
||||
/// [GetSystems]
|
||||
/// private IEnumerable<ISystem> _systems = Array.Empty<ISystem>();
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
[AttributeUsage(AttributeTargets.Field, Inherited = false, AllowMultiple = false)]
|
||||
public sealed class GetSystemsAttribute : Attribute
|
||||
{
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
namespace GFramework.SourceGenerators.Abstractions.Rule;
|
||||
namespace GFramework.Core.SourceGenerators.Abstractions.Rule;
|
||||
|
||||
/// <summary>
|
||||
/// 标记字段需要自动注入工具集合。
|
||||
@ -6,4 +6,4 @@ namespace GFramework.SourceGenerators.Abstractions.Rule;
|
||||
[AttributeUsage(AttributeTargets.Field, Inherited = false, AllowMultiple = false)]
|
||||
public sealed class GetUtilitiesAttribute : Attribute
|
||||
{
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
namespace GFramework.SourceGenerators.Abstractions.Rule;
|
||||
namespace GFramework.Core.SourceGenerators.Abstractions.Rule;
|
||||
|
||||
/// <summary>
|
||||
/// 标记字段需要自动注入单个工具实例。
|
||||
@ -6,4 +6,4 @@ namespace GFramework.SourceGenerators.Abstractions.Rule;
|
||||
[AttributeUsage(AttributeTargets.Field, Inherited = false, AllowMultiple = false)]
|
||||
public sealed class GetUtilityAttribute : Attribute
|
||||
{
|
||||
}
|
||||
}
|
||||
@ -18,16 +18,6 @@
|
||||
GF_ContextRegistration_001 | GFramework.SourceGenerators.rule | Warning | ContextRegistrationDiagnostics
|
||||
GF_ContextRegistration_002 | 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_002 | GFramework.SourceGenerators.Architecture | Error | AutoRegisterModuleDiagnostics
|
||||
GF_AutoModule_003 | GFramework.SourceGenerators.Architecture | Error | AutoRegisterModuleDiagnostics
|
||||
@ -1,10 +1,9 @@
|
||||
using System.Collections.Immutable;
|
||||
using GFramework.Core.SourceGenerators.Diagnostics;
|
||||
using GFramework.SourceGenerators.Common.Constants;
|
||||
using GFramework.SourceGenerators.Diagnostics;
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
using Microsoft.CodeAnalysis.Operations;
|
||||
|
||||
namespace GFramework.SourceGenerators.Analyzers;
|
||||
namespace GFramework.Core.SourceGenerators.Analyzers;
|
||||
|
||||
/// <summary>
|
||||
/// 分析 Context Get 使用点是否能在所属架构中找到静态可见的 Model、System、Utility 注册。
|
||||
@ -12,6 +11,17 @@ namespace GFramework.SourceGenerators.Analyzers;
|
||||
[DiagnosticAnalyzer(LanguageNames.CSharp)]
|
||||
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>
|
||||
@ -58,10 +68,12 @@ public sealed class ContextRegistrationAnalyzer : DiagnosticAnalyzer
|
||||
if (context.Node is not VariableDeclaratorSyntax variableDeclarator)
|
||||
return;
|
||||
|
||||
if (context.SemanticModel.GetDeclaredSymbol(variableDeclarator, context.CancellationToken) is not IFieldSymbol fieldSymbol)
|
||||
if (context.SemanticModel.GetDeclaredSymbol(variableDeclarator, context.CancellationToken) is not IFieldSymbol
|
||||
fieldSymbol)
|
||||
return;
|
||||
|
||||
if (!TryCreateBindingRequest(fieldSymbol, variableDeclarator.Identifier.GetLocation(), symbols, out var request))
|
||||
if (!TryCreateBindingRequest(fieldSymbol, variableDeclarator.Identifier.GetLocation(), symbols,
|
||||
out var request))
|
||||
return;
|
||||
|
||||
ReportMissingRegistration(context, registrationIndex, request);
|
||||
@ -272,17 +284,6 @@ public sealed class ContextRegistrationAnalyzer : DiagnosticAnalyzer
|
||||
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
|
||||
{
|
||||
Model,
|
||||
@ -368,22 +369,35 @@ public sealed class ContextRegistrationAnalyzer : DiagnosticAnalyzer
|
||||
{
|
||||
return new SymbolCache(
|
||||
compilation.GetTypeByMetadataName($"{PathContests.CoreNamespace}.Architectures.Architecture"),
|
||||
compilation.GetTypeByMetadataName($"{PathContests.CoreAbstractionsNamespace}.Architectures.IArchitecture"),
|
||||
compilation.GetTypeByMetadataName($"{PathContests.CoreAbstractionsNamespace}.Architectures.IArchitectureModule"),
|
||||
compilation.GetTypeByMetadataName($"{PathContests.CoreAbstractionsNamespace}.Architectures.IArchitectureContext"),
|
||||
compilation.GetTypeByMetadataName(
|
||||
$"{PathContests.CoreAbstractionsNamespace}.Architectures.IArchitecture"),
|
||||
compilation.GetTypeByMetadataName(
|
||||
$"{PathContests.CoreAbstractionsNamespace}.Architectures.IArchitectureModule"),
|
||||
compilation.GetTypeByMetadataName(
|
||||
$"{PathContests.CoreAbstractionsNamespace}.Architectures.IArchitectureContext"),
|
||||
compilation.GetTypeByMetadataName("System.Collections.Generic.IReadOnlyList`1"),
|
||||
compilation.GetTypeByMetadataName($"{PathContests.CoreNamespace}.Extensions.ContextAwareServiceExtensions"),
|
||||
compilation.GetTypeByMetadataName($"{PathContests.SourceGeneratorsAbstractionsPath}.Rule.GetModelAttribute"),
|
||||
compilation.GetTypeByMetadataName($"{PathContests.SourceGeneratorsAbstractionsPath}.Rule.GetModelsAttribute"),
|
||||
compilation.GetTypeByMetadataName($"{PathContests.SourceGeneratorsAbstractionsPath}.Rule.GetSystemAttribute"),
|
||||
compilation.GetTypeByMetadataName($"{PathContests.SourceGeneratorsAbstractionsPath}.Rule.GetSystemsAttribute"),
|
||||
compilation.GetTypeByMetadataName($"{PathContests.SourceGeneratorsAbstractionsPath}.Rule.GetUtilityAttribute"),
|
||||
compilation.GetTypeByMetadataName($"{PathContests.SourceGeneratorsAbstractionsPath}.Rule.GetUtilitiesAttribute"));
|
||||
compilation.GetTypeByMetadataName(
|
||||
$"{PathContests.CoreNamespace}.Extensions.ContextAwareServiceExtensions"),
|
||||
compilation.GetTypeByMetadataName(
|
||||
$"{PathContests.SourceGeneratorsAbstractionsPath}.Rule.GetModelAttribute"),
|
||||
compilation.GetTypeByMetadataName(
|
||||
$"{PathContests.SourceGeneratorsAbstractionsPath}.Rule.GetModelsAttribute"),
|
||||
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 readonly Compilation _compilation;
|
||||
private readonly IReadOnlyDictionary<INamedTypeSymbol, ArchitectureRegistrationData> _registrations;
|
||||
|
||||
private RegistrationIndex(
|
||||
Compilation compilation,
|
||||
IReadOnlyDictionary<INamedTypeSymbol, ArchitectureRegistrationData> registrations)
|
||||
@ -392,14 +406,12 @@ public sealed class ContextRegistrationAnalyzer : DiagnosticAnalyzer
|
||||
_registrations = registrations;
|
||||
}
|
||||
|
||||
private readonly Compilation _compilation;
|
||||
private readonly IReadOnlyDictionary<INamedTypeSymbol, ArchitectureRegistrationData> _registrations;
|
||||
|
||||
public static RegistrationIndex Build(
|
||||
Compilation compilation,
|
||||
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))
|
||||
{
|
||||
@ -690,7 +702,8 @@ public sealed class ContextRegistrationAnalyzer : DiagnosticAnalyzer
|
||||
return helperMethod.DeclaringSyntaxReferences.Length > 0;
|
||||
}
|
||||
|
||||
helperMethod = SymbolHelpers.ResolveHierarchyMethodImplementation(targetMethod, concreteType) ?? targetMethod;
|
||||
helperMethod = SymbolHelpers.ResolveHierarchyMethodImplementation(targetMethod, concreteType) ??
|
||||
targetMethod;
|
||||
return helperMethod.DeclaringSyntaxReferences.Length > 0;
|
||||
}
|
||||
}
|
||||
@ -813,9 +826,12 @@ public sealed class ContextRegistrationAnalyzer : DiagnosticAnalyzer
|
||||
if (SymbolEqualityComparer.Default.Equals(candidate.OriginalDefinition, method.OriginalDefinition))
|
||||
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;
|
||||
}
|
||||
}
|
||||
@ -1,10 +1,8 @@
|
||||
using System.Collections.Immutable;
|
||||
using GFramework.SourceGenerators.Diagnostics;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using GFramework.Core.SourceGenerators.Diagnostics;
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
using Microsoft.CodeAnalysis.Operations;
|
||||
|
||||
namespace GFramework.SourceGenerators.Analyzers;
|
||||
namespace GFramework.Core.SourceGenerators.Analyzers;
|
||||
|
||||
/// <summary>
|
||||
/// 优先级使用分析器,检测应该使用 GetAllByPriority 而非 GetAll 的场景
|
||||
@ -111,4 +109,4 @@ public sealed class PriorityUsageAnalyzer : DiagnosticAnalyzer
|
||||
{
|
||||
return type.AllInterfaces.Any(i => SymbolEqualityComparer.Default.Equals(i, interfaceType));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,10 +1,10 @@
|
||||
using GFramework.SourceGenerators.Abstractions.Architectures;
|
||||
using GFramework.Core.SourceGenerators.Abstractions.Architectures;
|
||||
using GFramework.Core.SourceGenerators.Diagnostics;
|
||||
using GFramework.SourceGenerators.Common.Constants;
|
||||
using GFramework.SourceGenerators.Common.Diagnostics;
|
||||
using GFramework.SourceGenerators.Common.Extensions;
|
||||
using GFramework.SourceGenerators.Diagnostics;
|
||||
|
||||
namespace GFramework.SourceGenerators.Architectures;
|
||||
namespace GFramework.Core.SourceGenerators.Architectures;
|
||||
|
||||
/// <summary>
|
||||
/// 为标记了 <see cref="AutoRegisterModuleAttribute" /> 的模块生成固定顺序的组件注册代码。
|
||||
@ -1,12 +1,8 @@
|
||||
using System.Text;
|
||||
using GFramework.Core.SourceGenerators.Diagnostics;
|
||||
using GFramework.SourceGenerators.Common.Constants;
|
||||
using GFramework.SourceGenerators.Common.Generator;
|
||||
using GFramework.SourceGenerators.Diagnostics;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
|
||||
namespace GFramework.SourceGenerators.Bases;
|
||||
namespace GFramework.Core.SourceGenerators.Bases;
|
||||
|
||||
/// <summary>
|
||||
/// Priority 特性生成器,为标记了 [Priority] 的类自动生成 IPrioritized 接口实现
|
||||
@ -150,4 +146,4 @@ public sealed class PriorityGenerator : MetadataAttributeClassGeneratorBase
|
||||
|
||||
return $"{metadataName}.Priority.g.cs";
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,6 @@
|
||||
using GFramework.SourceGenerators.Common.Constants;
|
||||
|
||||
namespace GFramework.SourceGenerators.Diagnostics;
|
||||
namespace GFramework.Core.SourceGenerators.Diagnostics;
|
||||
|
||||
internal static class AutoRegisterModuleDiagnostics
|
||||
{
|
||||
@ -1,6 +1,4 @@
|
||||
using Microsoft.CodeAnalysis;
|
||||
|
||||
namespace GFramework.SourceGenerators.Diagnostics;
|
||||
namespace GFramework.Core.SourceGenerators.Diagnostics;
|
||||
|
||||
/// <summary>
|
||||
/// 提供与上下文感知相关的诊断规则定义
|
||||
@ -18,4 +16,4 @@ public static class ContextAwareDiagnostic
|
||||
DiagnosticSeverity.Error,
|
||||
true
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,6 @@
|
||||
using GFramework.SourceGenerators.Common.Constants;
|
||||
|
||||
namespace GFramework.SourceGenerators.Diagnostics;
|
||||
namespace GFramework.Core.SourceGenerators.Diagnostics;
|
||||
|
||||
/// <summary>
|
||||
/// 提供 Context Get 注入生成器相关诊断。
|
||||
@ -96,4 +96,4 @@ public static class ContextGetDiagnostics
|
||||
SourceGeneratorsRuleCategory,
|
||||
DiagnosticSeverity.Error,
|
||||
true);
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,6 @@
|
||||
using GFramework.SourceGenerators.Common.Constants;
|
||||
|
||||
namespace GFramework.SourceGenerators.Diagnostics;
|
||||
namespace GFramework.Core.SourceGenerators.Diagnostics;
|
||||
|
||||
/// <summary>
|
||||
/// 提供 Context Get 注册可见性分析相关诊断。
|
||||
@ -1,6 +1,4 @@
|
||||
using Microsoft.CodeAnalysis;
|
||||
|
||||
namespace GFramework.SourceGenerators.Diagnostics;
|
||||
namespace GFramework.Core.SourceGenerators.Diagnostics;
|
||||
|
||||
/// <summary>
|
||||
/// 提供诊断描述符的静态类,用于GFramework日志生成器的编译时检查
|
||||
@ -18,4 +16,4 @@ internal static class LoggerDiagnostics
|
||||
"GFramework.Godot.Logging",
|
||||
DiagnosticSeverity.Warning,
|
||||
true);
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,4 @@
|
||||
using Microsoft.CodeAnalysis;
|
||||
|
||||
namespace GFramework.SourceGenerators.Diagnostics;
|
||||
namespace GFramework.Core.SourceGenerators.Diagnostics;
|
||||
|
||||
/// <summary>
|
||||
/// Priority 特性相关的诊断信息
|
||||
@ -86,4 +84,4 @@ internal static class PriorityDiagnostic
|
||||
isEnabledByDefault: true,
|
||||
description: "当获取实现了 IPrioritized 接口的服务时,应使用 GetAllByPriority 方法以确保按优先级排序."
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,204 @@
|
||||
using GFramework.Core.SourceGenerators.Abstractions.Enums;
|
||||
using GFramework.SourceGenerators.Common.Constants;
|
||||
using GFramework.SourceGenerators.Common.Diagnostics;
|
||||
using GFramework.SourceGenerators.Common.Generator;
|
||||
|
||||
namespace GFramework.Core.SourceGenerators.Enums;
|
||||
|
||||
/// <summary>
|
||||
/// 枚举扩展方法生成器,用于自动生成枚举相关的扩展方法
|
||||
/// </summary>
|
||||
[Generator]
|
||||
public sealed class EnumExtensionsGenerator : AttributeEnumGeneratorBase
|
||||
{
|
||||
private static string AttributeMetadataName =>
|
||||
$"{PathContests.SourceGeneratorsAbstractionsPath}.Enums.GenerateEnumExtensionsAttribute";
|
||||
|
||||
/// <summary>
|
||||
/// 仅用于 Syntax 粗筛选
|
||||
/// </summary>
|
||||
protected override string AttributeShortNameWithoutSuffix => "GenerateEnumExtensions";
|
||||
|
||||
/// <summary>
|
||||
/// 按元数据名称解析枚举上的 <c>GenerateEnumExtensionsAttribute</c>。
|
||||
/// </summary>
|
||||
/// <param name="compilation">当前编译上下文。</param>
|
||||
/// <param name="symbol">待检查的枚举符号。</param>
|
||||
/// <returns>匹配到的属性数据;若未标注目标属性则返回 <see langword="null" />。</returns>
|
||||
protected override AttributeData? ResolveAttribute(Compilation compilation, INamedTypeSymbol symbol)
|
||||
{
|
||||
var attrSymbol = compilation.GetTypeByMetadataName(AttributeMetadataName);
|
||||
|
||||
if (attrSymbol is null)
|
||||
return null;
|
||||
|
||||
return symbol.GetAttributes()
|
||||
.FirstOrDefault(a =>
|
||||
SymbolEqualityComparer.Default.Equals(a.AttributeClass, attrSymbol));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证候选符号仍然是可生成扩展方法的枚举类型。
|
||||
/// </summary>
|
||||
/// <param name="context">源生成诊断上下文。</param>
|
||||
/// <param name="compilation">当前编译上下文。</param>
|
||||
/// <param name="syntax">候选枚举声明语法。</param>
|
||||
/// <param name="symbol">候选命名类型符号。</param>
|
||||
/// <param name="attr">已解析出的生成器属性。</param>
|
||||
/// <returns>当符号满足生成前置条件时返回 <see langword="true" />。</returns>
|
||||
protected override bool ValidateSymbol(SourceProductionContext context, Compilation compilation,
|
||||
EnumDeclarationSyntax syntax,
|
||||
INamedTypeSymbol symbol, AttributeData attr)
|
||||
{
|
||||
if (symbol.TypeKind == TypeKind.Enum) return true;
|
||||
var loc = syntax.Identifier.GetLocation();
|
||||
context.ReportDiagnostic(Diagnostic.Create(
|
||||
CommonDiagnostics.ClassMustBePartial,
|
||||
loc,
|
||||
symbol.Name
|
||||
));
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 生成枚举扩展方法的源代码
|
||||
/// </summary>
|
||||
/// <param name="symbol">枚举类型符号</param>
|
||||
/// <param name="attr">属性数据</param>
|
||||
/// <returns>生成的源代码字符串</returns>
|
||||
protected override string Generate(INamedTypeSymbol symbol, AttributeData attr)
|
||||
{
|
||||
var ns = symbol.ContainingNamespace.IsGlobalNamespace
|
||||
? null
|
||||
: symbol.ContainingNamespace.ToDisplayString();
|
||||
|
||||
var generateIsMethods = GetNamedBooleanArgument(
|
||||
attr,
|
||||
nameof(GenerateEnumExtensionsAttribute.GenerateIsMethods),
|
||||
true);
|
||||
var generateIsInMethod = GetNamedBooleanArgument(
|
||||
attr,
|
||||
nameof(GenerateEnumExtensionsAttribute.GenerateIsInMethod),
|
||||
true);
|
||||
var enumName = symbol.Name;
|
||||
var fullEnumName = symbol.ToDisplayString();
|
||||
var members = symbol.GetMembers()
|
||||
.OfType<IFieldSymbol>()
|
||||
.Where(f => f.ConstantValue != null)
|
||||
.ToArray();
|
||||
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendLine("// <auto-generated />");
|
||||
sb.AppendLine("using System;");
|
||||
|
||||
sb.AppendLine(!string.IsNullOrEmpty(ns) ? $"namespace {ns}" : "namespace EnumExtensionsGenerated");
|
||||
|
||||
sb.AppendLine("{");
|
||||
|
||||
sb.AppendLine($" public static partial class {enumName}Extensions");
|
||||
sb.AppendLine(" {");
|
||||
|
||||
// 两个生成开关是彼此独立的契约,需要分别控制输出,并保持空行布局稳定,便于快照精确回归。
|
||||
var hasGeneratedMembers = false;
|
||||
|
||||
if (generateIsMethods)
|
||||
{
|
||||
hasGeneratedMembers = AppendIsMethods(
|
||||
sb,
|
||||
members,
|
||||
fullEnumName);
|
||||
}
|
||||
|
||||
if (generateIsInMethod)
|
||||
{
|
||||
if (hasGeneratedMembers)
|
||||
{
|
||||
sb.AppendLine();
|
||||
}
|
||||
|
||||
AppendIsInMethod(
|
||||
sb,
|
||||
fullEnumName);
|
||||
}
|
||||
|
||||
sb.AppendLine(" }");
|
||||
sb.AppendLine("}"); // namespace
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取生成文件的提示名称
|
||||
/// </summary>
|
||||
/// <param name="symbol">命名类型符号</param>
|
||||
/// <returns>生成文件的提示名称</returns>
|
||||
protected override string GetHintName(INamedTypeSymbol symbol)
|
||||
{
|
||||
return $"{symbol.Name}.EnumExtensions.g.cs";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 读取属性上的命名布尔参数,并在参数未显式提供时回退到属性契约默认值。
|
||||
/// </summary>
|
||||
/// <param name="attribute">待读取的属性数据。</param>
|
||||
/// <param name="argumentName">命名参数名称。</param>
|
||||
/// <param name="defaultValue">属性未提供该参数时使用的默认值。</param>
|
||||
/// <returns>解析得到的布尔值;若参数缺失或类型不匹配则返回 <paramref name="defaultValue" />。</returns>
|
||||
private static bool GetNamedBooleanArgument(AttributeData attribute, string argumentName, bool defaultValue)
|
||||
{
|
||||
foreach (var namedArgument in attribute.NamedArguments)
|
||||
{
|
||||
if (namedArgument.Key == argumentName &&
|
||||
namedArgument.Value.Value is bool value)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 为每个枚举成员追加单值判断扩展方法。
|
||||
/// </summary>
|
||||
/// <param name="builder">目标源码构建器。</param>
|
||||
/// <param name="members">需要生成扩展方法的枚举成员。</param>
|
||||
/// <param name="fullEnumName">枚举的完整类型名。</param>
|
||||
/// <returns>当至少生成了一个方法时返回 <see langword="true" />。</returns>
|
||||
private static bool AppendIsMethods(StringBuilder builder, IEnumerable<IFieldSymbol> members, string fullEnumName)
|
||||
{
|
||||
var hasGeneratedMembers = false;
|
||||
|
||||
foreach (var memberName in members.Select(m => m.Name))
|
||||
{
|
||||
if (hasGeneratedMembers)
|
||||
{
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
builder.AppendLine($" /// <summary>是否为 {memberName}</summary>");
|
||||
builder.AppendLine(
|
||||
$" public static bool Is{memberName}(this {fullEnumName} value) => value == {fullEnumName}.{memberName};");
|
||||
hasGeneratedMembers = true;
|
||||
}
|
||||
|
||||
return hasGeneratedMembers;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 追加用于多值匹配的 <c>IsIn</c> 扩展方法。
|
||||
/// </summary>
|
||||
/// <param name="builder">目标源码构建器。</param>
|
||||
/// <param name="fullEnumName">枚举的完整类型名。</param>
|
||||
private static void AppendIsInMethod(StringBuilder builder, string fullEnumName)
|
||||
{
|
||||
builder.AppendLine(" /// <summary>判断是否属于指定集合</summary>");
|
||||
builder.AppendLine(
|
||||
$" public static bool IsIn(this {fullEnumName} value, params {fullEnumName}[] values)");
|
||||
builder.AppendLine(" {");
|
||||
builder.AppendLine(" if (values == null) return false;");
|
||||
builder.AppendLine(" foreach (var v in values) if (value == v) return true;");
|
||||
builder.AppendLine(" return false;");
|
||||
builder.AppendLine(" }");
|
||||
}
|
||||
}
|
||||
@ -30,7 +30,7 @@
|
||||
<!-- Generator 编译期引用 SourceGenerators.Abstractions / Common / Core.Abstractions,但不打包 -->
|
||||
<ItemGroup>
|
||||
<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>
|
||||
@ -63,6 +63,6 @@
|
||||
<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.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>
|
||||
</Project>
|
||||
@ -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>
|
||||
@ -24,4 +24,4 @@ global using Microsoft.CodeAnalysis.Text;
|
||||
global using System.Globalization;
|
||||
global using System.IO;
|
||||
global using System.Text;
|
||||
global using System.Text.Json;
|
||||
global using System.Text.Json;
|
||||
@ -1,12 +1,9 @@
|
||||
using System.Text;
|
||||
using GFramework.SourceGenerators.Abstractions.Logging;
|
||||
using GFramework.Core.SourceGenerators.Abstractions.Logging;
|
||||
using GFramework.SourceGenerators.Common.Constants;
|
||||
using GFramework.SourceGenerators.Common.Extensions;
|
||||
using GFramework.SourceGenerators.Common.Generator;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
|
||||
namespace GFramework.SourceGenerators.Logging;
|
||||
namespace GFramework.Core.SourceGenerators.Logging;
|
||||
|
||||
/// <summary>
|
||||
/// 日志生成器,用于为标记了LogAttribute的类自动生成日志字段
|
||||
@ -97,4 +94,4 @@ public sealed class LoggerGenerator : TypeAttributeClassGeneratorBase
|
||||
{
|
||||
return $"{symbol.Name}.Logger.g.cs";
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,13 +1,9 @@
|
||||
using System.Text;
|
||||
using GFramework.Core.SourceGenerators.Diagnostics;
|
||||
using GFramework.SourceGenerators.Common.Constants;
|
||||
using GFramework.SourceGenerators.Common.Diagnostics;
|
||||
using GFramework.SourceGenerators.Common.Generator;
|
||||
using GFramework.SourceGenerators.Diagnostics;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
|
||||
namespace GFramework.SourceGenerators.Rule;
|
||||
namespace GFramework.Core.SourceGenerators.Rule;
|
||||
|
||||
/// <summary>
|
||||
/// 上下文感知生成器,用于为标记了ContextAware特性的类自动生成IContextAware接口实现
|
||||
@ -251,4 +247,4 @@ public sealed class ContextAwareGenerator : MetadataAttributeClassGeneratorBase
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,11 +1,10 @@
|
||||
using System.Text;
|
||||
using GFramework.Core.SourceGenerators.Diagnostics;
|
||||
using GFramework.SourceGenerators.Common.Constants;
|
||||
using GFramework.SourceGenerators.Common.Diagnostics;
|
||||
using GFramework.SourceGenerators.Common.Extensions;
|
||||
using GFramework.SourceGenerators.Common.Info;
|
||||
using GFramework.SourceGenerators.Diagnostics;
|
||||
|
||||
namespace GFramework.SourceGenerators.Rule;
|
||||
namespace GFramework.Core.SourceGenerators.Rule;
|
||||
|
||||
/// <summary>
|
||||
/// 为上下文感知类生成 Core 上下文 Get 注入方法。
|
||||
@ -894,4 +893,4 @@ public sealed class ContextGetGenerator : IIncrementalGenerator
|
||||
public List<FieldCandidateInfo> FieldCandidates { get; } = [];
|
||||
public ClassDeclarationSyntax? GetAllDeclaration { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -78,28 +78,6 @@ public class ArchitectureModulesBehaviorTests
|
||||
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>
|
||||
|
||||
@ -206,12 +206,6 @@ public class TestArchitectureWithRegistry : IArchitecture
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
[Obsolete("Use RegisterCqrsPipelineBehavior<TBehavior>() instead.")]
|
||||
public void RegisterMediatorBehavior<TBehavior>() where TBehavior : class
|
||||
{
|
||||
RegisterCqrsPipelineBehavior<TBehavior>();
|
||||
}
|
||||
|
||||
public IArchitectureModule InstallModule(IArchitectureModule module)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
@ -357,12 +351,6 @@ public class TestArchitectureWithoutRegistry : IArchitecture
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
[Obsolete("Use RegisterCqrsPipelineBehavior<TBehavior>() instead.")]
|
||||
public void RegisterMediatorBehavior<TBehavior>() where TBehavior : class
|
||||
{
|
||||
RegisterCqrsPipelineBehavior<TBehavior>();
|
||||
}
|
||||
|
||||
public IArchitectureModule InstallModule(IArchitectureModule module)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
|
||||
@ -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));
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -21,9 +21,11 @@
|
||||
<ProjectReference Include="..\GFramework.Tests.Common\GFramework.Tests.Common.csproj"/>
|
||||
<ProjectReference Include="..\GFramework.Core.Abstractions\GFramework.Core.Abstractions.csproj"/>
|
||||
<ProjectReference Include="..\GFramework.Core\GFramework.Core.csproj"/>
|
||||
<ProjectReference Include="..\GFramework.SourceGenerators.Abstractions\GFramework.SourceGenerators.Abstractions.csproj"/>
|
||||
<ProjectReference Include="..\GFramework.SourceGenerators.Common\GFramework.SourceGenerators.Common.csproj"/>
|
||||
<ProjectReference Include="..\GFramework.SourceGenerators\GFramework.SourceGenerators.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false"/>
|
||||
<ProjectReference Include="..\GFramework.Core.SourceGenerators.Abstractions\GFramework.Core.SourceGenerators.Abstractions.csproj"/>
|
||||
<ProjectReference Include="..\GFramework.SourceGenerators.Common\GFramework.SourceGenerators.Common.csproj"
|
||||
OutputItemType="Analyzer"
|
||||
ReferenceOutputAssembly="false"/>
|
||||
<ProjectReference Include="..\GFramework.Core.SourceGenerators\GFramework.Core.SourceGenerators.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false"/>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
using System.ComponentModel;
|
||||
using System.Reflection;
|
||||
using GFramework.Core.Abstractions.Architectures;
|
||||
using GFramework.Core.Abstractions.Enums;
|
||||
@ -8,7 +7,6 @@ using GFramework.Core.Abstractions.Model;
|
||||
using GFramework.Core.Abstractions.Systems;
|
||||
using GFramework.Core.Abstractions.Utility;
|
||||
using GFramework.Core.Environment;
|
||||
using GFramework.Core.Logging;
|
||||
|
||||
namespace GFramework.Core.Architectures;
|
||||
|
||||
@ -156,20 +154,6 @@ public abstract class Architecture : IArchitecture
|
||||
_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>
|
||||
/// 从指定程序集显式注册 CQRS 处理器。
|
||||
/// 该入口适用于把拆分到其他模块或扩展包程序集中的 handlers 接入当前架构。
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
using System.ComponentModel;
|
||||
using System.Reflection;
|
||||
using GFramework.Core.Abstractions.Architectures;
|
||||
using GFramework.Core.Abstractions.Logging;
|
||||
@ -25,20 +24,6 @@ internal sealed class ArchitectureModules(
|
||||
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>
|
||||
/// 从指定程序集显式注册 CQRS 处理器。
|
||||
/// 该入口用于把默认架构程序集之外的扩展处理器接入当前架构容器。
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,3 @@
|
||||
using System.ComponentModel;
|
||||
using System.Reflection;
|
||||
using GFramework.Core.Abstractions.Bases;
|
||||
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>
|
||||
/// 从指定程序集显式注册 CQRS 处理器。
|
||||
/// </summary>
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
using GFramework.SourceGenerators.Common.Constants;
|
||||
|
||||
namespace GFramework.SourceGenerators.Cqrs;
|
||||
namespace GFramework.Cqrs.SourceGenerators.Cqrs;
|
||||
|
||||
/// <summary>
|
||||
/// 为当前编译程序集生成 CQRS 处理器注册器,以减少运行时的程序集反射扫描成本。
|
||||
@ -337,10 +337,17 @@ public sealed class CqrsHandlerRegistryGenerator : IIncrementalGenerator
|
||||
return true;
|
||||
}
|
||||
|
||||
if (type is INamedTypeSymbol namedType &&
|
||||
SymbolEqualityComparer.Default.Equals(namedType.ContainingAssembly, compilation.Assembly))
|
||||
if (type is INamedTypeSymbol namedType)
|
||||
{
|
||||
runtimeTypeReference = RuntimeTypeReferenceSpec.FromReflectionLookup(
|
||||
if (SymbolEqualityComparer.Default.Equals(namedType.ContainingAssembly, compilation.Assembly))
|
||||
{
|
||||
runtimeTypeReference = RuntimeTypeReferenceSpec.FromReflectionLookup(
|
||||
GetReflectionTypeMetadataName(namedType));
|
||||
return true;
|
||||
}
|
||||
|
||||
runtimeTypeReference = RuntimeTypeReferenceSpec.FromExternalReflectionLookup(
|
||||
namedType.ContainingAssembly.Identity.ToString(),
|
||||
GetReflectionTypeMetadataName(namedType));
|
||||
return true;
|
||||
}
|
||||
@ -388,8 +395,10 @@ public sealed class CqrsHandlerRegistryGenerator : IIncrementalGenerator
|
||||
return true;
|
||||
}
|
||||
|
||||
genericTypeDefinitionReference = null;
|
||||
return false;
|
||||
genericTypeDefinitionReference = RuntimeTypeReferenceSpec.FromExternalReflectionLookup(
|
||||
genericTypeDefinition.ContainingAssembly.Identity.ToString(),
|
||||
GetReflectionTypeMetadataName(genericTypeDefinition));
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool CanReferenceFromGeneratedRegistry(Compilation compilation, ITypeSymbol type)
|
||||
@ -492,6 +501,9 @@ public sealed class CqrsHandlerRegistryGenerator : IIncrementalGenerator
|
||||
!registration.PreciseReflectedRegistrations.IsDefaultOrEmpty);
|
||||
var hasRuntimeInterfaceDiscovery = registrations.Any(static registration =>
|
||||
registration.RequiresRuntimeInterfaceDiscovery);
|
||||
var hasExternalAssemblyTypeLookups = registrations.Any(static registration =>
|
||||
registration.PreciseReflectedRegistrations.Any(static preciseRegistration =>
|
||||
preciseRegistration.ServiceTypeArguments.Any(ContainsExternalAssemblyTypeLookup)));
|
||||
var builder = new StringBuilder();
|
||||
builder.AppendLine("// <auto-generated />");
|
||||
builder.AppendLine("#nullable enable");
|
||||
@ -556,10 +568,13 @@ public sealed class CqrsHandlerRegistryGenerator : IIncrementalGenerator
|
||||
|
||||
builder.AppendLine(" }");
|
||||
|
||||
if (hasRuntimeInterfaceDiscovery)
|
||||
if (hasRuntimeInterfaceDiscovery || hasExternalAssemblyTypeLookups)
|
||||
{
|
||||
builder.AppendLine();
|
||||
AppendReflectionHelpers(builder);
|
||||
AppendReflectionHelpers(
|
||||
builder,
|
||||
hasRuntimeInterfaceDiscovery,
|
||||
hasExternalAssemblyTypeLookups);
|
||||
}
|
||||
|
||||
builder.AppendLine("}");
|
||||
@ -910,14 +925,79 @@ public sealed class CqrsHandlerRegistryGenerator : IIncrementalGenerator
|
||||
builder.Append(indent);
|
||||
builder.Append("var ");
|
||||
builder.Append(variableBaseName);
|
||||
builder.Append(" = registryAssembly.GetType(\"");
|
||||
builder.Append(EscapeStringLiteral(reflectionTypeMetadataName));
|
||||
builder.AppendLine("\", throwOnError: false, ignoreCase: false);");
|
||||
if (string.IsNullOrWhiteSpace(runtimeTypeReference.ReflectionAssemblyName))
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
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 assemblyIdentity, string typeMetadataName)");
|
||||
builder.AppendLine(" {");
|
||||
builder.AppendLine(" var assembly = ResolveReferencedAssembly(assemblyIdentity);");
|
||||
builder.AppendLine(
|
||||
" return assembly?.GetType(typeMetadataName, throwOnError: false, ignoreCase: false);");
|
||||
builder.AppendLine(" }");
|
||||
builder.AppendLine();
|
||||
builder.AppendLine(
|
||||
" private static global::System.Reflection.Assembly? ResolveReferencedAssembly(string assemblyIdentity)");
|
||||
builder.AppendLine(" {");
|
||||
builder.AppendLine(" global::System.Reflection.AssemblyName targetAssemblyName;");
|
||||
builder.AppendLine(" try");
|
||||
builder.AppendLine(" {");
|
||||
builder.AppendLine(
|
||||
" targetAssemblyName = new global::System.Reflection.AssemblyName(assemblyIdentity);");
|
||||
builder.AppendLine(" }");
|
||||
builder.AppendLine(" catch");
|
||||
builder.AppendLine(" {");
|
||||
builder.AppendLine(" return null;");
|
||||
builder.AppendLine(" }");
|
||||
builder.AppendLine();
|
||||
builder.AppendLine(
|
||||
" foreach (var assembly in global::System.AppDomain.CurrentDomain.GetAssemblies())");
|
||||
builder.AppendLine(" {");
|
||||
builder.AppendLine(
|
||||
" if (global::System.Reflection.AssemblyName.ReferenceMatchesDefinition(targetAssemblyName, assembly.GetName()))");
|
||||
builder.AppendLine(" return assembly;");
|
||||
builder.AppendLine(" }");
|
||||
builder.AppendLine();
|
||||
builder.AppendLine(" try");
|
||||
builder.AppendLine(" {");
|
||||
builder.AppendLine(
|
||||
" return global::System.Reflection.Assembly.Load(targetAssemblyName);");
|
||||
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
|
||||
// interface discovery after all direct / precise registrations have been emitted.
|
||||
builder.AppendLine(
|
||||
@ -1038,6 +1118,32 @@ public sealed class CqrsHandlerRegistryGenerator : IIncrementalGenerator
|
||||
.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(
|
||||
string HandlerInterfaceDisplayName,
|
||||
string ImplementationTypeDisplayName,
|
||||
@ -1058,6 +1164,7 @@ public sealed class CqrsHandlerRegistryGenerator : IIncrementalGenerator
|
||||
private sealed record RuntimeTypeReferenceSpec(
|
||||
string? TypeDisplayName,
|
||||
string? ReflectionTypeMetadataName,
|
||||
string? ReflectionAssemblyName,
|
||||
RuntimeTypeReferenceSpec? ArrayElementTypeReference,
|
||||
int ArrayRank,
|
||||
RuntimeTypeReferenceSpec? GenericTypeDefinitionReference,
|
||||
@ -1065,19 +1172,28 @@ public sealed class CqrsHandlerRegistryGenerator : IIncrementalGenerator
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@ -1085,7 +1201,7 @@ public sealed class CqrsHandlerRegistryGenerator : IIncrementalGenerator
|
||||
RuntimeTypeReferenceSpec genericTypeDefinitionReference,
|
||||
ImmutableArray<RuntimeTypeReferenceSpec> genericTypeArguments)
|
||||
{
|
||||
return new RuntimeTypeReferenceSpec(null, null, null, 0, genericTypeDefinitionReference,
|
||||
return new RuntimeTypeReferenceSpec(null, null, null, null, 0, genericTypeDefinitionReference,
|
||||
genericTypeArguments);
|
||||
}
|
||||
}
|
||||
@ -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>
|
||||
13
GFramework.Cqrs.SourceGenerators/GlobalUsings.cs
Normal file
13
GFramework.Cqrs.SourceGenerators/GlobalUsings.cs
Normal 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;
|
||||
@ -0,0 +1,2 @@
|
||||
; Shipped analyzer release
|
||||
; Intentionally empty until the split game source-generator package ships a stable analyzer release.
|
||||
@ -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
|
||||
@ -1,6 +1,6 @@
|
||||
using GFramework.SourceGenerators.Diagnostics;
|
||||
using GFramework.Game.SourceGenerators.Diagnostics;
|
||||
|
||||
namespace GFramework.SourceGenerators.Config;
|
||||
namespace GFramework.Game.SourceGenerators.Config;
|
||||
|
||||
/// <summary>
|
||||
/// 根据 AdditionalFiles 中的 JSON schema 生成配置类型和配置表包装。
|
||||
@ -31,7 +31,8 @@ public sealed class SchemaConfigGenerator : IIncrementalGenerator
|
||||
private const string LookupIndexReferencePropertyMessage =
|
||||
"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 />
|
||||
public void Initialize(IncrementalGeneratorInitializationContext context)
|
||||
@ -296,6 +297,7 @@ public sealed class SchemaConfigGenerator : IIncrementalGenerator
|
||||
/// <param name="property">属性 JSON 节点。</param>
|
||||
/// <param name="isRequired">属性是否必填。</param>
|
||||
/// <param name="displayPath">逻辑字段路径。</param>
|
||||
/// <param name="isDirectChildOfRoot">属性是否为根对象下的直接子属性。</param>
|
||||
/// <returns>解析后的属性信息或诊断。</returns>
|
||||
private static ParsedPropertyResult ParseProperty(
|
||||
string filePath,
|
||||
@ -953,6 +955,7 @@ public sealed class SchemaConfigGenerator : IIncrementalGenerator
|
||||
/// <param name="title">标题元数据。</param>
|
||||
/// <param name="description">说明元数据。</param>
|
||||
/// <param name="refTableName">目标引用表名称。</param>
|
||||
/// <param name="isIndexedLookup">是否为索引查找。</param>
|
||||
/// <returns>解析后的属性信息或诊断。</returns>
|
||||
private static ParsedPropertyResult ParseArrayProperty(
|
||||
string filePath,
|
||||
@ -3183,7 +3186,8 @@ public sealed class SchemaConfigGenerator : IIncrementalGenerator
|
||||
|
||||
var targets = dependency.Value
|
||||
.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()!)
|
||||
.Distinct(StringComparer.Ordinal)
|
||||
.ToArray();
|
||||
@ -1,6 +1,6 @@
|
||||
using GFramework.SourceGenerators.Common.Constants;
|
||||
|
||||
namespace GFramework.SourceGenerators.Diagnostics;
|
||||
namespace GFramework.Game.SourceGenerators.Diagnostics;
|
||||
|
||||
/// <summary>
|
||||
/// 提供配置 schema 代码生成相关诊断。
|
||||
@ -0,0 +1,56 @@
|
||||
<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>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<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>
|
||||
@ -1,8 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<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>
|
||||
<!--
|
||||
消费项目默认从 schemas/ 目录收集配置 schema。
|
||||
@ -12,15 +9,8 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<!--
|
||||
仅在 NuGet 打包布局存在时自动注入 analyzer。
|
||||
仓库内项目引用场景会通过 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.Game.SourceGenerators.dll"
|
||||
Condition="Exists('$(MSBuildThisFileDirectory)../analyzers/dotnet/cs/GFramework.Game.SourceGenerators.dll')"/>
|
||||
<Analyzer Include="$(MSBuildThisFileDirectory)../analyzers/dotnet/cs/GFramework.SourceGenerators.Common.dll"
|
||||
Condition="Exists('$(MSBuildThisFileDirectory)../analyzers/dotnet/cs/GFramework.SourceGenerators.Common.dll')"/>
|
||||
</ItemGroup>
|
||||
@ -33,8 +23,7 @@
|
||||
<AdditionalFiles Include="$(MSBuildProjectDirectory)/$(GFrameworkConfigSchemaDirectory)/**/*.schema.json"/>
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Ensure the analyzers are loaded -->
|
||||
<Target Name="EnsureGFrameworkAnalyzers" BeforeTargets="CoreCompile">
|
||||
<Message Text="Loading GFramework source generators" Importance="high"/>
|
||||
<Target Name="EnsureGFrameworkGameAnalyzers" BeforeTargets="CoreCompile">
|
||||
<Message Text="Loading GFramework.Game source generators" Importance="high"/>
|
||||
</Target>
|
||||
</Project>
|
||||
14
GFramework.Game.SourceGenerators/GlobalUsings.cs
Normal file
14
GFramework.Game.SourceGenerators/GlobalUsings.cs
Normal 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;
|
||||
@ -20,13 +20,14 @@
|
||||
<ProjectReference Include="..\GFramework.Game\GFramework.Game.csproj"/>
|
||||
<ProjectReference Include="..\GFramework.Core\GFramework.Core.csproj"/>
|
||||
<ProjectReference Include="..\GFramework.Godot\GFramework.Godot.csproj"/>
|
||||
<ProjectReference Include="..\GFramework.SourceGenerators.Abstractions\GFramework.SourceGenerators.Abstractions.csproj"
|
||||
OutputItemType="Analyzer"
|
||||
ReferenceOutputAssembly="false"/>
|
||||
<ProjectReference Include="..\GFramework.Core.SourceGenerators.Abstractions\GFramework.Core.SourceGenerators.Abstractions.csproj"/>
|
||||
<ProjectReference Include="..\GFramework.SourceGenerators.Common\GFramework.SourceGenerators.Common.csproj"
|
||||
OutputItemType="Analyzer"
|
||||
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"
|
||||
ReferenceOutputAssembly="false"/>
|
||||
</ItemGroup>
|
||||
@ -35,6 +36,6 @@
|
||||
通过仓库内的 targets 复用消费者默认约定,确保测试项目与真实消费项目一样
|
||||
自动拾取 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>
|
||||
|
||||
@ -1,9 +0,0 @@
|
||||
namespace GFramework.SourceGenerators.Abstractions.Rule;
|
||||
|
||||
/// <summary>
|
||||
/// 标记字段需要自动注入系统集合。
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Field, Inherited = false, AllowMultiple = false)]
|
||||
public sealed class GetSystemsAttribute : Attribute
|
||||
{
|
||||
}
|
||||
@ -39,7 +39,7 @@ public static class PathContests
|
||||
/// <summary>
|
||||
/// GFramework源代码生成器抽象层命名空间
|
||||
/// </summary>
|
||||
public const string SourceGeneratorsAbstractionsPath = $"{BaseNamespace}.SourceGenerators.Abstractions";
|
||||
public const string SourceGeneratorsAbstractionsPath = $"{CoreNamespace}.SourceGenerators.Abstractions";
|
||||
|
||||
/// <summary>
|
||||
/// GFramework Godot源代码生成器抽象层命名空间
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
using GFramework.SourceGenerators.Analyzers;
|
||||
using GFramework.Core.SourceGenerators.Analyzers;
|
||||
using GFramework.SourceGenerators.Tests.Core;
|
||||
|
||||
namespace GFramework.SourceGenerators.Tests.Analyzers;
|
||||
@ -9,128 +9,128 @@ namespace GFramework.SourceGenerators.Tests.Analyzers;
|
||||
public sealed class ContextRegistrationAnalyzerTests
|
||||
{
|
||||
private const string TestPreamble = """
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace GFramework.Core.Abstractions.Rule
|
||||
{
|
||||
public interface IContextAware { }
|
||||
}
|
||||
|
||||
namespace GFramework.Core.Abstractions.Model
|
||||
{
|
||||
public interface IModel : GFramework.Core.Abstractions.Rule.IContextAware { }
|
||||
}
|
||||
|
||||
namespace GFramework.Core.Abstractions.Systems
|
||||
{
|
||||
public interface ISystem : GFramework.Core.Abstractions.Rule.IContextAware { }
|
||||
}
|
||||
|
||||
namespace GFramework.Core.Abstractions.Utility
|
||||
{
|
||||
public interface IUtility : GFramework.Core.Abstractions.Rule.IContextAware { }
|
||||
}
|
||||
|
||||
namespace GFramework.Core.Abstractions.Architectures
|
||||
{
|
||||
public interface IArchitecture
|
||||
{
|
||||
T RegisterModel<T>(T model) where T : GFramework.Core.Abstractions.Model.IModel;
|
||||
void RegisterModel<T>(Action<T> onCreated = null) where T : class, GFramework.Core.Abstractions.Model.IModel;
|
||||
T RegisterSystem<T>(T system) where T : GFramework.Core.Abstractions.Systems.ISystem;
|
||||
void RegisterSystem<T>(Action<T> onCreated = null) where T : class, GFramework.Core.Abstractions.Systems.ISystem;
|
||||
T RegisterUtility<T>(T utility) where T : GFramework.Core.Abstractions.Utility.IUtility;
|
||||
void RegisterUtility<T>(Action<T> onCreated = null) where T : class, GFramework.Core.Abstractions.Utility.IUtility;
|
||||
IArchitectureModule InstallModule(IArchitectureModule module);
|
||||
}
|
||||
|
||||
public interface IArchitectureModule
|
||||
{
|
||||
void Install(IArchitecture architecture);
|
||||
}
|
||||
|
||||
public interface IArchitectureContext
|
||||
{
|
||||
TModel GetModel<TModel>() where TModel : class, GFramework.Core.Abstractions.Model.IModel;
|
||||
IReadOnlyList<TModel> GetModels<TModel>() where TModel : class, GFramework.Core.Abstractions.Model.IModel;
|
||||
TSystem GetSystem<TSystem>() where TSystem : class, GFramework.Core.Abstractions.Systems.ISystem;
|
||||
IReadOnlyList<TSystem> GetSystems<TSystem>() where TSystem : class, GFramework.Core.Abstractions.Systems.ISystem;
|
||||
TUtility GetUtility<TUtility>() where TUtility : class, GFramework.Core.Abstractions.Utility.IUtility;
|
||||
IReadOnlyList<TUtility> GetUtilities<TUtility>() where TUtility : class, GFramework.Core.Abstractions.Utility.IUtility;
|
||||
}
|
||||
}
|
||||
|
||||
namespace GFramework.Core.Architectures
|
||||
{
|
||||
public abstract class Architecture : GFramework.Core.Abstractions.Architectures.IArchitecture
|
||||
{
|
||||
protected abstract void OnInitialize();
|
||||
|
||||
public virtual T RegisterModel<T>(T model) where T : GFramework.Core.Abstractions.Model.IModel => model;
|
||||
|
||||
public virtual void RegisterModel<T>(Action<T> onCreated = null)
|
||||
where T : class, GFramework.Core.Abstractions.Model.IModel
|
||||
{
|
||||
}
|
||||
|
||||
public virtual T RegisterSystem<T>(T system) where T : GFramework.Core.Abstractions.Systems.ISystem => system;
|
||||
|
||||
public virtual void RegisterSystem<T>(Action<T> onCreated = null)
|
||||
where T : class, GFramework.Core.Abstractions.Systems.ISystem
|
||||
{
|
||||
}
|
||||
|
||||
public virtual T RegisterUtility<T>(T utility) where T : GFramework.Core.Abstractions.Utility.IUtility => utility;
|
||||
|
||||
public virtual void RegisterUtility<T>(Action<T> onCreated = null)
|
||||
where T : class, GFramework.Core.Abstractions.Utility.IUtility
|
||||
{
|
||||
}
|
||||
|
||||
public virtual GFramework.Core.Abstractions.Architectures.IArchitectureModule InstallModule(
|
||||
GFramework.Core.Abstractions.Architectures.IArchitectureModule module)
|
||||
{
|
||||
module.Install(this);
|
||||
return module;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace GFramework.Core.Extensions
|
||||
{
|
||||
public static class ContextAwareServiceExtensions
|
||||
{
|
||||
public static TModel GetModel<TModel>(this GFramework.Core.Abstractions.Rule.IContextAware contextAware)
|
||||
where TModel : class, GFramework.Core.Abstractions.Model.IModel => throw new NotImplementedException();
|
||||
|
||||
public static IReadOnlyList<TModel> GetModels<TModel>(this GFramework.Core.Abstractions.Rule.IContextAware contextAware)
|
||||
where TModel : class, GFramework.Core.Abstractions.Model.IModel => throw new NotImplementedException();
|
||||
|
||||
public static TSystem GetSystem<TSystem>(this GFramework.Core.Abstractions.Rule.IContextAware contextAware)
|
||||
where TSystem : class, GFramework.Core.Abstractions.Systems.ISystem => throw new NotImplementedException();
|
||||
|
||||
public static IReadOnlyList<TSystem> GetSystems<TSystem>(this GFramework.Core.Abstractions.Rule.IContextAware contextAware)
|
||||
where TSystem : class, GFramework.Core.Abstractions.Systems.ISystem => throw new NotImplementedException();
|
||||
|
||||
public static TUtility GetUtility<TUtility>(this GFramework.Core.Abstractions.Rule.IContextAware contextAware)
|
||||
where TUtility : class, GFramework.Core.Abstractions.Utility.IUtility => throw new NotImplementedException();
|
||||
|
||||
public static IReadOnlyList<TUtility> GetUtilities<TUtility>(this GFramework.Core.Abstractions.Rule.IContextAware contextAware)
|
||||
where TUtility : class, GFramework.Core.Abstractions.Utility.IUtility => throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
namespace GFramework.SourceGenerators.Abstractions.Rule
|
||||
{
|
||||
public sealed class GetModelAttribute : Attribute { }
|
||||
public sealed class GetModelsAttribute : Attribute { }
|
||||
public sealed class GetSystemAttribute : Attribute { }
|
||||
public sealed class GetSystemsAttribute : Attribute { }
|
||||
public sealed class GetUtilityAttribute : Attribute { }
|
||||
public sealed class GetUtilitiesAttribute : Attribute { }
|
||||
}
|
||||
""";
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace GFramework.Core.Abstractions.Rule
|
||||
{
|
||||
public interface IContextAware { }
|
||||
}
|
||||
|
||||
namespace GFramework.Core.Abstractions.Model
|
||||
{
|
||||
public interface IModel : GFramework.Core.Abstractions.Rule.IContextAware { }
|
||||
}
|
||||
|
||||
namespace GFramework.Core.Abstractions.Systems
|
||||
{
|
||||
public interface ISystem : GFramework.Core.Abstractions.Rule.IContextAware { }
|
||||
}
|
||||
|
||||
namespace GFramework.Core.Abstractions.Utility
|
||||
{
|
||||
public interface IUtility : GFramework.Core.Abstractions.Rule.IContextAware { }
|
||||
}
|
||||
|
||||
namespace GFramework.Core.Abstractions.Architectures
|
||||
{
|
||||
public interface IArchitecture
|
||||
{
|
||||
T RegisterModel<T>(T model) where T : GFramework.Core.Abstractions.Model.IModel;
|
||||
void RegisterModel<T>(Action<T> onCreated = null) where T : class, GFramework.Core.Abstractions.Model.IModel;
|
||||
T RegisterSystem<T>(T system) where T : GFramework.Core.Abstractions.Systems.ISystem;
|
||||
void RegisterSystem<T>(Action<T> onCreated = null) where T : class, GFramework.Core.Abstractions.Systems.ISystem;
|
||||
T RegisterUtility<T>(T utility) where T : GFramework.Core.Abstractions.Utility.IUtility;
|
||||
void RegisterUtility<T>(Action<T> onCreated = null) where T : class, GFramework.Core.Abstractions.Utility.IUtility;
|
||||
IArchitectureModule InstallModule(IArchitectureModule module);
|
||||
}
|
||||
|
||||
public interface IArchitectureModule
|
||||
{
|
||||
void Install(IArchitecture architecture);
|
||||
}
|
||||
|
||||
public interface IArchitectureContext
|
||||
{
|
||||
TModel GetModel<TModel>() where TModel : class, GFramework.Core.Abstractions.Model.IModel;
|
||||
IReadOnlyList<TModel> GetModels<TModel>() where TModel : class, GFramework.Core.Abstractions.Model.IModel;
|
||||
TSystem GetSystem<TSystem>() where TSystem : class, GFramework.Core.Abstractions.Systems.ISystem;
|
||||
IReadOnlyList<TSystem> GetSystems<TSystem>() where TSystem : class, GFramework.Core.Abstractions.Systems.ISystem;
|
||||
TUtility GetUtility<TUtility>() where TUtility : class, GFramework.Core.Abstractions.Utility.IUtility;
|
||||
IReadOnlyList<TUtility> GetUtilities<TUtility>() where TUtility : class, GFramework.Core.Abstractions.Utility.IUtility;
|
||||
}
|
||||
}
|
||||
|
||||
namespace GFramework.Core.Architectures
|
||||
{
|
||||
public abstract class Architecture : GFramework.Core.Abstractions.Architectures.IArchitecture
|
||||
{
|
||||
protected abstract void OnInitialize();
|
||||
|
||||
public virtual T RegisterModel<T>(T model) where T : GFramework.Core.Abstractions.Model.IModel => model;
|
||||
|
||||
public virtual void RegisterModel<T>(Action<T> onCreated = null)
|
||||
where T : class, GFramework.Core.Abstractions.Model.IModel
|
||||
{
|
||||
}
|
||||
|
||||
public virtual T RegisterSystem<T>(T system) where T : GFramework.Core.Abstractions.Systems.ISystem => system;
|
||||
|
||||
public virtual void RegisterSystem<T>(Action<T> onCreated = null)
|
||||
where T : class, GFramework.Core.Abstractions.Systems.ISystem
|
||||
{
|
||||
}
|
||||
|
||||
public virtual T RegisterUtility<T>(T utility) where T : GFramework.Core.Abstractions.Utility.IUtility => utility;
|
||||
|
||||
public virtual void RegisterUtility<T>(Action<T> onCreated = null)
|
||||
where T : class, GFramework.Core.Abstractions.Utility.IUtility
|
||||
{
|
||||
}
|
||||
|
||||
public virtual GFramework.Core.Abstractions.Architectures.IArchitectureModule InstallModule(
|
||||
GFramework.Core.Abstractions.Architectures.IArchitectureModule module)
|
||||
{
|
||||
module.Install(this);
|
||||
return module;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace GFramework.Core.Extensions
|
||||
{
|
||||
public static class ContextAwareServiceExtensions
|
||||
{
|
||||
public static TModel GetModel<TModel>(this GFramework.Core.Abstractions.Rule.IContextAware contextAware)
|
||||
where TModel : class, GFramework.Core.Abstractions.Model.IModel => throw new NotImplementedException();
|
||||
|
||||
public static IReadOnlyList<TModel> GetModels<TModel>(this GFramework.Core.Abstractions.Rule.IContextAware contextAware)
|
||||
where TModel : class, GFramework.Core.Abstractions.Model.IModel => throw new NotImplementedException();
|
||||
|
||||
public static TSystem GetSystem<TSystem>(this GFramework.Core.Abstractions.Rule.IContextAware contextAware)
|
||||
where TSystem : class, GFramework.Core.Abstractions.Systems.ISystem => throw new NotImplementedException();
|
||||
|
||||
public static IReadOnlyList<TSystem> GetSystems<TSystem>(this GFramework.Core.Abstractions.Rule.IContextAware contextAware)
|
||||
where TSystem : class, GFramework.Core.Abstractions.Systems.ISystem => throw new NotImplementedException();
|
||||
|
||||
public static TUtility GetUtility<TUtility>(this GFramework.Core.Abstractions.Rule.IContextAware contextAware)
|
||||
where TUtility : class, GFramework.Core.Abstractions.Utility.IUtility => throw new NotImplementedException();
|
||||
|
||||
public static IReadOnlyList<TUtility> GetUtilities<TUtility>(this GFramework.Core.Abstractions.Rule.IContextAware contextAware)
|
||||
where TUtility : class, GFramework.Core.Abstractions.Utility.IUtility => throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
namespace GFramework.Core.SourceGenerators.Abstractions.Rule
|
||||
{
|
||||
public sealed class GetModelAttribute : Attribute { }
|
||||
public sealed class GetModelsAttribute : Attribute { }
|
||||
public sealed class GetSystemAttribute : Attribute { }
|
||||
public sealed class GetSystemsAttribute : Attribute { }
|
||||
public sealed class GetUtilityAttribute : Attribute { }
|
||||
public sealed class GetUtilitiesAttribute : Attribute { }
|
||||
}
|
||||
""";
|
||||
|
||||
[Test]
|
||||
public async Task Reports_Warning_When_FieldInjectedModel_Is_Not_Registered()
|
||||
@ -142,7 +142,7 @@ public sealed class ContextRegistrationAnalyzerTests
|
||||
using GFramework.Core.Abstractions.Model;
|
||||
using GFramework.Core.Abstractions.Systems;
|
||||
using GFramework.Core.Architectures;
|
||||
using GFramework.SourceGenerators.Abstractions.Rule;
|
||||
using GFramework.Core.SourceGenerators.Abstractions.Rule;
|
||||
|
||||
public interface IInventoryModel : IModel { }
|
||||
|
||||
@ -180,7 +180,7 @@ public sealed class ContextRegistrationAnalyzerTests
|
||||
using GFramework.Core.Abstractions.Model;
|
||||
using GFramework.Core.Abstractions.Systems;
|
||||
using GFramework.Core.Architectures;
|
||||
using GFramework.SourceGenerators.Abstractions.Rule;
|
||||
using GFramework.Core.SourceGenerators.Abstractions.Rule;
|
||||
|
||||
public interface IInventoryModel : IModel { }
|
||||
|
||||
@ -255,7 +255,7 @@ public sealed class ContextRegistrationAnalyzerTests
|
||||
using GFramework.Core.Abstractions.Model;
|
||||
using GFramework.Core.Abstractions.Systems;
|
||||
using GFramework.Core.Architectures;
|
||||
using GFramework.SourceGenerators.Abstractions.Rule;
|
||||
using GFramework.Core.SourceGenerators.Abstractions.Rule;
|
||||
|
||||
public interface IInventoryModel : IModel { }
|
||||
|
||||
@ -297,7 +297,7 @@ public sealed class ContextRegistrationAnalyzerTests
|
||||
using GFramework.Core.Abstractions.Model;
|
||||
using GFramework.Core.Abstractions.Systems;
|
||||
using GFramework.Core.Architectures;
|
||||
using GFramework.SourceGenerators.Abstractions.Rule;
|
||||
using GFramework.Core.SourceGenerators.Abstractions.Rule;
|
||||
|
||||
public interface IInventoryModel : IModel { }
|
||||
|
||||
@ -337,7 +337,7 @@ public sealed class ContextRegistrationAnalyzerTests
|
||||
using GFramework.Core.Abstractions.Systems;
|
||||
using GFramework.Core.Abstractions.Utility;
|
||||
using GFramework.Core.Architectures;
|
||||
using GFramework.SourceGenerators.Abstractions.Rule;
|
||||
using GFramework.Core.SourceGenerators.Abstractions.Rule;
|
||||
|
||||
public interface IInventoryUtility : IUtility { }
|
||||
|
||||
@ -366,7 +366,8 @@ public sealed class ContextRegistrationAnalyzerTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Does_Not_Report_When_Inherited_OnInitialize_Calls_Virtual_Helper_Overridden_In_Derived_Architecture()
|
||||
public async Task
|
||||
Does_Not_Report_When_Inherited_OnInitialize_Calls_Virtual_Helper_Overridden_In_Derived_Architecture()
|
||||
{
|
||||
await AnalyzerTestDriver<ContextRegistrationAnalyzer>.RunAsync(
|
||||
Wrap("""
|
||||
@ -375,7 +376,7 @@ public sealed class ContextRegistrationAnalyzerTests
|
||||
using GFramework.Core.Abstractions.Model;
|
||||
using GFramework.Core.Abstractions.Systems;
|
||||
using GFramework.Core.Architectures;
|
||||
using GFramework.SourceGenerators.Abstractions.Rule;
|
||||
using GFramework.Core.SourceGenerators.Abstractions.Rule;
|
||||
|
||||
public interface IInventoryModel : IModel { }
|
||||
|
||||
@ -422,7 +423,7 @@ public sealed class ContextRegistrationAnalyzerTests
|
||||
using GFramework.Core.Abstractions.Model;
|
||||
using GFramework.Core.Abstractions.Systems;
|
||||
using GFramework.Core.Architectures;
|
||||
using GFramework.SourceGenerators.Abstractions.Rule;
|
||||
using GFramework.Core.SourceGenerators.Abstractions.Rule;
|
||||
|
||||
public interface IInventoryModel : IModel { }
|
||||
|
||||
@ -476,7 +477,7 @@ public sealed class ContextRegistrationAnalyzerTests
|
||||
using GFramework.Core.Abstractions.Model;
|
||||
using GFramework.Core.Abstractions.Systems;
|
||||
using GFramework.Core.Architectures;
|
||||
using GFramework.SourceGenerators.Abstractions.Rule;
|
||||
using GFramework.Core.SourceGenerators.Abstractions.Rule;
|
||||
|
||||
public interface IInventoryModel : IModel { }
|
||||
|
||||
@ -531,7 +532,7 @@ public sealed class ContextRegistrationAnalyzerTests
|
||||
using GFramework.Core.Abstractions.Model;
|
||||
using GFramework.Core.Abstractions.Systems;
|
||||
using GFramework.Core.Architectures;
|
||||
using GFramework.SourceGenerators.Abstractions.Rule;
|
||||
using GFramework.Core.SourceGenerators.Abstractions.Rule;
|
||||
|
||||
public interface IInventoryModel : IModel { }
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
using GFramework.SourceGenerators.Architectures;
|
||||
using GFramework.Core.SourceGenerators.Architectures;
|
||||
using GFramework.SourceGenerators.Tests.Core;
|
||||
|
||||
namespace GFramework.SourceGenerators.Tests.Architectures;
|
||||
@ -14,9 +14,9 @@ public class AutoRegisterModuleGeneratorTests
|
||||
{
|
||||
const string source = """
|
||||
using System;
|
||||
using GFramework.SourceGenerators.Abstractions.Architectures;
|
||||
using GFramework.Core.SourceGenerators.Abstractions.Architectures;
|
||||
|
||||
namespace GFramework.SourceGenerators.Abstractions.Architectures
|
||||
namespace GFramework.Core.SourceGenerators.Abstractions.Architectures
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
|
||||
public sealed class AutoRegisterModuleAttribute : Attribute { }
|
||||
@ -70,7 +70,7 @@ public class AutoRegisterModuleGeneratorTests
|
||||
using GFramework.Core.Abstractions.Model;
|
||||
using GFramework.Core.Abstractions.Systems;
|
||||
using GFramework.Core.Abstractions.Utility;
|
||||
using GFramework.SourceGenerators.Abstractions.Architectures;
|
||||
using GFramework.Core.SourceGenerators.Abstractions.Architectures;
|
||||
|
||||
public sealed class PlayerModel : IModel { }
|
||||
public sealed class CombatSystem : ISystem { }
|
||||
@ -118,7 +118,7 @@ public class AutoRegisterModuleGeneratorTests
|
||||
const string commonSource = """
|
||||
using System;
|
||||
|
||||
namespace GFramework.SourceGenerators.Abstractions.Architectures
|
||||
namespace GFramework.Core.SourceGenerators.Abstractions.Architectures
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
|
||||
public sealed class AutoRegisterModuleAttribute : Attribute { }
|
||||
@ -180,42 +180,42 @@ public class AutoRegisterModuleGeneratorTests
|
||||
""";
|
||||
|
||||
const string partASource = """
|
||||
namespace TestApp
|
||||
{
|
||||
using GFramework.SourceGenerators.Abstractions.Architectures;
|
||||
namespace TestApp
|
||||
{
|
||||
using GFramework.Core.SourceGenerators.Abstractions.Architectures;
|
||||
|
||||
// Padding ensures this attribute lives later in the file than the attributes in PartB.
|
||||
// The generator should still place it first because PartA sorts before PartB.
|
||||
// padding 01
|
||||
// padding 02
|
||||
// padding 03
|
||||
// padding 04
|
||||
// padding 05
|
||||
// padding 06
|
||||
// padding 07
|
||||
// padding 08
|
||||
// padding 09
|
||||
// padding 10
|
||||
[AutoRegisterModule]
|
||||
[RegisterUtility(typeof(AudioUtility))]
|
||||
public partial class GameplayModule
|
||||
{
|
||||
}
|
||||
}
|
||||
""";
|
||||
// Padding ensures this attribute lives later in the file than the attributes in PartB.
|
||||
// The generator should still place it first because PartA sorts before PartB.
|
||||
// padding 01
|
||||
// padding 02
|
||||
// padding 03
|
||||
// padding 04
|
||||
// padding 05
|
||||
// padding 06
|
||||
// padding 07
|
||||
// padding 08
|
||||
// padding 09
|
||||
// padding 10
|
||||
[AutoRegisterModule]
|
||||
[RegisterUtility(typeof(AudioUtility))]
|
||||
public partial class GameplayModule
|
||||
{
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
const string partBSource = """
|
||||
namespace TestApp
|
||||
{
|
||||
using GFramework.SourceGenerators.Abstractions.Architectures;
|
||||
namespace TestApp
|
||||
{
|
||||
using GFramework.Core.SourceGenerators.Abstractions.Architectures;
|
||||
|
||||
[RegisterSystem(typeof(CombatSystem))]
|
||||
[RegisterModel(typeof(PlayerModel))]
|
||||
public partial class GameplayModule
|
||||
{
|
||||
}
|
||||
}
|
||||
""";
|
||||
[RegisterSystem(typeof(CombatSystem))]
|
||||
[RegisterModel(typeof(PlayerModel))]
|
||||
public partial class GameplayModule
|
||||
{
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
const string expected = """
|
||||
// <auto-generated />
|
||||
@ -247,7 +247,8 @@ public class AutoRegisterModuleGeneratorTests
|
||||
},
|
||||
GeneratedSources =
|
||||
{
|
||||
(typeof(AutoRegisterModuleGenerator), "TestApp_GameplayModule.AutoRegisterModule.g.cs", NormalizeLineEndings(expected))
|
||||
(typeof(AutoRegisterModuleGenerator), "TestApp_GameplayModule.AutoRegisterModule.g.cs",
|
||||
NormalizeLineEndings(expected))
|
||||
}
|
||||
},
|
||||
DisabledDiagnostics = { "GF_Common_Trace_001" }
|
||||
@ -265,9 +266,9 @@ public class AutoRegisterModuleGeneratorTests
|
||||
const string source = """
|
||||
#nullable enable
|
||||
using System;
|
||||
using GFramework.SourceGenerators.Abstractions.Architectures;
|
||||
using GFramework.Core.SourceGenerators.Abstractions.Architectures;
|
||||
|
||||
namespace GFramework.SourceGenerators.Abstractions.Architectures
|
||||
namespace GFramework.Core.SourceGenerators.Abstractions.Architectures
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
|
||||
public sealed class AutoRegisterModuleAttribute : Attribute { }
|
||||
@ -319,7 +320,7 @@ public class AutoRegisterModuleGeneratorTests
|
||||
namespace TestApp
|
||||
{
|
||||
using GFramework.Core.Abstractions.Model;
|
||||
using GFramework.SourceGenerators.Abstractions.Architectures;
|
||||
using GFramework.Core.SourceGenerators.Abstractions.Architectures;
|
||||
|
||||
public sealed class PlayerModel : IModel { }
|
||||
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
using System.IO;
|
||||
using GFramework.SourceGenerators.Bases;
|
||||
using GFramework.Core.SourceGenerators.Bases;
|
||||
using GFramework.SourceGenerators.Tests.Core;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace GFramework.SourceGenerators.Tests.Bases;
|
||||
|
||||
@ -20,7 +19,7 @@ public class PriorityGeneratorSnapshotTests
|
||||
const string source = """
|
||||
using System;
|
||||
|
||||
namespace GFramework.SourceGenerators.Abstractions.Bases
|
||||
namespace GFramework.Core.SourceGenerators.Abstractions.Bases
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
|
||||
public sealed class PriorityAttribute : Attribute
|
||||
@ -40,7 +39,7 @@ public class PriorityGeneratorSnapshotTests
|
||||
|
||||
namespace TestApp
|
||||
{
|
||||
using GFramework.SourceGenerators.Abstractions.Bases;
|
||||
using GFramework.Core.SourceGenerators.Abstractions.Bases;
|
||||
|
||||
[Priority(10)]
|
||||
public partial class MySystem
|
||||
@ -68,7 +67,7 @@ public class PriorityGeneratorSnapshotTests
|
||||
const string source = """
|
||||
using System;
|
||||
|
||||
namespace GFramework.SourceGenerators.Abstractions.Bases
|
||||
namespace GFramework.Core.SourceGenerators.Abstractions.Bases
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
|
||||
public sealed class PriorityAttribute : Attribute
|
||||
@ -88,7 +87,7 @@ public class PriorityGeneratorSnapshotTests
|
||||
|
||||
namespace TestApp
|
||||
{
|
||||
using GFramework.SourceGenerators.Abstractions.Bases;
|
||||
using GFramework.Core.SourceGenerators.Abstractions.Bases;
|
||||
|
||||
[Priority(-100)]
|
||||
public partial class CriticalSystem
|
||||
@ -116,7 +115,7 @@ public class PriorityGeneratorSnapshotTests
|
||||
const string source = """
|
||||
using System;
|
||||
|
||||
namespace GFramework.SourceGenerators.Abstractions.Bases
|
||||
namespace GFramework.Core.SourceGenerators.Abstractions.Bases
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
|
||||
public sealed class PriorityAttribute : Attribute
|
||||
@ -145,7 +144,7 @@ public class PriorityGeneratorSnapshotTests
|
||||
|
||||
namespace TestApp
|
||||
{
|
||||
using GFramework.SourceGenerators.Abstractions.Bases;
|
||||
using GFramework.Core.SourceGenerators.Abstractions.Bases;
|
||||
using GFramework.Core.Abstractions.Bases;
|
||||
|
||||
[Priority(PriorityGroup.High)]
|
||||
@ -174,7 +173,7 @@ public class PriorityGeneratorSnapshotTests
|
||||
const string source = """
|
||||
using System;
|
||||
|
||||
namespace GFramework.SourceGenerators.Abstractions.Bases
|
||||
namespace GFramework.Core.SourceGenerators.Abstractions.Bases
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
|
||||
public sealed class PriorityAttribute : Attribute
|
||||
@ -194,7 +193,7 @@ public class PriorityGeneratorSnapshotTests
|
||||
|
||||
namespace TestApp
|
||||
{
|
||||
using GFramework.SourceGenerators.Abstractions.Bases;
|
||||
using GFramework.Core.SourceGenerators.Abstractions.Bases;
|
||||
|
||||
[Priority(20)]
|
||||
public partial class GenericSystem<T>
|
||||
@ -212,4 +211,4 @@ public class PriorityGeneratorSnapshotTests
|
||||
"PriorityGenerator",
|
||||
"GenericClass"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
using System.Collections.Immutable;
|
||||
using System.IO;
|
||||
using GFramework.SourceGenerators.Config;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using GFramework.Game.SourceGenerators.Config;
|
||||
|
||||
namespace GFramework.SourceGenerators.Tests.Config;
|
||||
|
||||
@ -85,4 +84,4 @@ public static class SchemaGeneratorTestDriver
|
||||
return _text;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,4 @@
|
||||
using System.IO;
|
||||
using Microsoft.CodeAnalysis.CSharp.Testing;
|
||||
using Microsoft.CodeAnalysis.Testing;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace GFramework.SourceGenerators.Tests.Core;
|
||||
|
||||
@ -17,10 +14,12 @@ public static class GeneratorSnapshotTest<TGenerator>
|
||||
/// </summary>
|
||||
/// <param name="source">输入的源代码字符串</param>
|
||||
/// <param name="snapshotFolder">快照文件存储的文件夹路径</param>
|
||||
/// <param name="snapshotFileNameSelector">将生成文件名映射为快照文件名的规则;为空时使用原始生成文件名。</param>
|
||||
/// <returns>异步任务</returns>
|
||||
public static async Task RunAsync(
|
||||
string source,
|
||||
string snapshotFolder)
|
||||
string snapshotFolder,
|
||||
Func<string, string>? snapshotFileNameSelector = null)
|
||||
{
|
||||
var test = new CSharpSourceGeneratorTest<TGenerator, DefaultVerifier>
|
||||
{
|
||||
@ -38,9 +37,11 @@ public static class GeneratorSnapshotTest<TGenerator>
|
||||
|
||||
foreach (var (filename, content) in generated)
|
||||
{
|
||||
// 不同测试套件可能需要将生成文件映射到非 .cs 快照,以避免测试资产被当作可编译源码参与构建。
|
||||
var snapshotFileName = snapshotFileNameSelector?.Invoke(filename) ?? filename;
|
||||
var path = Path.Combine(
|
||||
snapshotFolder,
|
||||
filename);
|
||||
snapshotFileName);
|
||||
|
||||
if (!File.Exists(path))
|
||||
{
|
||||
@ -57,7 +58,7 @@ public static class GeneratorSnapshotTest<TGenerator>
|
||||
Assert.That(
|
||||
Normalize(expected),
|
||||
Is.EqualTo(Normalize(content.ToString())),
|
||||
$"Snapshot mismatch: {filename}");
|
||||
$"Snapshot mismatch: {snapshotFileName}");
|
||||
}
|
||||
}
|
||||
|
||||
@ -70,4 +71,4 @@ public static class GeneratorSnapshotTest<TGenerator>
|
||||
{
|
||||
return text.Replace("\r\n", "\n").Trim();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
using System.Reflection;
|
||||
using GFramework.SourceGenerators.Cqrs;
|
||||
using GFramework.Cqrs.SourceGenerators.Cqrs;
|
||||
using GFramework.SourceGenerators.Tests.Core;
|
||||
|
||||
namespace GFramework.SourceGenerators.Tests.Cqrs;
|
||||
@ -252,6 +252,84 @@ public class CqrsHandlerRegistryGeneratorTests
|
||||
|
||||
""";
|
||||
|
||||
private const string ExternalAssemblyPreciseLookupExpected = """
|
||||
// <auto-generated />
|
||||
#nullable enable
|
||||
|
||||
[assembly: global::GFramework.Cqrs.CqrsHandlerRegistryAttribute(typeof(global::GFramework.Generated.Cqrs.__GFrameworkGeneratedCqrsHandlerRegistry))]
|
||||
|
||||
namespace GFramework.Generated.Cqrs;
|
||||
|
||||
internal sealed class __GFrameworkGeneratedCqrsHandlerRegistry : global::GFramework.Cqrs.ICqrsHandlerRegistry
|
||||
{
|
||||
public void Register(global::Microsoft.Extensions.DependencyInjection.IServiceCollection services, global::GFramework.Core.Abstractions.Logging.ILogger logger)
|
||||
{
|
||||
if (services is null)
|
||||
throw new global::System.ArgumentNullException(nameof(services));
|
||||
if (logger is null)
|
||||
throw new global::System.ArgumentNullException(nameof(logger));
|
||||
|
||||
var registryAssembly = typeof(global::GFramework.Generated.Cqrs.__GFrameworkGeneratedCqrsHandlerRegistry).Assembly;
|
||||
|
||||
var implementationType0 = typeof(global::TestApp.DerivedHandler);
|
||||
if (implementationType0 is not null)
|
||||
{
|
||||
var serviceType0_0Argument0 = ResolveReferencedAssemblyType("Dependency, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null", "Dep.VisibilityScope+ProtectedRequest");
|
||||
var serviceType0_0Argument1Element = ResolveReferencedAssemblyType("Dependency, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null", "Dep.VisibilityScope+ProtectedResponse");
|
||||
if (serviceType0_0Argument0 is not null && serviceType0_0Argument1Element is not null)
|
||||
{
|
||||
var serviceType0_0 = typeof(global::GFramework.Cqrs.Abstractions.Cqrs.IRequestHandler<,>).MakeGenericType(serviceType0_0Argument0, serviceType0_0Argument1Element.MakeArrayType());
|
||||
global::Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddTransient(
|
||||
services,
|
||||
serviceType0_0,
|
||||
implementationType0);
|
||||
logger.Debug("Registered CQRS handler TestApp.DerivedHandler as GFramework.Cqrs.Abstractions.Cqrs.IRequestHandler<Dep.VisibilityScope.ProtectedRequest, Dep.VisibilityScope.ProtectedResponse[]>.");
|
||||
}
|
||||
global::Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddTransient(
|
||||
services,
|
||||
typeof(global::GFramework.Cqrs.Abstractions.Cqrs.IRequestHandler<global::Dep.VisibleRequest, string>),
|
||||
implementationType0);
|
||||
logger.Debug("Registered CQRS handler TestApp.DerivedHandler as GFramework.Cqrs.Abstractions.Cqrs.IRequestHandler<Dep.VisibleRequest, string>.");
|
||||
}
|
||||
}
|
||||
|
||||
private static global::System.Type? ResolveReferencedAssemblyType(string assemblyIdentity, string typeMetadataName)
|
||||
{
|
||||
var assembly = ResolveReferencedAssembly(assemblyIdentity);
|
||||
return assembly?.GetType(typeMetadataName, throwOnError: false, ignoreCase: false);
|
||||
}
|
||||
|
||||
private static global::System.Reflection.Assembly? ResolveReferencedAssembly(string assemblyIdentity)
|
||||
{
|
||||
global::System.Reflection.AssemblyName targetAssemblyName;
|
||||
try
|
||||
{
|
||||
targetAssemblyName = new global::System.Reflection.AssemblyName(assemblyIdentity);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
foreach (var assembly in global::System.AppDomain.CurrentDomain.GetAssemblies())
|
||||
{
|
||||
if (global::System.Reflection.AssemblyName.ReferenceMatchesDefinition(targetAssemblyName, assembly.GetName()))
|
||||
return assembly;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
return global::System.Reflection.Assembly.Load(targetAssemblyName);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
""";
|
||||
|
||||
/// <summary>
|
||||
/// 验证生成器会为当前程序集中的 request、notification 和 stream 处理器生成稳定顺序的注册器。
|
||||
/// </summary>
|
||||
@ -827,10 +905,10 @@ public class CqrsHandlerRegistryGeneratorTests
|
||||
|
||||
/// <summary>
|
||||
/// 验证当外部基类暴露的 handler interface 含有生成注册器顶层上下文不可直接引用的 protected 类型时,
|
||||
/// 生成器会保留已知直注册,并只对剩余未知接口做本地 interface discovery。
|
||||
/// 生成器会输出定向程序集查找,而不是继续退回 implementation 级接口发现。
|
||||
/// </summary>
|
||||
[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 = """
|
||||
namespace GFramework.Cqrs.Abstractions.Cqrs
|
||||
@ -923,39 +1001,9 @@ public class CqrsHandlerRegistryGeneratorTests
|
||||
contractsReference,
|
||||
dependencyReference);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(
|
||||
generatedSource,
|
||||
Does.Contain("var implementationType0 = typeof(global::TestApp.DerivedHandler);"));
|
||||
Assert.That(
|
||||
generatedSource,
|
||||
Does.Contain(
|
||||
"var knownServiceTypes0 = new global::System.Collections.Generic.HashSet<global::System.Type>();"));
|
||||
Assert.That(
|
||||
generatedSource,
|
||||
Does.Contain(
|
||||
"// Remaining runtime interface discovery target: GFramework.Cqrs.Abstractions.Cqrs.IRequestHandler<Dep.VisibilityScope.ProtectedRequest, Dep.VisibilityScope.ProtectedResponse[]>"));
|
||||
Assert.That(
|
||||
generatedSource,
|
||||
Does.Contain(
|
||||
"knownServiceTypes0.Add(typeof(global::GFramework.Cqrs.Abstractions.Cqrs.IRequestHandler<global::Dep.VisibleRequest, string>));"));
|
||||
Assert.That(
|
||||
generatedSource,
|
||||
Does.Contain(
|
||||
"RegisterRemainingReflectedHandlerInterfaces(services, logger, implementationType0, knownServiceTypes0);"));
|
||||
Assert.That(
|
||||
generatedSource,
|
||||
Does.Contain("if (knownServiceTypes.Contains(handlerInterface))"));
|
||||
Assert.That(
|
||||
generatedSource,
|
||||
Does.Contain(
|
||||
"Registered CQRS handler TestApp.DerivedHandler as GFramework.Cqrs.Abstractions.Cqrs.IRequestHandler<Dep.VisibleRequest, string>."));
|
||||
Assert.That(
|
||||
generatedSource,
|
||||
Does.Not.Contain(
|
||||
"typeof(global::GFramework.Cqrs.Abstractions.Cqrs.IRequestHandler<global::Dep.VisibilityScope.ProtectedRequest"));
|
||||
});
|
||||
Assert.That(
|
||||
generatedSource,
|
||||
Is.EqualTo(ExternalAssemblyPreciseLookupExpected));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@ -1,209 +1,213 @@
|
||||
using System.IO;
|
||||
using GFramework.SourceGenerators.Enums;
|
||||
using GFramework.Core.SourceGenerators.Enums;
|
||||
using GFramework.SourceGenerators.Tests.Core;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace GFramework.SourceGenerators.Tests.Enums;
|
||||
|
||||
/// <summary>
|
||||
/// 验证枚举扩展生成器在不同属性开关组合下的快照输出。
|
||||
/// </summary>
|
||||
[TestFixture]
|
||||
public class EnumExtensionsGeneratorSnapshotTests
|
||||
{
|
||||
private const string EnumAttributeNamespace = "GFramework.Core.SourceGenerators.Abstractions.Enums";
|
||||
|
||||
/// <summary>
|
||||
/// 验证默认配置会为普通枚举生成逐项判断方法与集合判断方法。
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task Snapshot_BasicEnum_IsMethods()
|
||||
{
|
||||
const string source = """
|
||||
using System;
|
||||
|
||||
namespace GFramework.SourceGenerators.Abstractions.Enums
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Enum)]
|
||||
public sealed class GenerateEnumExtensionsAttribute : Attribute
|
||||
{
|
||||
public bool GenerateIsMethods { get; set; } = true;
|
||||
public bool GenerateIsInMethod { get; set; } = true;
|
||||
}
|
||||
}
|
||||
|
||||
namespace TestApp
|
||||
{
|
||||
using GFramework.SourceGenerators.Abstractions.Enums;
|
||||
|
||||
[GenerateEnumExtensions]
|
||||
public enum Status
|
||||
{
|
||||
Active,
|
||||
Inactive,
|
||||
Pending
|
||||
}
|
||||
}
|
||||
""";
|
||||
var source = BuildSource(
|
||||
"""
|
||||
public enum Status
|
||||
{
|
||||
Active,
|
||||
Inactive,
|
||||
Pending
|
||||
}
|
||||
""");
|
||||
|
||||
await GeneratorSnapshotTest<EnumExtensionsGenerator>.RunAsync(
|
||||
source,
|
||||
Path.Combine(
|
||||
TestContext.CurrentContext.TestDirectory,
|
||||
"enums",
|
||||
"snapshots",
|
||||
"EnumExtensionsGenerator",
|
||||
"BasicEnum_IsMethods"));
|
||||
GetSnapshotFolder("BasicEnum_IsMethods"),
|
||||
GetSnapshotFileName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证默认配置在较小枚举上仍会生成集合判断方法。
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task Snapshot_BasicEnum_IsInMethod()
|
||||
{
|
||||
const string source = """
|
||||
using System;
|
||||
|
||||
namespace GFramework.SourceGenerators.Abstractions.Enums
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Enum)]
|
||||
public sealed class GenerateEnumExtensionsAttribute : Attribute
|
||||
{
|
||||
public bool GenerateIsMethods { get; set; } = true;
|
||||
public bool GenerateIsInMethod { get; set; } = true;
|
||||
}
|
||||
}
|
||||
|
||||
namespace TestApp
|
||||
{
|
||||
using GFramework.SourceGenerators.Abstractions.Enums;
|
||||
|
||||
[GenerateEnumExtensions]
|
||||
public enum Status
|
||||
{
|
||||
Active,
|
||||
Inactive
|
||||
}
|
||||
}
|
||||
""";
|
||||
var source = BuildSource(
|
||||
"""
|
||||
public enum Status
|
||||
{
|
||||
Active,
|
||||
Inactive
|
||||
}
|
||||
""");
|
||||
|
||||
await GeneratorSnapshotTest<EnumExtensionsGenerator>.RunAsync(
|
||||
source,
|
||||
Path.Combine(
|
||||
TestContext.CurrentContext.TestDirectory,
|
||||
"enums",
|
||||
"snapshots",
|
||||
"EnumExtensionsGenerator",
|
||||
"BasicEnum_IsInMethod"));
|
||||
GetSnapshotFolder("BasicEnum_IsInMethod"),
|
||||
GetSnapshotFileName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证带显式位标志值的枚举也会生成对应扩展方法。
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task Snapshot_EnumWithFlagValues()
|
||||
{
|
||||
const string source = """
|
||||
using System;
|
||||
|
||||
namespace GFramework.SourceGenerators.Abstractions.Enums
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Enum)]
|
||||
public sealed class GenerateEnumExtensionsAttribute : Attribute
|
||||
{
|
||||
public bool GenerateIsMethods { get; set; } = true;
|
||||
public bool GenerateIsInMethod { get; set; } = true;
|
||||
}
|
||||
}
|
||||
|
||||
namespace TestApp
|
||||
{
|
||||
using GFramework.SourceGenerators.Abstractions.Enums;
|
||||
|
||||
[GenerateEnumExtensions]
|
||||
[Flags]
|
||||
public enum Permissions
|
||||
{
|
||||
None = 0,
|
||||
Read = 1,
|
||||
Write = 2,
|
||||
Execute = 4
|
||||
}
|
||||
}
|
||||
""";
|
||||
var source = BuildSource(
|
||||
"""
|
||||
[Flags]
|
||||
public enum Permissions
|
||||
{
|
||||
None = 0,
|
||||
Read = 1,
|
||||
Write = 2,
|
||||
Execute = 4
|
||||
}
|
||||
""");
|
||||
|
||||
await GeneratorSnapshotTest<EnumExtensionsGenerator>.RunAsync(
|
||||
source,
|
||||
Path.Combine(
|
||||
TestContext.CurrentContext.TestDirectory,
|
||||
"enums",
|
||||
"snapshots",
|
||||
"EnumExtensionsGenerator",
|
||||
"EnumWithFlagValues"));
|
||||
GetSnapshotFolder("EnumWithFlagValues"),
|
||||
GetSnapshotFileName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证关闭逐项判断开关后仅保留集合判断方法。
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task Snapshot_DisableIsMethods()
|
||||
{
|
||||
const string source = """
|
||||
using System;
|
||||
|
||||
namespace GFramework.SourceGenerators.Abstractions.Enums
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Enum)]
|
||||
public sealed class GenerateEnumExtensionsAttribute : Attribute
|
||||
{
|
||||
public bool GenerateIsMethods { get; set; } = true;
|
||||
public bool GenerateIsInMethod { get; set; } = true;
|
||||
}
|
||||
}
|
||||
|
||||
namespace TestApp
|
||||
{
|
||||
using GFramework.SourceGenerators.Abstractions.Enums;
|
||||
|
||||
[GenerateEnumExtensions(GenerateIsMethods = false)]
|
||||
public enum Status
|
||||
{
|
||||
Active,
|
||||
Inactive
|
||||
}
|
||||
}
|
||||
""";
|
||||
var source = BuildSource(
|
||||
"""
|
||||
public enum Status
|
||||
{
|
||||
Active,
|
||||
Inactive
|
||||
}
|
||||
""",
|
||||
"[GenerateEnumExtensions(GenerateIsMethods = false)]");
|
||||
|
||||
await GeneratorSnapshotTest<EnumExtensionsGenerator>.RunAsync(
|
||||
source,
|
||||
Path.Combine(
|
||||
TestContext.CurrentContext.TestDirectory,
|
||||
"enums",
|
||||
"snapshots",
|
||||
"EnumExtensionsGenerator",
|
||||
"DisableIsMethods"));
|
||||
GetSnapshotFolder("DisableIsMethods"),
|
||||
GetSnapshotFileName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证关闭集合判断开关后仅保留逐项判断方法。
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task Snapshot_DisableIsInMethod()
|
||||
{
|
||||
const string source = """
|
||||
using System;
|
||||
|
||||
namespace GFramework.SourceGenerators.Abstractions.Enums
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Enum)]
|
||||
public sealed class GenerateEnumExtensionsAttribute : Attribute
|
||||
{
|
||||
public bool GenerateIsMethods { get; set; } = true;
|
||||
public bool GenerateIsInMethod { get; set; } = true;
|
||||
}
|
||||
}
|
||||
|
||||
namespace TestApp
|
||||
{
|
||||
using GFramework.SourceGenerators.Abstractions.Enums;
|
||||
|
||||
[GenerateEnumExtensions(GenerateIsInMethod = false)]
|
||||
public enum Status
|
||||
{
|
||||
Active,
|
||||
Inactive
|
||||
}
|
||||
}
|
||||
""";
|
||||
var source = BuildSource(
|
||||
"""
|
||||
public enum Status
|
||||
{
|
||||
Active,
|
||||
Inactive
|
||||
}
|
||||
""",
|
||||
"[GenerateEnumExtensions(GenerateIsInMethod = false)]");
|
||||
|
||||
await GeneratorSnapshotTest<EnumExtensionsGenerator>.RunAsync(
|
||||
source,
|
||||
GetSnapshotFolder("DisableIsInMethod"),
|
||||
GetSnapshotFileName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证同时关闭两个生成开关时不会输出任何扩展方法。
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task Snapshot_DisableAllGeneratedMethods()
|
||||
{
|
||||
var source = BuildSource(
|
||||
"""
|
||||
public enum Status
|
||||
{
|
||||
Active,
|
||||
Inactive
|
||||
}
|
||||
""",
|
||||
"[GenerateEnumExtensions(GenerateIsMethods = false, GenerateIsInMethod = false)]");
|
||||
|
||||
await GeneratorSnapshotTest<EnumExtensionsGenerator>.RunAsync(
|
||||
source,
|
||||
GetSnapshotFolder("DisableAllGeneratedMethods"),
|
||||
GetSnapshotFileName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将运行时测试目录映射回仓库内已提交的枚举快照目录。
|
||||
/// </summary>
|
||||
/// <param name="scenarioName">快照场景名称。</param>
|
||||
/// <returns>场景对应的绝对快照目录。</returns>
|
||||
private static string GetSnapshotFolder(string scenarioName)
|
||||
{
|
||||
return Path.GetFullPath(
|
||||
Path.Combine(
|
||||
TestContext.CurrentContext.TestDirectory,
|
||||
"enums",
|
||||
"..",
|
||||
"..",
|
||||
"..",
|
||||
"Enums",
|
||||
"snapshots",
|
||||
"EnumExtensionsGenerator",
|
||||
"DisableIsInMethod"));
|
||||
scenarioName));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将生成器输出文件名映射为非 C# 快照文件名,避免快照资产被命名校验和项目编译误判为源码。
|
||||
/// </summary>
|
||||
/// <param name="generatedFileName">生成器输出的提示文件名。</param>
|
||||
/// <returns>对应的快照文件名。</returns>
|
||||
private static string GetSnapshotFileName(string generatedFileName)
|
||||
{
|
||||
return Path.ChangeExtension(generatedFileName, ".txt");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 构造最小自洽的测试输入源码,以稳定驱动枚举扩展生成器的快照测试。
|
||||
/// </summary>
|
||||
/// <param name="enumBody">要注入到测试命名空间中的枚举声明文本。</param>
|
||||
/// <param name="attributeUsage">枚举上的属性使用方式,默认启用所有生成选项。</param>
|
||||
/// <returns>包含内联测试属性与目标枚举声明的完整源码。</returns>
|
||||
/// <remarks>
|
||||
/// 这里内联声明 <c>GenerateEnumExtensionsAttribute</c>,以便每个快照输入保持最小自洽。
|
||||
/// 属性命名空间必须与生成器按 metadata name 查找的契约保持一致;如果命名空间、属性名或参数发生变更,
|
||||
/// 需要同步更新该模板与相关快照,否则测试可能出现静默漂移。
|
||||
/// </remarks>
|
||||
private static string BuildSource(string enumBody, string attributeUsage = "[GenerateEnumExtensions]")
|
||||
{
|
||||
// 保持属性声明与测试输入同处一个模板中,能够明确锁定生成器对元数据名称和可选参数的语义假设。
|
||||
return $$"""
|
||||
using System;
|
||||
|
||||
namespace {{EnumAttributeNamespace}}
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Enum)]
|
||||
public sealed class GenerateEnumExtensionsAttribute : Attribute
|
||||
{
|
||||
public bool GenerateIsMethods { get; set; } = true;
|
||||
public bool GenerateIsInMethod { get; set; } = true;
|
||||
}
|
||||
}
|
||||
|
||||
namespace TestApp
|
||||
{
|
||||
using {{EnumAttributeNamespace}};
|
||||
|
||||
{{attributeUsage}}
|
||||
{{enumBody}}
|
||||
}
|
||||
""";
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,21 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
namespace TestApp
|
||||
{
|
||||
public static partial class StatusExtensions
|
||||
{
|
||||
/// <summary>是否为 Active</summary>
|
||||
public static bool IsActive(this TestApp.Status value) => value == TestApp.Status.Active;
|
||||
|
||||
/// <summary>是否为 Inactive</summary>
|
||||
public static bool IsInactive(this TestApp.Status value) => value == TestApp.Status.Inactive;
|
||||
|
||||
/// <summary>判断是否属于指定集合</summary>
|
||||
public static bool IsIn(this TestApp.Status value, params TestApp.Status[] values)
|
||||
{
|
||||
if (values == null) return false;
|
||||
foreach (var v in values) if (value == v) return true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,24 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
namespace TestApp
|
||||
{
|
||||
public static partial class StatusExtensions
|
||||
{
|
||||
/// <summary>是否为 Active</summary>
|
||||
public static bool IsActive(this TestApp.Status value) => value == TestApp.Status.Active;
|
||||
|
||||
/// <summary>是否为 Inactive</summary>
|
||||
public static bool IsInactive(this TestApp.Status value) => value == TestApp.Status.Inactive;
|
||||
|
||||
/// <summary>是否为 Pending</summary>
|
||||
public static bool IsPending(this TestApp.Status value) => value == TestApp.Status.Pending;
|
||||
|
||||
/// <summary>判断是否属于指定集合</summary>
|
||||
public static bool IsIn(this TestApp.Status value, params TestApp.Status[] values)
|
||||
{
|
||||
if (values == null) return false;
|
||||
foreach (var v in values) if (value == v) return true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,8 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
namespace TestApp
|
||||
{
|
||||
public static partial class StatusExtensions
|
||||
{
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
namespace TestApp
|
||||
{
|
||||
public static partial class StatusExtensions
|
||||
{
|
||||
/// <summary>是否为 Active</summary>
|
||||
public static bool IsActive(this TestApp.Status value) => value == TestApp.Status.Active;
|
||||
|
||||
/// <summary>是否为 Inactive</summary>
|
||||
public static bool IsInactive(this TestApp.Status value) => value == TestApp.Status.Inactive;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,15 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
namespace TestApp
|
||||
{
|
||||
public static partial class StatusExtensions
|
||||
{
|
||||
/// <summary>判断是否属于指定集合</summary>
|
||||
public static bool IsIn(this TestApp.Status value, params TestApp.Status[] values)
|
||||
{
|
||||
if (values == null) return false;
|
||||
foreach (var v in values) if (value == v) return true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,27 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
namespace TestApp
|
||||
{
|
||||
public static partial class PermissionsExtensions
|
||||
{
|
||||
/// <summary>是否为 None</summary>
|
||||
public static bool IsNone(this TestApp.Permissions value) => value == TestApp.Permissions.None;
|
||||
|
||||
/// <summary>是否为 Read</summary>
|
||||
public static bool IsRead(this TestApp.Permissions value) => value == TestApp.Permissions.Read;
|
||||
|
||||
/// <summary>是否为 Write</summary>
|
||||
public static bool IsWrite(this TestApp.Permissions value) => value == TestApp.Permissions.Write;
|
||||
|
||||
/// <summary>是否为 Execute</summary>
|
||||
public static bool IsExecute(this TestApp.Permissions value) => value == TestApp.Permissions.Execute;
|
||||
|
||||
/// <summary>判断是否属于指定集合</summary>
|
||||
public static bool IsIn(this TestApp.Permissions value, params TestApp.Permissions[] values)
|
||||
{
|
||||
if (values == null) return false;
|
||||
foreach (var v in values) if (value == v) return true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -23,10 +23,16 @@
|
||||
</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>
|
||||
<Compile Remove="**\snapshots\**\*.cs"/>
|
||||
<Compile Remove="**\Snapshots\**\*.cs"/>
|
||||
<None Include="**\snapshots\**\*.cs"/>
|
||||
<None Include="**\Snapshots\**\*.cs"/>
|
||||
<Folder Include="rule\snapshots\ContextAwareGenerator\"/>
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
using System.IO;
|
||||
using GFramework.SourceGenerators.Logging;
|
||||
using GFramework.Core.SourceGenerators.Logging;
|
||||
using GFramework.SourceGenerators.Tests.Core;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace GFramework.SourceGenerators.Tests.Logging;
|
||||
|
||||
@ -14,7 +13,7 @@ public class LoggerGeneratorSnapshotTests
|
||||
const string source = """
|
||||
using System;
|
||||
|
||||
namespace GFramework.SourceGenerators.Abstractions.Logging
|
||||
namespace GFramework.Core.SourceGenerators.Abstractions.Logging
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public sealed class LogAttribute : Attribute
|
||||
@ -86,7 +85,7 @@ public class LoggerGeneratorSnapshotTests
|
||||
|
||||
namespace TestApp
|
||||
{
|
||||
using GFramework.SourceGenerators.Abstractions.Logging;
|
||||
using GFramework.Core.SourceGenerators.Abstractions.Logging;
|
||||
|
||||
[Log]
|
||||
public partial class MyService
|
||||
@ -111,7 +110,7 @@ public class LoggerGeneratorSnapshotTests
|
||||
const string source = """
|
||||
using System;
|
||||
|
||||
namespace GFramework.SourceGenerators.Abstractions.Logging
|
||||
namespace GFramework.Core.SourceGenerators.Abstractions.Logging
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public sealed class LogAttribute : Attribute
|
||||
@ -183,7 +182,7 @@ public class LoggerGeneratorSnapshotTests
|
||||
|
||||
namespace TestApp
|
||||
{
|
||||
using GFramework.SourceGenerators.Abstractions.Logging;
|
||||
using GFramework.Core.SourceGenerators.Abstractions.Logging;
|
||||
|
||||
[Log(Name = "CustomLogger")]
|
||||
public partial class MyService
|
||||
@ -208,7 +207,7 @@ public class LoggerGeneratorSnapshotTests
|
||||
const string source = """
|
||||
using System;
|
||||
|
||||
namespace GFramework.SourceGenerators.Abstractions.Logging
|
||||
namespace GFramework.Core.SourceGenerators.Abstractions.Logging
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public sealed class LogAttribute : Attribute
|
||||
@ -280,7 +279,7 @@ public class LoggerGeneratorSnapshotTests
|
||||
|
||||
namespace TestApp
|
||||
{
|
||||
using GFramework.SourceGenerators.Abstractions.Logging;
|
||||
using GFramework.Core.SourceGenerators.Abstractions.Logging;
|
||||
|
||||
[Log(FieldName = "MyLogger")]
|
||||
public partial class MyService
|
||||
@ -305,7 +304,7 @@ public class LoggerGeneratorSnapshotTests
|
||||
const string source = """
|
||||
using System;
|
||||
|
||||
namespace GFramework.SourceGenerators.Abstractions.Logging
|
||||
namespace GFramework.Core.SourceGenerators.Abstractions.Logging
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public sealed class LogAttribute : Attribute
|
||||
@ -377,7 +376,7 @@ public class LoggerGeneratorSnapshotTests
|
||||
|
||||
namespace TestApp
|
||||
{
|
||||
using GFramework.SourceGenerators.Abstractions.Logging;
|
||||
using GFramework.Core.SourceGenerators.Abstractions.Logging;
|
||||
|
||||
[Log(IsStatic = false)]
|
||||
public partial class MyService
|
||||
@ -402,7 +401,7 @@ public class LoggerGeneratorSnapshotTests
|
||||
const string source = """
|
||||
using System;
|
||||
|
||||
namespace GFramework.SourceGenerators.Abstractions.Logging
|
||||
namespace GFramework.Core.SourceGenerators.Abstractions.Logging
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public sealed class LogAttribute : Attribute
|
||||
@ -474,7 +473,7 @@ public class LoggerGeneratorSnapshotTests
|
||||
|
||||
namespace TestApp
|
||||
{
|
||||
using GFramework.SourceGenerators.Abstractions.Logging;
|
||||
using GFramework.Core.SourceGenerators.Abstractions.Logging;
|
||||
|
||||
[Log(AccessModifier = "public")]
|
||||
public partial class MyService
|
||||
@ -499,7 +498,7 @@ public class LoggerGeneratorSnapshotTests
|
||||
const string source = """
|
||||
using System;
|
||||
|
||||
namespace GFramework.SourceGenerators.Abstractions.Logging
|
||||
namespace GFramework.Core.SourceGenerators.Abstractions.Logging
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public sealed class LogAttribute : Attribute
|
||||
@ -571,7 +570,7 @@ public class LoggerGeneratorSnapshotTests
|
||||
|
||||
namespace TestApp
|
||||
{
|
||||
using GFramework.SourceGenerators.Abstractions.Logging;
|
||||
using GFramework.Core.SourceGenerators.Abstractions.Logging;
|
||||
|
||||
[Log]
|
||||
public partial class MyService<T>
|
||||
@ -589,4 +588,4 @@ public class LoggerGeneratorSnapshotTests
|
||||
"LoggerGenerator",
|
||||
"GenericClass"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
using System.IO;
|
||||
using GFramework.SourceGenerators.Rule;
|
||||
using GFramework.Core.SourceGenerators.Rule;
|
||||
using GFramework.SourceGenerators.Tests.Core;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace GFramework.SourceGenerators.Tests.Rule;
|
||||
|
||||
@ -24,7 +23,7 @@ public class ContextAwareGeneratorSnapshotTests
|
||||
const string source = """
|
||||
using System;
|
||||
|
||||
namespace GFramework.SourceGenerators.Abstractions.Rule
|
||||
namespace GFramework.Core.SourceGenerators.Abstractions.Rule
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public sealed class ContextAwareAttribute : Attribute { }
|
||||
@ -74,7 +73,7 @@ public class ContextAwareGeneratorSnapshotTests
|
||||
|
||||
namespace TestApp
|
||||
{
|
||||
using GFramework.SourceGenerators.Abstractions.Rule;
|
||||
using GFramework.Core.SourceGenerators.Abstractions.Rule;
|
||||
using GFramework.Core.Abstractions.Rule;
|
||||
|
||||
[ContextAware]
|
||||
@ -93,4 +92,4 @@ public class ContextAwareGeneratorSnapshotTests
|
||||
"snapshots",
|
||||
"ContextAwareGenerator"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
using GFramework.SourceGenerators.Rule;
|
||||
using GFramework.Core.SourceGenerators.Rule;
|
||||
using GFramework.SourceGenerators.Tests.Core;
|
||||
|
||||
namespace GFramework.SourceGenerators.Tests.Rule;
|
||||
@ -12,9 +12,9 @@ public class ContextGetGeneratorTests
|
||||
var source = """
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using GFramework.SourceGenerators.Abstractions.Rule;
|
||||
using GFramework.Core.SourceGenerators.Abstractions.Rule;
|
||||
|
||||
namespace GFramework.SourceGenerators.Abstractions.Rule
|
||||
namespace GFramework.Core.SourceGenerators.Abstractions.Rule
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
|
||||
public sealed class ContextAwareAttribute : Attribute { }
|
||||
@ -102,9 +102,9 @@ public class ContextGetGeneratorTests
|
||||
{
|
||||
var source = """
|
||||
using System;
|
||||
using GFramework.SourceGenerators.Abstractions.Rule;
|
||||
using GFramework.Core.SourceGenerators.Abstractions.Rule;
|
||||
|
||||
namespace GFramework.SourceGenerators.Abstractions.Rule
|
||||
namespace GFramework.Core.SourceGenerators.Abstractions.Rule
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
|
||||
public sealed class ContextAwareAttribute : Attribute { }
|
||||
@ -148,7 +148,7 @@ public class ContextGetGeneratorTests
|
||||
[ContextAware]
|
||||
public partial class InventoryPanel
|
||||
{
|
||||
[global::GFramework.SourceGenerators.Abstractions.Rule.GetModel]
|
||||
[global::GFramework.Core.SourceGenerators.Abstractions.Rule.GetModel]
|
||||
private IInventoryModel _model = null!;
|
||||
}
|
||||
}
|
||||
@ -184,9 +184,9 @@ public class ContextGetGeneratorTests
|
||||
var source = """
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using GFramework.SourceGenerators.Abstractions.Rule;
|
||||
using GFramework.Core.SourceGenerators.Abstractions.Rule;
|
||||
|
||||
namespace GFramework.SourceGenerators.Abstractions.Rule
|
||||
namespace GFramework.Core.SourceGenerators.Abstractions.Rule
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
|
||||
public sealed class GetAllAttribute : Attribute { }
|
||||
@ -291,9 +291,9 @@ public class ContextGetGeneratorTests
|
||||
{
|
||||
var source = """
|
||||
using System;
|
||||
using GFramework.SourceGenerators.Abstractions.Rule;
|
||||
using GFramework.Core.SourceGenerators.Abstractions.Rule;
|
||||
|
||||
namespace GFramework.SourceGenerators.Abstractions.Rule
|
||||
namespace GFramework.Core.SourceGenerators.Abstractions.Rule
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
|
||||
public sealed class GetAllAttribute : Attribute { }
|
||||
@ -382,9 +382,9 @@ public class ContextGetGeneratorTests
|
||||
{
|
||||
var source = """
|
||||
using System;
|
||||
using GFramework.SourceGenerators.Abstractions.Rule;
|
||||
using GFramework.Core.SourceGenerators.Abstractions.Rule;
|
||||
|
||||
namespace GFramework.SourceGenerators.Abstractions.Rule
|
||||
namespace GFramework.Core.SourceGenerators.Abstractions.Rule
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
|
||||
public sealed class ContextAwareAttribute : Attribute { }
|
||||
@ -460,9 +460,9 @@ public class ContextGetGeneratorTests
|
||||
{
|
||||
var source = """
|
||||
using System;
|
||||
using GFramework.SourceGenerators.Abstractions.Rule;
|
||||
using GFramework.Core.SourceGenerators.Abstractions.Rule;
|
||||
|
||||
namespace GFramework.SourceGenerators.Abstractions.Rule
|
||||
namespace GFramework.Core.SourceGenerators.Abstractions.Rule
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
|
||||
public sealed class GetAllAttribute : Attribute { }
|
||||
@ -543,9 +543,9 @@ public class ContextGetGeneratorTests
|
||||
{
|
||||
var source = MarkupTestSource.Parse("""
|
||||
using System;
|
||||
using GFramework.SourceGenerators.Abstractions.Rule;
|
||||
using GFramework.Core.SourceGenerators.Abstractions.Rule;
|
||||
|
||||
namespace GFramework.SourceGenerators.Abstractions.Rule
|
||||
namespace GFramework.Core.SourceGenerators.Abstractions.Rule
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
|
||||
public sealed class GetAllAttribute : Attribute { }
|
||||
@ -644,9 +644,9 @@ public class ContextGetGeneratorTests
|
||||
{
|
||||
var source = MarkupTestSource.Parse("""
|
||||
using System;
|
||||
using GFramework.SourceGenerators.Abstractions.Rule;
|
||||
using GFramework.Core.SourceGenerators.Abstractions.Rule;
|
||||
|
||||
namespace GFramework.SourceGenerators.Abstractions.Rule
|
||||
namespace GFramework.Core.SourceGenerators.Abstractions.Rule
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
|
||||
public sealed class GetAllAttribute : Attribute { }
|
||||
@ -753,9 +753,9 @@ public class ContextGetGeneratorTests
|
||||
{
|
||||
var source = """
|
||||
using System;
|
||||
using GFramework.SourceGenerators.Abstractions.Rule;
|
||||
using GFramework.Core.SourceGenerators.Abstractions.Rule;
|
||||
|
||||
namespace GFramework.SourceGenerators.Abstractions.Rule
|
||||
namespace GFramework.Core.SourceGenerators.Abstractions.Rule
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
|
||||
public sealed class ContextAwareAttribute : Attribute { }
|
||||
@ -845,9 +845,9 @@ public class ContextGetGeneratorTests
|
||||
{
|
||||
var source = """
|
||||
using System;
|
||||
using GFramework.SourceGenerators.Abstractions.Rule;
|
||||
using GFramework.Core.SourceGenerators.Abstractions.Rule;
|
||||
|
||||
namespace GFramework.SourceGenerators.Abstractions.Rule
|
||||
namespace GFramework.Core.SourceGenerators.Abstractions.Rule
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Field, Inherited = false)]
|
||||
public sealed class GetServiceAttribute : Attribute { }
|
||||
@ -922,9 +922,9 @@ public class ContextGetGeneratorTests
|
||||
{
|
||||
var source = MarkupTestSource.Parse("""
|
||||
using System;
|
||||
using GFramework.SourceGenerators.Abstractions.Rule;
|
||||
using GFramework.Core.SourceGenerators.Abstractions.Rule;
|
||||
|
||||
namespace GFramework.SourceGenerators.Abstractions.Rule
|
||||
namespace GFramework.Core.SourceGenerators.Abstractions.Rule
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Field, Inherited = false)]
|
||||
public sealed class GetModelAttribute : Attribute { }
|
||||
@ -989,9 +989,9 @@ public class ContextGetGeneratorTests
|
||||
var source = MarkupTestSource.Parse("""
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using GFramework.SourceGenerators.Abstractions.Rule;
|
||||
using GFramework.Core.SourceGenerators.Abstractions.Rule;
|
||||
|
||||
namespace GFramework.SourceGenerators.Abstractions.Rule
|
||||
namespace GFramework.Core.SourceGenerators.Abstractions.Rule
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Field, Inherited = false)]
|
||||
public sealed class GetModelsAttribute : Attribute { }
|
||||
@ -1060,9 +1060,9 @@ public class ContextGetGeneratorTests
|
||||
{
|
||||
var source = MarkupTestSource.Parse("""
|
||||
using System;
|
||||
using GFramework.SourceGenerators.Abstractions.Rule;
|
||||
using GFramework.Core.SourceGenerators.Abstractions.Rule;
|
||||
|
||||
namespace GFramework.SourceGenerators.Abstractions.Rule
|
||||
namespace GFramework.Core.SourceGenerators.Abstractions.Rule
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Field, Inherited = false)]
|
||||
public sealed class GetModelAttribute : Attribute { }
|
||||
@ -1131,9 +1131,9 @@ public class ContextGetGeneratorTests
|
||||
{
|
||||
var source = MarkupTestSource.Parse("""
|
||||
using System;
|
||||
using GFramework.SourceGenerators.Abstractions.Rule;
|
||||
using GFramework.Core.SourceGenerators.Abstractions.Rule;
|
||||
|
||||
namespace GFramework.SourceGenerators.Abstractions.Rule
|
||||
namespace GFramework.Core.SourceGenerators.Abstractions.Rule
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Field, Inherited = false)]
|
||||
public sealed class GetModelAttribute : Attribute { }
|
||||
@ -1203,9 +1203,9 @@ public class ContextGetGeneratorTests
|
||||
var source = """
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using GFramework.SourceGenerators.Abstractions.Rule;
|
||||
using GFramework.Core.SourceGenerators.Abstractions.Rule;
|
||||
|
||||
namespace GFramework.SourceGenerators.Abstractions.Rule
|
||||
namespace GFramework.Core.SourceGenerators.Abstractions.Rule
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Field, Inherited = false)]
|
||||
public sealed class GetModelsAttribute : Attribute { }
|
||||
|
||||
@ -1,113 +0,0 @@
|
||||
using System.Text;
|
||||
using GFramework.SourceGenerators.Common.Constants;
|
||||
using GFramework.SourceGenerators.Common.Diagnostics;
|
||||
using GFramework.SourceGenerators.Common.Generator;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
|
||||
namespace GFramework.SourceGenerators.Enums;
|
||||
|
||||
/// <summary>
|
||||
/// 枚举扩展方法生成器,用于自动生成枚举相关的扩展方法
|
||||
/// </summary>
|
||||
[Generator]
|
||||
public sealed class EnumExtensionsGenerator : AttributeEnumGeneratorBase
|
||||
{
|
||||
private static string AttributeMetadataName =>
|
||||
$"{PathContests.SourceGeneratorsAbstractionsPath}.Enums.GenerateEnumExtensionsAttribute";
|
||||
|
||||
/// <summary>
|
||||
/// 仅用于 Syntax 粗筛选
|
||||
/// </summary>
|
||||
protected override string AttributeShortNameWithoutSuffix => "GenerateEnumExtensions";
|
||||
|
||||
protected override AttributeData? ResolveAttribute(Compilation compilation, INamedTypeSymbol symbol)
|
||||
{
|
||||
var attrSymbol = compilation.GetTypeByMetadataName(AttributeMetadataName);
|
||||
|
||||
if (attrSymbol is null)
|
||||
return null;
|
||||
|
||||
return symbol.GetAttributes()
|
||||
.FirstOrDefault(a =>
|
||||
SymbolEqualityComparer.Default.Equals(a.AttributeClass, attrSymbol));
|
||||
}
|
||||
|
||||
protected override bool ValidateSymbol(SourceProductionContext context, Compilation compilation,
|
||||
EnumDeclarationSyntax syntax,
|
||||
INamedTypeSymbol symbol, AttributeData attr)
|
||||
{
|
||||
if (symbol.TypeKind == TypeKind.Enum) return true;
|
||||
var loc = syntax.Identifier.GetLocation();
|
||||
context.ReportDiagnostic(Diagnostic.Create(
|
||||
CommonDiagnostics.ClassMustBePartial,
|
||||
loc,
|
||||
symbol.Name
|
||||
));
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 生成枚举扩展方法的源代码
|
||||
/// </summary>
|
||||
/// <param name="symbol">枚举类型符号</param>
|
||||
/// <param name="attr">属性数据</param>
|
||||
/// <returns>生成的源代码字符串</returns>
|
||||
protected override string Generate(INamedTypeSymbol symbol, AttributeData attr)
|
||||
{
|
||||
var ns = symbol.ContainingNamespace.IsGlobalNamespace
|
||||
? null
|
||||
: symbol.ContainingNamespace.ToDisplayString();
|
||||
|
||||
var enumName = symbol.Name;
|
||||
var fullEnumName = symbol.ToDisplayString();
|
||||
var members = symbol.GetMembers()
|
||||
.OfType<IFieldSymbol>()
|
||||
.Where(f => f.ConstantValue != null)
|
||||
.ToArray();
|
||||
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendLine("// <auto-generated />");
|
||||
sb.AppendLine("using System;");
|
||||
|
||||
sb.AppendLine(!string.IsNullOrEmpty(ns) ? $"namespace {ns}" : "namespace EnumExtensionsGenerated");
|
||||
|
||||
sb.AppendLine("{");
|
||||
|
||||
sb.AppendLine($" public static partial class {enumName}Extensions");
|
||||
sb.AppendLine(" {");
|
||||
|
||||
// 生成 IsX 方法
|
||||
foreach (var memberName in members.Select(m => m.Name))
|
||||
{
|
||||
sb.AppendLine($" /// <summary>是否为 {memberName}</summary>");
|
||||
sb.AppendLine(
|
||||
$" public static bool Is{memberName}(this {fullEnumName} value) => value == {fullEnumName}.{memberName};");
|
||||
sb.AppendLine();
|
||||
}
|
||||
|
||||
// 生成 IsIn 方法
|
||||
sb.AppendLine(" /// <summary>判断是否属于指定集合</summary>");
|
||||
sb.AppendLine($" public static bool IsIn(this {fullEnumName} value, params {fullEnumName}[] values)");
|
||||
sb.AppendLine(" {");
|
||||
sb.AppendLine(" if (values == null) return false;");
|
||||
sb.AppendLine(" foreach (var v in values) if (value == v) return true;");
|
||||
sb.AppendLine(" return false;");
|
||||
sb.AppendLine(" }");
|
||||
|
||||
sb.AppendLine(" }");
|
||||
sb.AppendLine("}"); // namespace
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取生成文件的提示名称
|
||||
/// </summary>
|
||||
/// <param name="symbol">命名类型符号</param>
|
||||
/// <returns>生成文件的提示名称</returns>
|
||||
protected override string GetHintName(INamedTypeSymbol symbol)
|
||||
{
|
||||
return $"{symbol.Name}.EnumExtensions.g.cs";
|
||||
}
|
||||
}
|
||||
@ -29,21 +29,24 @@
|
||||
<None Remove="GFramework.Core\**"/>
|
||||
<None Remove="GFramework.Game\**"/>
|
||||
<None Remove="GFramework.Godot\**"/>
|
||||
<None Update="GFramework.SourceGenerators\logging\README.md">
|
||||
<Link>GFramework.SorceGenerators\logging\README.md</Link>
|
||||
<None Update="GFramework.Core.SourceGenerators\logging\README.md">
|
||||
<Link>GFramework.Core.SourceGenerators\logging\README.md</Link>
|
||||
</None>
|
||||
<None Update="GFramework.SourceGenerators\README.md">
|
||||
<Link>GFramework.SorceGenerators\README.md</Link>
|
||||
<None Update="GFramework.Core.SourceGenerators\README.md">
|
||||
<Link>GFramework.Core.SourceGenerators\README.md</Link>
|
||||
</None>
|
||||
<None Update="GFramework.SourceGenerators\AnalyzerReleases.Shipped.md">
|
||||
<Link>GFramework.SorceGenerators\AnalyzerReleases.Shipped.md</Link>
|
||||
<None Update="GFramework.Core.SourceGenerators\AnalyzerReleases.Shipped.md">
|
||||
<Link>GFramework.Core.SourceGenerators\AnalyzerReleases.Shipped.md</Link>
|
||||
</None>
|
||||
<None Update="GFramework.SourceGenerators\AnalyzerReleases.Unshipped.md">
|
||||
<Link>GFramework.SorceGenerators\AnalyzerReleases.Unshipped.md</Link>
|
||||
<None Update="GFramework.Core.SourceGenerators\AnalyzerReleases.Unshipped.md">
|
||||
<Link>GFramework.Core.SourceGenerators\AnalyzerReleases.Unshipped.md</Link>
|
||||
</None>
|
||||
<None Remove="GFramework.Godot.SourceGenerators\**"/>
|
||||
<None Remove="GFramework.SorceGenerators\**"/>
|
||||
<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.Tests\**"/>
|
||||
<None Remove="GFramework.Godot.SourceGenerators.Tests\**"/>
|
||||
@ -77,18 +80,21 @@
|
||||
<Compile Remove="GFramework.Core\**"/>
|
||||
<Compile Remove="GFramework.Game\**"/>
|
||||
<Compile Remove="GFramework.Godot\**"/>
|
||||
<Compile Update="GFramework.SourceGenerators\enums\EnumExtensionsGenerator.cs">
|
||||
<Link>GFramework.SorceGenerators\enums\EnumExtensionsGenerator.cs</Link>
|
||||
<Compile Update="GFramework.Core.SourceGenerators\enums\EnumExtensionsGenerator.cs">
|
||||
<Link>GFramework.Core.SourceGenerators\enums\EnumExtensionsGenerator.cs</Link>
|
||||
</Compile>
|
||||
<Compile Update="GFramework.SourceGenerators\logging\Diagnostic.cs">
|
||||
<Link>GFramework.SorceGenerators\logging\Diagnostic.cs</Link>
|
||||
<Compile Update="GFramework.Core.SourceGenerators\logging\Diagnostic.cs">
|
||||
<Link>GFramework.Core.SourceGenerators\logging\Diagnostic.cs</Link>
|
||||
</Compile>
|
||||
<Compile Update="GFramework.SourceGenerators\logging\LoggerGenerator.cs">
|
||||
<Link>GFramework.SorceGenerators\logging\LoggerGenerator.cs</Link>
|
||||
<Compile Update="GFramework.Core.SourceGenerators\logging\LoggerGenerator.cs">
|
||||
<Link>GFramework.Core.SourceGenerators\logging\LoggerGenerator.cs</Link>
|
||||
</Compile>
|
||||
<Compile Remove="GFramework.Godot.SourceGenerators\**"/>
|
||||
<Compile Remove="GFramework.SorceGenerators\**"/>
|
||||
<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.Tests\**"/>
|
||||
<Compile Remove="GFramework.Godot.SourceGenerators.Tests\**"/>
|
||||
@ -118,8 +124,11 @@
|
||||
<EmbeddedResource Remove="GFramework.Game\**"/>
|
||||
<EmbeddedResource Remove="GFramework.Godot\**"/>
|
||||
<EmbeddedResource Remove="GFramework.Godot.SourceGenerators\**"/>
|
||||
<EmbeddedResource Remove="GFramework.SorceGenerators\**"/>
|
||||
<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.Tests\**"/>
|
||||
<EmbeddedResource Remove="GFramework.Godot.SourceGenerators.Tests\**"/>
|
||||
|
||||
@ -2,10 +2,6 @@
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GFramework", "GFramework.csproj", "{9BEDDD6C-DF8B-4E71-9C75-F44EC669ABBD}"
|
||||
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}"
|
||||
EndProject
|
||||
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
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GFramework.Tests.Common", "GFramework.Tests.Common\GFramework.Tests.Common.csproj", "{1100EE3E-A12D-4DE5-ABA8-591D3126570B}"
|
||||
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
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
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|x86.ActiveCfg = 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.Build.0 = 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|x86.ActiveCfg = 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
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
||||
@ -68,8 +68,11 @@ dotnet add package GeWuYou.GFramework.Game.Abstractions
|
||||
# Godot 集成(仅 Godot 项目需要)
|
||||
dotnet add package GeWuYou.GFramework.Godot
|
||||
|
||||
# 源码生成器(可选,但推荐)
|
||||
dotnet add package GeWuYou.GFramework.SourceGenerators
|
||||
# 按场景选择源码生成器(可选,但推荐)
|
||||
dotnet add package GeWuYou.GFramework.Core.SourceGenerators
|
||||
dotnet add package GeWuYou.GFramework.Game.SourceGenerators
|
||||
dotnet add package GeWuYou.GFramework.Godot.SourceGenerators
|
||||
dotnet add package GeWuYou.GFramework.Cqrs.SourceGenerators
|
||||
```
|
||||
|
||||
## 可选模块导入
|
||||
|
||||
@ -246,9 +246,10 @@ public class GameArchitecture : Architecture
|
||||
优先使用程序集级生成注册器,失败时自动回退到反射扫描;如果同一程序集已经由默认路径或其他模块接入,框架会自动去重,避免重复注册
|
||||
handler。
|
||||
|
||||
`RegisterCqrsPipelineBehavior<TBehavior>()` 是推荐入口;旧的 `RegisterMediatorBehavior<TBehavior>()`
|
||||
仅作为兼容名称保留,当前已标记为 `Obsolete` 并从 IntelliSense 主路径隐藏,计划在未来 major 版本中移除。
|
||||
`ContextAwareMediator*Extensions` 与 `MediatorCoroutineExtensions` 也遵循同样的弃用节奏。当前接口支持两种形式:
|
||||
`RegisterCqrsPipelineBehavior<TBehavior>()` 是唯一保留的公开入口;旧的 `Mediator` 兼容别名与扩展已移除,不再继续维护。
|
||||
如果你正在从旧版本迁移,显式替换关系就是
|
||||
`RegisterMediatorBehavior<TBehavior>() -> RegisterCqrsPipelineBehavior<TBehavior>()`。
|
||||
当前接口支持两种形式:
|
||||
|
||||
- 开放泛型行为,例如 `LoggingBehavior<,>`,用于匹配所有请求
|
||||
- 封闭行为类型,例如某个只服务于单一请求的 `SpecialBehavior`
|
||||
|
||||
@ -153,31 +153,36 @@ GameProject/
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\GFramework.Game\GFramework.Game.csproj" />
|
||||
<ProjectReference Include="..\GFramework.SourceGenerators.Abstractions\GFramework.SourceGenerators.Abstractions.csproj"
|
||||
OutputItemType="Analyzer"
|
||||
ReferenceOutputAssembly="false" />
|
||||
<ProjectReference Include="..\GFramework.Core.SourceGenerators.Abstractions\GFramework.Core.SourceGenerators.Abstractions.csproj" />
|
||||
<ProjectReference Include="..\GFramework.SourceGenerators.Common\GFramework.SourceGenerators.Common.csproj"
|
||||
OutputItemType="Analyzer"
|
||||
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"
|
||||
ReferenceOutputAssembly="false" />
|
||||
</ItemGroup>
|
||||
|
||||
<Import Project="..\GFramework.SourceGenerators\GeWuYou.GFramework.SourceGenerators.targets" />
|
||||
<Import Project="..\GFramework.Game.SourceGenerators\GeWuYou.GFramework.Game.SourceGenerators.targets" />
|
||||
</Project>
|
||||
```
|
||||
|
||||
这段配置的作用:
|
||||
|
||||
- `GFramework.Game` 提供运行时 `YamlConfigLoader`、`ConfigRegistry`、`GameConfigBootstrap`、`GameConfigModule` 和只读表实现
|
||||
- 三个 `ProjectReference(... OutputItemType="Analyzer")` 把生成器接进当前消费者项目
|
||||
- `GeWuYou.GFramework.SourceGenerators.targets` 自动把 `schemas/**/*.schema.json` 加入 `AdditionalFiles`
|
||||
- `GFramework.Core.SourceGenerators.Abstractions` 提供 `Core` 侧 source-generator attributes
|
||||
- `GFramework.SourceGenerators.Common`、`GFramework.Core.SourceGenerators` 与 `GFramework.Game.SourceGenerators`
|
||||
共同把生成器接进当前消费者项目
|
||||
- `GeWuYou.GFramework.Game.SourceGenerators.targets` 自动把 `schemas/**/*.schema.json` 加入 `AdditionalFiles`
|
||||
|
||||
如果你使用打包后的 NuGet,而不是仓库内项目引用,原则保持不变:
|
||||
|
||||
- 运行时项目需要引用 `GeWuYou.GFramework.Game`
|
||||
- 生成器项目需要引用 `GeWuYou.GFramework.SourceGenerators`
|
||||
- 配置 schema 生成器需要引用 `GeWuYou.GFramework.Game.SourceGenerators`
|
||||
- 如果同一项目还会使用 `[Log]`、`[ContextAware]`、`[GetSystem]` 等 Core 侧生成器特性,再额外引用
|
||||
`GeWuYou.GFramework.Core.SourceGenerators`
|
||||
- schema 目录默认仍然是 `schemas/`
|
||||
|
||||
如果你的 schema 不放在默认目录,可以在项目文件里覆盖:
|
||||
|
||||
@ -6,13 +6,18 @@ GFramework 提供多种安装方式,您可以根据项目需求选择合适的
|
||||
|
||||
GFramework 采用模块化设计,不同包提供不同的功能:
|
||||
|
||||
| 包名 | 说明 | 适用场景 |
|
||||
|---------------------------------------|---------|-----------|
|
||||
| `GeWuYou.GFramework` | 聚合元包 | 快速试用、原型开发 |
|
||||
| `GeWuYou.GFramework.Core` | 核心框架 | 生产项目推荐 |
|
||||
| `GeWuYou.GFramework.Game` | 游戏模块 | 需要游戏特定功能 |
|
||||
| `GeWuYou.GFramework.Godot` | Godot集成 | Godot项目必需 |
|
||||
| `GeWuYou.GFramework.SourceGenerators` | 源码生成器 | 推荐安装 |
|
||||
| 包名 | 说明 | 适用场景 |
|
||||
|---------------------------------------------|-------------|--------------------------------|
|
||||
| `GeWuYou.GFramework` | 聚合元包 | 快速试用、原型开发 |
|
||||
| `GeWuYou.GFramework.Core` | 核心框架 | 生产项目推荐 |
|
||||
| `GeWuYou.GFramework.Game` | 游戏模块 | 需要游戏特定功能 |
|
||||
| `GeWuYou.GFramework.Godot` | Godot集成 | Godot项目必需 |
|
||||
| `GeWuYou.GFramework.Core.SourceGenerators` | Core 源码生成器 | `[Log]`、`[ContextAware]`、架构注入等 |
|
||||
| `GeWuYou.GFramework.Game.SourceGenerators` | Game 源码生成器 | 配置 schema / 配表生成 |
|
||||
| `GeWuYou.GFramework.Godot.SourceGenerators` | Godot 源码生成器 | Godot 节点、UI、项目元数据生成 |
|
||||
| `GeWuYou.GFramework.Cqrs.SourceGenerators` | CQRS 源码生成器 | 处理器注册表生成 |
|
||||
|
||||
当前 NuGet 发布按模块拆分 source generator 包,不存在 `GeWuYou.GFramework.SourceGenerators` 聚合包。
|
||||
|
||||
## 安装方式
|
||||
|
||||
@ -30,8 +35,17 @@ dotnet add package GeWuYou.GFramework.Game.Abstractions
|
||||
# Godot 集成(仅 Godot 项目需要)
|
||||
dotnet add package GeWuYou.GFramework.Godot
|
||||
|
||||
# 源码生成器(可选,但推荐)
|
||||
dotnet add package GeWuYou.GFramework.SourceGenerators
|
||||
# Core 侧源码生成器([Log] / [ContextAware] / [GetSystem] 等)
|
||||
dotnet add package GeWuYou.GFramework.Core.SourceGenerators
|
||||
|
||||
# Game 配置 schema 生成器
|
||||
dotnet add package GeWuYou.GFramework.Game.SourceGenerators
|
||||
|
||||
# Godot 生成器(仅 Godot 项目需要)
|
||||
dotnet add package GeWuYou.GFramework.Godot.SourceGenerators
|
||||
|
||||
# CQRS 处理器注册生成器(仅使用 CQRS source generator 时需要)
|
||||
dotnet add package GeWuYou.GFramework.Cqrs.SourceGenerators
|
||||
```
|
||||
|
||||
### 2. 使用 PackageReference
|
||||
@ -56,8 +70,14 @@ dotnet add package GeWuYou.GFramework.SourceGenerators
|
||||
<!-- Godot 集成 -->
|
||||
<PackageReference Include="GeWuYou.GFramework.Godot" Version="1.0.0" />
|
||||
|
||||
<!-- 源码生成器 -->
|
||||
<PackageReference Include="GeWuYou.GFramework.SourceGenerators" Version="1.0.0"
|
||||
<!-- 按场景选择的源码生成器 -->
|
||||
<PackageReference Include="GeWuYou.GFramework.Core.SourceGenerators" Version="1.0.0"
|
||||
PrivateAssets="all" ExcludeAssets="runtime" />
|
||||
<PackageReference Include="GeWuYou.GFramework.Game.SourceGenerators" Version="1.0.0"
|
||||
PrivateAssets="all" ExcludeAssets="runtime" />
|
||||
<PackageReference Include="GeWuYou.GFramework.Godot.SourceGenerators" Version="1.0.0"
|
||||
PrivateAssets="all" ExcludeAssets="runtime" />
|
||||
<PackageReference Include="GeWuYou.GFramework.Cqrs.SourceGenerators" Version="1.0.0"
|
||||
PrivateAssets="all" ExcludeAssets="runtime" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@ -183,6 +203,8 @@ dotnet build
|
||||
|
||||
检查:
|
||||
|
||||
- 确保安装了 `GeWuYou.GFramework.SourceGenerators`
|
||||
- 确保安装了与你正在使用的特性对应的拆分生成器包,例如:
|
||||
`GeWuYou.GFramework.Core.SourceGenerators`、`GeWuYou.GFramework.Game.SourceGenerators`、
|
||||
`GeWuYou.GFramework.Godot.SourceGenerators` 或 `GeWuYou.GFramework.Cqrs.SourceGenerators`
|
||||
- 重启 IDE
|
||||
- 清理并重新构建项目
|
||||
|
||||
@ -30,7 +30,16 @@ GFramework.SourceGenerators 是 GFramework 框架的源代码生成器包,通
|
||||
|
||||
## 概述
|
||||
|
||||
GFramework.SourceGenerators 利用 Roslyn 源代码生成器技术,在编译时分析你的代码并自动生成常用的样板代码,让开发者专注于业务逻辑而不是重复的模板代码。
|
||||
GFramework 的 source generators 利用 Roslyn 源代码生成器技术,在编译时分析你的代码并自动生成常用的样板代码,让开发者专注于业务逻辑而不是重复的模板代码。
|
||||
|
||||
当前 NuGet 发布按模块拆分为:
|
||||
|
||||
- `GeWuYou.GFramework.Core.SourceGenerators`
|
||||
- `GeWuYou.GFramework.Game.SourceGenerators`
|
||||
- `GeWuYou.GFramework.Godot.SourceGenerators`
|
||||
- `GeWuYou.GFramework.Cqrs.SourceGenerators`
|
||||
|
||||
不存在 `GeWuYou.GFramework.SourceGenerators` 或 `GeWuYou.GFramework.SourceGenerators.Attributes` 这类聚合包。
|
||||
|
||||
### 核心设计理念
|
||||
|
||||
@ -74,22 +83,31 @@ GFramework.SourceGenerators 利用 Roslyn 源代码生成器技术,在编译
|
||||
### NuGet 包安装
|
||||
|
||||
```xml
|
||||
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="GeWuYou.GFramework.SourceGenerators" Version="1.0.0"/>
|
||||
<PackageReference Include="GeWuYou.GFramework.SourceGenerators.Attributes" Version="1.0.0"/>
|
||||
<PackageReference Include="GeWuYou.GFramework.Core.SourceGenerators"
|
||||
PrivateAssets="all"
|
||||
ExcludeAssets="runtime" />
|
||||
<PackageReference Include="GeWuYou.GFramework.Game.SourceGenerators"
|
||||
PrivateAssets="all"
|
||||
ExcludeAssets="runtime" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
```
|
||||
|
||||
如果你只使用 Godot 生成器或 CQRS 处理器注册生成器,请把上面的包替换为对应的
|
||||
`GeWuYou.GFramework.Godot.SourceGenerators` 或 `GeWuYou.GFramework.Cqrs.SourceGenerators`。
|
||||
这些拆分包会同时带上各自需要的 abstractions 程序集,不需要再额外安装单独的 `*.Attributes` 包。
|
||||
实际接入时请替换为当前发布版本,或与项目中其余 `GeWuYou.GFramework.*` 包保持同一版本。
|
||||
|
||||
### Config Schema 文件约定
|
||||
|
||||
当项目引用 `GeWuYou.GFramework.SourceGenerators` 的打包产物时,生成器会默认从 `schemas/**/*.schema.json` 收集配置 schema
|
||||
当项目引用 `GeWuYou.GFramework.Game.SourceGenerators` 的打包产物时,生成器会默认从 `schemas/**/*.schema.json` 收集配置
|
||||
schema
|
||||
文件并作为 `AdditionalFiles` 输入。
|
||||
|
||||
这意味着消费者项目通常只需要维护如下结构:
|
||||
@ -150,7 +168,7 @@ Config Schema 生成器会扫描 `*.schema.json` 文件,并生成:
|
||||
### 基础使用
|
||||
|
||||
```csharp
|
||||
using GFramework.SourceGenerators.Attributes;
|
||||
using GFramework.Core.SourceGenerators.Abstractions.Logging;
|
||||
|
||||
[Log]
|
||||
public partial class PlayerController
|
||||
@ -229,7 +247,7 @@ public static partial class MathHelper
|
||||
|
||||
```csharp
|
||||
using GFramework.Core.Abstractions.Controller;
|
||||
using GFramework.SourceGenerators.Abstractions.Rule;
|
||||
using GFramework.Core.SourceGenerators.Abstractions.Rule;
|
||||
|
||||
[ContextAware]
|
||||
public partial class PlayerController : IController
|
||||
@ -337,8 +355,8 @@ public async Task TestPlayerController()
|
||||
|
||||
```csharp
|
||||
using GFramework.Core.Abstractions.Controller;
|
||||
using GFramework.SourceGenerators.Abstractions.Logging;
|
||||
using GFramework.SourceGenerators.Abstractions.Rule;
|
||||
using GFramework.Core.SourceGenerators.Abstractions.Logging;
|
||||
using GFramework.Core.SourceGenerators.Abstractions.Rule;
|
||||
|
||||
[Log]
|
||||
[ContextAware]
|
||||
@ -364,7 +382,7 @@ public partial class AdvancedController : IController
|
||||
### 基础使用
|
||||
|
||||
```csharp
|
||||
using GFramework.SourceGenerators.Abstractions.Enums;
|
||||
using GFramework.Core.SourceGenerators.Abstractions.Enums;
|
||||
|
||||
[GenerateEnumExtensions]
|
||||
public enum GameState
|
||||
@ -543,7 +561,7 @@ AutoRegisterModule 生成器面向 GFramework 模块安装场景,为类上的
|
||||
### 基础示例
|
||||
|
||||
```csharp
|
||||
using GFramework.SourceGenerators.Abstractions.Architectures;
|
||||
using GFramework.Core.SourceGenerators.Abstractions.Architectures;
|
||||
|
||||
[AutoRegisterModule]
|
||||
[RegisterModel(typeof(RunStateModel))]
|
||||
@ -846,8 +864,8 @@ public class InefficientController : IController
|
||||
|
||||
```csharp
|
||||
using GFramework.Core.Abstractions.Controller;
|
||||
using GFramework.SourceGenerators.Abstractions.Logging;
|
||||
using GFramework.SourceGenerators.Abstractions.Rule;
|
||||
using GFramework.Core.SourceGenerators.Abstractions.Logging;
|
||||
using GFramework.Core.SourceGenerators.Abstractions.Rule;
|
||||
|
||||
[Log]
|
||||
[ContextAware]
|
||||
@ -936,8 +954,8 @@ public enum CharacterState
|
||||
}
|
||||
|
||||
using GFramework.Core.Abstractions.Controller;
|
||||
using GFramework.SourceGenerators.Abstractions.Logging;
|
||||
using GFramework.SourceGenerators.Abstractions.Rule;
|
||||
using GFramework.Core.SourceGenerators.Abstractions.Logging;
|
||||
using GFramework.Core.SourceGenerators.Abstractions.Rule;
|
||||
|
||||
[Log]
|
||||
[ContextAware]
|
||||
@ -1218,11 +1236,14 @@ public partial class ServiceComponent : IService
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
A[GFramework.SourceGenerators] --> B[GFramework.SourceGenerators.Abstractions]
|
||||
A[GeWuYou.GFramework.Core.SourceGenerators] --> B[GFramework.Core.SourceGenerators.Abstractions]
|
||||
A --> C[GFramework.SourceGenerators.Common]
|
||||
A --> D[GFramework.Core.Abstractions]
|
||||
A --> E[Microsoft.CodeAnalysis.CSharp]
|
||||
A --> F[Microsoft.CodeAnalysis.Analyzers]
|
||||
G[GeWuYou.GFramework.Game.SourceGenerators] --> C
|
||||
H[GeWuYou.GFramework.Godot.SourceGenerators] --> C
|
||||
I[GeWuYou.GFramework.Cqrs.SourceGenerators] --> C
|
||||
```
|
||||
|
||||
## 版本兼容性
|
||||
|
||||
@ -74,8 +74,11 @@ dotnet add package GeWuYou.GFramework
|
||||
# Godot 集成
|
||||
dotnet add package GeWuYou.GFramework.Godot
|
||||
|
||||
# 源码生成器(可选,但推荐)
|
||||
dotnet add package GeWuYou.GFramework.SourceGenerators
|
||||
# Core 侧源码生成器([Log] / [ContextAware] 等)
|
||||
dotnet add package GeWuYou.GFramework.Core.SourceGenerators
|
||||
|
||||
# Godot 侧源码生成器([GetNode] / [AutoUiPage] 等)
|
||||
dotnet add package GeWuYou.GFramework.Godot.SourceGenerators
|
||||
```
|
||||
|
||||
::: details 分包安装(了解即可)
|
||||
@ -93,8 +96,11 @@ dotnet add package GeWuYou.GFramework.Game.Abstractions
|
||||
# Godot 集成
|
||||
dotnet add package GeWuYou.GFramework.Godot
|
||||
|
||||
# 源码生成器
|
||||
dotnet add package GeWuYou.GFramework.SourceGenerators
|
||||
# Core 侧源码生成器
|
||||
dotnet add package GeWuYou.GFramework.Core.SourceGenerators
|
||||
|
||||
# Godot 侧源码生成器
|
||||
dotnet add package GeWuYou.GFramework.Godot.SourceGenerators
|
||||
```
|
||||
|
||||
:::
|
||||
@ -108,7 +114,8 @@ dotnet add package GeWuYou.GFramework.SourceGenerators
|
||||
3. 安装以下包:
|
||||
- `GeWuYou.GFramework`
|
||||
- `GeWuYou.GFramework.Godot`
|
||||
- `GeWuYou.GFramework.SourceGenerators`
|
||||
- `GeWuYou.GFramework.Core.SourceGenerators`
|
||||
- `GeWuYou.GFramework.Godot.SourceGenerators`
|
||||
|
||||

|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user