关于EF Core 生成数据库表的实体模型(Entity)和前端提交数据的校验模型(DTO/Input Model)

當前文章的短網址連結為: https://unos.top/aprp

最佳实践:

强烈推荐将 EF Core 生成数据库表的实体模型(Entity)和前端提交数据的校验模型(DTO/Input Model)完全分开,而不是直接在 EF Core Entity 上添加 [Required]、[EmailAddress] 等 DataAnnotations 用于前端校验。

为什么不能直接在 EF Core Entity 上加校验 Annotations?

  • EF Core Entity 的主要职责 是数据库映射和持久化。DataAnnotations(如 [Required]、[MaxLength]、[Key] 等)在这里主要用于生成数据库 Schema(例如 [Required] 会让列变成 NOT NULL,[MaxLength(100)] 会设置列长度)。
  • EF Core 不会自动在 SaveChanges() 时校验 DataAnnotations(不像老版的 EF6)。如果你只依赖 Entity 上的注解,前端提交无效数据仍然可能进入数据库。
  • 直接暴露 Entity 的问题(尤其是 Web API):
    • 安全风险:容易发生 over-posting(用户恶意提交 Entity 中不应修改的字段,如 Id、IsAdmin)。
    • 紧耦合:数据库结构一改,API 接口就得跟着改,破坏 API 契约稳定性。
    • 性能/简洁:Entity 可能包含导航属性、内部字段,前端不需要全部返回。
    • 校验规则不灵活:创建用户和更新用户可能需要不同的校验逻辑。

微软官方和社区主流推荐(Web API + EF Core 项目):使用 DTO 专门负责输入/输出和校验,Entity 只负责数据层。

推荐架构分层(最佳实践)

  1. Data Layer:EF Core Entity(可少量加 DB 约束注解)。
  2. Application/API Layer:DTO(带完整 DataAnnotations 校验)。
  3. Mapping:用 AutoMapper(推荐)或手动 new User { … } 把 DTO 转成 Entity。

完整案例(.NET 10 LTS + EF Core + Web API / Minimal API)

假设我们有一个 User 实体,需要前端提交创建用户。

1. EF Core Entity(Data/Models/User.cs)

using Microsoft.EntityFrameworkCore;
using System.ComponentModel.DataAnnotations;  // 这里只用于 DB 约束

public class User
{
    [Key]
    public int Id { get; set; }

    [Required]                    // 只影响 DB 列 NOT NULL
    [MaxLength(100)]              // 只影响 DB 列长度
    public string Name { get; set; } = string.Empty;

    [Required]
    [MaxLength(200)]
    public string Email { get; set; } = string.Empty;

    public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
}
C#

2. 前端提交用的 DTO(Application/Dtos/CreateUserDto.cs)

using System.ComponentModel.DataAnnotations;

public class CreateUserDto
{
    [Required(ErrorMessage = "姓名不能为空")]
    [StringLength(100, MinimumLength = 2, ErrorMessage = "姓名长度必须在2-100字符之间")]
    public string Name { get; set; } = string.Empty;

    [Required(ErrorMessage = "邮箱不能为空")]
    [EmailAddress(ErrorMessage = "邮箱格式不正确")]
    public string Email { get; set; } = string.Empty;

    // 可以加更多自定义校验,如密码确认、手机号正则等
}
C#

3. Controller 写法(传统 Web API)

[ApiController]
[Route("api/[controller]")]
public class UsersController : ControllerBase
{
    private readonly AppDbContext _context;

    public UsersController(AppDbContext context) => _context = context;

    [HttpPost]
    public async Task<IActionResult> Create([FromBody] CreateUserDto dto)
    {
        if (!ModelState.IsValid)   // ASP.NET Core 自动根据 DataAnnotations 校验
        {
            return BadRequest(ModelState);  // 返回 400 + 详细错误
        }

        var user = new User
        {
            Name = dto.Name,
            Email = dto.Email
        };

        _context.Users.Add(user);
        await _context.SaveChangesAsync();

        return CreatedAtAction(nameof(GetById), new { id = user.Id }, user);
    }
}
C#

4. .NET 10 Minimal API 写法(更简洁,内置校验支持)

在 Program.cs 里先注册:

builder.Services.AddValidation();   // .NET 10 新特性
C#

然后直接用:

app.MapPost("/api/users", async (CreateUserDto dto, AppDbContext db) =>
{
    // .NET 10 会自动校验 DataAnnotations,无需手动 ModelState
    var user = new User { Name = dto.Name, Email = dto.Email };
    db.Users.Add(user);
    await db.SaveChangesAsync();
    return Results.Created($"/api/users/{user.Id}", user);
});
C#

额外提示(进阶最佳实践)

  • 输出也用 DTO:返回给前端时再创建一个 UserResponseDto,避免把 Entity 完整暴露。
  • 复杂校验:DataAnnotations 够用简单场景;如果需要跨字段校验(如密码和确认密码必须一致),可以实现 IValidatableObject 或切换到 FluentValidation(更现代、更易测试)。
  • 映射工具:安装 AutoMapper + AutoMapper.Extensions.Microsoft.DependencyInjection,配置 CreateMap<CreateUserDto, User>() 自动转换。
  • 小项目例外:如果你的项目非常简单(纯 CRUD,无复杂业务),可以直接在 Entity 上加注解并复用。但一旦项目变大,马上会后悔——分离是长期维护的王道

0 0 投票数
文章评分
订阅评论
提醒
guest

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理

0 评论
最旧
最新 最多投票
内联反馈
查看所有评论