冠军

导航

Entity Framework 4.1 之二 : 覆盖默认的约定

原文名称:Entity Framework 4.1: Override conventions (2)
原文地址:http://vincentlauzon.wordpress.com/2011/04/06/entity-framework-4-1-override-conventions-2/
 
看到 Entity Framework 4.1 推荐英文教程,为了帮大家看起来方便一些,简单翻译一下。这是一个系列,共有 8 篇,这是第 2 篇。
  1. Entity Framework 4.1 之一 : 基础
  2. Entity Framework 4.1 之二 : 覆盖默认的约定
  3. Entity Framework 4.1 之三 : 贪婪加载和延迟加载
  4. Entity Framework 4.1 之四:复杂类型
  5. Entity Framework 4.1 之五:多对多的关系
  6. Entity Framework 4.1 之六:乐观并发
  7. Entity Framework 4.1 之七:继承
  8. Entity Framework 4.1 之八:绕过 EF 查询映射
在这篇文章中,我将讨论如何覆盖默认的约定。
我们已经看过了在 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,你可以到处重用你的模型,还能从验证规则中获得好处。

posted on 2011-05-06 17:12  冠军  阅读(23137)  评论(24编辑  收藏  举报