Entity Framework 4.1 之二 : 覆盖默认的约定
原文名称:Entity Framework 4.1: Override conventions (2)
看到 Entity Framework 4.1 推荐英文教程,为了帮大家看起来方便一些,简单翻译一下。这是一个系列,共有 8 篇,这是第 2 篇。
在这篇文章中,我将讨论如何覆盖默认的约定。
我们已经看过了在 EF4.1 Code First 中模型与数据库中的默认约定。当这些约定不能满足我们的时候,我们有两种不同的途径来覆盖这些约定:
- 拦截模型的构建器,使用流畅的 API 来修改模型
- 为我们的模型增加标签
在未来的版本中,我们还将有能力增加或者删除约定,现在还没有提供这个能力。
对于我们基本的例子,我们使用下面所示的订单类。
public class Order
{
public int OrderID { get; set; }
public string OrderTitle { get; set; }
public string CustomerName { get; set; }
public DateTime TransactionDate { get; set; }
}
构建器
我们从模型的构建器开始吧。为了使用模型构建器,我们必须重写 DbContext 的一个方法 OnModelCreating.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
// Map schemas
modelBuilder.Entity<Order>().ToTable("efdemo.Order");
}
默认情况下,EF 将实体映射到数据库中 dbo 架构下的同名表上,这里我将订单映射到数据库中 efdemo 架构下的 Order 表。
模型构建器提供了一种流畅的 API 方式,由于方法返回同样的对象,意味着可以使用链式的方法将操作连接在一起。下面是另外一个例子。
modelBuilder.Entity<Order>().Property(x => x.OrderID)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity)
.IsRequired()
.HasColumnName("TheOrderID");
在这里,我们对属性 OrderID 做了三件事:
-
标识它是标识列,自增长的列
-
必须的列,非空
-
映射到数据库中的 TheOrderID
这里,我们将涉及 EF4.1 的强大的功能:通过 Lambda 表达式,可以逐条定义属性,而不是使用字符串来标识属性,这有以下的优势:
-
有智能提示
-
有编译时的错误检查
-
属性的类型是已知的,你可以在之后定义,例如,只有字符串可以有最大或最小长度。
那么,通常我们重写什么约定呢?看一下 Order 类,下面的情况不太符合默认约定:
-
表必须属于 dbo Schema
-
OrderID 是主键,但不是自增长的类型
-
所有其他列是非空的
-
字符串列的长度是 128
我们可以通过模型构建器重写这些约定:
// Map schemas
modelBuilder.Entity<Order>().ToTable("efdemo.Order");
// Identity Column
modelBuilder.Entity<Order>().Property(x => x.OrderID)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
// String columns
modelBuilder.Entity<Order>().Property(x => x.OrderTitle)
.IsRequired()
.HasMaxLength(64);
modelBuilder.Entity<Order>().Property(x => x.CustomerName)
.IsRequired()
.HasMaxLength(32);
// Date Columns
modelBuilder.Entity<Order>().Property(x => x.TransactionDate)
.IsRequired();
看起来有点冗长,但是不用修改模型,纯粹的 POCO.
使用标注
现在,我们在看一下另外一种方式,使用标签。
这种方式的代码要少得多,感觉更加自然,通过标签来说明属性。唯一的问题是你的类不再 POCO,虽然使用的类并没有来自 EF,而是 System.ComponentModel.DataAnnotations , .NET 的验证模块。看一下例子吧。
public class Order
{
public int OrderID { get; set; }
[Required]
[StringLength(32, MinimumLength = 2)]
public string OrderTitle { get; set; }
[Required]
[StringLength(64, MinimumLength = 5)]
public string CustomerName { get; set; }
[Required]
public DateTime TransactionDate { get; set; }
}
这里我们告诉模型构建器将 OrderTitle 映射到非空的长度为32 的 nvarchar 列,最小长度为 2没有映射到架构中,但是将被用来验证。
这个例子我给出了一些业务标注,也可以加上一些技术标注。
public class Order
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int OrderNumber { get; set; }
…
}
这里我们强制 OrderNumber 属性作为主键,而且是一个自增长的列。
还可以使用 OnModelCreated 或者其他的标签来覆盖约定,有一些限制需要注意,例如(这是我注意到的):
-
表名不支持使用标签进行标注
-
最小长度在 OnModelCreated 中不支持
-
正则表达式在 OnModelCreated 中不支持
着比较好理解,OnModelCreated 属于表映射,(正则或者最小长度不能在数据库中表示),标注支持常见的验证。
我的原则:
-
使用标注来丰富模型的验证规则
-
使用 OnModelCreated 来完成数据库的约束(主键,自增长,表名,列类型等等)
这些方式可以使你的模型更加丰富而且保持 POCO,你可以到处重用你的模型,还能从验证规则中获得好处。