如何在Roslyn的DiagnosticAnalyzer与CodeFixProvider中获取Workspace?
解决Roslyn分析器中获取Workspace及自动添加Logger配置到appsettings.json的问题
我之前在做Roslyn分析器的时候也踩过AdhocWorkspace的坑——它本质是一个临时的空工作区,根本不会包含你当前正在分析的项目,所以你拿到的Solution自然是空的。下面给你一步步讲怎么正确实现需求:
1. 正确获取当前分析环境的Workspace
你不需要手动创建AdhocWorkspace,应该从分析上下文里直接获取真实的Workspace。在你的DiagnosticAnalyzer类中,通过SyntaxNodeAnalysisContext或者CompilationAnalysisContext就能拿到:
public override void Initialize(AnalysisContext context) { context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); context.EnableConcurrentExecution(); // 这里替换成你需要触发分析的语法节点,比如类声明、方法调用等 context.RegisterSyntaxNodeAction(AnalyzeNode, SyntaxKind.ClassDeclaration); } private void AnalyzeNode(SyntaxNodeAnalysisContext context) { // 两种获取Workspace的方式都可以 var workspace = context.Options.Workspace; // 或者从Compilation获取:var workspace = context.Compilation.Workspace; if (workspace == null) return; // 后续操作... }
2. 定位项目中的appsettings.json文档
拿到Workspace后,你可以定位到当前分析项目下的appsettings.json文件:
// 获取当前正在分析的项目 var currentProject = context.Compilation.Project; // 查找项目中的appsettings.json文档 var appsettingsDoc = currentProject.Documents .FirstOrDefault(d => d.Name.Equals("appsettings.json", StringComparison.OrdinalIgnoreCase)); if (appsettingsDoc == null) { // 如果找不到文件,可以触发诊断提示用户 var diagnostic = Diagnostic.Create(YourMissingFileRule, context.Node.GetLocation(), "未找到appsettings.json文件"); context.ReportDiagnostic(diagnostic); return; }
3. 检查Logger配置是否存在
读取文档文本并解析JSON,检查是否包含你需要的Logger配置:
// 获取文档内容 var text = await appsettingsDoc.GetTextAsync(context.CancellationToken); var jsonContent = text.ToString(); // 解析JSON using var jsonDoc = JsonDocument.Parse(jsonContent); var hasValidLoggerConfig = false; // 检查Logging节点及子配置 if (jsonDoc.RootElement.TryGetProperty("Logging", out var loggingElem)) { // 这里可以根据你的需求调整检查逻辑,比如检查LogLevel或自定义Logger节点 hasValidLoggerConfig = loggingElem.TryGetProperty("LogLevel", out _) || loggingElem.EnumerateObject().Any(p => p.Name == "YourCustomLogger"); } // 如果没有配置,触发诊断 if (!hasValidLoggerConfig) { var diagnostic = Diagnostic.Create(YourMissingConfigRule, context.Node.GetLocation(), "appsettings.json中缺少Logger配置"); context.ReportDiagnostic(diagnostic); }
4. 通过Code Fix自动添加配置
在你的CodeFixProvider中,修改appsettings.json的内容并生成新的解决方案:
public override async Task RegisterCodeFixesAsync(CodeFixContext context) { var diagnostic = context.Diagnostics.First(); var currentProject = context.Document.Project; var appsettingsDoc = currentProject.Documents .FirstOrDefault(d => d.Name.Equals("appsettings.json", StringComparison.OrdinalIgnoreCase)); if (appsettingsDoc == null) return; // 读取并解析现有JSON var text = await appsettingsDoc.GetTextAsync(context.CancellationToken); var jsonObj = JsonSerializer.Deserialize<Dictionary<string, object>>(text.ToString()) ?? new Dictionary<string, object>(); // 添加Logger配置(根据你的需求调整内容) if (!jsonObj.ContainsKey("Logging")) { jsonObj["Logging"] = new Dictionary<string, object> { ["LogLevel"] = new Dictionary<string, string> { ["Default"] = "Information", ["Microsoft.AspNetCore"] = "Warning" }, ["YourCustomLogger"] = new Dictionary<string, string> { ["LogLevel"] = "Debug" } }; } // 序列化回格式化后的JSON var updatedJson = JsonSerializer.Serialize(jsonObj, new JsonSerializerOptions { WriteIndented = true }); var updatedText = SourceText.From(updatedJson); // 生成更新后的文档和解决方案 var updatedDoc = appsettingsDoc.WithText(updatedText); var updatedSolution = updatedDoc.Project.Solution; // 注册Code Fix操作 context.RegisterCodeFix( CodeAction.Create( title: "添加Logger配置到appsettings.json", createChangedSolution: _ => Task.FromResult(updatedSolution), equivalenceKey: nameof(AddLoggerConfigCodeFix)), diagnostic); }
几个需要注意的细节
- 异步操作:所有涉及文档读取、修改的操作都是异步的,要确保使用
await处理。 - JSON格式化:用
WriteIndented = true保证修改后的JSON保持可读性。 - 多项目场景:如果解决方案有多个项目,要确保修改的是当前分析项目的appsettings.json,避免误改其他项目的配置。
- 错误处理:针对文件不存在、JSON格式错误等情况,要添加对应的诊断提示,提升用户体验。
内容的提问来源于stack exchange,提问作者Vijay Nirmal
相关产品推荐
相关产品推荐

