65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 Web API ODataController 和 MVC5 演示的 knockout JS

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.60/5 (7投票s)

2015年12月15日

CPOL

5分钟阅读

viewsIcon

16855

downloadIcon

467

这是一个 C# MVC Web 项目,集成了 Knockout JS 和 Web API OData 控制器。本文使用的技术和版本包括 MVC 5.0、OData 控制器 4.0、Knockout Js 3.4 和 jQuery 2.0.3。

引言

Knockout JS

Knockout JS 是一个数据绑定 JavaScript 库。它是一个广为人知的库,用于创建丰富、响应式的 UI 和底层数据模型。使用此库的优点在于,当数据模型发生变化时,相应的 UI 部分会得到更新。在使用 knockout JS 时,无需编写任何额外的代码来跟踪数据模型或 UI 的变化。

Web API OData 控制器

OData 是一个开放标准协议,它使我们能够构建和使用可查询的 RESTful API。换句话说,我们可以创建 URI 来获取特定详细信息,并根据我们的需求使用这些结果。

例如,要获取前 10 个产品列表,我们可以构建一个 URL:

好消息是,构建的查询将直接在数据库上执行,因此对应用程序没有直接的性能影响,我们甚至可以创建任何复杂的查询。

如今,大多数应用程序都在客户端使用 knockout 进行数据模型处理,并使用 Web API 进行进一步的通信或服务层操作。这意味着客户端数据模型直接与服务层通信。

考虑到以上几点,我在这里尝试通过使用 knockout 作为前端的数据绑定、Web API OData 控制器作为服务层以及 MVC5 作为前端层来实现应用程序的完整流程。

背景

在我学习 MVC5.0、knockout(客户端数据绑定)和 Web API OData 控制器时,我一直在寻找一个简单的演示,能够使用所有这些技术来解释从前端到数据访问的整个应用程序流程,但幸运或不幸的是,我没有找到任何简化的示例。因此,我花费了一些时间,并提出了这个简单的演示。

此演示的主要目的是:

  • 通过 Web API OData 控制器将数据从数据访问层获取到服务层
  • config.cs 文件配置 WebAPI
  • 从客户端数据模型访问 Web API 方法
  • 使用 knockout 将客户端数据模型绑定到 HTML
  • 与 HTML 控件交互以更新客户端数据模型

Using the Code

让我们来看一个非常简单的例子。

步骤 1

我创建了两个类库项目;第一个用于实体,第二个用于数据访问。

在实体项目中,我引入了一个名为 product 的实体,它包含一些属性。

public class Product
    {
        public int Id { get; set; }
        public string Title { get; set; }
        public string Description { get; set; }
        public string ImageUrl { get; set; }
        public double Price { get; set; }
        public string Category { get; set; }
    }

在数据访问层,我引入了一个名为 Repository 的文件夹,并在其中创建了 IProductRepository 接口和 ProductRepository 类。

public interface IProductRepository
    {
        List<Product> GetProducts();
        Product GetProductById(int id);
    }

这里我将不使用任何特定的数据访问概念,因此在这个演示中,我将使用硬编码的值。

Public class ProductRepository
    {
        public List<Product> GetProducts()
        {
            List<Product> products = new List<Product>();
            produts.Add(new Product { Id = 1, Title = "Product1", 
            Description = "Test Product", ImageUrl = "", Price = 1.01, Category = "P1" });
            produts.Add(new Product { Id = 2, Title = "Product2", 
            Description = "Test Product", ImageUrl = "", Price = 2.01, Category = "P2" });
            produts.Add(new Product { Id = 3, Title = "Product3", 
            Description = "Test Product", ImageUrl = "", Price = 3.01, Category = "P3" });
            produts.Add(new Product { Id = 4, Title = "Product4", 
            Description = "Test Product", ImageUrl = "", Price = 4.01, Category = "P4" });
            produts.Add(new Product { Id = 5, Title = "Product5", 
            Description = "Test Product", ImageUrl = "", Price = 5.01, Category = "P1" });
           return produts;
        }

 public Product GetProductById(int id)
    {
       var listProducts = GetProducts();

       return listProducts.Where(i => i.Id == id).Select
              (p => new Product { Id = p.Id, Title = p.Title, 
              Description = p.Description, Category = p.Category }).FirstOrDefault();
        }
    }

到目前为止,我的解决方案如下所示:

第二步

现在我们将创建 WebApi 项目。

添加新项目并选择 **ASP.NET Web 应用程序** 模板。

点击 **确定** 并选择 WebAPI 模板。

删除了不必要的文件。

向 webApi 项目添加 ASP.NET Odata 引用。

controller 文件夹内添加了一个 ProductController.cs 文件。现在我的解决方案如下所示:

