原文名称:Entity Framework 4.1: Override conventions (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 属于表映射,(正则或者最小长度不能在数据库中表示),标注支持常见的验证。
我的原则:
这些方式可以使你的模型更加丰富而且保持 POCO,你可以到处重用你的模型,还能从验证规则中获得好处。