第 2 部分:创建域模型

作者:Rick Anderson

下载已完成项目

添加模型

可通过三种方法处理实体框架:

  • 数据库优先:从数据库开始,实体框架生成代码。
  • 模型优先:从视觉对象模型开始,实体框架将生成数据库和代码。
  • 代码优先:从代码开始,实体框架生成数据库。

我们使用的是代码优先方法,因此首先将域对象定义为 POCO (普通旧 CLR 对象) 。 使用代码优先方法时,域对象不需要任何额外的代码来支持数据库层,例如事务或持久性。 (具体而言,它们不需要继承自 EntityObject 类。) 仍可以使用数据注释来控制 Entity Framework 如何创建数据库架构。

由于 POCO 不携带描述 数据库状态的任何额外属性,因此可以轻松地将其序列化为 JSON 或 XML。 但是,这并不意味着应始终将实体框架模型直接公开给客户端,我们将在本教程的后面部分看到。

我们将创建以下 POCO:

  • 产品
  • 订单
  • OrderDetail

若要创建每个类,请右键单击 解决方案资源管理器 中的 Models 文件夹。 在上下文菜单中选择“ 添加 ”,然后选择“ 类”。

“模型”文件夹的“解决方案资源管理器”菜单的屏幕截图。“添加”菜单处于打开状态,并突出显示了“类”选项。

Product使用以下实现添加类:

namespace ProductStore.Models
{
    using System.ComponentModel.DataAnnotations;

    public class Product
    {
        [ScaffoldColumn(false)]
        public int Id { get; set; }
        [Required]
        public string Name { get; set; }
        public decimal Price { get; set; }
        public decimal ActualCost { get; set; }
    }
}

按照约定,实体框架使用 Id 属性作为主键,并将其映射到数据库表中的标识列。 创建新 Product 实例时,不会为 Id设置值,因为数据库会生成值。

ScaffoldColumn 属性指示 ASP.NET MVC 在生成编辑器窗体时跳过Id该属性。 Required 属性用于验证模型。 它指定 属性 Name 必须是非空字符串。

Order添加 类:

namespace ProductStore.Models
{
    using System.Collections.Generic;
    using System.ComponentModel.DataAnnotations;

    public class Order
    {
        public int Id { get; set; }
        [Required]
        public string Customer { get; set; }

        // Navigation property
        public  ICollection<OrderDetail> OrderDetails { get; set; }
    }
}

OrderDetail添加 类:

namespace ProductStore.Models
{
    public class OrderDetail
    {
        public int Id { get; set; }
        public int Quantity { get; set; }
        public int OrderId { get; set; }
        public int ProductId { get; set; }

        // Navigation properties
        public Product Product { get; set; }
        public Order Order { get; set; }
    }
}

外键关系

一个订单包含许多订单详细信息,每个订单详细信息都引用单个产品。 为了表示这些关系, OrderDetail 类定义了名为 OrderIdProductId的属性。 实体框架将推断这些属性表示外键,并将外键约束添加到数据库。

“订单”、“产品”和“订单”类的 Visual Studio 菜单的屏幕截图。

OrderOrderDetail 类还包括“导航”属性,这些属性包含对相关对象的引用。 给定订单后,可以按照导航属性导航到订单中的产品。

立即编译项目。 实体框架使用反射来发现模型的属性,因此它需要一个已编译的程序集来创建数据库架构。

配置Media-Type格式化程序

媒体类型格式化程序是在 Web API 写入 HTTP 响应正文时序列化数据的对象。 内置格式化程序支持 JSON 和 XML 输出。 默认情况下,这两个格式化程序都按值序列化所有对象。

如果对象图包含循环引用,则按值序列化会产生问题。 和 OrderDetail 类就是这种情况Order,因为每个类都有对另一个类的引用。 格式化程序将遵循引用,按值写入每个对象,然后进入圆圈。 因此,我们需要更改默认行为。

在 解决方案资源管理器 中,展开 App_Start 文件夹并打开名为 WebApiConfig.cs 的文件。 将以下代码添加到 WebApiConfig 类:

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        config.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "api/{controller}/{id}",
            defaults: new { id = RouteParameter.Optional }
        );

        // New code:
        var json = config.Formatters.JsonFormatter;
        json.SerializerSettings.PreserveReferencesHandling =
            Newtonsoft.Json.PreserveReferencesHandling.Objects;

        config.Formatters.Remove(config.Formatters.XmlFormatter);
    }
}

此代码设置 JSON 格式化程序以保留对象引用,并从管道中完全删除 XML 格式化程序。 (可以配置 XML 格式化程序来保留对象引用,但这需要稍微多一点工作,而此应用程序只需要 JSON。有关详细信息,请参阅 处理循环对象引用。)