asp.net mvc5 step by step(二)——Data Annotations(data 注释)
以下使用在Entity Framework Code First
Entity Framework Code First 利用一种被称为约定(Conventions)优于配置(Configuration)的编程模式允许你使用自己的 domain classes 来表示 EF 所依赖的模型去执行查询、更改追踪、以及更新功能,这意味着你的 domain classes 必须遵循 EF 所使用的约定。然而,如果你的 domain classes 不能遵循 EF 所使用的约定,此时你就需要有能力去增加一些配置使得你的 classes 能够满足 EF 所需要的信息。
Code First 提供了两种方式来配置你的类:
- DataAnnotations, 使用简单属性;
- Fluent API, 以编程命令行式的方式来描述你的配置
本文将关注 DataAnnotations(位于命名空间 System.ComponentModel.DataAnnotations)
为了便于示例,我们先创建两个类:Blog 和 Post
以上类都是遵循约定的,但是你也可以使用 annotations 为 EF 提供更多的信息
Key主键
EF 依赖于每一个实体都有一个主键从而能够追踪实体。 在约定下,主键是通过寻找名称为 ID 或 ClassName + ID 的属性确定。那么如果类中没有此类的属性呢?我们把类改造如下(把 Id 改为 PrimaryTrackingKey)
此时如果我们添加 Scaffolded 会出现如下错误
我们需要使用 key annotation 确定哪一个属性是主键
我们使用 Code First's Database Generation 功能看看数据库的情况
Required非空
用于指示字段非空
运行看一下结果
我们在看一下后台数据库情况
MaxLength and MinLength长度限制
顾名思义,就是限制字段的长度
看一下后台数据库
运行看结果
NotMapped忽略
类中有些属性,如通过计算获得或其它列的合并而来的,我们并不希望其记录在数据库中,此时就可以使用 NotMapped Annotation
对于类也是一样的
ComplexType复杂类型
一般这种情况是不常见的:类中包含另一个类即一个完整的实体是由一系列类的集合来描述。例如我们在我们的模型中增加一个叫 BlogDetails 的类
注意 BlogDetails 不包含任何主键属性, 在领域驱动设计中被称为值对象,而在 EF 中则被称为复杂类型(ComplexType). Complex Types 是无法自我追踪的,但是我们可以通过 ComplexType Annotation 来改变这种尴尬
可以看到,在数据库中表 Blog 包含 BlogDetails 的所有两个属性,默认列名前缀为 complex type 即 BlogDetails
ConcurrencyCheck并发检查
ConcurrencyCheck Annotation 允许你可以在一个或多个属性上设置一个标记用于当用户更新或删除实体时做并发检查。
如果此时同时对此列进行更新,则会抛出异常 DbUpdateConcurrencyException
TimeStamp时间戳
通常用行版本号或时间戳来检查并发,除了使用 ConcurrencyCheck Annotation, 还可以使用更为精确的 TimsStamp, 前提是这个属性的类型是字节数组(byte array). Code First 在对待 TimsStamp 属性和 ConcurrencyCheck 属性是一样的,只不过能确保数据库中生成的字段是 non-nullable
在一个给定的类中只能有一个 TimsStamp 属性
看一下后台数据库
Table and Column表/列名
指定匹配到数据库的表/列名
可以看到表/列名已更改
DatabaseGenerated自增长
DatabaseGeneratedOption 有三个选项:
- DatabaseGeneratedOption.Computed: 在用 Code First 生成数据库的时候你可以在 byte 或 timestamp 列上使用 DatabaseGenerated Annotation,否则就应该在数据库存在的情况下使用因为如果数据库不存在,此时 Code First 不知道为计算列(Computed Column)选择使用什么样的公式
- DatabaseGeneratedOption.Identity: 如果主键为 integer 型,则数据库默认为自增长(效果等同于设置DatabaseGeneratedOption.Identity), 如果主键是 GUID 类型,则要显式设置自增长
- DatabaseGeneratedOption.None: 如果不想自增长,可设置成 DatabaseGeneratedOption.None
ForeignKey 外键
看看数据库
InverseProperty属性反转
InverseProperty 一般使用在当类间有多重关系的时候。
例如在 Post 中你可能不仅需要追踪谁写的也还要追踪有谁编辑了博客
此时你会看到后台数据库有四个外键 Person_Id, Person_Id1, CreatedBy_Id and UpdatedBy_Id
为了解决这个问题,我们可以使用 InverseProperty annotation 来明确属性间的指向
再一次看看数据库,情况已经发生了变化
以下用于验证数据
来自:https://msdn.microsoft.com/zh-cn/library/ee256141(VS.100).aspx
如何:使用 DataAnnotations 特性验证模型数据
本主题阐释了如何使用 System.ComponentModel.DataAnnotations 命名空间中的特性指定对数据模型中的各个字段的验证。 这些特性用于定义常见的验证模式,例如范围检查和必填字段。 System.ComponentModel.DataAnnotations 特性使 MVC 能够提供客户端和服务器验证检查,而无需您进行额外的编码。
System.ComponentModel.DataAnnotations 特性可用于实体数据模型 (EDM)、LINQ to SQL 和其他数据模型。 还可以创建自定义验证特性。 有关更多信息,请参见如何:使用自定义特性在数据模型中自定义数据字段验证。
与本主题对应的包含源代码的 Visual Studio 项目可从 Download(下载)网页获得。
下图显示了客户端验证失败时自动显示在浏览器中的错误消息。
使用 DataAnnotations 特性添加验证
-
向项目添加类以包含分部类定义。 有关更多信息,请参见如何:在数据模型中自定义数据字段验证。
-
向与正在使用的数据模型的命名空间匹配的分部类添加一个命名空间声明。
注意 在分部类中使用的不正确的命名空间会导致裸分部类,这是一种与任何其他类都不关联的分部类。 裸分部类是数据模型无法识别 DataAnnotations 特性的常见原因。 若要避免此问题,可以复制数据模型代码中的命名空间以确保您具有正确的命名空间。
-
命名分部类以匹配表示数据模型中的表的类声明,并将其粘贴到新的类文件中。
注意 类名称完全匹配是非常重要的。 确保分部类的名称匹配的最简单方法就是复制它。
下面的示例显示了 AdventureWorksLT_2008 示例数据库中的 Product 表的分部类。 在本示例中,由 MvcDA 命名空间表示数据库。
using System.ComponentModel.DataAnnotations; namespace MvcDA { [MetadataType(typeof(ProductMD))] public partial class Product { public class ProductMD { [StringLength(50),Required] public object Name { get; set; } [StringLength(15)] public object Color { get; set; } [Range(0, 9999)] public object Weight { get; set; } // public object NoSuchProperty { get; set; } } } }
注意 类中包括名为 NoSuchProperty 的属性并已将其注释掉。 可以如本主题后面所述,将此属性用来测试分部类。
-
创建一个关联类(有时称为“合作者类”),该类包含表示表的分部类的属性。
关联类可以有任意名称,但习惯的做法在表类名称的后面追加“MD”或“MetaData”。 关联类必须用于 EDM 或 LINQ-to-SQL 模型,因为 CLR 类型不能用新的特性标记现有的属性。 如果直接使用 CLR 对象(有时称为“简单传统 CLR 对象 (POCO)”类型),则可以将特性直接应用于模型。
-
通过使用 MetadataTypeAttribute 特性将新类与表类关联。
在前面的示例中,以下代码行将新类与表类关联:
[MetadataType(typeof(ProductMD))]
-
将 System.ComponentModel.DataAnnotations 特性应用于属性。 可以将任意数量的特性应用于每个属性。
如果无法识别您应用的数据批注,则应验证是否可以在项目中识别分部类。 以下过程建议了一种测试类的方法。
测试分部类
-
将一个属性添加到分部类,其名称与相应的数据模型中的属性不匹配。
例如,取消对前一过程的示例中显示的 NoSuchProperty 属性的注释。
-
运行项目并输入要验证的数据。
如果可以识别分部类,则在进行验证时,将会引发 InvalidOperationException 异常并显示如下类似的消息:
类型“MvcDA.Product”的关联元数据类型包含下面的未知属性或字段:NoSuchProperty。 请确保这些成员的名称与主类型中的属性名称相匹配。
如果分部类为裸分部类,则不会引发异常。 在这种情况下,检查分部类名称以确保它与数据模型中相应的类名称匹配,并且使用了正确的命名空间。
-
删除在分部类中添加的用于测试的属性。
在添加服务器端验证后,可以通过包括必要的 JavaScript 验证文件和启用客户端验证来添加客户端验证。
添加客户端验证
-
向包含要验证的元素的视图添加对 JavaScript 验证文件的引用。 (常见做法是在 Site.master 文件中包括这些引用,使其应用于每个视图。)
下面的示例演示如何引用为了包括客户端验证所需要的 JavaScript 文件。 (在此示例中,引用了库的调试版本。)该示例还演示如何调用 EnableClientValidation 方法以便启用客户端验证。 启用客户端验证的常见方法是向 Site.master 文件中添加下面的代码。 但是,也可以将此代码添加到实现客户端验证的视图页面中。 如果在母版页中包括下面的代码,则每个视图将自动启用客户端验证。
<head runat="server"> <script src="<%= Url.Content("~/Scripts/MicrosoftAjax.debug.js") %>" type="text/javascript"></script> <script src="<%= Url.Content("~/Scripts/MicrosoftMvcAjax.debug.js") %>" type="text/javascript"></script> <script src="<%= Url.Content("~/Scripts/MicrosoftMvcValidation.debug.js") %>" type="text/javascript"></script> <% Html.EnableClientValidation();%> </head>
若要编译代码示例,需要以下组件:
-
Microsoft Visual Studio 2008 Service Pack 1 或 Visual Web Developer 2008 速成版 Service Pack 1。
-
AdventureWorksLT_2008 示例数据库。 有关如何下载和安装 SQL Server 示例数据库的信息,请参见 CodePlex 站点上的 Microsoft SQL Server Product Samples: Database(Microsoft SQL Server 产品示例:数据库)。 请确保针对所运行的 SQL Server 版本(Microsoft SQL Server 2005 或 Microsoft SQL Server 2008)安装了正确版本的示例数据库。
-
一个 MVC 应用程序,它具有从 AdventureWorksLT_2008 示例数据库创建的 EDM。 有关更多信息,请参见 ASP.NET 网站上的教程 Creating Model Classes with the Entity Framework(使用实体框架创建模型类)。