ProductController 的调用中,我继承了 ODataController 类。我已注释掉方法名称顶部的 HttpGetEnableQuery 属性,因为这次我们不打算使用此功能。

 public class ProductController : ODataController
    {
        ProductRepository _productRepository;
        public ProductController()
        {
            _productRepository = new ProductRepository();
        }
        //[HttpGet]
        //[EnableQuery]
        [ODataRoute("GetProducts1()")]
        public String GetProducts()
        {
           return JsonConvert.SerializeObject(_productRepository.GetProducts());
        }
        public String GetProductsById(int id)
        {
            return JsonConvert.SerializeObject(_productRepository.GetProductById(id));
        }
    }

在这里,我为 ODataRoute 编写了 GetProducts1;以区分 ODataRoute 和方法名称,我将在后面进一步解释。

现在,一个重要的部分是 webApi 配置。

在此文件中,我们通常定义函数及其配置,例如它们的输入参数、返回类型。

将实体、函数映射到模型,以及最重要的路由。

在此示例中,我固定了路由名称和前缀,以便从前端发送统一的请求。

  public static void Register(HttpConfiguration config)
        {
            ODataConventionModelBuilder modelBuilder = new ODataConventionModelBuilder();
            modelBuilder.EntitySet<Product>("Products");
            // Function parameter maps with ODataRoute parameter name in controller
            var getProductConfig = modelBuilder.Function("GetProducts1");
            getProductConfig.Returns<String>();
            var getProductByIdConfig = modelBuilder.Function("GetProductById");
            getProductByIdConfig.Parameter<int>("Id");
            getProductByIdConfig.Returns<String>();
            config.MapODataServiceRoute(routeName: "ODataRoute", 
              routePrefix: "ProductOData", model: modelBuilder.GetEdmModel());
        }

您可以看到,模型构建器正在映射到 oDataRoute 而不是函数名称(GetProducts1 而不是 GetProducts),因此 **函数配置依赖于数据路由而不是函数**。

步骤 3

是时候添加前端网站了。

我为这个演示选择了 MVC 模板。

删除了不必要的文件。

我添加了一个名为 product 的 MVC 控制器 Controller

添加了一个视图:Index 视图

部分视图:_productList

现在我的解决方案如下所示:

对于客户端模型,在 script 文件夹内创建 menu.jsproductlist.js,它们将分别绑定到菜单和 _productlist 视图。

通过 nugget 包管理器导出 knockoutJS 组件。

将 knockout 引用添加到 BundleConfig.cs 文件。

在路由配置中设置与 product 控制器的路由。

对于此演示,将 Web API 和 Web 应用程序的项目 URL 设置为相同,这样,一旦运行应用程序,并行服务也将运行并提供输出。

对于 Web App

对于 API

product.js 中,我们将调用此 Web API 方法并将其推送到可观察的 Array。然后,此可观察数组将绑定到 HTML 页面。

var productListViewModel = function () {
    var self = this;
    self._listProduct = [];
    self.Products = ko.observableArray([]);
    self.getProducts = function () {
        var productDataUrl = "Api/ProductOData/GetProducts1";
        $.ajax({
            url: productDataUrl,
            type: "GET",
            success: function (result) {
                self._listProduct = $.parseJSON(result.value);
                self.Products(self._listProduct);
                            },
            error: function (data) {
                alert(data);
            }
        })
    }
};
var vm = new productListViewModel();
vm.getProducts();
ko.applyBindings(vm, $("#divList")[0]); 

首先,我们将使用 foreach 循环遍历数组,然后将每个元素绑定到相应的控件。

<table class="table table-striped table-bordered table-condensed">
        <tr>
            <th>Title</th>
            <th>Description</th>
            <th>Image</th>
            <th>Price</th>
            <th>Category</th>
        </tr>
        <tbody data-bind="foreach:Products">
            <tr>
                <td data-bind="text:Title"></td>
                <td data-bind="text:Description"></td>
                <td data-bind="text:ImageUrl"></td>
                <td data-bind="text:Price"></td>
                <td data-bind="text:Category"></td>
                <td><button class="btn btn-mini btn-danger" 
                     data-bind="click: $parent.removeProfile">Remove</button></td>
                <td><button class="btn btn-mini btn-danger" 
                     data-bind="click: $parent.editProfile">Edit</button></td>
            </tr>
        </tbody>
       
    </table>

关注点

在本文中,我们讨论了应用程序的整个流程,包括:

  • 数据从数据访问层流向前端
  • 数据从数据访问层流向服务层
  • Web API OData 控制器
  • Web API 配置,包括函数/路由
  • 数据从服务层流向客户端数据模型
  • Knockout 将服务器端与客户端数据模型以及客户端数据模型与 HTML 控件进行绑定

我写这篇文章是为了方便初学者。在我的下一篇文章中,我将介绍更多关于 OData 控制器的概念。

希望本文对您有所帮助。

源代码

我已将此示例项目与本文一起上传。这是一个使用 Visual Studio 2015 的 ASP.NET 4.5 版本的 MVC 项目。您可以下载此示例代码,以更好地理解这些概念和实现。

历史

  • 2005 年 12 月 16 日:初始版本
© . All rights reserved.