【.com原创稿件】数据校验是每个项目都必须做的,可以防止不符合系统规范的数据进入系统,导致系统不稳定甚至崩溃。我们可以自己写代码(包括前台和后台代码)进行验证,但是一方面代码量大,另一方面验证代码覆盖可能不完整。但是在EntityFrameworkCore(以下简称EFCore)中,这些问题都可以得到解决。EFCore中有两种验证模式,内置模型验证和第三方扩展模型验证。下面我将分别对这两种模式进行说明。在解释之前,让我们创建必要的模型。publicclassUser{publicintId{get;set;}publicstringName{get;set;}publicintAge{get;set;}}1.内置模型校验EFCore中没有FluentAPI方式来校验数据,所以我们只能使用DataAnnotations(数据注释)数据验证方法,即通过添加特征来验证数据的方法。例如,我们要验证User模型中Name的长度。Name的长度不能大于5,我们只需要在Name属性上加上StringLength数据注解即可。StringLength位于命名空间System.ComponentModel.DataAnnotations中。修改User模型代码如下:publicclassUser{publicintId{get;set;}[StringLength(5)]publicstringName{get;set;}publicintAge{get;set;}}以上代码限制Name属性的数据长度为5通过StringLength(5)数据注解,在数据提交时按照这个约定进行数据验证。接下来,我们将通过数据注释中的验证器验证刚刚添加的功能。首先我们需要创建一个上下文扩展方法:publicstaticListExecuteValidation(DbContextcontext){Listresult=newList();varmodels=context.ChangeTracker.Entries().Where(p=>(p.State==EntityState.Added)||(p.State==EntityState.Modified));foreach(varmodelinmodels){varentity=model.Entity;varvalProvider=newValidationDbContextServiceProvider(context);varvalContext=newValidationContext(entity,valProvider,null);上面代码中的Listerror=newList();if(!Validator.TryValidateObject(entity,valContext,error,true)){result.AddRange(error);}returnresult.ToList();}}我们使用ChangeTracker方法找出被跟踪的实体,然后筛选出需要添加和更新的实体,并对这些实体进行数据验证。最后,我们通过Validator中的TryValidateObject方法对实体数据进行验证,并返回验证错误信息。在业务代码中,我们调用之前定义的ExecuteValidation方法进行校验。如果验证通过,我们调用EFCore的SaveChange()方法。如果失败,我们调用相应的处理代码。代码片段如下:if(context.ExecuteValidation().Any()){foreach(varerrorincontext.ExecuteValidation()){//处理代码}}else{context.SaveChange();}说到这里,就是估计很多朋友会说每次业务代码都写这个太麻烦了,而且还产生了很多重复代码。那么如何解决重复代码的问题呢?这时候肯定有小伙伴想到了通过重写SaveChanges方法在这个方法中加入验证码,这样就可以解决刚才的问题,达到一劳永逸的效果。具体代码如下:=>(p.State==EntityState.Added)||(p.State==EntityState.Modified));foreach(varmodelinmodels){varentity=model.Entity;varcontext=newValidationContext(entity,provider,items);Listresults=newList();if(!Validator.TryValidateObject(entity,context,results,true)){foreach(varresultinresults){if(result!=ValidationResult.Success){thrownewValidationException(result.ErrorMessage);}}}}returnbase.SaveChanges();}通过上面的代码,可以做到一处写验证,多处使用。具体思路同上,这里不再赘述。2、第三方扩展模型验证上面我说的是通过数据注解来验证数据,但是如果你使用FluentAPI方式,是没有办法解决文章开头提到的问题的,因为FluentAPI方式不提供对数据模型验证的支持。这时候我们可以使用第三方扩展。EFCore中用于模型数据验证的常用第三方扩展是FluentValidation.AspNetCore。在使用之前,我们需要在NuGet中下载这个扩展。FluentValidation.AspNetCore安装完成后,我们需要为模型创建一个验证器。验证器是从AbstractValidator继承的类。验证规则是在验证器构造函数中使用RuleFor方法定义的。代码如下:publicclassModelValidator:AbstractValidator{publicModelValidator(){RuleFor(p=>p.Name).NotEmpty().WithMessage("Namecannotbeempty");RuleFor(p=>p.Name).MaximumLength(5).WithMessage("名称的长度为5个字节");}}上面的代码进行了两次验证,一是验证Name字段是否为空,二是验证name字段的长度Name字段,这里我们通过MaximumLength指定Name字段的最大长度为5个字节。然后我们通过WithMessage方法返回我们自定义的错误信息。在我们定义了验证规则之后,下一步就是将我们定义的验证规则与应用程序连接起来。这里我们需要使用AddFluentValidation进行注入。比如在Asp.NetCore程序中,我们把注入程序写到Startup的ConfigureServices方法中。我们调用AddFluentValidation方法为Asp.NetCore添加FluentValidation服务,然后使用RegisterValidatorsFromAssembly方法将自定义验证代码注入到容器中。代码片段如下:publicvoidConfigureServices(IServiceCollectionservices){services.AddMvc().AddFluentValidation(p=>p.RegisterValidatorsFromAssemblyContaining());}在需要验证数据的地方,我们通过ModelState获取验证状态,以及验证通过则执行后续代码,验证失败则执行处理代码。示例代码如下:if(ModelState.IsValid){//后续代码}else{//Validation不通过处理代码}这里需要注意一点,当传入的entity为null时,报错message会被返回,因为在AbstractValidator中有一个EnsureInstanceNotNull方法,当实例为null时会抛出异常,即使重写该方法也无法返回自定义的错误信息。如果需要验证实体集合,需要使用RuleForEach方法,自定义验证规则可以使用SetValidator方法。3.小结本文讲解了EFCore数据校验的方法。虽然说的是EFCore的方法,但同样适用于EF6。这些内容都是常用的,上面的部分代码可以在大部分项目中使用。作者简介:朱刚,化名苗叔,国内某技术博客认证专家,.NET高级开发工程师。曾就职于初创公司,从事企业级安全监控系统开发。【原创稿件,合作网站转载请注明原作者和出处为.com】