EF Core 数据验证

简介: EF Core 数据验证

数据验证是每个项目必须存在的,可以防止不符合系统规范的数据进入系统进而导致系统不稳定甚至崩溃。我们可以自己编写代码(包括前台和后台代码)进行验证,但是这样一方面代码量较大,另一方面有可能验证代码覆盖不完全。但是在 Entity Framework Core (以下简称 EF Core )中这些问题全可以解决。在 EF Core 中有两种验证模式,分别是内置模型验证和第三方扩展模型验证。下面我分别对这两种模式进行讲解,在讲解前我们先来创建必须的模型。

public class User
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int Age { get; set; }
}

零、内置模型验证

在 EF Core 中并没有 Fluent API 模式对数据进行验证,因此我们只能通过 Data Annotations (数据注解)方式来进行数据验证,也就是添加特性的方法来验证数据。例如我们要验证 User 模型中的 Name 的长度,Name 长度不能大于 5 ,我们只需在 Name 属性上增加 StringLength 数据注解即可, StringLength 位于命名空间 System.ComponentModel.DataAnnotations 中,修改 User 模型代码如下:

public class User
{
    public int Id { get; set; }
    [StringLength(5)]
    public string Name { get; set; }
    public int Age { get; set; }
}

上述代码通过 StringLength(5) 数据注解将 Name 属性的数据长度限定在 5 ,并且在数据提交时按照这个约定进行验证数据。下面我们就通过数据注解中的验证器来验证刚才添加的特性。首先我们要创建一个上下文的扩展方法:

public static List<ValidationResult> ExecuteValidation(DbContext context)
{
    List<ValidationResult> result = new List<ValidationResult>();
    var models = context.ChangeTracker.Entries()
        .Where(p =>
        (p.State == EntityState.Added) || (p.State == EntityState.Modified));
    foreach (var model in models)
    {
        var entity = model.Entity;
        var valProvider = new ValidationDbContextServiceProvider(context);
        var valContext = new ValidationContext(entity, valProvider, null);
        List<ValidationResult> error = new List<ValidationResult>();
        if(!Validator.TryValidateObject(entity,valContext,error,true))
        {
            result.AddRange(error);
        }
        return result.ToList();
    }
}

在上述代码中我们通过 ChangeTracker 方法找出被追踪的实体,然后过滤出需要添加和更新的实体,对这些实体进行数据验证。最后我们通过 Validator 中的 TryValidateObject 方法验证实体数据并返回校验错误信息。在业务代码中我们调用前面定义的 ExecuteValidation 方法进行验证,如果验证通过就调用 EF Core 的 SaveChange() 方法,如果未通过就调用相应的处理代码,代码片段如下:

if(context.ExecuteValidation().Any())
{
  foreach(var error in context.ExecuteValidation())
  {
    //处理代码
  }
}
else
{
  context.SaveChange();
}

讲到这里估计会有很多小伙伴说每个业务代码中都要这么写太麻烦了,而且也常生了大量的重复代码。那么重复代码这个问题该怎么解决呢?这时一定有部分小伙伴想到了通过重写 SaveChanges 方法,将验证代码加入到这个方法中,这样就可以解决刚才的那个问题,达到一劳永逸的效果。具体代码如下:

public override int SaveChanges(bool acceptAllChangesOnSucces)
{
    var provider = ((IInfrastructure<IServiceProvider>)this).Instance;
    var items = new Dictionary<object, object>();
    var models = this.ChangeTracker.Entries()
        .Where(
        p => (p.State == EntityState.Added)||(p.State==EntityState.Modified));
    foreach (var model in models)
    {
        var entity = model.Entity;
        var context = new ValidationContext(entity, provider, items);
        List<ValidationResult> results = new List<ValidationResult>();
        if(!Validator.TryValidateObject(entity,context,results,true))
        {
            foreach (var result in results)
            {
                if(result!=ValidationResult.Success)
                {
                    throw new ValidationException(result.ErrorMessage);
                }
            }
        }
    }
    return base.SaveChanges();
}

通过上述代码就可以一处编写验证,多处使用了。具体的思路和前面所讲的一样,这里就不再进行讲解了。


一、第三方扩展模型验证

前面所讲的是通过数据注解的方式来进行数据验证的,但是如果是使用 Fluent API 的方式就没办法解决文章开头所说的问题,因为Fluent API 模式并没有提供对数据模型的验证。这时我们可以使用第三方扩展,在 EF Core 中常用的模型数据验证第三方扩展是 FluentValidation.AspNetCore 。在使用前我们需要在 NuGet 中下载此扩展。

