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

作者:Rick Anderson

下载已完成项目

添加管理员控制器

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

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

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

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

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

注意

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

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

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

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

下面是添加到项目的内容:

  • DbContext 派生的名为 OrdersContext 的类。 此类提供 POCO 模型与数据库之间的粘附。
  • 名为 的 AdminControllerWeb API 控制器。 此控制器支持对实例执行 Product CRUD 操作。 它使用 OrdersContext 类与 Entity Framework 通信。
  • Web.config 文件中的新数据库连接字符串。

解决方案资源管理器项目视图的屏幕截图。突出显示了 AdminController dot c s 和 OrdersContext dot c s。

打开 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 PUT
PostProduct 创建新产品。 api/products POST
DeleteProduct 删除产品。 api/products/id DELETE

每个方法调用 以 OrdersContext 查询数据库。 修改集合的方法 (PUT、POST 和 DELETE) 调用 db.SaveChanges ,以保留对数据库的更改。 控制器按 HTTP 请求创建,然后释放,因此有必要在方法返回之前保留更改。

添加数据库初始值设定项

实体框架有一个很好的功能,可用于在启动时填充数据库,并在模型更改时自动重新创建数据库。 此功能在开发过程中很有用,因为即使更改了模型,也始终具有一些测试数据。

在解决方案资源管理器中,右键单击 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. 第一个请求的完成速度可能很慢,因为实体框架需要创建数据库并设定其种子。 响应应类似于以下内容:

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