30+个.NET开发最佳实践:从Web Forms到.NET 8的血泪经验总结

B站影视 韩国电影 2025-10-01 14:57 1

摘要:在经历了Web Forms、MVC、.NET Core到如今.NET 8的十余年开发历程后,我亲眼目睹项目如何从整洁走向混乱——并非因为开发者不够努力,而是因为忽视了最佳实践。

在经历了Web Forms、MVC、.NET Core到如今.NET 8的十余年开发历程后,我亲眼目睹项目如何从整洁走向混乱——并非因为开发者不够努力,而是因为忽视了最佳实践。

以下是我通过艰难教训或从优秀团队中学到的30多个最佳实践。我将以实用方式分享,只讲真正有帮助的内容。

原因:保持代码低耦合和可测试性。没有DI,逻辑会紧耦合,导致代码僵化且难以测试。

错误示例

正确示例

public class MyController{private readonly IMyService _service;public MyController(IMyService service){_service = service;}}

我的经验:曾参与一个没有DI的项目——所有服务都用new创建。我们无法为测试模拟任何东西。改用DI不仅使代码可测试,还更好地分离了关注点。

原因:早期发现错误并防止回归。特别关注核心业务规则。

使用工具:xUnit、Moq或NSubstitute。

[Fact]public void Should_Return_True_When_Valid{var validator = new OrderValidator;var result = validator.IsValid("ORD123");Assert.True(result);}

原因:控制器应只负责协调。业务逻辑会导致控制器臃肿,且逻辑不可测试、重复。

错误示例

public IActionResult Save(Order order){if(order.Amount > 1000)order.IsDiscounted = true;// 保存到数据库}

更好做法

public IActionResult Save(Order order){_orderService.ApplyDiscount(order);_orderService.Save(order);}

原因:代码中的密钥是重大安全风险,也使环境管理困难。

使用:IConfiguration、appsettings.json或Azure Key Vault。

错误示例

var apiKey = "SECRET123";

正确示例

// appsettings.json"MyService": {"ApiKey": "xyz"}var key = _config["MyService:ApiKey"];

原因:集中式错误处理避免重复try-catch,允许记录、跟踪和返回适当消息。

app.UseExceptionHandler("/error");

或编写中间件:

public class ErrorHandlingMiddleware{public async Task Invoke(HttpContext context){try { await _next(context); }catch(Exception ex){_logger.LogError(ex, "未处理异常");context.Response.StatusCode = 500;}}}

原因:这些原则创建可维护、可扩展和可测试的代码。

• 单一职责:一个类=一个职责• 开闭原则:对扩展开放,对修改关闭• 里氏替换:子类型应可替换基类型• 接口隔离:不强制实现未使用的方法• 依赖倒置:依赖抽象

原因:防止线程阻塞。避免.Result或.Wait。

错误示例

var data = service.GetData.Result;

正确示例

var data = await service.GetDataAsync;

真实案例:曾因.Result导致生产环境死锁,花了2小时才定位。

原因:资源泄漏导致内存和性能问题。

使用using语句:

using (var client = new HttpClient) { ... }

使用IHttpClientFactory避免频繁实例化。

原因:避免套接字耗尽并提高可测试性。

services.AddHttpClient;

原因:静默捕获异常会隐藏错误。

错误示例

try { ... } catch { }

正确示例

catch(Exception ex){_logger.LogError(ex, "操作失败");}

原因:防止过度提交并减少与数据库的紧耦合。

public class OrderDto{public int Id { get; set; }public decimal Amount { get; set; }}

原因:更易理解、测试和维护。

将大方法拆分为小方法。

原因:将验证逻辑移出模型和控制器。

public class OrderValidator : AbstractValidator{public OrderValidator{RuleFor(x => x.Amount).GreaterThan(0);}}

原因:防止注入攻击和系统崩溃。

使用[ApiController]和模型验证。

原因:代码应自解释。

避免DoTask,推荐GenerateReportForUser。

原因:防止未授权访问。

原因:更易导航和扩展。

分层:

• 控制器• 服务层• 仓储层• 模型/DTOs

原因:日志帮助调试和监控应用。

_logger.LogInformation("订单已处理: {@order}", order);

原因:避免魔法字符串。

services.Configure(config.GetSection("MySettings"));

原因:保持控制器代码整洁。例如:日志、CORS、认证、异常处理。

原因:提高频繁请求的性能。

IMemoryCache.TryGetValue(key, out result);

原因:防止内存溢出并提升性能。

.Skip(page * pageSize).Take(pageSize);23. 使用MiniProfiler或BenchmarkDotNet进行分析

原因:发现慢查询/方法。

原因:防止NullReferenceException。

var name = user?.Profile?.Name;

原因:非线程安全。破坏可测试性。

使用DI配合Singleton或Scoped服务。

原因:避免破坏现有客户端。

原因:减少样板映射代码。

var userDto = userMapper.ToUserDto(user);

原因:隐藏真实问题。只包装高风险调用。

原因:端到端安全网。

var client = factory.CreateClient;

原因:安全风险。

使用掩码:

_logger.Log("Token: ****");

原因:帮助开发者测试和理解API。

builder.Services.AddSwaggerGen;

原因:允许优雅关闭和响应式API。

public async Task GetOrders(CancellationToken cancellationToken)

编写整洁、可维护的代码是一种习惯。最佳实践就是您的地图——忽略它们,您将陷入技术债的丛林。

来源:架构师老卢

相关推荐