You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Source Generator无法访问系统库属性的构造参数

问题分析与解决方案

核心问题原因

  1. 程序集引用缺失:System.ComponentModel.DataAnnotations.Schema.ColumnAttribute 位于 System.ComponentModel.Annotations NuGet包中,若项目未引用该包,编译上下文无法找到该类型,导致compilation.GetTypeByMetadataName返回null。
  2. 属性匹配方式错误:使用ToDisplayString() == "Column"匹配属性类型不可靠,显示字符串可能受命名空间缩写、语法糖等影响,应该用符号相等比较器直接对比类型符号。
  3. 参数读取不完整: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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 01:41:17