文章目录
前言
ASP.NET Core Web API 结合 Entity Framework Core (EF Core) 的分层项目架构是一种常见的开发模式,旨在通过职责分离提高代码的可维护性、可测试性和扩展性。
一、核心
分层架构将应用按功能划分为多个逻辑层,各层职责明确,通过接口或 DTO(Data Transfer Object)通信。
二、项目分层结构
Presentation层 (Web API) - 处理 HTTP 请求/响应
Application层 - 业务逻辑和用例实现
Domain层 - 实体模型和仓储接口
Infrastructure层 - EFCore 实现和数据库配置
MyAspNetCoreWebApplication.sln ├─ MyAspNetCoreWebApplication.MyWebApplication(ASP.NET Core Web API) ├─ MyAspNetCoreWebApplication.ApplicationLibrary(Class Library) ├─ MyAspNetCoreWebApplication.DomainLibrary(Class Library) └─ MyAspNetCoreWebApplication.InfrastructureLibrary(Class Library)
1)安装 NuGet 包
Web 项目
Microsoft.EntityFrameworkCore.SqlServer
InfrastructureLibrary项目
Install-Package Microsoft.EntityFrameworkCore
Install-Package Microsoft.EntityFrameworkCore.Design
Install-Package Microsoft.EntityFrameworkCore.Tools
Microsoft.Extensions.Configuration
Microsoft.Extensions.Configuration.EnvironmentVariables
2)领域模型和仓储接口 (Domain 层)
- 实体(Entity)
目录结构:DomainLibrary-Entity- Book.cs
public class Book { public long Id { get; set; } public string Title { get; set; } public double Price { get; set; } public string AuthorId { get; set; } public string AuthorName { get; set; } }
- Product.cs
public class Product { public long Id { get; set; } public string Name { get; set; } public string Description { get; set; } public double Price { get; set; } }
- 仓储接口(RepositoryInterface)
目录结构:DomainLibrary-Repository- IBookRepository.cs
public interface IBookRepository { Task<IEnumerable<Book>> GetAllAsync(); Task<Book> GetByIdAsync(long id); Task AddAsync(Book book); Task DeleteAsync(long id); Task UpdateAsync(Book book); }
- IProjectRepository.cs
public interface IProjectRepository { Task<IEnumerable<Product>> GetAllAsync(); Task<Product> GetByIdAsync(long id); Task AddAsync(Product project); Task DeleteAsync(long id); Task UpdateAsync(Product project); }
3)基础设施层实现 (Infrastructure 层)
数据库上下文类
目录结构:InfrastructureLibrary-Data- MyDbContext.cs
public class MyDbContext : DbContext { public MyDbContext(DbContextOptions<MyDbContext> options) : base(options) { } public DbSet<Product> Projects { get; set; } public DbSet<Book> Books { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); modelBuilder.ApplyConfigurationsFromAssembly(this.GetType().Assembly); } }
- MyDbContextDesignFactory.cs
internal class MyDbContextDesignFactory : IDesignTimeDbContextFactory<MyDbContext> { public MyDbContext CreateDbContext(string[] args) { // 手动构建配置(读取环境变量) var configuration = new ConfigurationBuilder() .AddEnvironmentVariables() // 添加环境变量作为配置源 .Build(); string connStr = configuration.GetSection("ConnStr").Value; if (connStr == null) { throw new Exception("连接字符串为空"); } DbContextOptionsBuilder<MyDbContext> opt = new DbContextOptionsBuilder<MyDbContext>(); opt.UseSqlServer(connStr); return new MyDbContext(opt.Options); } }
- MyDbContext.cs
配置类
目录结构:InfrastructureLibrary-Config- BookConfig.cs
public class BookConfig : IEntityTypeConfiguration<Book> { public void Configure(EntityTypeBuilder<Book> builder) { builder.ToTable("T_Books"); } }
- ProductConfig.cs
public class ProductConfig : IEntityTypeConfiguration<Product> { public void Configure(EntityTypeBuilder<Product> builder) { builder.ToTable("T_Products"); builder.Property(p => p.Name).HasMaxLength(100).IsRequired(); builder.Property(p => p.Price).HasColumnType("decimal(18,2)"); } }
- BookConfig.cs
实现类Repositories
目录结构:InfrastructureLibrary-Repositories- BookRepository.cs
public class BookRepository { private readonly MyDbContext _dbContext; public BookRepository(MyDbContext context) { _dbContext = context; } public async Task<IEnumerable<Book>> GetAllAsync() => await _dbContext.Books.ToListAsync(); public async Task<Book> GetByIdAsync(long id) => await _dbContext.Books.FindAsync(id); public async Task AddAsync(Book book) { if (book != null) { await _dbContext.Books.AddAsync(book); await _dbContext.SaveChangesAsync(); } } public async Task DeleteAsync(long id) { Book p = await GetByIdAsync(id); if (p != null) { _dbContext.Books.Remove(p); await _dbContext.SaveChangesAsync(); } } public async Task UpdateAsync(Book book) { if (book != null) { _dbContext.Books.Update(book); await _dbContext.SaveChangesAsync(); } } }
- ProductRepository.cs
public class ProductRepository : IProjectRepository { private readonly MyDbContext _dbContext; public ProductRepository(MyDbContext dbContext) { _dbContext = dbContext; } public async Task<IEnumerable<Product>> GetAllAsync() => await _dbContext.Projects.ToListAsync(); public async Task<Product> GetByIdAsync(long id) => await _dbContext.Projects.FindAsync(id); public async Task AddAsync(Product project) { if (project!=null) { await _dbContext.Projects.AddAsync(project); await _dbContext.SaveChangesAsync(); } } public async Task DeleteAsync(long id) { Product p=await GetByIdAsync(id); if (p != null) { _dbContext.Projects.Remove(p); await _dbContext.SaveChangesAsync(); } } public async Task UpdateAsync(Product project) { if (project != null) { _dbContext.Projects.Update(project); await _dbContext.SaveChangesAsync(); } } }
- BookRepository.cs
4)应用层服务 (Application 层)
- 服务类
目录结构:ApplicationLibrary-Services- BookService.cs
public class BookService { private readonly IBookRepository bookRepository; public BookService(IBookRepository bookRepository) { this.bookRepository = bookRepository; } public async Task<IEnumerable<Book>> GetAllBookAsync() { return await bookRepository.GetAllAsync(); } public async Task<Book> GetBookById(long id) { return await bookRepository.GetByIdAsync(id); } public async Task AddBook(Book book) => await bookRepository.AddAsync(book); public async Task UpdateBook(Book book) => await bookRepository.UpdateAsync(book); public async Task DeleteBook(long id) => await bookRepository.DeleteAsync(id); }
- ProductService.cs
public class ProductService { private readonly IProjectRepository _repository; public ProductService(IProjectRepository repository) { _repository = repository; } public async Task<IEnumerable<Product>> GetAllProjectAsync() { return await _repository.GetAllAsync(); } public async Task<Product> GetProductById(long id) { return await _repository.GetByIdAsync(id); } public async Task AddProduct(Product product) =>await _repository.AddAsync(product); public async Task UpdateProject(Product product) => await _repository.UpdateAsync(product); public async Task DeleteProduct(long id) => await _repository.DeleteAsync(id); }
- BookService.cs
5)Web API 配置
Program.cs 配置
- Program.cs
using DomainLibrary.Repository; using InfrastructureLibrary.Repositories; using Microsoft.EntityFrameworkCore; using ApplicationLibrary.Services; using InfrastructureLibrary.Data; var builder = WebApplication.CreateBuilder(args); // Add services to the container. //配置数据库 builder.Services.AddDbContext<MyDbContext>(opt => { string connStr = builder.Configuration.GetConnectionString("DefaultConnection");//GetSection("ConnStr").Value; opt.UseSqlServer(connStr); }); // 注册仓储 builder.Services.AddScoped<IProjectRepository, ProductRepository>(); //注册服务 builder.Services.AddScoped<ProductService>(); builder.Services.AddControllers(); builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); var app = builder.Build(); // 自动迁移数据库(可选) using (var scope = app.Services.CreateScope()) { var db = scope.ServiceProvider.GetRequiredService<MyDbContext>(); db.Database.Migrate(); } // Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) { app.UseSwagger(); app.UseSwaggerUI(); } app.UseHttpsRedirection(); app.UseAuthorization(); app.MapControllers(); app.Run();
- Program.cs
appsettings.json配置
- appsettings.json
{ "Logging": { "LogLevel": { "Default": "Information", "Microsoft.AspNetCore": "Warning" } }, "AllowedHosts": "*", "ConnectionStrings": { "DefaultConnection": "Server=XXX;Database=XXX;User Id=sa;Password=XXX;TrustServerCertificate=True;Trusted_Connection=True;MultipleActiveResultSets=True" } }
- appsettings.json
6)控制器 (Web 层)
- 控制器示例
- TestController.cs
[Route("api/[controller]/[action]")] [ApiController] public class TestController : ControllerBase { private readonly MyDbContext _db; private readonly ProductService _productService; public TestController(MyDbContext db, ProductService productService) { _db = db; _productService = productService; } [HttpGet] public ActionResult<string> Get() { var count= _db.Books.Count(); return $"count={count}"; } [HttpGet] public async Task<IActionResult> GetAllProductsAsync() { var res=await _productService.GetAllProjectAsync(); return Ok(res); } }
- TestController.cs
7)数据库迁移
- 打开包管理器控制台
- 设置默认项目为 InfrastructureLibrary
- 执行命令:
Add-Migration Init Update-Database
三、关键点说明
分层依赖:
- Web 层引用 Application 、 Infrastructure、Domain
- Application 层引用 Domain、Infrastructure
- Infrastructure 层引用 Domain
依赖注入:
- 通过构造函数注入仓储和服务
- 使用 AddScoped 注册生命周期服务
异步操作:
- 所有数据库操作使用 async/await
- 使用 ToListAsync() 和 FindAsync() 等异步方法
领域驱动设计:
- 仓储模式隔离数据访问细节
- 领域模型保持纯净无基础设施依赖
总结
分层架构在 ASP.NET Core Web API + EF Core 项目中通过职责分离提升了代码质量,但需要权衡设计复杂度和实际需求。通过合理分层、依赖注入和接口抽象,可以构建高可维护、可测试的应用程序,同时避免过度设计。最终,架构选择应服务于业务需求,而非盲目追求分层形式。