使用Source Generator无法访问系统库属性的构造参数
问题分析与解决方案
核心问题原因
- 程序集引用缺失:
System.ComponentModel.DataAnnotations.Schema.ColumnAttribute位于System.ComponentModel.AnnotationsNuGet包中,若项目未引用该包,编译上下文无法找到该类型,导致compilation.GetTypeByMetadataName返回null。 - 属性匹配方式错误:使用
ToDisplayString() == "Column"匹配属性类型不可靠,显示字符串可能受命名空间缩写、语法糖等影响,应该用符号相等比较器直接对比类型符号。 - 参数读取不完整:
ColumnAttribute的列名既可以通过构造参数传递,也可以通过Name命名参数传递,原代码只处理了构造参数的情况。
分步解决
1. 确保项目引用依赖包
在使用生成器的项目中安装System.ComponentModel.Annotations包:
dotnet add package System.ComponentModel.Annotations
2. 修改属性符号匹配与参数读取逻辑
替换原代码中GetTypesToGenerate方法的实现,修复符号匹配并覆盖两种参数传递场景:
static IEnumerable<ClassToGenerate> GetTypesToGenerate(Compilation compilation, IEnumerable<ClassDeclarationSyntax> classes, CancellationToken ct) { // 获取ColumnAttribute的类型符号 var columnAttributeSymbol = compilation.GetTypeByMetadataName("System.ComponentModel.DataAnnotations.Schema.ColumnAttribute"); // 找不到类型符号时直接返回,可添加诊断提示 if (columnAttributeSymbol == null) { return Enumerable.Empty<ClassToGenerate>(); } foreach (var @class in classes) { Debug.WriteLine($"Checking class {@class}"); ct.ThrowIfCancellationRequested(); var semanticModel = compilation.GetSemanticModel(@class.SyntaxTree); if (semanticModel.GetDeclaredSymbol(@class) is not INamedTypeSymbol classSymbol) continue; var info = new ClassToGenerate(classSymbol, new()); foreach (var member in classSymbol.GetMembers()) { if (member is IPropertySymbol propertySymbol) { if (propertySymbol.DeclaredAccessibility == Accessibility.Public && propertySymbol.SetMethod != null) { var columnName = propertySymbol.Name; // 使用符号相等比较器精准匹配ColumnAttribute var columnAttribute = propertySymbol.GetAttributes().FirstOrDefault(a => SymbolEqualityComparer.Default.Equals(a.AttributeClass, columnAttributeSymbol)); if (columnAttribute is not null) { // 优先读取构造参数:[Column("ColumnName")] if (!columnAttribute.ConstructorArguments.IsDefaultOrEmpty) { var nameArg = columnAttribute.ConstructorArguments.First(); if (nameArg.Value is string name) { columnName = name; } } // 读取命名参数:[Column(Name = "ColumnName")] else { var nameArg = columnAttribute.NamedArguments.FirstOrDefault(a => a.Key == "Name"); if (nameArg.Value.Value is string name) { columnName = name; } } } info.Properties.Add((columnName, propertySymbol)); } } } yield return info; } }
3. (可选)添加诊断提示
当无法找到ColumnAttribute类型时,添加诊断信息提示用户补全依赖:
// 在GetTypesToGenerate方法开头添加 if (columnAttributeSymbol == null) { var descriptor = new DiagnosticDescriptor( id: "BQD002", title: "Missing dependency", messageFormat: "Could not find ColumnAttribute. Ensure System.ComponentModel.Annotations NuGet package is installed.", category: "BigQueryMapperGenerator", DiagnosticSeverity.Warning, isEnabledByDefault: true); // 需将SourceProductionContext传递到此处,或在Execute方法中统一处理 // context.ReportDiagnostic(Diagnostic.Create(descriptor, Location.None)); return Enumerable.Empty<ClassToGenerate>(); }
关键注意事项
- 符号相等比较:Source Generator中永远使用
SymbolEqualityComparer.Default对比符号,不要依赖字符串显示格式,避免因命名空间导入、别名等导致匹配失败。 - 依赖范围:生成器本身不需要引用
System.ComponentModel.Annotations,但使用生成器的项目必须引用,因为编译上下文基于用户项目的依赖构建。 - 参数覆盖:处理属性时要覆盖构造参数和命名参数两种场景,确保所有使用方式都能被正确解析。
内容的提问来源于stack exchange,提问作者halomademeapc
相关产品推荐
相关产品推荐

