65.9K
CodeProject 正在变化。 阅读更多。
Home

Fluent API 与数据注释 - 使用配置 - 第一部分

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.95/5 (19投票s)

2012年10月15日

CPOL

4分钟阅读

viewsIcon

69289

领域模型层将更好地与基础设施实现隔离开。所有可以使用 DataAnnotations 配置的内容,都可以使用 Fluent API 来实现。反之则不然。因此,从配置选项和灵活性的角度来看,Fluent API 是“更好”的。


如果您考虑 DDD(领域驱动设计)架构设计,您必须满足的一件事是隔离我们的领域模型层。要实现这一点,您必须将领域实体与任何其他层(如基础设施层、数据持久层,其中您选择了某种数据技术)隔离开。因此,您需要满足领域类(实体、值对象、领域服务等)的 **持久化无关原则 (Persistence Ignorance Principle)**。这时就会出现正确选择的问题。这就是为什么 POCO 实体是 DDD 的正确选择。

如果您深入研究 Code First,您会发现,最初将 POCO 领域实体类映射到最终数据库表的方式是基于约定。我有一个疑问?如果这些约定不符合要求(如果您需要调整到现有数据库或其他情况)怎么办?那么您需要自定义这些约定。您可以通过两种方式来做到这一点。让我们开始描述这两种方式。

Code First 利用了一种称为“约定优于配置”的编程模式。Code First 提供了两种方式将这些配置添加到您的类中。一种是使用简单的特性,称为 `DataAnnotations`,位于“.NET Framework”一部分的 `System.ComponentModel.DataAnnotations` 命名空间中;另一种是使用 Code First 的 Fluent API,它提供了一种以命令式的方式,在代码中描述配置的方法。使用 Fluent API 进行自定义,更符合 PI(*持久化无关*),因为我们的领域模型将不关心这些将在我们的数据持久化基础设施层中定义的映射,因此,我们的领域模型层将更好地与基础设施实现隔离开。

所有您可以使用 `DataAnnotations` 配置的内容,都可以使用 Fluent API 来实现。反之则不然。因此,从配置选项和灵活性的角度来看,Fluent API 是“更好”的。学习 Fluent API 几乎是必须的,而 `DataAnnotations` 对于简单的应用程序来说是“锦上添花”。

我们将描述一个 MVC4 Code First 应用程序的示例。

public class Author
{
 public int AuthorId { get; set; }
 public string Name { get; set; }
 public string Description { get; set; }
 public byte[] Photo { get; set; }
 public virtual ICollection<Book> Books { get; set; }
}
public class Book
{
 public int BookId { get; set; }
 public string Name { get; set; }
 public DateTime PublishDate { get; set; }
 public Author Author { get; set; }
}
public class LibraryDB:DbContext
{
 public DbSet<Author> Authors { get; set; }
 public DbSet<Book> Books { get; set; }
}

`Author` 和 `Book` 类描述了一个特定的 `Author`,该 `Author` 可能拥有一组 `Books`。因此,一个 `Author` 对象可能有一个或多个与之关联的 `Books`。我们的 `LibraryDB` 类将继承自 `DbContext`,以获得 `DbContext` 的所有功能。此外,它将公开返回可查询集(`DbSet`)的 `Author` 类和 `Book` 类的属性。最后一个类代表了您可以在应用程序中使用的一个完整的数据层。对于 `DbContext`,您将能够查询、更改、跟踪和保存 `Author` 和 `Book` 数据。感谢 `DbContext` :)

让我们从 `Author` 类开始。有三件事我想更改

  1. 确保 `Name` 是必填的。
  2. 将 `Description` 字段的最大长度限制为 500 个字符。
  3. 将 `Photo` 存储在 SQL Server 的 image 类型中。

我们需要“.NET 4.0”一部分的“`System.ComponentModel.DataAnnotations`”程序集。应用以下代码

public class Author
{
 public int AuthorId { get; set; }
 [Required]
 public string Name { get; set; }
 [MaxLength(500)]
 public string Description { get; set; }
 [Column(TypeName="image")]
 public byte[] Photo { get; set; }
 public virtual ICollection<Book> Books { get; set; }
}
public class Book
{
 public int BookId { get; set; }
 [Required]
 public string Name { get; set; }
 public DateTime PublishDate { get; set; }
 public Author Author { get; set; }
}

`Required` 注解不需要额外信息,而 `MaxLength` 和 `Column` 有您需要提供的参数信息。提供给 `Column` 注解的参数特定于您要映射的数据库。我们想将 `Photo` 存储在 SQL Server 的 image 字段中。因此,我们配置了数据类型。所有这三个注解都将影响数据库架构。在 `Book` 类上,我们只放置了 required 注解,没有字符限制,因此在数据库架构中,您将发现长度是 `nvarchar(max)`。请注意,我们也可以使用 `MinLength`,这是一个有趣的注解。虽然 `MaxLength` 有一个数据库对应项,但 `MinLength` 没有。`MinLength` 将用于 EF 验证,而不影响数据库。

`MinLength` 是唯一可以使用 Data Annotation 实现的配置,但在 Fluent API 配置中没有对应的项。

如果您打开数据库表,您将找到我们在类中所做的更改。我们稍后将讨论 EF 数据库迁移。

使用 Fluent API 配置

Fluent API 的概念并非仅限于 Code First 或 EF。其基础是通过链式方法调用来生成易于开发人员阅读的代码。每次调用的返回类型决定了下一次调用的有效方法。开发人员喜欢 Fluent API 还有更多原因。

public class LibraryDB:DbContext
{
 public DbSet<Author> Authors { get; set; }
 public DbSet<Book> Books { get; set; }
 protected override void OnModelCreating(DbModelBuilder modelBuilder)
 {
   modelBuilder.Entity<Author>()
    .Property(n => n.Name).IsRequired();
   modelBuilder.Entity<Author>()
    .Property(d => d.Description).HasMaxLength(500);
   modelBuilder.Entity<Author>()
    .Property(p => p.Photo).HasColumnType(“image”);
   modelBuilder.Entity<Book>()
    .Property(n => n.Name).IsRequired();
 }
}

让我们描述一下上述代码的机制,它实际上是如何工作的。Julia Lerman & Rowan Miller 在他们的“Programming Entity Framework Code First”一书中对此进行了精彩的描述。“当构建模型时,`DBContext` 首先查看类,从中学习它能学到的一切。此时,上下文已准备好推理模型,但开发人员有机会中断上下文并进行额外的配置。`DbContext.OnModelCreating` 方法在上下文构建模型之前被调用。该方法是虚拟的,因此您可以重写它并插入自己的逻辑。这就是 Fluent API 配置所在的位置。”

敬请期待:稍后带来更高级的内容…


© . All rights reserved.