通过


第 3 部分:创建管理员控制器

作者:里克·安德森

下载已完成的项目

添加管理员控制器

在本部分中,我们将添加一个 Web API 控制器,该控制器支持对产品执行 CRUD(创建、读取、更新和删除)操作。 控制器将使用 Entity Framework 与数据库层通信。 只有管理员才能使用此控制器。 客户将通过另一个控制器访问产品。

在解决方案资源管理器中,右键单击“控制器”文件夹。 选择 “添加 ”,然后选择 “控制器”。

解决方案资源管理器控制器菜单的屏幕截图。已选择“添加”选项,并突出显示“控制器”。

“添加控制器 ”对话框中,将控制器 AdminController命名为 。 在 “模板”下,选择“使用 Entity Framework 执行读/写操作的 API 控制器”。 在 Model 类下,选择“Product(ProductStore.Models)”。 在 “数据上下文”下,选择“<新建数据上下文>”。

“添加控制器”对话框的屏幕截图。数据上下文类菜单处于打开状态,并突出显示了新的数据上下文。

注释

如果 Model 类 下拉列表未显示任何模型类,请确保编译项目。 实体框架使用反射,因此需要编译的程序集。

选择“<新建数据上下文>”将打开 “新建数据上下文 ”对话框。 命名数据上下文 ProductStore.Models.OrdersContext

“新建数据上下文”对话框的屏幕截图。文本框显示键入的新数据上下文的名称。

单击“ 确定 ”以消除 “新建数据上下文 ”对话框。 在 “添加控制器 ”对话框中,单击“ 添加”。

以下是被添加到项目中的内容:

  • 一个命名为OrdersContext的类派生自DbContext。 此类在 POCO 模型和数据库之间提供了连接。
  • 名为 .. 的 Web API 控制器 AdminController。 此控制器支持执行 CRUD 操作于 Product 实例。 它使用该 OrdersContext 类与 Entity Framework 通信。
  • Web.config 文件中的新数据库连接字符串。

解决方案资源管理器项目视图的屏幕截图。AdminController.cs 和 OrdersContext.cs 被突出显示。

打开OrdersContext.cs文件。 请注意,构造函数指定数据库连接字符串的名称。 此名称是指已添加到 Web.config的连接字符串。

public OrdersContext() : base("name=OrdersContext")

OrdersContext 类添加以下属性:

public DbSet<Order> Orders { get; set; }
public DbSet<OrderDetail> OrderDetails { get; set; }

DbSet 表示可以查询的一组实体。 下面是该 OrdersContext 类的完整列表:

public class OrdersContext : DbContext
{
    public OrdersContext() : base("name=OrdersContext")
    {
    }

    public DbSet<Order> Orders { get; set; }
    public DbSet<OrderDetail> OrderDetails { get; set; }
    public DbSet<Product> Products { get; set; }
}

AdminController 类定义实现基本 CRUD 功能的五种方法。 每个方法对应于客户端可以调用的 URI:

控制器方法 说明 URI HTTP 方法
GetProducts 获取所有产品。 api/products GET
GetProduct 按 ID 查找产品。 api/products/id GET
PutProduct 更新产品。 api/products/id 置入
PostProduct 创建新产品。 api/products POST
删除产品 删除产品。 api/products/id 删除

每个方法调用 OrdersContext 查询数据库。 可以修改集合的 PUT、POST 和 DELETE 方法调用 db.SaveChanges 来将更改持久化保存到数据库。 控制器是按 HTTP 请求创建的,然后释放,因此需要在方法返回之前保留更改。

添加数据库初始化器

Entity Framework 具有一项很好的功能,可用于在启动时填充数据库,并在模型发生更改时自动重新创建数据库。 此功能在开发期间非常有用,因为即使更改模型,也始终具有一些测试数据。

在解决方案资源管理器中,右键单击 Models 文件夹并创建名为 OrdersContextInitializer 的新类。 粘贴以下实现:

namespace ProductStore.Models
{
    using System;
    using System.Collections.Generic;
    using System.Data.Entity;

    public class OrdersContextInitializer : DropCreateDatabaseIfModelChanges<OrdersContext>
    {
        protected override void Seed(OrdersContext context)
        {
            var products = new List<Product>()            
            {
                new Product() { Name = "Tomato Soup", Price = 1.39M, ActualCost = .99M },
                new Product() { Name = "Hammer", Price = 16.99M, ActualCost = 10 },
                new Product() { Name = "Yo yo", Price = 6.99M, ActualCost = 2.05M }
            };

            products.ForEach(p => context.Products.Add(p));
            context.SaveChanges();

            var order = new Order() { Customer = "Bob" };
            var od = new List<OrderDetail>()
            {
                new OrderDetail() { Product = products[0], Quantity = 2, Order = order},
                new OrderDetail() { Product = products[1], Quantity = 4, Order = order }
            };
            context.Orders.Add(order);
            od.ForEach(o => context.OrderDetails.Add(o));

            context.SaveChanges();
        }
    }
}

通过继承自 DropCreateDatabaseIfModelChanges 类,我们告诉 Entity Framework 在修改模型类时删除数据库。 实体框架创建(或重新创建)数据库时,它会调用 Seed 方法来填充表。 我们使用 Seed 方法添加一些示例产品以及示例订单。

此功能非常适合测试,但不要在生产环境中使用 DropCreateDatabaseIfModelChanges 类,因为如果有人更改模型类,可能会丢失数据。

接下来,打开 Global.asax 并将以下代码添加到 Application_Start 方法:

System.Data.Entity.Database.SetInitializer(
    new ProductStore.Models.OrdersContextInitializer());

向控制器发送请求

此时,我们尚未编写任何客户端代码,但可以使用 Web 浏览器或 HTTP 调试工具(如 Fiddler)调用 Web API。 在 Visual Studio 中,按 F5 开始调试。 您的 Web 浏览器将打开到http://localhost:*portnum*/,其中portnum 是某个端口号。

将 HTTP 请求发送到“http://localhost:*portnum*/api/admin。 第一个请求可能会很慢,因为 Entity Framework 需要创建并初始化数据库。 响应应类似于以下内容:

HTTP/1.1 200 OK
Server: ASP.NET Development Server/10.0.0.0
Date: Mon, 18 Jun 2012 04:30:33 GMT
X-AspNet-Version: 4.0.30319
Cache-Control: no-cache
Pragma: no-cache
Expires: -1
Content-Type: application/json; charset=utf-8
Content-Length: 175
Connection: Close

[{"Id":1,"Name":"Tomato Soup","Price":1.39,"ActualCost":0.99},{"Id":2,"Name":"Hammer",
"Price":16.99,"ActualCost":10.00},{"Id":3,"Name":"Yo yo","Price":6.99,"ActualCost":
2.05}]