FluentValidation.AspNetCore 安装完成后我们需要为模型创建验证器,验证器是一个继承自 AbstractValidator 的类,验证规则使用 RuleFor 方法定义在验证器构造函数中。代码如下:

public class ModelValidator:AbstractValidator<User>
{
    public ModelValidator()
    {
        RuleFor(p => p.Name).NotEmpty().WithMessage("姓名不能为空");
        RuleFor(p => p.Name).MaximumLength(5).WithMessage("姓名长度在5字节");
    }
}

上述代码进行了两个验证,一个是验证 Name 字段是否为空,另一个是验证 Name 字段的长度,其中我们通过 MaximumLength 规定了 Name 字段的最长长度为 5 字节。之后我们通过 WithMessage 方法返回我们自定义的错误信息。

我们定义完验证规则后下一步就是将我们定义的验证规则与应用程序连接起来,这里我们需要用到 AddFluentValidation 来注入,例如在 Asp.Net Core 程序中我们将注入程序写入 Startup 的 ConfigureServices 方法里。我们调用 AddFluentValidation 方法会将 FluentValidation 服务添加到 Asp.Net Core 中,然后使用 RegisterValidatorsFromAssembly 方法将自定义的验证代码注入到容器中,代码段如下:

public void ConfigureServices(IServiceCollection services)
{
  services.AddMvc()
    .AddFluentValidation(p=>
      p.RegisterValidatorsFromAssemblyContaining<Startup>());
}

在需要验证数据的地方我们通过 ModelState 获取验证状态,验证通过就执行后续代码,不通过就执行处理代码。示例代码如下:

if(ModelState.IsValid)
{
  //后续代码
}
else
{
  //验证不通过处理代码
}

这里有一点需要注意,当传递的实体为 null 时,将返回错误信息,这是因为 AbstractValidator 中存在 EnsureInstanceNotNull 方法,这个方法在实例为 null 时会抛出异常,即使重写该方法也无法返回自定义的错误信息。如果需要验证实体集合就需要使用 RuleForEach 方法即可,对于自定义验证规则则可使用 SetValidator 方法。


二、总结

本篇文章讲解了 EF Core 数据验证的方法,虽然讲的时 EF Core 的方法,但是同样也适用于 EF6 ,这些内容是常用的,上述部分代码可以在大部分项目中通用。


目录
相关文章
|
5月前
|
关系型数据库 MySQL 数据库
EF Core反向工程
EF Core反向工程,数据库创建表使用命令生成上下文
57 0
|
SQL 存储 数据处理
5.1EF Core原理
对普通集合使用where等方法查询出来的返回值为IEnumerable类型 但是对DbSet使用用where等方法出查询出来的返回值为IQueryable类型
|
API 数据库 索引
4.2EF Core的实体类配置
本文主要讨论实体类和数据表是如何映射的,以及实体类中的属性是如何与数据表中的列映射的
|
开发框架 算法 .NET
4.1EF Core
EF Core是微软官方的ORM框架,ORM即对象关系映射,也就是我们可以直接操作C#中的对象就可以完成数据库的操作。
|
缓存 关系型数据库 MySQL
C#-EF Core使用MySQL数据库
Entity Framework Core (EF Core)是微软推荐的基于.NET Core 的应用程序数据访问技术。开源,轻量级,可扩展并且支持跨平台开发。EF Core是一种对象关系映射器(ORM),通过应用程序实体对象和关系数据库中的数据的映射,使得开发人员能够以面向对象的方式处理数据。
485 0
|
关系型数据库 MySQL 编译器
在 EF Core 7 中实现强类型 ID
本文主要介绍 DDD 中的强类型 ID 的概念,及其在 EF 7 中的实现,以及使用 LessCode.EFCore.StronglyTypedId 这种更简易的上手方式。
204 0
在 EF Core 7 中实现强类型 ID
|
存储 SQL XML
Entity Framework学习笔记——EF简介(一篇文章告诉你什么是EF)
Entity Framework是以ADO.NET为基础,面向数据的“实体框架”。以下简称EF。 它利用了抽象化数据结构的方式,将每个数据库对象都转换成应用程序对象 (entity),数据字段都转换为属性 (property),关系则转换为结合属性 (association),让数据库的 E/R 模型完全的转成对象模型,如此让程序设计师能用最熟悉的编程语言来调用访问。
Entity Framework学习笔记——EF简介(一篇文章告诉你什么是EF)
|
存储 SQL XML
EF Core-2
上篇 概念性讲述CRQS(https://www.cnblogs.com/ccaa/p/12545582.html)
108 0
EF Core-2
|
存储 XML 缓存
EF Core-1
前几天了解到EF Core的开发模式:DB First(数据库优先),Model First(模式优先),Code First(代码优先)。
122 0
EF Core-1