第2部分:创建域模型Part 2: Creating the Domain Models

作者: Mike Wassonby Mike Wasson

下载完成的项目Download Completed Project

添加模型Add Models

有三种方法可用于实体框架:There are three ways to approach Entity Framework:

  • 数据库优先:从数据库开始,实体框架生成代码。Database-first: You start with a database, and Entity Framework generates the code.
  • 模型优先:从视觉对象模型开始,实体框架生成数据库和代码。Model-first: You start with a visual model, and Entity Framework generates both the database and code.
  • 代码优先:从代码开始,实体框架生成数据库。Code-first: You start with code, and Entity Framework generates the database.

我们使用代码优先方法,我们首先将域对象定义为 Poco (普通的 CLR 对象)。We are using the code-first approach, so we start by defining our domain objects as POCOs (plain-old CLR objects). 在代码优先方法中,域对象不需要任何额外的代码就可以支持数据库层,如事务或持久性。With the code-first approach, domain objects don't need any extra code to support the database layer, such as transactions or persistence. (具体而言,它们不需要从EntityObject类继承。)你仍可以使用数据批注来控制实体框架创建数据库架构的方式。(Specifically, they do not need to inherit from the EntityObject class.) You can still use data annotations to control how Entity Framework creates the database schema.

由于 Poco 不携带任何其他用于描述数据库状态的属性,因此可以轻松地将其序列化为 JSON 或 XML。Because POCOs do not carry any extra properties that describe database state, they can easily be serialized to JSON or XML. 但是,这并不意味着您应该始终向客户端公开您的实体框架模型,因为我们将在本教程的后面部分介绍。However, that does not mean you should always expose your Entity Framework models directly to clients, as we'll see later in the tutorial.

我们将创建以下 Poco:We will create the following POCOs:

  • 产品Product
  • 顺序Order
  • OrderDetailOrderDetail

若要创建每个类,请在解决方案资源管理器中右键单击 "模型" 文件夹。To create each class, right-click the Models folder in Solution Explorer. 从上下文菜单中,选择 "添加",然后选择 "类"。From the context menu, select Add and then select Class.

添加具有以下实现的 Product 类:Add a Product class with the following implementation:

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 属性作为主键,并将其映射到数据库表中的标识列。By convention, Entity Framework uses the Id property as the primary key and maps it to an identity column in the database table. 创建新的 Product 实例时,不会为 Id设置值,因为数据库会生成值。When you create a new Product instance, you won't set a value for Id, because the database generates the value.

ScaffoldColumn特性指示 ASP.NET MVC 在生成编辑器窗体时跳过 Id 属性。The ScaffoldColumn attribute tells ASP.NET MVC to skip the Id property when generating an editor form. 必需的属性用于验证模型。The Required attribute is used to validate the model. 它指定 Name 属性必须为非空字符串。It specifies that the Name property must be a non-empty string.

添加 Order 类:Add the Order class:

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 类:Add the OrderDetail class:

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; }
    }
}

外键关系Foreign Key Relations

订单包含许多订单详细信息,每个订单详细信息都指单个产品。An order contains many order details, and each order detail refers to a single product. 为了表示这些关系,OrderDetail 类定义了名为 OrderIdProductId属性。To represent these relations, the OrderDetail class defines properties named OrderId and ProductId. 实体框架将推断这些属性表示外键,并将对数据库添加外键约束。Entity Framework will infer that these properties represent foreign keys, and will add foreign-key constraints to the database.

OrderOrderDetail 类还包括 "导航" 属性,其中包含对相关对象的引用。The Order and OrderDetail classes also include "navigation" properties, which contain references to the related objects. 给定订单后,您可以按照导航属性导航到订单中的产品。Given an order, you can navigate to the products in the order by following the navigation properties.

立即编译该项目。Compile the project now. 实体框架使用反射来发现模型的属性,因此它需要已编译的程序集来创建数据库架构。Entity Framework uses reflection to discover the properties of the models, so it requires a compiled assembly to create the database schema.

配置媒体类型格式化程序Configure the Media-Type Formatters

媒体类型格式化程序是一个对象,该对象在 Web API 写入 HTTP 响应正文时序列化数据。A media-type formatter is an object that serializes your data when Web API writes the HTTP response body. 内置格式化程序支持 JSON 和 XML 输出。The built-in formatters support JSON and XML output. 默认情况下,这两个格式化程序都按值序列化所有对象。By default, both of these formatters serialize all objects by value.

如果对象图包含循环引用,则按值进行序列化会导致出现问题。Serialization by value creates a problem if an object graph contains circular references. 这与 OrderOrderDetail 类的情况完全相同,因为每个类都具有对另一个的引用。That's exactly the case with the Order and OrderDetail classes, because each holds a reference to the other. 格式化程序将跟随引用,按值写入每个对象,并使用圆圈。The formatter will follow the references, writing each object by value, and go in circles. 因此,需要更改默认行为。Therefore, we need to change the default behavior.

在解决方案资源管理器中,展开 "应用_启动" 文件夹,然后打开名为 "WebApiConfig.cs" 的文件。In Solution Explorer, expand the App_Start folder and open the file named WebApiConfig.cs. 将下面的代码添加到 WebApiConfig 类中:Add the following code to the WebApiConfig class:

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 格式化程序。This code sets the JSON formatter to preserve object references, and removes the XML formatter from the pipeline entirely. (您可以配置 XML 格式化程序以保留对象引用,但要做的工作要多一些,并且我们只需要此应用程序的 JSON。(You can configure the XML formatter to preserve object references, but it's a little more work, and we only need JSON for this application. 有关详细信息,请参阅处理循环对象引用。)For more information, see Handling Circular Object References.)