关于 dotnet-agent-gotchas
面向生成或修改 .NET 代码的 AI Agent,汇总常见错误,并以警示、反例、修正示例和相关技能索引帮助代码审查。它聚焦发现问题,不提供完整实现教程。
主要功能
- 01涵盖异步、NuGet 与过时 API
- 02检查项目结构和可空注解
- 03提示源生成器及 AOT 配置问题
- 04列出测试、依赖注入和质量反模式
适用场景
- 01审查 AI Agent 生成的 .NET 代码
- 02排查测试组织或依赖注入错误
- 03查找问题对应的深入技能指南
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ wshaddix/ dotnet-skills --skill dotnet-agent-gotchas安装后在 Claude Code 中输入/dotnet-agent-gotchas确认已加载。
详细介绍
dotnet-agent-gotchas
概述 / 范围界定
AI Agent 在生成或修改 .NET 代码时常犯的错误,按类别整理。每个类别都提供简短警告、反模式代码、修正后的代码,以及指向负责深入指导的规范技能的交叉引用。本技能不提供完整的实现教程——只指出错误,并指引至正确的技能。
**不在范围内:**深入的 async/await 模式(由 [skill:dotnet-csharp-async-patterns] 负责)、完整的依赖注入指导(由 [skill:dotnet-csharp-dependency-injection] 负责)、NRT 使用模式(由 [skill:dotnet-csharp-nullable-reference-types] 负责)、源生成器编写(由 [skill:dotnet-csharp-source-generators] 负责)、测试框架功能(由 [skill:dotnet-testing-strategy] 负责)、安全漏洞缓解(由 [skill:dotnet-security-owasp] 负责)。
前置条件
.NET 8.0+ SDK。熟悉 SDK 风格的项目和 C# 语言功能。
交叉引用:[skill:dotnet-csharp-async-patterns]、[skill:dotnet-csharp-dependency-injection]、[skill:dotnet-csharp-nullable-reference-types]、[skill:dotnet-csharp-source-generators]、[skill:dotnet-testing-strategy]、[skill:dotnet-security-owasp]。
类别 1:误用 Async/Await
**警告:**Agent 经常使用 .Result 或 .Wait() 阻塞异步方法,导致 ASP.NET Core 和 UI 上下文中出现死锁。另一个常见错误是调用即忘(fire-and-forget),导致异常被悄然吞掉。
反模式
// WRONG: blocking on async -- deadlock risk in synchronization contexts
public Order GetOrder(int id)
{
var order = _repository.GetOrderAsync(id).Result; // DEADLOCK
return order;
}
// WRONG: fire-and-forget with no error handling
public void ProcessOrder(Order order)
{
_ = _emailService.SendConfirmationAsync(order); // exception silently lost
}
修正后
// CORRECT: async all the way
public async Task<Order> GetOrderAsync(int id, CancellationToken ct = default)
{
var order = await _repository.GetOrderAsync(id, ct);
return order;
}
// CORRECT: background work via IHostedService or explicit error handling
public async Task ProcessOrderAsync(Order order, CancellationToken ct = default)
{
await _emailService.SendConfirmationAsync(order, ct);
}
有关完整的 async/await 指导,包括 ValueTask、ConfigureAwait 和取消传播,请参阅 [skill:dotnet-csharp-async-patterns]。
类别 2:NuGet 包错误
**警告:**Agent 生成错误的包名称、未选择加入便引用预发布版本,或添加已弃用/替代的包。ASP.NET Core 共享框架包的主版本必须与项目 TFM 的主版本一致。
反模式
<PackageReference Include="EntityFrameworkCore" Version="9.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="9.0.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="7.0.0" />
修正后
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.0" />
有关项目文件约定和集中式包管理指导,请参阅 [skill:dotnet-csproj-reading]。
类别 3:使用已弃用的 API
**警告:**Agent 生成使用已弃用且不安全 API 的代码:BinaryFormatter(反序列化容易导致 CVE)、WebClient(已由 HttpClient 替代),以及较旧的加密 API(RNGCryptoServiceProvider、SHA1CryptoServiceProvider)。
反模式
// WRONG: BinaryFormatter is banned in .NET 8+ (SYSLIB0011)
var formatter = new BinaryFormatter();
formatter.Serialize(stream, data);
// WRONG: WebClient is obsolete -- use HttpClient via IHttpClientFactory
var client = new WebClient();
var html = client.DownloadString("https://example.com");
// WRONG: obsolete crypto API (SYSLIB0023)
using var rng = new RNGCryptoServiceProvider();
rng.GetBytes(buffer);
修正后
// CORRECT: use System.Text.Json for serialization
var json = JsonSerializer.Serialize(data);
await File.WriteAllTextAsync("data.json", json);
// CORRECT: use IHttpClientFactory (registered via DI)
public class MyService(HttpClient httpClient)
{
public async Task<string> GetHtmlAsync(CancellationToken ct = default)
=> await httpClient.GetStringAsync("https://example.com", ct);
}
// CORRECT: modern RandomNumberGenerator (static API)
RandomNumberGenerator.Fill(buffer);
有关完整的已弃用安全模式目录和 OWASP 缓解措施,请参阅 [skill:dotnet-security-owasp]。
类别 4:项目结构错误
**警告:**Agent 使用错误的 SDK 类型、为框架已包含的库添加 PackageReference 条目,或创建无效的 ProjectReference 路径。
反模式
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net9.0</TargetFramework>
</PropertyGroup>
</Project>
<PackageReference Include="Microsoft.Extensions.Logging" Version="9.0.0" />
<ProjectReference Include="..\..\Core\MyApp.Core.csproj" />
修正后
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
</PropertyGroup>
</Project>
<ProjectReference Include="..\MyApp.Core\MyApp.Core.csproj" />
有关 SDK 类型、项目组织和项目引用约定,请参阅 [skill:dotnet-project-structure]。
类别 5:可空引用类型注解错误
**警告:**Agent 滥用 null-forgiving 运算符(!)来消除警告,而不是修正可空性;或者忘记启用可空上下文。
反模式
// WRONG: null-forgiving operator hides a real null risk
public string GetUserName(int id)
{
var user = _db.Users.Find(id);
return user!.Name; // NullReferenceException if user not found
}
// WRONG: nullable not enabled, so annotations are meaningless
// Missing <Nullable>enable</Nullable> in .csproj
public string? GetOptionalValue() => null; // no compiler warnings without nullable context
修正后
// CORRECT: handle null explicitly
public string GetUserName(int id)
{
var user = _db.Users.Find(id);
if (user is null)
{
throw new InvalidOperationException($"User {id} not found.");
}
return user.Name;
}
<PropertyGroup>
<Nullable>enable</Nullable>
</PropertyGroup>
有关完整的 NRT 使用模式和注解策略,请参阅 [skill:dotnet-csharp-nullable-reference-types]。
类别 6:源生成器配置错误
**警告:**当源生成器需要扩展类时,Agent 忘记将类标记为 partial;或者使用错误的输出类型,导致生成器输出无法编译。
反模式
// WRONG: missing partial keyword -- source generator cannot augment this class
[JsonSerializable(typeof(WeatherForecast))]
internal class WeatherJsonContext : JsonSerializerContext
{
}
// WRONG: generator expects a class but agent declared a struct
[LoggerMessage(EventId = 1, Level = LogLevel.Information, Message = "Processing {Item}")]
public static partial struct LogMessages // struct is invalid for LoggerMessage
{
}
修正后
// CORRECT: partial class allows source generator to emit companion code
[JsonSerializable(typeof(WeatherForecast))]
internal partial class WeatherJsonContext : JsonSerializerContext
{
}
// CORRECT: LoggerMessage requires partial method in a partial class
public static partial class Log
{
[LoggerMessage(EventId = 1, Level = LogLevel.Information, Message = "Processing {Item}")]
public static partial void ProcessingItem(ILogger logger, string item);
}
有关源生成器配置、诊断和调试,请参阅 [skill:dotnet-csharp-source-generators]。
类别 7:裁剪/AOT 警告抑制
警告: Agent 使用 #pragma 或 [UnconditionalSuppressMessage] 来抑制裁剪和 AOT 警告,而不是修复底层的反射/动态用法。抑制警告会掩盖已发布应用中的运行时故障。
反模式
// WRONG: suppressing trim warning instead of fixing it
#pragma warning disable IL2026
var type = Type.GetType(typeName); // reflection not trim-safe
var instance = Activator.CreateInstance(type!);
#pragma warning restore IL2026
// WRONG: app-level suppression in .csproj hides all trim warnings
// <NoWarn>IL2026;IL2046;IL3050</NoWarn>
修正后
// CORRECT: use compile-time type resolution or [DynamicallyAccessedMembers]
public T CreateInstance<T>() where T : new()
{
return new T(); // no reflection, trim-safe
}
// For unavoidable reflection, annotate correctly:
public object CreateInstance(
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type type)
{
return Activator.CreateInstance(type)
?? throw new InvalidOperationException($"Cannot create {type.Name}");
}
<PublishTrimmed>true</PublishTrimmed>
<EnableTrimAnalyzer>true</EnableTrimAnalyzer>
<IsTrimmable>true</IsTrimmable>
请参阅 [skill:dotnet-csproj-reading],了解裁剪和 AOT 配置的 MSBuild 属性指南。
类别 8:测试组织反模式
警告: Agent 将测试类放在生产项目中、使用错误的测试 SDK 配置,或混用测试框架特性。
反模式
// WRONG: test class in the production project (not in a separate test project)
// File: src/MyApp.Api/OrderServiceTests.cs
namespace MyApp.Api;
public class OrderServiceTests
{
[Fact] // xUnit attribute in production code -- ships test dependencies to users
public void CalculateTotal_ReturnsCorrectSum() { }
}
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="xunit.v3" Version="3.2.2" />
</ItemGroup>
</Project>
修正后
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<IsTestProject>true</IsTestProject>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="xunit.v3" Version="3.2.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.5" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.0.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\MyApp.Api\MyApp.Api.csproj" />
</ItemGroup>
</Project>
请参阅 [skill:dotnet-testing-strategy],了解测试组织、命名约定和测试类型选择指南。
类别 9:DI 注册错误
警告: Agent 忘记注册服务、使用错误的生命周期(单例捕获作用域服务),或创建会导致内存泄漏和并发错误的被俘获依赖项。
反模式
// WRONG: scoped service injected into singleton -- captive dependency
builder.Services.AddSingleton<OrderProcessor>(); // singleton
builder.Services.AddScoped<IOrderRepository, OrderRepository>(); // scoped
public class OrderProcessor(IOrderRepository repo) // repo is captured as singleton!
{
public async Task ProcessAsync(int orderId, CancellationToken ct)
{
var order = await repo.GetByIdAsync(orderId, ct); // same DbContext forever
}
}
// WRONG: missing registration causes runtime exception
// builder.Services.AddScoped<IOrderRepository, OrderRepository>(); // forgot this line
// InvalidOperationException: Unable to resolve service for type 'IOrderRepository'
修正后
// CORRECT: lifetimes must not capture shorter-lived dependencies
builder.Services.AddScoped<OrderProcessor>(); // scoped, matches repository lifetime
builder.Services.AddScoped<IOrderRepository, OrderRepository>();
// Or if OrderProcessor must be singleton, inject IServiceScopeFactory:
builder.Services.AddSingleton<OrderProcessor>();
public class OrderProcessor(IServiceScopeFactory scopeFactory)
{
public async Task ProcessAsync(int orderId, CancellationToken ct)
{
await using var scope = scopeFactory.CreateAsyncScope();
var repo = scope.ServiceProvider.GetRequiredService<IOrderRepository>();
var order = await repo.GetByIdAsync(orderId, ct);
}
}
请参阅 [skill:dotnet-csharp-dependency-injection],了解生命周期规则、注册模式和服务作用域管理。
Slopwatch 反模式
这些模式表明 Agent 在掩盖问题,而不是修复问题。每次代码审查都应检查这些模式。请参阅 [skill:dotnet-slopwatch],了解用于检测这些模式的自动化质量门禁。
1. 禁用或跳过测试
// RED FLAG: skipping tests to make the build pass
[Fact(Skip = "Flaky, will fix later")] // test never gets fixed
public void CriticalBusinessLogic_WorksCorrectly() { }
// RED FLAG: commenting out failing tests
// [Fact]
// public void CalculateTotal_HandlesNegative() { ... }
// RED FLAG: conditional compilation to hide tests
#if false
[Fact]
public void ImportantEdgeCase() { }
#endif
修复: 调查并修复底层问题。如果测试确实因时序问题而不稳定,请使用 [Retry](xUnit v3)或修复非确定性问题。切勿为了让构建通过而禁用测试。
2. 警告抑制
// RED FLAG: blanket warning suppression
#pragma warning disable CS8600, CS8602, CS8604 // suppress all nullability warnings
var result = GetData();
result.Process();
#pragma warning restore CS8600, CS8602, CS8604
// RED FLAG: project-level suppression hiding real issues
// <NoWarn>CS8618;CS8625;IL2026</NoWarn>
修复: 解决底层的可空性或裁剪问题。添加适当的空值检查、正确使用可空注解,或针对裁剪警告使用 [DynamicallyAccessedMembers]。
3. 空 catch 块
// RED FLAG: swallowing exceptions silently
try
{
await _service.ProcessAsync(data, ct);
}
catch (Exception) { } // failure is invisible
// RED FLAG: catch-and-ignore with misleading comment
catch (Exception ex)
{
// TODO: add logging
}
修复: 至少要记录异常。优先捕获特定异常类型,并进行适当处理。
4. 未说明理由地静默分析器
// RED FLAG: suppressing analyzer with no explanation
[SuppressMessage("Design", "CA1062")]
public void Process(string input) { }
// RED FLAG: disabling analyzer rules in .editorconfig globally
// dotnet_diagnostic.CA1062.severity = none
修复: 修改代码以满足分析器规则,或在抑制特性中提供有记录的理由:[SuppressMessage("Design", "CA1062", Justification = "Input validated by middleware")]。
5. 从测试中移除断言
// RED FLAG: test with no assertions -- always passes
[Fact]
public async Task CreateOrder_Succeeds()
{
var service = new OrderService();
await service.CreateOrderAsync(new Order());
// no Assert -- this test proves nothing
}
修复: 每个测试都必须至少有一个断言,用于验证预期行为。如果测试验证的是副作用,则应断言该副作用(数据库状态、已发布事件、日志输出)。
交叉引用
- [skill:dotnet-csharp-async-patterns] -- async/await 深入模式、
ValueTask、取消 - [skill:dotnet-csharp-dependency-injection] -- DI 生命周期规则、注册模式、作用域管理
- [skill:dotnet-csharp-nullable-reference-types] -- NRT 注解、可空上下文、流分析
- [skill:dotnet-csharp-source-generators] -- 生成器配置、分部类要求、诊断
- [skill:dotnet-testing-strategy] -- 测试类型选择、组织、命名约定
- [skill:dotnet-security-owasp] -- OWASP 缓解措施、已弃用安全 API 目录