通过


第 2 部分:创建域模型

作者:里克·安德森

下载已完成的项目

添加模型

可通过三种方法接近 Entity Framework:

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

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

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

我们将创建以下 POCO:

  • 产品
  • 下单
  • 订单详情

若要创建每个类,请右键单击解决方案资源管理器中的 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; }
    }
}

按照约定,Entity Framework 使用 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 定义命名 OrderId 的属性和 ProductId。 Entity Framework 将推断这些属性表示外键,并将外键约束添加到数据库。

Orders、Products 和 OrderDetails 类的 Visual Studio 菜单的屏幕截图。

这些 Order 属性和 OrderDetail 类还包括“导航”属性,这些属性包含对相关对象的引用。 在接收到订单后,可以按照导航属性定位到订单中的产品。

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

配置媒体类型格式化程序

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

如果对象图包含循环引用,则按值进行序列化将产生问题。 这正是类Order和类OrderDetail之间的情况,因为每个类都持有对另一个类的引用。 格式化程序将遵循引用关系,按值写入对象,并循环处理。 因此,我们需要更改默认行为。

在解决方案资源管理器中,展开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。有关详细信息,请参阅 处理循环对象引用。)