第一百五十二期:白话Entity Framework Core数据验证

数据验证是每个项目必须存在的,可以防止不符合系统规范的数据进入系统进而导致系统不稳定甚至崩溃。我们可以自己编写代码进行验证,但是这样一方面代码量较大,另一方面有可能验证代码覆盖不完全。但是在 Entity Framework Core (以下简称 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<T> 的类,验证规则使用 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 ,这些内容是常用的,上述部分代码可以在大部分项目中通用。

阅读目录(置顶)(长期更新计算机领域知识)

阅读目录(置顶)(长期更新计算机领域知识)

阅读目录(置顶)(长期科技领域知识)

歌谣带你看java面试题

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/423855.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

小程序云开发实战 - 口袋工具之“历史上的今天”

前言 本项目是一个基于云开发的小程序。 本文选取项目中的一个页面 -- 历史上的今天 来做一个云开发的分享&#xff0c;会涉及云函数和云数据库。 由于是实战项目&#xff0c;关于小程序的基础知识请移步官方文档&#xff0c;本文不再赘述。 项目预览 微信搜索&#xff1a; 口袋…

第一百五十三期: 云迁移可能失败的5种方式以及成功的5种方式

通过将应用程序迁移到云平台中&#xff0c;企业可以提高安全性、数据访问、可扩展性和IT灵活性。将业务迁移到云平台还可以为企业节省成本。以下是导致企业云迁移失败的五个主要原因以及其解决方法。 作者&#xff1a;Andy Patrizio 对于大多数企业而言&#xff0c;将业务迁移…

每天一个linux命令(30): chown命令

原文地址&#xff1a;http://www.cnblogs.com/peida/archive/2012/12/04/2800684.html chown将指定文件的拥有者改为指定的用户或组&#xff0c;用户可以是用户名或者用户ID&#xff1b;组可以是组名或者组ID&#xff1b;文件是以空格分开的要改变权限的文件列表&#xff0c;支…

mybatis学习(14):log4j:ERROR Category option 1 not a decimal integer.

log4j:ERROR Category option " 1 " not a decimal integer. java.lang.NumberFormatException: For input string: " 1 " 修改log4j.xml配置文件 ### \u914D\u7F6E\u6839 ### log4j.rootLogger debug,console ,fileAppender,dailyRollingFile,ROLLING_…

在PEA上海做演讲主题:大型、高负载网站架构和应用初探

主题&#xff1a;大型、高负载网站架构和应用初探时间&#xff1a;30-45分钟 开题&#xff1a;163,sina,sohu等网站他们有很多应用程序都是PHP写的&#xff0c;为什么他们究竟是如何能做出同时跑几千人甚至上万同时在线应用程序呢? 挑选性能更好web服务器 单台 Apache web se…

数论考试题(b) 求约数的约数的最大个数

题意&#xff1a;求1~n范围里约数的约数的个数加起来最多的是哪个数 及其总数 题解&#xff1a; /* 对一个数质因数分解 首先要知道两个公式&#xff1a;约数的个数的公式&#xff0c;和约数的约数的个数的公式(详见题解) 然后发现&#xff1a;质因数分解后 小的次冥尽量大 会…

Anaconda 安装

参考网址&#xff1a;http://www.jianshu.com/p/169403f7e40c http://blog.csdn.net/qq_26898461/article/details/51488326 Anaconda基础 什么是Anaconda Anaconda是一个用于科学计算的Python发行版&#xff0c;支持 Linux, Mac, Windows系统&#xff0c;提供了包管理与环境管…

mybatis学习(15):mybatis连接mysql数据库

目录结构 com.geyao.mybatis.mapper BlogMapper类 package com.geyao.mybatis.mapper;import com.geyao.mybatis.pojo.Blog;public interface BlogMapper {Blog selectBlog(String id); }BlogMapper.xml <?xml version"1.0" encoding"UTF-8" ?>…

nodejs对mongodb数据库的增删改查操作(转载)

首先要确保mongodb的正确安装&#xff0c;安装参照&#xff1a;http://docs.mongodb.org/manual/tutorial/install-mongodb-on-debian-or-ubuntu-linux 另外可以参考我的另一篇博文 http://www.cnblogs.com/sexintercourse/p/5774310.html 指导mongo和nodejs的开发 然后下载nod…

mybatis学习(16):不使用接口的方式

目录结构 com.geyao.mybatis.mapper BlogMapper类 package com.geyao.mybatis.mapper;import com.geyao.mybatis.pojo.Blog;public interface BlogMapper {Blog selectBlog(Inteage id); }BlogMapper.xml <?xml version"1.0" encoding"UTF-8" ?>…

AS3图像抖动效果源码。

DemoSourceWebsite转载于:https://www.cnblogs.com/xxcainiao/archive/2008/05/07/1186805.html

mybatis学习(17):列名与属性名不一致的情况(使用别名)

目录结构 com.geyao.mybatis.mapper BlogMapper类 package com.geyao.mybatis.mapper;import com.geyao.mybatis.pojo.Blog;public interface BlogMapper {Blog selectBlog(Inteage id); }BlogMapper.xml <?xml version"1.0" encoding"UTF-8" ?>…

mybatis学习(18):列名与属性名不一致的情况(使用ResultMap)

目录结构 com.geyao.mybatis.mapper BlogMapper类 package com.geyao.mybatis.mapper;import com.geyao.mybatis.pojo.Blog;public interface BlogMapper {Blog selectBlog(Integer id);Blog selectBlog2(Integer id); }BlogMapper.xml <?xml version"1.0" en…

MVCToolKit中HtmlHelper.ActionLink的BUG?

分页时<%if (ViewData.Accounts.IsPreviousPage) { %><%Html.ActionLink<SpeakOutFreely.Controllers.ManageController>(c >c.List(ViewData.Accounts.PageIndex -1), "Previous")%><%} %><%if (ViewData.Accounts.IsNextPage) { %&g…

PyChram简单使用教程

一、PyChram下载 官网&#xff1a;http://www.jetbrains.com/pycharm Windows:http://www.jetbrains.com/pycharm/download/#sectionwindows Linux:http://www.jetbrains.com/pycharm/download/#sectionlinux Mac:http://www.jetbrains.com/pycharm/download/# 二、PyChram简单…

mybatis学习(19):模糊查询#

目录结构 com.geyao.mybatis.mapper BlogMapper类 package com.geyao.mybatis.mapper;import java.util.List;import com.geyao.mybatis.pojo.Blog;public interface BlogMapper {Blog selectBlog(Integer id);Blog selectBlog2(Integer id);List<Blog> selectBlogByTi…

mybatis学习(20):模糊查询$

目录结构 com.geyao.mybatis.mapper BlogMapper类 package com.geyao.mybatis.mapper;import java.util.List;import com.geyao.mybatis.pojo.Blog;public interface BlogMapper {Blog selectBlog(Integer id);Blog selectBlog2(Integer id);List<Blog> selectBlogByTi…

mybatis学习(21):MySQL 字符串 转换 CAST与CONVERT 函数的用法

MySQL 的CAST()和CONVERT()函数可用来获取一个类型的值&#xff0c;并产生另一个类型的值。两者具体的语法如下&#xff1a; Sql代码 CAST(value as type); CONVERT(value, type); 就是CAST(xxx AS 类型), CONVERT(xxx,类型)。 Sql代码 mysql> SELECT CAST(3.35 …

【转载】快速、可伸缩和流式的AJAX代理--跨域持续内容分发

原文&#xff1a;《Fast, Scalable, Streaming AJAX Proxy - continuously deliver data from across domains》 作者&#xff1a;Omar Al ZabirURL&#xff1a; http://www.codeproject.com/KB/ajax/ajaxproxy.aspx Download source - 16.1 KB Introduction Due to browsers p…

pycharm常用设置(keymap设置及eclipse常用快捷键总结)

2015-04-15 13:23 23800人阅读 评论(0) 收藏 举报分类&#xff1a;openstack-环境及安装配置&#xff08;10&#xff09; 版权声明&#xff1a;欢迎大家转载&#xff0c;转载请注明出处blog.csdn.net/tantexian。 设置pycharm为Eclipse快捷键后使用总结&#xff1a; Ctrl O 根…