ASP.NET Core 分层项目中EFCore的使用
文章目录
- 前言
- 一、核心
- 二、项目分层结构
- 1)安装 NuGet 包
- Web 项目
- InfrastructureLibrary项目
- 2)领域模型和仓储接口 (Domain 层)
- 3)基础设施层实现 (Infrastructure 层)
- 4)应用层服务 (Application 层)
- 5)Web API 配置
- 6)控制器 (Web 层)
- 7)数据库迁移
- 三、关键点说明
- 分层依赖:
- 依赖注入:
- 异步操作:
- 领域驱动设计:
- 总结
前言
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 项目中通过职责分离提升了代码质量,但需要权衡设计复杂度和实际需求。通过合理分层、依赖注入和接口抽象,可以构建高可维护、可测试的应用程序,同时避免过度设计。最终,架构选择应服务于业务需求,而非盲目追求分层形式。