diff --git a/JiShe.CollectBus.sln b/JiShe.CollectBus.sln index 1ecb18c..2e5826f 100644 --- a/JiShe.CollectBus.sln +++ b/JiShe.CollectBus.sln @@ -62,6 +62,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "0.Docs", "0.Docs", "{D8346C readme.md = readme.md EndProjectSection EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JiShe.CollectBus.SourceExtend", "shared\JiShe.CollectBus.SourceExtend\JiShe.CollectBus.SourceExtend.csproj", "{A34A4EA6-AEB0-4199-8E3A-6F042DBB51D9}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -148,6 +150,10 @@ Global {75B7D419-C261-577D-58D6-AA3ACED9129F}.Debug|Any CPU.Build.0 = Debug|Any CPU {75B7D419-C261-577D-58D6-AA3ACED9129F}.Release|Any CPU.ActiveCfg = Release|Any CPU {75B7D419-C261-577D-58D6-AA3ACED9129F}.Release|Any CPU.Build.0 = Release|Any CPU + {A34A4EA6-AEB0-4199-8E3A-6F042DBB51D9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A34A4EA6-AEB0-4199-8E3A-6F042DBB51D9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A34A4EA6-AEB0-4199-8E3A-6F042DBB51D9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A34A4EA6-AEB0-4199-8E3A-6F042DBB51D9}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -173,6 +179,7 @@ Global {8A61DF78-069B-40B5-8811-614E2960443E} = {3C3F9DB2-EC97-4464-B49F-BF1A0C2B46DC} {E27377CC-E2D3-4237-060F-96EA214D3129} = {3C3F9DB2-EC97-4464-B49F-BF1A0C2B46DC} {75B7D419-C261-577D-58D6-AA3ACED9129F} = {3C3F9DB2-EC97-4464-B49F-BF1A0C2B46DC} + {A34A4EA6-AEB0-4199-8E3A-6F042DBB51D9} = {EBF7C01F-9B4F-48E6-8418-2CBFDA51EB0B} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {4324B3B4-B60B-4E3C-91D8-59576B4E26DD} diff --git a/modules/JiShe.CollectBus.IoTDB/JiShe.CollectBus.IoTDB.csproj b/modules/JiShe.CollectBus.IoTDB/JiShe.CollectBus.IoTDB.csproj index 8cc4b33..afd5a8a 100644 --- a/modules/JiShe.CollectBus.IoTDB/JiShe.CollectBus.IoTDB.csproj +++ b/modules/JiShe.CollectBus.IoTDB/JiShe.CollectBus.IoTDB.csproj @@ -1,15 +1,16 @@  - - net8.0 - enable - enable - + + net8.0 + enable + enable + - + + diff --git a/modules/JiShe.CollectBus.IoTDB/Model/TableModelSingleMeasuringEntity.cs b/modules/JiShe.CollectBus.IoTDB/Model/TableModelSingleMeasuringEntity.cs index 4308dae..d48f766 100644 --- a/modules/JiShe.CollectBus.IoTDB/Model/TableModelSingleMeasuringEntity.cs +++ b/modules/JiShe.CollectBus.IoTDB/Model/TableModelSingleMeasuringEntity.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; +using JiShe.CollectBus.Common.Attributes; using JiShe.CollectBus.IoTDB.Attribute; using JiShe.CollectBus.IoTDB.Enums; using JiShe.CollectBus.IoTDB.Provider; @@ -13,6 +14,7 @@ namespace JiShe.CollectBus.IoTDB.Model /// Table模型单项数据实体 /// [EntityType(EntityTypeEnum.TableModel)] + [GenerateAccessors] public class TableModelSingleMeasuringEntity : IoTEntity { /// diff --git a/services/JiShe.CollectBus.Domain/JiShe.CollectBus.Domain.csproj b/services/JiShe.CollectBus.Domain/JiShe.CollectBus.Domain.csproj index a60ffe1..1914993 100644 --- a/services/JiShe.CollectBus.Domain/JiShe.CollectBus.Domain.csproj +++ b/services/JiShe.CollectBus.Domain/JiShe.CollectBus.Domain.csproj @@ -23,7 +23,7 @@ - + diff --git a/shared/JiShe.CollectBus.Common/JiShe.CollectBus.Common.csproj b/shared/JiShe.CollectBus.Common/JiShe.CollectBus.Common.csproj index 05645ef..fdb6bfd 100644 --- a/shared/JiShe.CollectBus.Common/JiShe.CollectBus.Common.csproj +++ b/shared/JiShe.CollectBus.Common/JiShe.CollectBus.Common.csproj @@ -17,7 +17,7 @@ - + diff --git a/shared/JiShe.CollectBus.SourceExtend/ComplexTypeSourceGenerator.cs b/shared/JiShe.CollectBus.SourceExtend/ComplexTypeSourceGenerator.cs new file mode 100644 index 0000000..352cd4a --- /dev/null +++ b/shared/JiShe.CollectBus.SourceExtend/ComplexTypeSourceGenerator.cs @@ -0,0 +1,187 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Text; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace JiShe.CollectBus.SourceExtend +{ + /// + /// 复杂类型源生成器 + /// + [Generator] + public sealed class PropertyAccessorGenerator : IIncrementalGenerator + { + private const string AttributeName = "GenerateAccessors"; + + public void Initialize(IncrementalGeneratorInitializationContext context) + { + // 步骤1:筛选带有 [GenerateAccessors] 的类 + var classDeclarations = context.SyntaxProvider + .CreateSyntaxProvider( + predicate: static (s, _) => IsClassWithAttribute(s), + transform: static (ctx, _) => GetClassDeclaration(ctx)) + .Where(static c => c is not null); + + // 步骤2:编译符号并生成代码 + var compilationAndClasses = context.CompilationProvider.Combine(classDeclarations.Collect()); + + context.RegisterSourceOutput(compilationAndClasses, (spc, source) => + GenerateCode(source.Left, source.Right!, spc)); + } + + private static bool IsClassWithAttribute(SyntaxNode node) + { + return node is ClassDeclarationSyntax { AttributeLists.Count: > 0 }; + } + + private static ClassDeclarationSyntax? GetClassDeclaration(GeneratorSyntaxContext context) + { + var classDecl = (ClassDeclarationSyntax)context.Node; + foreach (var attributeList in classDecl.AttributeLists) + { + foreach (var attribute in attributeList.Attributes) + { + var symbol = context.SemanticModel.GetSymbolInfo(attribute).Symbol; + if (symbol?.ContainingType.ToDisplayString() == $"{nameof(PropertyAccessorGenerator)}.{AttributeName}") + { + return classDecl; + } + } + } + return null; + } + + private static void GenerateCode( + Compilation compilation, + IEnumerable classes, + SourceProductionContext context) + { + var processedTypes = new HashSet(SymbolEqualityComparer.Default); + + foreach (var classDecl in classes.Distinct()) + { + var model = compilation.GetSemanticModel(classDecl.SyntaxTree); + var classSymbol = model.GetDeclaredSymbol(classDecl)!; + + if (!processedTypes.Add(classSymbol)) continue; + + var code = BuildAccessorsForType(classSymbol, compilation, processedTypes); + context.AddSource($"{classSymbol.Name}Accessors.g.cs", code); + } + } + + private static string BuildAccessorsForType( + INamedTypeSymbol classSymbol, + Compilation compilation, + HashSet processedTypes) + { + var code = new StringBuilder(); + code.AppendLine("// "); + code.AppendLine("#nullable enable"); + code.AppendLine(); + code.AppendLine($"namespace {classSymbol.ContainingNamespace.ToDisplayString()};"); + code.AppendLine(); + + // 处理泛型参数 + var genericParams = classSymbol.IsGenericType + ? $"<{string.Join(", ", classSymbol.TypeParameters.Select(t => t.Name))}>" + : ""; + + code.AppendLine($"public static class {classSymbol.Name}Accessors{genericParams}"); + code.AppendLine("{"); + + foreach (var prop in classSymbol.GetMembers().OfType()) + { + if (prop.IsIndexer) continue; + + GeneratePropertyAccessors(prop, code, compilation, processedTypes); + } + + code.AppendLine("}"); + return code.ToString(); + } + + private static void GeneratePropertyAccessors( + IPropertySymbol prop, + StringBuilder code, + Compilation compilation, + HashSet processedTypes) + { + var propType = prop.Type; + var parentType = prop.ContainingType.ToDisplayString(); + + // 处理元组类型 + if (propType.IsTupleType && propType is INamedTypeSymbol tupleType) + { + GenerateTupleAccessors(prop, tupleType, code); + } + else if (propType is INamedTypeSymbol namedType) + { + GenerateStandardAccessors(prop, namedType, code); + ProcessNestedType(namedType, compilation, processedTypes); + } + } + + private static void GenerateTupleAccessors(IPropertySymbol prop, INamedTypeSymbol tupleType, StringBuilder code) + { + var elements = tupleType.TupleElements; + var parentType = prop.ContainingType.ToDisplayString(); + + for (int i = 0; i < elements.Length; i++) + { + var element = elements[i]; + var elementType = element.Type.ToDisplayString(); + var elementName = element.CorrespondingTupleField?.Name ?? $"Item{i + 1}"; + + code.AppendLine($" public static {elementType} Get{prop.Name}_{elementName}({parentType} obj)"); + code.AppendLine($" => obj.{prop.Name}.{elementName};"); + + if (prop.SetMethod != null) + { + var assignments = elements.Select((e, idx) => + idx == i ? "value" : $"obj.{prop.Name}.{e.Name}"); + + code.AppendLine($" public static void Set{prop.Name}_{elementName}({parentType} obj, {elementType} value)"); + code.AppendLine($" => obj.{prop.Name} = ({string.Join(", ", assignments)});"); + } + } + } + + private static void GenerateStandardAccessors(IPropertySymbol prop, INamedTypeSymbol propType, StringBuilder code) + { + var typeName = propType.ToDisplayString(); + var parentType = prop.ContainingType.ToDisplayString(); + + code.AppendLine($" public static {typeName} Get{prop.Name}({parentType} obj)"); + code.AppendLine($" => obj.{prop.Name};"); + + if (prop.SetMethod != null) + { + code.AppendLine($" public static void Set{prop.Name}({parentType} obj, {typeName} value)"); + code.AppendLine($" => obj.{prop.Name} = value;"); + } + } + + private static void ProcessNestedType(INamedTypeSymbol typeSymbol, Compilation compilation, HashSet processedTypes) + { + if (ShouldGenerateForType(typeSymbol) && processedTypes.Add(typeSymbol)) + { + var code = BuildAccessorsForType(typeSymbol, compilation, processedTypes); + // 注意:实际项目中需要通过 SourceProductionContext 添加源代码 + } + } + + private static bool ShouldGenerateForType(INamedTypeSymbol typeSymbol) + { + return !typeSymbol.IsTupleType && + !typeSymbol.IsAnonymousType && + !typeSymbol.IsNativeIntegerType && + typeSymbol.DeclaredAccessibility == Accessibility.Public && + !typeSymbol.Name.StartsWith("<"); + } + } +} \ No newline at end of file diff --git a/shared/JiShe.CollectBus.SourceExtend/GenerateAccessorsAttribute.cs b/shared/JiShe.CollectBus.SourceExtend/GenerateAccessorsAttribute.cs new file mode 100644 index 0000000..b739a54 --- /dev/null +++ b/shared/JiShe.CollectBus.SourceExtend/GenerateAccessorsAttribute.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace JiShe.CollectBus.SourceExtend +{ + /// + /// 标记需要生成源码的类 + /// + [AttributeUsage(AttributeTargets.Class)] + public class GenerateAccessorsAttribute : Attribute + { + } +} diff --git a/shared/JiShe.CollectBus.SourceExtend/JiShe.CollectBus.SourceExtend.csproj b/shared/JiShe.CollectBus.SourceExtend/JiShe.CollectBus.SourceExtend.csproj new file mode 100644 index 0000000..a06bde8 --- /dev/null +++ b/shared/JiShe.CollectBus.SourceExtend/JiShe.CollectBus.SourceExtend.csproj @@ -0,0 +1,12 @@ + + + + netstandard2.1 + 12.0 + enable + true + + + + +