FEATURED · 精选文章

手把手搭建C#企业级项目:从分层架构到完整API实现

发布时间 / 2026/8/2 2:04:22
来源 / 创域科博编辑部
栏目 / 资讯中心
手把手搭建C#企业级项目:从分层架构到完整API实现 很多C#开发者都有这样的困惑跟着教程学会了语法也做过一些小练习但一到实际工作中面对一个需要从零开始构建的“标准企业级项目”时却不知从何下手。Controller、Service、Repository这些层到底该怎么划分依赖注入怎么配才合理数据库连接和事务又该如何管理网上能找到的要么是零散的“学生管理系统”Demo要么是过于庞大、依赖特定商业框架的解决方案中间缺少一个清晰、完整、能直接复用到真实工作场景的桥梁。这篇文章要解决的正是这个“最后一公里”的问题。我们不只讲语法而是手把手带你搭建一个具备标准企业级架构雏形的C#项目。你将清晰地看到一个可维护、可扩展、职责分明的后端项目是如何从文件夹结构开始一步步构建起来的。我们会从最简单的控制台程序开始逐步引入ASP.NET Core Web API、Entity Framework Core、分层架构、依赖注入、仓储模式、单元测试等核心概念并最终落地一个包含用户管理和产品目录的微型业务系统。读完本文你将获得一个可以直接作为模板的完整项目结构理解每一层存在的意义并掌握如何将新的业务需求填充到这个框架中。无论你是刚学完C#基础想进阶的初学者还是有一定经验但想系统化工程实践的开发者这篇文章都将为你提供一条明确的实践路径。1. 为什么你需要一个“标准”的企业级项目结构在开始敲代码之前我们必须先达成一个共识为什么不能把所有代码都写在Program.cs或者Controller里企业级项目结构的价值远不止于“看起来规范”。核心价值是应对变化与协作。想象一下当业务逻辑需要修改时如果它和数据库访问代码、API接口代码纠缠在一起你很可能改一处而崩三处。当需要更换数据库比如从SQL Server迁移到PostgreSQL时如果SQL语句散落在上百个业务方法里这几乎是一项不可能完成的任务。当新同事加入面对一个没有清晰结构的“意大利面条式”代码库他的学习成本和犯错概率会急剧上升。一个标准的分层架构如经典的三层或领域驱动设计中的分层通过分离关注点来解决这些问题。每一层有明确的职责表现层 (Presentation Layer):只负责接收HTTP请求、验证基础格式、返回响应。它不应该知道数据从哪里来。业务逻辑层 (Business Logic Layer / Service Layer):包含核心的业务规则和流程。它是系统的“大脑”协调数据流转和业务决策。数据访问层 (Data Access Layer / Repository Layer):负责与数据库、文件系统、外部API等数据源打交道。它封装了所有数据持久化的细节。这样当UI从Web API变成桌面应用时你只需替换表现层当数据库变更时你只需调整数据访问层。业务逻辑作为最核心的资产保持稳定。接下来我们就从零开始构建这样一个结构清晰的项目。2. 项目结构与技术栈选型我们将创建一个名为EnterpriseDemo的解决方案。技术栈选择当前最主流、最稳定的组合.NET 8 最新的LTS长期支持版本性能和支持都有保障。ASP.NET Core Web API 构建RESTful API的事实标准。Entity Framework Core 8 ORM框架用于对象关系映射支持Code First开发模式。SQL Server LocalDB / SQLite 为了演示方便我们使用轻量级的LocalDBWindows或SQLite跨平台生产环境可无缝切换至SQL Server、PostgreSQL等。xUnitMoq 用于编写单元测试和模拟依赖。Swagger/OpenAPI 自动生成API文档。最终的解决方案结构如下使用Visual Studio 2022或Rider或通过CLI创建EnterpriseDemo.sln ├── src/ │ ├── EnterpriseDemo.API/ (表现层 - ASP.NET Core Web API 项目) │ ├── EnterpriseDemo.Core/ (核心层 - 实体、枚举、接口、DTO等) │ ├── EnterpriseDemo.Application/ (应用层 - 业务逻辑、服务、映射) │ └── EnterpriseDemo.Infrastructure/ (基础设施层 - 数据访问、外部服务集成) └── tests/ └── EnterpriseDemo.Tests/ (单元测试项目)这种结构清晰地区分了职责并且通过项目引用而非DLL来管理依赖比如API项目引用Application和InfrastructureApplication引用CoreInfrastructure引用Core和Application对于接口。3. 环境准备与项目创建3.1 开发环境准备安装 .NET 8 SDK: 前往 微软官网 下载并安装。安装 IDE: 推荐使用Visual Studio 2022(社区版免费) 并确保安装了“ASP.NET和Web开发”工作负载。或者使用JetBrains Rider、Visual Studio Code。数据库: 确保已安装SQL Server Express LocalDB(通常随VS安装) 或SQLite。3.2 使用 CLI 创建解决方案和项目打开终端PowerShell, CMD, 或 Bash执行以下命令# 创建解决方案目录并进入 mkdir EnterpriseDemo cd EnterpriseDemo # 创建解决方案文件 dotnet new sln -n EnterpriseDemo # 创建各个项目 dotnet new webapi -n EnterpriseDemo.API -f net8.0 --no-https -o src/EnterpriseDemo.API dotnet new classlib -n EnterpriseDemo.Core -f net8.0 -o src/EnterpriseDemo.Core dotnet new classlib -n EnterpriseDemo.Application -f net8.0 -o src/EnterpriseDemo.Application dotnet new classlib -n EnterpriseDemo.Infrastructure -f net8.0 -o src/EnterpriseDemo.Infrastructure dotnet new xunit -n EnterpriseDemo.Tests -f net8.0 -o tests/EnterpriseDemo.Tests # 将项目添加到解决方案 dotnet sln add src/EnterpriseDemo.API/EnterpriseDemo.API.csproj dotnet sln add src/EnterpriseDemo.Core/EnterpriseDemo.Core.csproj dotnet sln add src/EnterpriseDemo.Application/EnterpriseDemo.Application.csproj dotnet sln add src/EnterpriseDemo.Infrastructure/EnterpriseDemo.Infrastructure.csproj dotnet sln add tests/EnterpriseDemo.Tests/EnterpriseDemo.Tests.csproj3.3 配置项目依赖关系这是架构清晰的关键一步依赖必须单向流动高层模块不依赖低层模块细节。# API 层依赖 Application 和 Infrastructure dotnet add src/EnterpriseDemo.API/EnterpriseDemo.API.csproj reference src/EnterpriseDemo.Application/EnterpriseDemo.Application.csproj dotnet add src/EnterpriseDemo.API/EnterpriseDemo.API.csproj reference src/EnterpriseDemo.Infrastructure/EnterpriseDemo.Infrastructure.csproj # Application 层依赖 Core dotnet add src/EnterpriseDemo.Application/EnterpriseDemo.Application.csproj reference src/EnterpriseDemo.Core/EnterpriseDemo.Core.csproj # Infrastructure 层依赖 Core 和 Application (为实现Application中定义的接口) dotnet add src/EnterpriseDemo.Infrastructure/EnterpriseDemo.Infrastructure.csproj reference src/EnterpriseDemo.Core/EnterpriseDemo.Core.csproj dotnet add src/EnterpriseDemo.Infrastructure/EnterpriseDemo.Infrastructure.csproj reference src/EnterpriseDemo.Application/EnterpriseDemo.Application.csproj # Tests 项目依赖所有需要测试的项目 dotnet add tests/EnterpriseDemo.Tests/EnterpriseDemo.Tests.csproj reference src/EnterpriseDemo.API/EnterpriseDemo.API.csproj dotnet add tests/EnterpriseDemo.Tests/EnterpriseDemo.Tests.csproj reference src/EnterpriseDemo.Application/EnterpriseDemo.Application.csproj dotnet add tests/EnterpriseDemo.Tests/EnterpriseDemo.Tests.csproj reference src/EnterpriseDemo.Infrastructure/EnterpriseDemo.Infrastructure.csproj4. 核心层 (Core)定义领域模型与契约Core 项目应该保持“纯净”不依赖任何其他项目。它包含系统的核心概念。4.1 定义领域实体在EnterpriseDemo.Core/Entities/文件夹下创建两个实体类Product.cs和User.cs。// 文件src/EnterpriseDemo.Core/Entities/Product.cs using System; using System.ComponentModel.DataAnnotations; namespace EnterpriseDemo.Core.Entities { public class Product { [Key] public int Id { get; set; } [Required] [MaxLength(100)] public string Name { get; set; } string.Empty; [MaxLength(500)] public string? Description { get; set; } [Range(0, double.MaxValue)] public decimal Price { get; set; } public int StockQuantity { get; set; } public DateTime CreatedAt { get; set; } DateTime.UtcNow; public DateTime? UpdatedAt { get; set; } } }// 文件src/EnterpriseDemo.Core/Entities/User.cs using System; using System.ComponentModel.DataAnnotations; namespace EnterpriseDemo.Core.Entities { public class User { [Key] public int Id { get; set; } [Required] [MaxLength(50)] public string Username { get; set; } string.Empty; [Required] [EmailAddress] [MaxLength(100)] public string Email { get; set; } string.Empty; // 注意实际项目中密码不应以明文存储这里仅为演示。 // 应使用哈希加盐处理如 Identity 的 PasswordHasher。 [Required] public string PasswordHash { get; set; } string.Empty; public bool IsActive { get; set; } true; public DateTime CreatedAt { get; set; } DateTime.UtcNow; public DateTime? LastLoginAt { get; set; } } }4.2 定义数据访问接口仓储模式仓储模式抽象了数据访问逻辑。我们在Core层定义接口在Infrastructure层实现。在EnterpriseDemo.Core/Interfaces/下创建通用仓储接口和具体实体的仓储接口。// 文件src/EnterpriseDemo.Core/Interfaces/IGenericRepository.cs using System.Linq.Expressions; namespace EnterpriseDemo.Core.Interfaces { public interface IGenericRepositoryT where T : class { TaskT? GetByIdAsync(int id); TaskIEnumerableT GetAllAsync(); TaskIEnumerableT FindAsync(ExpressionFuncT, bool predicate); TaskT AddAsync(T entity); Task UpdateAsync(T entity); Task DeleteAsync(T entity); Taskbool ExistsAsync(int id); } }// 文件src/EnterpriseDemo.Core/Interfaces/IProductRepository.cs using EnterpriseDemo.Core.Entities; namespace EnterpriseDemo.Core.Interfaces { public interface IProductRepository : IGenericRepositoryProduct { // 可以定义Product特有的数据访问方法 TaskIEnumerableProduct GetProductsByPriceRangeAsync(decimal minPrice, decimal maxPrice); } }同理创建IUserRepository.cs。这样业务逻辑层Application只依赖于这些接口而不关心底层是用Entity Framework、Dapper还是别的什么实现的。5. 基础设施层 (Infrastructure)实现数据持久化这一层负责实现Core层定义的接口并处理与外部资源数据库、文件、API等的交互。5.1 添加 EF Core 包并配置 DbContext首先为EnterpriseDemo.Infrastructure项目添加必要的 NuGet 包。cd src/EnterpriseDemo.Infrastructure dotnet add package Microsoft.EntityFrameworkCore.SqlServer dotnet add package Microsoft.EntityFrameworkCore.Tools # 如果使用SQLite # dotnet add package Microsoft.EntityFrameworkCore.Sqlite然后创建数据库上下文AppDbContext.cs。// 文件src/EnterpriseDemo.Infrastructure/Data/AppDbContext.cs using EnterpriseDemo.Core.Entities; using Microsoft.EntityFrameworkCore; namespace EnterpriseDemo.Infrastructure.Data { public class AppDbContext : DbContext { public AppDbContext(DbContextOptionsAppDbContext options) : base(options) { } public DbSetProduct Products { get; set; } public DbSetUser Users { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); // 这里可以配置实体关系、索引、种子数据等 modelBuilder.EntityProduct(entity { entity.HasIndex(p p.Name).IsUnique(); // 产品名称唯一索引 entity.Property(p p.Price).HasPrecision(18, 2); // 价格精度 }); modelBuilder.EntityUser(entity { entity.HasIndex(u u.Username).IsUnique(); entity.HasIndex(u u.Email).IsUnique(); }); // 种子数据 modelBuilder.EntityProduct().HasData( new Product { Id 1, Name 示例产品A, Description 这是一个种子产品, Price 99.99m, StockQuantity 100 }, new Product { Id 2, Name 示例产品B, Description 另一个种子产品, Price 199.99m, StockQuantity 50 } ); } } }5.2 实现仓储类在EnterpriseDemo.Infrastructure/Repositories/下实现具体的仓储。// 文件src/EnterpriseDemo.Infrastructure/Repositories/GenericRepository.cs using EnterpriseDemo.Core.Interfaces; using EnterpriseDemo.Infrastructure.Data; using Microsoft.EntityFrameworkCore; using System.Linq.Expressions; namespace EnterpriseDemo.Infrastructure.Repositories { public class GenericRepositoryT : IGenericRepositoryT where T : class { protected readonly AppDbContext _context; protected readonly DbSetT _dbSet; public GenericRepository(AppDbContext context) { _context context; _dbSet context.SetT(); } public virtual async TaskT? GetByIdAsync(int id) { return await _dbSet.FindAsync(id); } public virtual async TaskIEnumerableT GetAllAsync() { return await _dbSet.ToListAsync(); } public virtual async TaskIEnumerableT FindAsync(ExpressionFuncT, bool predicate) { return await _dbSet.Where(predicate).ToListAsync(); } public virtual async TaskT AddAsync(T entity) { await _dbSet.AddAsync(entity); await _context.SaveChangesAsync(); return entity; } public virtual async Task UpdateAsync(T entity) { _dbSet.Update(entity); await _context.SaveChangesAsync(); } public virtual async Task DeleteAsync(T entity) { _dbSet.Remove(entity); await _context.SaveChangesAsync(); } public virtual async Taskbool ExistsAsync(int id) { var entity await GetByIdAsync(id); return entity ! null; } } }// 文件src/EnterpriseDemo.Infrastructure/Repositories/ProductRepository.cs using EnterpriseDemo.Core.Entities; using EnterpriseDemo.Core.Interfaces; using EnterpriseDemo.Infrastructure.Data; using Microsoft.EntityFrameworkCore; namespace EnterpriseDemo.Infrastructure.Repositories { public class ProductRepository : GenericRepositoryProduct, IProductRepository { public ProductRepository(AppDbContext context) : base(context) { } public async TaskIEnumerableProduct GetProductsByPriceRangeAsync(decimal minPrice, decimal maxPrice) { return await _context.Products .Where(p p.Price minPrice p.Price maxPrice) .OrderBy(p p.Price) .ToListAsync(); } } }UserRepository的实现类似。5.3 配置依赖注入Service Extensions为了保持Program.cs的整洁我们创建一个扩展方法来集中注册Infrastructure层的服务。// 文件src/EnterpriseDemo.Infrastructure/DependencyInjection.cs using EnterpriseDemo.Core.Interfaces; using EnterpriseDemo.Infrastructure.Data; using EnterpriseDemo.Infrastructure.Repositories; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; namespace EnterpriseDemo.Infrastructure { public static class DependencyInjection { public static IServiceCollection AddInfrastructure(this IServiceCollection services, IConfiguration configuration) { // 配置数据库上下文 services.AddDbContextAppDbContext(options options.UseSqlServer( configuration.GetConnectionString(DefaultConnection), b b.MigrationsAssembly(typeof(AppDbContext).Assembly.FullName))); // 重要指定迁移程序集 // 注册仓储 services.AddScoped(typeof(IGenericRepository), typeof(GenericRepository)); services.AddScopedIProductRepository, ProductRepository(); services.AddScopedIUserRepository, UserRepository(); return services; } } }6. 应用层 (Application)实现业务逻辑Application层是系统的核心它包含业务规则、工作流和用例。它依赖于Core层的接口并通过Infrastructure层注入的具体实现来操作数据。6.1 定义数据传输对象 (DTOs)Controller不应直接接收或返回实体对象应使用DTO。在EnterpriseDemo.Application/DTOs/下创建。// 文件src/EnterpriseDemo.Application/DTOs/ProductDto.cs using System.ComponentModel.DataAnnotations; namespace EnterpriseDemo.Application.DTOs { public class ProductDto { public int Id { get; set; } [Required] [StringLength(100)] public string Name { get; set; } string.Empty; [StringLength(500)] public string? Description { get; set; } [Range(0.01, double.MaxValue)] public decimal Price { get; set; } [Range(0, int.MaxValue)] public int StockQuantity { get; set; } } public class CreateProductDto { [Required] [StringLength(100)] public string Name { get; set; } string.Empty; [StringLength(500)] public string? Description { get; set; } [Range(0.01, double.MaxValue)] public decimal Price { get; set; } [Range(0, int.MaxValue)] public int StockQuantity { get; set; } } public class UpdateProductDto : CreateProductDto { // 更新DTO可能包含ID或其他标识这里继承自Create } }6.2 定义服务接口与实现在EnterpriseDemo.Application/Interfaces/和EnterpriseDemo.Application/Services/下创建。// 文件src/EnterpriseDemo.Application/Interfaces/IProductService.cs using EnterpriseDemo.Application.DTOs; namespace EnterpriseDemo.Application.Interfaces { public interface IProductService { TaskIEnumerableProductDto GetAllProductsAsync(); TaskProductDto? GetProductByIdAsync(int id); TaskProductDto CreateProductAsync(CreateProductDto createProductDto); Task UpdateProductAsync(int id, UpdateProductDto updateProductDto); Task DeleteProductAsync(int id); TaskIEnumerableProductDto GetProductsByPriceRangeAsync(decimal minPrice, decimal maxPrice); } }// 文件src/EnterpriseDemo.Application/Services/ProductService.cs using AutoMapper; using EnterpriseDemo.Application.DTOs; using EnterpriseDemo.Application.Interfaces; using EnterpriseDemo.Core.Entities; using EnterpriseDemo.Core.Interfaces; using Microsoft.Extensions.Logging; namespace EnterpriseDemo.Application.Services { public class ProductService : IProductService { private readonly IProductRepository _productRepository; private readonly IMapper _mapper; private readonly ILoggerProductService _logger; public ProductService(IProductRepository productRepository, IMapper mapper, ILoggerProductService logger) { _productRepository productRepository; _mapper mapper; _logger logger; } public async TaskIEnumerableProductDto GetAllProductsAsync() { var products await _productRepository.GetAllAsync(); _logger.LogInformation(获取了所有产品共 {Count} 条记录, products.Count()); return _mapper.MapIEnumerableProductDto(products); } public async TaskProductDto? GetProductByIdAsync(int id) { var product await _productRepository.GetByIdAsync(id); if (product null) { _logger.LogWarning(未找到ID为 {ProductId} 的产品, id); return null; } return _mapper.MapProductDto(product); } public async TaskProductDto CreateProductAsync(CreateProductDto createProductDto) { var productEntity _mapper.MapProduct(createProductDto); productEntity.CreatedAt DateTime.UtcNow; var createdProduct await _productRepository.AddAsync(productEntity); _logger.LogInformation(创建了新产品ID: {ProductId}, 名称: {ProductName}, createdProduct.Id, createdProduct.Name); return _mapper.MapProductDto(createdProduct); } public async Task UpdateProductAsync(int id, UpdateProductDto updateProductDto) { var productEntity await _productRepository.GetByIdAsync(id); if (productEntity null) { throw new KeyNotFoundException($未找到ID为 {id} 的产品); } _mapper.Map(updateProductDto, productEntity); productEntity.UpdatedAt DateTime.UtcNow; await _productRepository.UpdateAsync(productEntity); _logger.LogInformation(更新了产品ID: {ProductId}, id); } public async Task DeleteProductAsync(int id) { var productEntity await _productRepository.GetByIdAsync(id); if (productEntity null) { throw new KeyNotFoundException($未找到ID为 {id} 的产品); } await _productRepository.DeleteAsync(productEntity); _logger.LogInformation(删除了产品ID: {ProductId}, id); } public async TaskIEnumerableProductDto GetProductsByPriceRangeAsync(decimal minPrice, decimal maxPrice) { var products await _productRepository.GetProductsByPriceRangeAsync(minPrice, maxPrice); return _mapper.MapIEnumerableProductDto(products); } } }注意这里使用了AutoMapper进行对象映射。需要为Application项目添加AutoMapper.Extensions.Microsoft.DependencyInjection包。6.3 配置AutoMapper Profile在EnterpriseDemo.Application/Common/Mappings/下创建映射配置。// 文件src/EnterpriseDemo.Application/Common/Mappings/MappingProfile.cs using AutoMapper; using EnterpriseDemo.Application.DTOs; using EnterpriseDemo.Core.Entities; namespace EnterpriseDemo.Application.Common.Mappings { public class MappingProfile : Profile { public MappingProfile() { CreateMapProduct, ProductDto().ReverseMap(); CreateMapProduct, CreateProductDto().ReverseMap(); CreateMapProduct, UpdateProductDto().ReverseMap(); // 对于Update我们可能希望部分字段忽略可以更精细配置 CreateMapUpdateProductDto, Product() .ForAllMembers(opts opts.Condition((src, dest, srcMember) srcMember ! null)); } } }6.4 应用层依赖注入配置同样创建一个扩展方法来注册Application层的服务。// 文件src/EnterpriseDemo.Application/DependencyInjection.cs using EnterpriseDemo.Application.Common.Mappings; using EnterpriseDemo.Application.Interfaces; using EnterpriseDemo.Application.Services; using Microsoft.Extensions.DependencyInjection; using System.Reflection; namespace EnterpriseDemo.Application { public static class DependencyInjection { public static IServiceCollection AddApplication(this IServiceCollection services) { // 注册AutoMapper从当前程序集扫描Profile services.AddAutoMapper(Assembly.GetExecutingAssembly()); // 注册应用服务 services.AddScopedIProductService, ProductService(); services.AddScopedIUserService, UserService(); // 假设有UserService return services; } } }7. 表现层 (API)构建RESTful端点现在我们来到最外层构建供客户端调用的API。7.1 配置API项目首先为EnterpriseDemo.API项目添加对Microsoft.EntityFrameworkCore.Design包的引用用于迁移。cd src/EnterpriseDemo.API dotnet add package Microsoft.EntityFrameworkCore.Design然后修改appsettings.json添加数据库连接字符串。// 文件src/EnterpriseDemo.API/appsettings.json { Logging: { LogLevel: { Default: Information, Microsoft.AspNetCore: Warning } }, ConnectionStrings: { DefaultConnection: Server(localdb)\\mssqllocaldb;DatabaseEnterpriseDemoDb;Trusted_ConnectionTrue;MultipleActiveResultSetstrue;TrustServerCertificateTrue }, AllowedHosts: * }7.2 配置Program.cs这是应用的入口负责组装所有部件。// 文件src/EnterpriseDemo.API/Program.cs using EnterpriseDemo.Application; using EnterpriseDemo.Infrastructure; var builder WebApplication.CreateBuilder(args); // 添加服务到容器 builder.Services.AddControllers(); builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); // 添加Swagger // 注册我们自定义的各层服务 builder.Services.AddApplication(); builder.Services.AddInfrastructure(builder.Configuration); var app builder.Build(); // 配置HTTP请求管道 if (app.Environment.IsDevelopment()) { app.UseSwagger(); app.UseSwaggerUI(); } app.UseHttpsRedirection(); app.UseAuthorization(); app.MapControllers(); // 确保数据库被创建并应用迁移仅用于开发环境 using (var scope app.Services.CreateScope()) { var dbContext scope.ServiceProvider.GetRequiredServiceInfrastructure.Data.AppDbContext(); dbContext.Database.EnsureCreated(); // 或使用 dbContext.Database.Migrate() 如果使用了迁移 } app.Run();7.3 创建Product控制器在Controllers文件夹下创建ProductsController.cs。// 文件src/EnterpriseDemo.API/Controllers/ProductsController.cs using EnterpriseDemo.Application.DTOs; using EnterpriseDemo.Application.Interfaces; using Microsoft.AspNetCore.Mvc; namespace EnterpriseDemo.API.Controllers { [Route(api/[controller])] [ApiController] public class ProductsController : ControllerBase { private readonly IProductService _productService; public ProductsController(IProductService productService) { _productService productService; } // GET: api/products [HttpGet] public async TaskActionResultIEnumerableProductDto GetProducts() { var products await _productService.GetAllProductsAsync(); return Ok(products); } // GET: api/products/5 [HttpGet({id})] public async TaskActionResultProductDto GetProduct(int id) { var product await _productService.GetProductByIdAsync(id); if (product null) { return NotFound(); } return Ok(product); } // POST: api/products [HttpPost] public async TaskActionResultProductDto CreateProduct(CreateProductDto createProductDto) { if (!ModelState.IsValid) { return BadRequest(ModelState); } var createdProduct await _productService.CreateProductAsync(createProductDto); return CreatedAtAction(nameof(GetProduct), new { id createdProduct.Id }, createdProduct); } // PUT: api/products/5 [HttpPut({id})] public async TaskIActionResult UpdateProduct(int id, UpdateProductDto updateProductDto) { if (id ! updateProductDto.Id) // 假设UpdateProductDto包含Id { return BadRequest(ID不匹配); } if (!ModelState.IsValid) { return BadRequest(ModelState); } try { await _productService.UpdateProductAsync(id, updateProductDto); } catch (KeyNotFoundException) { return NotFound(); } return NoContent(); } // DELETE: api/products/5 [HttpDelete({id})] public async TaskIActionResult DeleteProduct(int id) { try { await _productService.DeleteProductAsync(id); } catch (KeyNotFoundException) { return NotFound(); } return NoContent(); } // GET: api/products/price-range?min10max100 [HttpGet(price-range)] public async TaskActionResultIEnumerableProductDto GetProductsByPriceRange([FromQuery] decimal min, [FromQuery] decimal max) { if (min max) { return BadRequest(最低价格不能高于最高价格); } var products await _productService.GetProductsByPriceRangeAsync(min, max); return Ok(products); } } }8. 运行与验证8.1 运行项目在终端中导航到src/EnterpriseDemo.API目录。运行dotnet run。控制台会输出应用监听的地址通常是https://localhost:5001和http://localhost:5000。8.2 使用Swagger UI测试API在浏览器中打开https://localhost:5001/swagger(或对应的HTTP地址)。你将看到自动生成的API文档列出了Products的所有端点。尝试执行以下操作GET /api/products: 应返回两个种子产品。POST /api/products: 点击“Try it out”填入JSON数据创建新产品。{ name: 新测试产品, description: 通过API创建, price: 299.99, stockQuantity: 10 }GET /api/products/{id}: 使用新创建产品的ID获取它。PUT /api/products/{id}: 更新产品信息。DELETE /api/products/{id}: 删除产品。GET /api/products/price-range?min50max200: 查询价格范围内的产品。8.3 验证数据库可以使用SQL Server Object Explorer(VS) 或Azure Data Studio等工具连接到(localdb)\mssqllocaldb查看EnterpriseDemoDb数据库中的Products和Users表确认数据已持久化。9. 常见问题与排查思路问题现象可能原因排查方式解决方案启动时报错无法找到AppDbContext的构造函数1.AppDbContext未在Program.cs中通过AddDbContext注册。2. 连接字符串配置错误。1. 检查Program.cs中是否调用了AddInfrastructure。2. 检查appsettings.json中的ConnectionStrings节点。1. 确保services.AddInfrastructure(configuration);被调用。2. 确认连接字符串格式正确数据库实例存在。运行迁移命令dotnet ef migrations add InitialCreate失败1. 未在启动项目API中安装Microsoft.EntityFrameworkCore.Design。2. 未指定启动项目和DbContext所在项目。查看错误信息通常提示“No DbContext was found”。1. 确保在API项目中安装了Microsoft.EntityFrameworkCore.Design包。2. 使用完整命令dotnet ef migrations add InitialCreate -s ../EnterpriseDemo.API -p ../EnterpriseDemo.InfrastructureSwagger页面能打开但调用API返回4041. 控制器路由配置错误。2. 请求的HTTP方法或URL不正确。1. 检查控制器上的[Route(api/[controller])]属性。2. 在Swagger UI中查看每个端点的完整路径。1. 确保控制器继承自ControllerBase并具有[ApiController]属性。2. 严格按照Swagger显示的URL和参数格式调用。AutoMapper映射失败缺少类型映射配置1.MappingProfile未正确注册到AutoMapper。2. DTO和Entity的属性名或类型不匹配。1. 检查AddApplication方法中是否调用了AddAutoMapper。2. 在服务中注入IMapper并单步调试查看映射异常信息。1. 确保AddAutoMapper(Assembly.GetExecutingAssembly())能扫描到包含MappingProfile的程序集。2. 在MappingProfile中为复杂的映射关系添加自定义CreateMap配置。依赖注入错误无法解析服务1. 服务如IProductService未在DI容器中注册。2. 服务生命周期配置错误如Scoped服务在Singleton中注入。查看启动时的异常堆栈信息会明确指出哪个服务无法解析。1. 检查IProductService和ProductService是否在AddApplication中通过AddScoped注册。2. 确保服务生命周期匹配。通常仓储和服务使用Scoped。10. 最佳实践与工程建议异步编程 如上所示全程使用async/await避免阻塞线程提高Web应用的并发能力。日志记录 在服务层使用ILogger记录关键操作和异常便于生产环境排查问题。避免在Controller中记录过多业务日志。异常处理 在Controller中进行基本的模型验证和异常捕获返回合适的HTTP状态码如404、400。更复杂的业务异常可以考虑使用自定义异常和全局异常过滤器。输入验证 除了Controller的[ApiController]自动模型验证在DTO上使用数据注解如[Required],[StringLength]进行声明式验证。复杂规则应在服务层实现。跨域请求 (CORS) 如果前端独立部署需要在Program.cs中配置CORS策略。环境配置 使用appsettings.Development.json和appsettings.Production.json管理不同环境的配置如数据库连接字符串、日志级别。单元测试 为服务层编写单元测试使用xUnit和Moq模拟仓储依赖确保业务逻辑正确性。迁移管理 对于生产环境使用EF Core的代码迁移 (dotnet ef migrations) 来管理数据库架构变更而不是EnsureCreated。API版本控制 当API需要重大变更时考虑引入API版本控制如Microsoft.AspNetCore.Mvc.Versioning。性能考量 对于大型数据集在仓储层实现分页查询避免一次性加载所有数据。考虑使用AsNoTracking()进行只读查询以提升性能。至此你已经拥有了一个结构清晰、职责分明、可测试、可扩展的C#企业级项目骨架。这个项目模板清晰地展示了分层架构、依赖注入、仓储模式、DTO映射等核心概念是如何协同工作的。你可以在此基础上轻松地添加新的实体、业务逻辑和API端点例如实现完整的用户认证授权JWT、更复杂的查询过滤、文件上传、缓存集成、消息队列等高级功能。真正的企业级项目正是在这样健壮的基础上通过不断解决具体的业务需求而演化出来的。建议你将此项目作为模板保存并在未来的开发中反复实践和优化这些模式。
RELATED — 相关阅读

相关资讯

LATEST — 最新资讯

最新发布

TODAY — 本日精选

新闻

WEEKLY — 本周精选

新闻

MONTHLY — 本月精选

新闻