很多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、更復雜的查詢過濾、文件上傳、緩存集成、消息隊列等高級功能。真正的企業級項目正是在這樣健壯的基礎上通過不斷解決具體的業務需求而演化出來的。建議你將此項目作為模板保存并在未來的開發中反復實踐和優化這些模式。