1. 大型ASP.NET Core解决方案架构设计
1.1 分层架构实践
在构建大型ASP.NET Core解决方案时,分层架构是最基础的设计模式。我通常会创建以下四个明确分层的项目:
-
Presentation层:负责处理HTTP请求和响应
- 包含Controllers、ViewModels和API端点
- 推荐使用Minimal API或传统MVC模式
- 示例项目命名:
CompanyName.ProductName.Web
-
Application层:业务逻辑核心
- 包含服务接口和实现
- 处理工作单元和事务边界
- 示例项目命名:
CompanyName.ProductName.Application
-
Domain层:领域模型
- 包含实体、值对象、领域事件
- 定义仓储接口
- 示例项目命名:
CompanyName.ProductName.Domain
-
Infrastructure层:技术实现细节
- 包含EF Core上下文、仓储实现
- 外部服务集成
- 示例项目命名:
CompanyName.ProductName.Infrastructure
重要提示:各层之间应该通过NuGet包引用建立依赖关系,而不是项目引用。这能强制实施架构边界,防止层间渗透。
1.2 Clean Architecture深度解析
Clean Architecture的核心是依赖规则:内层不应当知道外层的任何信息。在我的项目中,我通过以下方式实现:
-
依赖方向控制:
- 所有项目引用都指向中心
- 外层项目可以引用内层项目,反之则禁止
- 使用接口隔离具体实现
-
项目结构示例:
bash复制src/
├── Core/ # 领域层
├── Infrastructure/ # 基础设施实现
└── Web/ # 表现层
- 依赖注入配置:
csharp复制// 在Web项目中配置
services.AddApplication(); // 应用层服务
services.AddInfrastructure(Configuration); // 基础设施实现
1.3 洋葱架构实战技巧
洋葱架构强调以领域模型为核心。我在实际项目中发现这些实践特别有用:
-
领域事件处理:
- 使用MediatR实现进程内消息总线
- 领域事件在核心层定义
- 处理程序可以在外层实现
-
接口定义位置:
- 仓储接口定义在Domain层
- 服务接口定义在Application层
- 实现都在Infrastructure层
-
跨层通信规范:
- 禁止直接跨层调用
- 通过接口抽象进行通信
- 使用DTO在不同层间传递数据
2. 设计原则与资源管理
2.1 SOLID原则在ASP.NET Core中的具体应用
单一职责原则(SRP)
每个Controller应该只关注一个业务功能。我通常这样做:
csharp复制// 不好的做法:一个Controller做太多事
public class ProductController : Controller
{
public IActionResult ManageProducts() { ... }
public IActionResult ManageOrders() { ... } // 违反SRP
}
// 好的做法:拆分职责
public class ProductController : Controller { ... }
public class OrderController : Controller { ... }
开放封闭原则(OCP)
通过策略模式实现扩展开放:
csharp复制public interface IPaymentStrategy
{
Task ProcessPayment(decimal amount);
}
public class CreditCardPayment : IPaymentStrategy { ... }
public class PayPalPayment : IPaymentStrategy { ... }
// 使用时
public class PaymentService
{
private readonly IPaymentStrategy _strategy;
public PaymentService(IPaymentStrategy strategy)
{
_strategy = strategy;
}
public Task Process(decimal amount) => _strategy.ProcessPayment(amount);
}
2.2 DbContext生命周期管理实战
EF Core DbContext的最佳实践:
-
生命周期选择:
- Web应用中默认使用Scoped生命周期
- 控制台应用中使用Transient
- 禁止使用Singleton(会导致内存泄漏)
-
连接池优化:
csharp复制services.AddDbContextPool<AppDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("Default")),
poolSize: 128); // 根据负载调整
- 显式释放资源:
csharp复制// 对于非DI创建的资源
using (var context = new AppDbContext())
{
// 操作context
} // 自动调用Dispose()
// C# 8简化写法
using var context = new AppDbContext();
3. 测试与可观测性
3.1 单元测试最佳实践
-
测试金字塔应用:
- 70%单元测试(快速反馈)
- 20%集成测试(验证组件交互)
- 10%端到端测试(验证完整流程)
-
Moq框架高级用法:
csharp复制var mockRepo = new Mock<IProductRepository>();
mockRepo.Setup(x => x.GetByIdAsync(It.IsAny<int>()))
.ReturnsAsync((int id) => new Product { Id = id });
// 验证方法调用次数
mockRepo.Verify(x => x.SaveAsync(It.IsAny<Product>()), Times.Once);
- 测试DbContext:
csharp复制var options = new DbContextOptionsBuilder<AppDbContext>()
.UseInMemoryDatabase(databaseName: "TestDb")
.Options;
using (var context = new AppDbContext(options))
{
// 测试代码
}
3.2 结构化日志与分布式追踪
- Serilog配置示例:
csharp复制Log.Logger = new LoggerConfiguration()
.MinimumLevel.Information()
.WriteTo.Console(outputTemplate:
"[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj}{NewLine}{Exception}")
.WriteTo.File("logs/app-.log", rollingInterval: RollingInterval.Day)
.CreateLogger();
- OpenTelemetry集成:
csharp复制services.AddOpenTelemetryTracing(builder =>
{
builder.AddAspNetCoreInstrumentation()
.AddHttpClientInstrumentation()
.AddEntityFrameworkCoreInstrumentation()
.AddJaegerExporter();
});
- 日志上下文增强:
csharp复制// 在Middleware中
using (LogContext.PushProperty("TraceId", context.TraceIdentifier))
{
await _next(context);
}
4. 数据访问与API设计
4.1 EF Core性能优化技巧
- 查询优化策略:
- 使用AsNoTracking()只读查询
- 避免N+1查询问题
- 使用投影查询只选择需要的字段
csharp复制// 好的做法
var products = await context.Products
.Where(p => p.Price > 100)
.Select(p => new { p.Id, p.Name })
.AsNoTracking()
.ToListAsync();
- 批量操作优化:
csharp复制// 使用EF Core 7.0的批量操作
await context.Products
.Where(p => p.Price < 50)
.ExecuteUpdateAsync(p => p.SetProperty(x => x.IsDiscounted, true));
4.2 API版本控制实现
- URL路径版本控制:
csharp复制services.AddApiVersioning(options =>
{
options.DefaultApiVersion = new ApiVersion(1, 0);
options.AssumeDefaultVersionWhenUnspecified = true;
options.ReportApiVersions = true;
});
// 在Controller上
[ApiVersion("1.0")]
[Route("api/v{version:apiVersion}/products")]
public class ProductsV1Controller : ControllerBase { ... }
- 版本迁移策略:
- 维护至少两个最新版本
- 提供6个月的弃用期
- 使用[Obsolete]标记即将弃用的端点
5. 安全与部署
5.1 生产环境迁移策略
-
安全迁移检查清单:
- [ ] 备份生产数据库
- [ ] 在预发布环境验证迁移
- [ ] 准备回滚脚本
- [ ] 选择低峰期执行
-
事务性迁移示例:
bash复制# 使用--transactional参数
dotnet ef database update --transactional
- 蓝绿部署集成:
yaml复制# Azure DevOps示例
- task: DotNetCoreCLI@2
displayName: 'Run EF Migrations'
inputs:
command: 'ef'
arguments: 'database update --connection "$(ProductionDbConnection)"'
5.2 密钥管理实践
- 开发环境配置:
bash复制# 设置用户机密
dotnet user-secrets set "ConnectionStrings:Default" "Server=localhost;..."
- 生产环境密钥管理:
csharp复制// 使用Azure Key Vault
builder.Configuration.AddAzureKeyVault(
new Uri("https://your-vault.vault.azure.net/"),
new DefaultAzureCredential());
- 密钥轮换策略:
- 每90天轮换一次密钥
- 使用双密钥过渡期
- 自动化密钥更新流程
6. 高级主题与最佳实践
6.1 国际化实现细节
- 资源文件组织:
bash复制Resources/
├── Controllers/
│ ├── HomeController.en-US.resx
│ └── HomeController.zh-CN.resx
└── Models/
├── Product.en-US.resx
└── Product.zh-CN.resx
- 中间件配置:
csharp复制app.UseRequestLocalization(options =>
{
var supportedCultures = new[] { "en-US", "zh-CN", "ja-JP" };
options.AddSupportedCultures(supportedCultures)
.AddSupportedUICultures(supportedCultures)
.SetDefaultCulture("en-US");
});
6.2 输入验证强化
- 自定义验证属性:
csharp复制public class ValidProductNameAttribute : ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext context)
{
if (value is string name && !name.Contains("test"))
return ValidationResult.Success;
return new ValidationResult("Invalid product name");
}
}
- FluentValidation集成:
csharp复制services.AddControllers()
.AddFluentValidation(fv =>
fv.RegisterValidatorsFromAssemblyContaining<Startup>());
- 全局异常处理:
csharp复制app.UseExceptionHandler(appError =>
{
appError.Run(async context =>
{
var contextFeature = context.Features.Get<IExceptionHandlerFeature>();
if (contextFeature?.Error is ValidationException validationException)
{
context.Response.StatusCode = (int)HttpStatusCode.BadRequest;
await context.Response.WriteAsJsonAsync(validationException.Errors);
}
});
});
在实际项目中,我发现这些架构模式和最佳实践的组合使用可以显著提高应用的可维护性和扩展性。特别是在团队协作环境中,清晰的架构分层和严格的设计原则能够减少代码冲突,提高开发效率。