在这份指南中,我们将介绍如何构建一个 CRUD API 使用 ASP.NET Core 、 Entity Framework Core (EF Core) 和 PostgreSQL。应用程序将遵循 整洁架构原则 ,将代码组织到仓库层和服务层中。这种结构促进了不同职责的分离,使代码库更加易维护和测试。这种结构使代码更清晰,更容易维护和测试。
1. 项目搭建创建一个新的ASP.NET Core Web API
我们将从创建一个新的ASP.NET Core Web API项目开始,这将成为我们应用的基础。
- 打开终端(或命令行提示符)。
- 运行以下命令以创建项目如下:
mkdir PostgresCrud # 创建一个名为PostgresCrud的目录
cd PostgresCrud # 进入该目录
dotnet new webapi -n PostgresCrud # 使用dotnet创建一个新的webapi项目,命名为PostgresCrud
进入全屏,退出全屏
dotnet new webapi
创建一个新的默认配置的 Web API 项目。cd PostgresCrud
切换到 PostgresCrud 文件夹。
现在,我们有一个叫 PostgresCrud
的 ASP.NET Core Web API 项目。
在appsettings.json
文件中配置连接字符串
PostgreSQL的连接串将存储在appsettings.json
文件中。该文件包含了应用程序所需的配置,比如数据库连接信息。
示例配置appsettings.json
:
{
"ConnectionStrings": {
"DefaultConnection": "Host=localhost;Database=webapi_demo;Username=postgres;Password=mysecretpassword"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}
切换到全屏模式 退出全屏
- Host=localhost : PostgreSQL 数据库是在本地主机上运行。
- Database=webapi_demo : 数据库名称为。
- Username=postgres : PostgreSQL 用户名是。
- Password=mysecretpassword : PostgreSQL 密码是。
在我们开始与PostgreSQL数据库互动之前,我们需要先安装必要的NuGet包。
添加 Microsoft.EntityFrameworkCore 包 - 用于 Entity Framework Core
添加 Npgsql.EntityFrameworkCore.PostgreSQL 包 - 用于 PostgreSQL 数据库的 Entity Framework Core
添加 Swashbuckle.AspNetCore 包 - 用于生成 API 文档的 Swagger 工具
进入全屏 退出全屏
Microsoft.EntityFrameworkCore
:添加 Entity Framework Core (EF Core) ORM 以处理数据库操作。Npgsql.EntityFrameworkCore.PostgreSQL
:为 PostgreSQL 数据库提供支持。Swashbuckle.AspNetCore
:添加 Swagger 以生成 API 文档并进行测试。
ApplicationDbContext
是什么?
ApplicationDbContext
类起到连接我们应用程序和 PostgreSQL 数据库的桥梁作用。它包含表示数据库中表的 DbSet
属性,这些属性对应于数据库中的表。
文件名:Data/ApplicationDbContext.cs
using Microsoft.EntityFrameworkCore;
using PostgresCrud.Entities;
命名空间 PostgresCrud.Data
{
公共类 public ApplicationDbContext : DbContext
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options) { }
public DbSet<Product> Products { get; set; }
}
}
进入全屏 退出全屏
DbContext
: EF Core 提供的用于数据库操作的基础类,或者说,一个数据库操作的基类。DbSet<Product> Products
:它代表 PostgreSQL 中的Products
表,每个DbSet
对象都对应数据库中的一张表。
你知道什么是实体吗?
实体定义了数据库表的结构。在这种情况下,Product 实体代表存储在 Products
表中的产品。
文件:Entities/Product.cs
命名空间 PostgresCrud.Entities
{
public class Product
{
public int Id { get; set; }
public string Name { get; set; } = string.Empty;
public decimal Price { get; set; }
}
}
切换到全屏 退出全屏
Id
:这是产品的唯一标识符。Name
:产品的名称。Price
:产品的售价。
实现仓储模式
仓库模式(Repository Pattern)帮助我们将数据库操作封装在一个单独的类里,从而使代码更整洁和有条理。仓库将通过EF Core与数据库交互。
定义仓库的接口
文件路径:仓储层/IProductRepository.cs
using PostgresCrud.Entities;
namespace PostgresCrud.Repositories
{
public interface IProductRepository
{
Task<IEnumerable<Product>> GetAllAsync(); // 获取所有产品的异步方法
Task<Product> GetByIdAsync(int id); // 根据ID获取产品的异步方法
Task AddAsync(Product product); // 添加产品的异步方法
Task UpdateAsync(Product product); // 更新产品的异步方法
Task DeleteAsync(int id); // 根据ID删除产品的异步方法
}
}
进入全屏 退出全屏
IProductRepository
接口定义了五个增删改查(CRUD)操作:
——
GetAllAsync()
: 获取所有产品。GetByIdAsync(int id)
:根据ID获取产品。AddAsync(Product product)
:添加新商品。UpdateAsync(Product product)
:更新现有商品。DeleteAsync(int id)
:根据ID删除商品。
实现仓库模式
文件:Repositories/ProductRepository.cs
using Microsoft.EntityFrameworkCore; // 导入 Entity Framework Core 用于数据库相关操作
using PostgresCrud.Data; // 导入应用程序数据库上下文
using PostgresCrud.Entities; // 导入 Product 实体
namespace PostgresCrud.Repositories
{
public class ProductRepository : IProductRepository
{
private readonly ApplicationDbContext _context; // 数据库上下文用于与数据库交互
public ProductRepository(ApplicationDbContext context)
{
_context = context; // 通过构造函数注入数据库上下文对象
}
// 获取所有产品
public async Task<IEnumerable<Product>> GetAllAsync()
{
// 将 Products 表转换为列表并异步返回
return await _context.Products.ToListAsync();
}
// 通过 ID 获取产品
public async Task<Product> GetByIdAsync(int id)
{
// 使用 FindAsync 根据主键(ID)查找产品
return await _context.Products.FindAsync(id);
}
// 添加新产品到数据库
public async Task AddAsync(Product product)
{
// 将产品添加到数据库上下文中
await _context.Products.AddAsync(product);
// 异步保存更改到数据库
await _context.SaveChangesAsync();
}
// 更新现有产品
public async Task UpdateAsync(Product product)
{
// 在数据库上下文中标记产品已更新
_context.Products.Update(product);
}
// 删除指定 ID 的产品
public async Task DeleteAsync(int id)
{
// 使用提供的 ID 在数据库中查找产品
var product = await _context.Products.FindAsync(id);
// 如果找到产品,则将其删除
if (product != null)
{
_context.Products.Remove(product);
// 异步保存更改到数据库
await _context.SaveChangesAsync();
}
}
}
}
点击全屏 点击退出全屏
- ProductRepository 类实现了
IProductRepository
接口定义的 CRUD 方法。它通过ApplicationDbContext
与数据库进行直接交互。
在ASP.NET Core应用程序中,服务层充当控制器(API层)和仓储(数据访问层)之间的桥梁。不仅包含业务逻辑,还确保数据在传递给API请求者之前进行恰当的处理。
定义服务接口
文件名:Services/IProductService.cs
using PostgresCrud.DTOs;
namespace PostgresCrud.Services
{
public interface IProductService
{
Task<IEnumerable<ProductResponseDto>> 异步获取所有产品();
Task<ProductResponseDto> 异步通过Id获取产品(int id);
Task 异步添加产品(ProductRequestDto productDto);
Task 异步更新产品(int id, ProductRequestDto productDto);
Task 异步删除产品(int id);
}
}
全屏 退出全屏
该服务接口包括以下方法。
- 从仓库中取出产品。
- 将 实体模型 转换为 DTO 以便传输数据。
服务的实施
文件:Services/ProductService.cs
,如下所示:
using PostgresCrud.Entities; // 导入 Product 实体
using PostgresCrud.DTOs; // 导入请求和响应的数据传输对象 (DTOs)
using PostgresCrud.Repositories; // 导入用于数据访问的 Product 仓库
namespace PostgresCrud.Services
{
public class ProductService : IProductService
{
private readonly IProductRepository _productRepository; // 用于数据库操作的仓库实例
public ProductService(IProductRepository productRepository)
{
_productRepository = productRepository; // 通过构造函数注入仓库实例
}
// 获取所有产品,将其转换为 DTO 并返回列表
public async Task<IEnumerable<ProductResponseDto>> GetAllProductsAsync()
{
var products = await _productRepository.GetAllAsync(); // 从仓库获取所有产品
// 将每个产品实体转换为 ProductResponseDto 并返回
return products.Select(p => new ProductResponseDto
{
Id = p.Id,
Name = p.Name,
Price = p.Price
});
}
// 根据 ID 获取产品,并将其转换为 DTO
public async Task<ProductResponseDto> GetProductByIdAsync(int id)
{
var product = await _productRepository.GetByIdAsync(id); // 根据 ID 获取产品
// 如果未找到产品,则抛出异常
if (product == null)
throw new KeyNotFoundException("产品未找到");
// 将实体转换为 DTO 并返回
return new ProductResponseDto
{
Id = product.Id,
Name = product.Name,
Price = product.Price
};
}
// 使用请求 DTO 添加新产品
public async Task AddProductAsync(ProductRequestDto productDto)
{
// 将 DTO 转换为实体
var product = new Product
{
Name = productDto.Name,
Price = productDto.Price
};
// 将新产品添加到数据库
await _productRepository.AddAsync(product);
}
// 使用新数据更新现有产品
public async Task UpdateProductAsync(int id, ProductRequestDto productDto)
{
var product = await _productRepository.GetByIdAsync(id); // 根据 ID 获取产品
// 如果产品不存在,则抛出异常
if (product == null)
throw new KeyNotFoundException("产品未找到");
// 使用 DTO 中的新值更新产品字段
product.Name = productDto.Name;
product.Price = productDto.Price;
// 将更新后的产品保存到数据库
await _productRepository.UpdateAsync(product);
}
// 根据 ID 删除产品
public async Task DeleteProductAsync(int id)
{
var product = await _productRepository.GetByIdAsync(id); // 根据 ID 获取产品
// 如果产品不存在,则抛出异常
if (product == null)
throw new KeyNotFoundException("产品未找到");
// 从数据库中删除产品
await _productRepository.DeleteAsync(id);
}
}
}
全屏模式(点击退出)
双击退出
服务层将实体模型转换为DTO(数据传输对象),使数据更便于API消费者处理。它在调用仓库层之前处理所有必要的业务逻辑。
服务方法分类方法 | 描述 |
---|---|
GetAllProductsAsync |
获取所有产品,将其转换为 DTO 并返回。 |
GetProductByIdAsync |
根据 ID 获取单个产品。如果未找到,则抛出 KeyNotFoundException 异常。 |
AddProductAsync |
将 ProductRequestDto 转换为 Product 实体并将其保存到数据库。 |
UpdateProductAsync |
检查产品是否存在,更新其字段并保存更改。如果产品不存在,则抛出 ProductNotFoundException 异常。 |
DeleteProductAsync |
检查产品是否存在,如果存在则删除。如果未找到,则抛出 ProductNotFoundException 异常。 |
创建产品控制
ProductController将负责定义API端点。
文件路径:Controllers/ProductController.cs
using Microsoft.AspNetCore.Mvc; // 导入ASP.NET Core MVC框架
using PostgresCrud.Services; // 导入服务层模块
using PostgresCrud.DTOs; // 导入数据传输对象(DTOs)
namespace PostgresCrud.Controllers
{
[ApiController] // 指定这是一个API控制器
[Route("api/[controller]")] // 定义路由为'api/product'
public class ProductController : ControllerBase
{
private readonly IProductService _productService; // 用于处理业务逻辑的服务实例
public ProductController(IProductService productService)
{
_productService = productService; // 通过构造函数注入服务
}
// 异步获取所有产品
[HttpGet]
public async Task<IActionResult> GetAll()
{
var products = await _productService.GetAllProductsAsync(); // 获取所有产品
return Ok(products); // 返回包含产品数据的200 OK响应
}
// 异步通过ID获取单个产品
[HttpGet("{id}")]
public async Task<IActionResult> GetById(int id)
{
try
{
var product = await _productService.GetProductByIdAsync(id); // 获取指定产品
return Ok(product); // 如果找到产品,则返回200 OK响应;如果没有找到产品,则返回404 Not Found响应
}
catch (KeyNotFoundException)
{
return NotFound(); // 如果产品不存在,则返回404 Not Found响应
}
}
// 异步添加新产品
[HttpPost]
public async Task<IActionResult> Add(ProductRequestDto productDto)
{
await _productService.AddProductAsync(productDto); // 添加新产品
return CreatedAtAction(nameof(GetById), new { id = productDto.Id }, productDto);
// 返回201 Created响应,并通过定位头指向新创建的产品
}
// 异步更新现有产品
[HttpPut("{id}")]
public async Task<IActionResult> Update(int id, ProductRequestDto productDto)
{
try
{
await _productService.UpdateProductAsync(id, productDto); // 更新产品
return NoContent(); // 如果操作成功,则返回204 No Content响应
}
catch (KeyNotFoundException)
{
return NotFound(); // 如果产品不存在,则返回404 Not Found响应
}
}
// 异步删除产品
[HttpDelete("{id}")]
public async Task<IActionResult> Delete(int id)
{
try
{
await _productService.DeleteProductAsync(id); // 删除指定的产品
return NoContent(); // 如果操作成功,则返回204 No Content响应
}
catch (KeyNotFoundException)
{
return NotFound(); // 如果产品不存在,则返回404 Not Found响应
}
}
}
}
进入全屏 退出全屏
下面是一个解释 ProductController
中每个方法的表格:
方法 | HTTP 动词 | 路由 | 描述 |
---|---|---|---|
GetAll |
GET |
/api/product |
获取所有产品并以 JSON 数组形式返回结果。 |
GetById |
GET |
/api/product/{id} |
通过 ID 获取单个产品。如果产品不存在,则返回 404 Not Found 。 |
Add |
POST |
/api/product |
接收一个 ProductRequestDto 来创建一个新的产品。 |
Update |
PUT |
/api/product/{id} |
使用所提供的 ID 和 DTO 更新现有产品。 |
Delete |
DELETE |
/api/product/{id} |
通过 ID 删除产品。删除成功时返回 204 No Content ,如果产品不存在则返回 404 Not Found 。 |
最后,将所有内容连接起来,在 Program.cs 文件里为依赖注入功能注册服务和存储库。
代码文件:Program.cs
using Microsoft.EntityFrameworkCore;
using PostgresCrud.Data;
using PostgresCrud.Repositories;
using PostgresCrud.Services;
var builder = WebApplication.CreateBuilder(args);
// 向容器中添加服务。
builder.Services.AddControllers();
// 配置 PostgreSQL 数据库连接
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseNpgsql(builder.Configuration.GetConnectionString("DefaultConnection")));
// 注册仓储与服务
builder.Services.AddScoped<IProductRepository, ProductRepository>();
builder.Services.AddScoped<IProductService, ProductService>();
// 如需了解更多关于 Swagger/OpenAPI 配置的信息,请访问 https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
// 配置 HTTP 请求管道如下。
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
进入全屏 退出全屏
10. 运行应用程序完成所有实施后,我们现在就可以跑这个应用了。
- 如有必要,应用任何数据库迁移:
运行以下命令来添加初始迁移并更新数据库:
dotnet ef migrations add InitialCreate
dotnet ef database update
全屏 全屏退出
- 打开应用
dotnet run (运行.NET程序)
进入全屏,退出全屏
通过 Swagger UI 访问 API,地址为 http://localhost:5052/swagger
。
通过这种结构,我们得到了:
- 设置一个ASP.NET Core Web API项目。
- 使用PostgreSQL和Entity Framework Core来处理数据库操作。
- 按照清洁架构原则,将关注点分开,比如存储库、服务和控制器。
- 实现了一个带有结构化错误处理功能和适当HTTP状态码的CRUD API。
这种结构使得我们的应用程序在增长时更容易管理和测试,并且可以更方便地进行扩展。
[OOP]:面向对象编程(OOP)
[CRUD]:增删查改(CRUD操作)
[JVM]:Java虚拟机(JVM)
[SUT]:系统被测试(SUT)
共同学习,写下你的评论
评论加载中...
作者其他优质文章