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

作者:微信公众号:【架构师老卢】
8-26 19:34
9

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

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

1. 正确使用依赖注入(DI)

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

错误示例

var service = new MyService();

正确示例

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

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

2. 编写单元测试——即使是业务逻辑

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

使用工具:xUnit、Moq或NSubstitute。

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

3. 避免在控制器中编写业务逻辑

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

错误示例

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

更好做法

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

4. 永不硬编码配置或密钥

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

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

错误示例

var apiKey = "SECRET123";

正确示例

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

5. 使用全局异常处理

原因:集中式错误处理避免重复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;
        }
    }
}

6. 遵循SOLID原则

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

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

7. 正确使用Async/Await

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

错误示例

var data = service.GetData().Result;

正确示例

var data = await service.GetDataAsync();

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

8. 释放使用的资源

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

使用using语句:

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

使用IHttpClientFactory避免频繁实例化。

9. 使用IHttpClientFactory

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

services.AddHttpClient<IMyService, MyService>();

10. 不要吞掉异常

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

错误示例

try { ... } catch { }

正确示例

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

11. 使用DTO替代EF模型

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

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

12. 保持方法体积小巧

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

将大方法拆分为小方法。

13. 使用FluentValidation

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

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

14. 始终验证输入

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

使用[ApiController]和模型验证。

15. 使用有意义的命名

原因:代码应自解释。

避免DoTask(),推荐GenerateReportForUser()。

16. 使用[Authorize]保护API

原因:防止未授权访问。

[Authorize(Roles = "Admin")]

17. 保持项目结构清晰

原因:更易导航和扩展。

分层:

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

18. 实现日志记录

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

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

19. 优先使用配置绑定

原因:避免魔法字符串。

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

20. 使用中间件处理横切关注点

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

21. 智能缓存

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

IMemoryCache.TryGetValue(key, out result);

22. 对API响应分页

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

.Skip(page * pageSize).Take(pageSize);

23. 使用MiniProfiler或BenchmarkDotNet进行分析

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

24. 使用空条件运算符处理null

原因:防止NullReferenceException。

var name = user?.Profile?.Name;

25. 避免静态状态

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

使用DI配合Singleton或Scoped服务。

26. 为API版本控制

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

[Route("api/v1/[controller]")]

27. 使用Mapperly

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

var userDto = userMapper.ToUserDto(user);

28. 不要过度使用Try-Catch

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

29. 编写集成测试

原因:端到端安全网。

var client = factory.CreateClient();

30. 避免记录敏感信息

原因:安全风险。

使用掩码:

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

31. 使用Swagger生成API文档

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

builder.Services.AddSwaggerGen();

32. 使用CancellationToken

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

public async Task GetOrders(CancellationToken cancellationToken)

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

相关留言评论
昵称:
邮箱:
阅读排行