问题

在 Web API 中使用 OData Function 和 Action。

解决方案

可以通过 ODataModelBuilder,使用 OData 构建 ASP.NET Web API, EntityCollectionConfiguration,EnityTypeConfiguration 类中提供的一系列 Function 和 Action 来自定义 Function 和 Action。

当我们都建自己的 ODataModelBuilder 的时候,可以指定 Function 或 Action 名称并定义他们的输入参数。如清单12-12 所示。

清单 12-12.

  1. var ODataBuilder = new ODataConventionModelBuilder();
  2.  
  3. ODataBuilder.EntitySet<Player>("Players");
  4.  
  5. var player = ODataBuilder.EntityType<Player>();
  6.  
  7. // Function – 读取数据
  8.  
  9. player.Function("PercentageOfAllGoals").Returns<double>();
  10.  
  11. // Action – 请求操作
  12.  
  13. player.Action("TradePlayer").Parameter<string>("NewTeam");

Controller Action 和 OData Function/Action 之间是通过命名的约定建立关联,因此,我们需要在 OData 的 controller 中添加合适的 Action。

工作原理

ASP.NET WEB API 从 2.2 版本开始支持 OData,而且,已经成为 OData 3.0 规范的一部分。另一方面,在之前 Web API 中 OData 的 Action 也是可以使用的。

我们是可以以 Web API Action 的形式定义 OData Function/Action 同时暴露给客户端访问。

使用 Action 或 Function 的主要优势是,我们可以将查询的责任转交给服务器,尤其是复杂查询的时候,可以减轻客户端的不必要的麻烦。

OData 的 Action 和 Function 是有点不一样的;他们都是在规范中被定义的“一组可以被执或可以作为服务或可以作为资源的操作的扩展”。主要的不同是

  • Function:可以能没有什么结果,但是必须有返回值
  • Action:可以对服务器产生影响,但是不能有返回值
  • 另外,Function 可以在 $filter 中被调用

在实现了 OData 的 ASP.NET WEB API 中,Action 和 Function 是连同 OData 约定一起被定义,他们是通过 ODataConventionModelBuilder 的实例定义。WEB API OData 的构建支持三种类型(级别)的操作:

  • 服务 Action/Function:ODataModelBuilder 直接定义
  • 集合 Action/Function:EntityCollectionConfiguration 直接定义
  • 实体 Action/Function:EntityTypeConfiguration直接定义

代码演示

如清单 12-13 所示,一个简单的数据集合,为了演示的方面,在 Controller 中通过内存进行数据的操作,还有一个 Player 的 DTO 的类。

我们就使用这些代码模拟 OData 的三种类型:服务,集合,实体绑定。演示中主要关注在 Function 上,但是, Action 的定义和使用也是几乎一样的。也就是说,在所有使用 Function 声明的方法的地方,都换成 Action 声明的方法是没有毛病的。

清单 12-13. 内存数据和实体模型

  1. public class Player
  2. {
  3. public int Id { get; set; }
  4.  
  5. public string Name { get; set; }
  6.  
  7. public string Team { get; set; }
  8.  
  9. public SkaterStat Stats { get; set; }
  10. }
  11.  
  12. public class SkaterStat
  13. {
  14. public int Goals { get; set; }
  15.  
  16. public int Assists { get; set; }
  17.  
  18. public int GamesPlayed { get; set; }
  19. }
  20.  
  21. public class PlayersController : ODataController
  22. {
  23. private static List<Player> _players = new List<Player>
  24. {
  25. new Player
  26. {
  27. Id = ,
  28. Name = "Filip",
  29. Team = "Whales",
  30. Stats = new SkaterStat
  31. {
  32. GamesPlayed = ,
  33. Goals = ,
  34. Assists =
  35. }
  36. },
  37. new Player
  38. {
  39. Id = ,
  40. Name = "Felix",
  41. Team = "Whales",
  42. Stats = new SkaterStat
  43. {
  44. GamesPlayed = ,
  45. Goals = ,
  46. Assists =
  47. }
  48. new Player
  49. {
  50. Id = ,
  51. Name = "Luiz",
  52. Team = "Dolphins",
  53. Stats = new SkaterStat
  54. {
  55. GamesPlayed = ,
  56. Goals = ,
  57. Assists =
  58. }
  59. },
  60. new Player
  61. {
  62. Id = ,
  63. Name = "Terry",
  64. Team = "Dolphins",
  65. Stats = new SkaterStat
  66. {
  67. GamesPlayed = ,
  68. Goals = ,
  69. Assists =
  70. }
  71. }
  72. };
  73. }

前面提到的 Function 方法,来自于 ODataModelBuilder;EntityCollectionConfiguration,EntityTypeConfiguration,都返回一个 FunctionConfiguration 的实例,我们就是用它来配置我们的 Function,例如,在 $filter 中是否支持 Function,接收什么样的参数,应该返回什么。例如,这个演示的 Startup 类中定义了 ODataModelBuilder的 三个 OData  Function 类型和一个实体类型,如清单 12-14 所示。

清单 12-14 OData Function 服务、集合、实体

  1. public class Startup
  2. {
  3.  
  4. public void Configuration(IAppBuilder builder)
  5. {
  6.  
  7. var ODataBuilder = new ODataConventionModelBuilder();
  8.  
  9. ODataBuilder.EntitySet<Player>("Players");
  10.  
  11. var player = ODataBuilder.EntityType<Player>();
  12.  
  13. /* 集合 Function */
  14.  
  15. player.Collection.Function("TopPpg").ReturnsCollection<Player>();
  16.  
  17. /* 实体 Function */
  18.  
  19. player.Function("PercentageOfAllGoals").Returns<double>();
  20.  
  21. /* 服务 Function */
  22.  
  23. var serviceFunc = ODataBuilder.Function("TotalTeamPoints");
  24.  
  25. serviceFunc.Returns<int>().Parameter<string>("team");
  26.  
  27. serviceFunc.IncludeInServiceDocument = true;
  28.  
  29. var edm = ODataBuilder.GetEdmModel();
  30.  
  31. var config = new HttpConfiguration();
  32.  
  33. config.MapODataServiceRoute("Default OData", "OData", edm);
  34.  
  35. builder.UseWebApi(config);
  36.  
  37. }
  38.  
  39. }

TopPpg 是一个集合 Function,他将返回每场比赛最高分(得分+助攻)比例 player 的集合。PercentageOfAllGoals 是一个实体 Function,返回每场比赛给定参赛者相对所有得分的分数比例。这个 Function 需要客户端传一个 key(player ID),但是,需要注意的是,这个 key 是实体对象的 Id,不需要在 Function 中特殊指明。最后,TotalTeamPoints 是无限制的服务 Function,也就是说,不是特指某一个 player,而是传入一个队名最为参数,同时返回整个队内所有队员分数(得分+助攻)的总和。另外,TotalTeamPoints 也会包含在文档服务中,/OData/$metadata ,作为 Function 入口。

这些 Function 在 Action 中都是使用的 LINQ 表达式。无限制服务的 Function 使用了 ODataRoute 属性,因为默认的 EMD 驱动路由约定不能完成整个功能。

12-15/ 使用 OData Function 来暴露 Controller 的 Action

  1. [HttpGet]
  2. public IEnumerable<Player> TopPpg()
  3. {
  4. var result = _players.OrderByDescending(x => (double)(x.Stats.Goals + x.Stats.Assists) / (double)x.Stats.GamesPlayed).Take();
  5. return (result);
  6. }
  7.  
  8. [HttpGet]
  9. public IHttpActionResult PercentageOfAllGoals(int key)
  10. {
  11. var player = _players.FirstOrDefault(x => x.Id == key);
  12. if (player == null)
  13. return (NotFound());
  14. var result = (double)player.Stats.Goals / (double)_players.Sum(x => x.Stats.Goals) * ;
  15. return (Ok(result));
  16. }
  17.  
  18. [HttpGet]
  19. [ODataRoute("TotalTeamPoints(team={team})")]
  20. public int TotalTeamPoints([FromODataUri] string team)
  21. {
  22. var result = _players.Where(x => string.Equals(x.Team, team, StringComparison.
  23. InvariantCultureIgnoreCase))
  24. .Sum(x => x.Stats.Goals + x.Stats.Assists);
  25. return (result);
  26. }

在这些地方,可以在 URI 中使用 Function 名称来调用他们。根据规范,调用 OData Function 的时候需要使用括号:

  • /OData/Players/Default.TopPpg()
  • /OData/Players(1)/Default.PercentageOfAllGoals()
  • /OData/TotalTeamPoints(team='Whales')

关于 OData 在 ASP.NET WEB API 中的介绍就此告一段落,接下来,一段时间将介绍关于 Route 的东西。

[水煮 ASP.NET Web API2 方法论](12-4)OData 支持的 Function 和 Action的更多相关文章

  1. [水煮 ASP.NET Web API2 方法论](3-9)空气路由的设置

    阅读导航 问题 解决方案 工作原理 代码演示 在此解释一下,空气路由,是本人臆想出来,觉着更能表达 IgnoreRoute 的意图,如果看着辣眼睛^^,请见谅. 问题 我们在之定义过集中式路由,集中式 ...

  2. [水煮 ASP.NET Web API2 方法论](1-2)在 WebForm 应用程序中添加 ASP.NET Web API

    问题 怎么样将 Asp.Net Web Api 加入到 Asp.Net Web From 应用程序中 解决方案 在 Visual Studio 2013 中,创建新的 Web From,可以直接在&q ...

  3. [水煮 ASP.NET Web API2 方法论](1-5)ASP.NET Web API Scaffolding(模板)

    问题 我们想快速启动一个 ASP.NET Web API 解决方案. 解决方案 APS.NET 模板一开始就支持 ASP.NET Web API.使用模板往我们的项目中添加 Controller,在我 ...

  4. [水煮 ASP.NET Web API2 方法论](1-4)从 MVC Controller 链接到 API Controller 以及反向链接

    问题 想创建一个从 ASP.NET MVC controller 到 ASP.NET Web API controller 的直接链接,或者反向链接. 解决方案 可以使用 System.Web.Htt ...

  5. [水煮 ASP.NET Web API2 方法论](1-1)在MVC 应用程序中添加 ASP.NET Web API

    问题 怎么样将 Asp.Net Web Api 加入到现有的 Asp.Net MVC 项目中 解决方案 在 Visual Studio 2012 中就已经把 Asp.Net Web Api 自动地整合 ...

  6. [水煮 ASP.NET Web API2 方法论](1-3)如何接收 HTML 表单请求

    问题 我们想创建一个能够处理 HTML表单的 ASP.NET Web API 应用程序(使用 application/x-www-form-urlencoded 方式提交数据). 解决方案 我们可以创 ...

  7. [水煮 ASP.NET Web API2 方法论](1-6)Model Validation

    问题 想要 ASP.NET Web API 执行模型验证,同时可以和 ASP.NET MVC 共享一些验证逻辑. 解决方案 ASP.NET Web API 与 ASP.NET MVC 支持一样的验证机 ...

  8. [水煮 ASP.NET Web API2 方法论](1-7)CSRF-Cross-Site Request Forgery

    问题 通过 CSRF(Cross-Site Request Forgery)防护,保护从 MVC 页面提交到ASP.NET Web API 的数据. 解决方案 ASP.NET 已经加入了 CSRF 防 ...

  9. [水煮 ASP.NET Web API2 方法论](1-8)添加 Session 状态

    问题 ASP.NET Web API 构建 Web 应用程序时,要求使用 Session 在服务器存储一些用户特定的信息 解决方案 ASP.NET Web API 不支持 Session,因为 API ...

  10. [水煮 ASP.NET Web API2 方法论](12-1)创建 OData

    问题 怎样用在 Web API 中创建 OData 服务. 解决方案 对于我们来说,在 Web API 中使用 OData最简单的方式就是使用 ASP.NET 模板来创建Odata Controlle ...

随机推荐

  1. STL使用总结

    转载于http://blog.csdn.net/daisy_chenting/article/details/6898184 1.    概述 泛型编程思想最早缘于A.Stepanov提出的部分算法可 ...

  2. Codeforces Round #341 (Div. 2)A

    A. Wet Shark and Odd and Even time limit per test 2 seconds memory limit per test 256 megabytes inpu ...

  3. [codeforces/gym/101350/L]维护“凸包”

    题目链接:http://codeforces.com/gym/101350/problems 给定n个墙,每个墙有一个高度,要支持动态修改墙的高度和查询这个“容器”能盛多少水. (队友)观察发现,能盛 ...

  4. mapper的前后缀

    1.<trim prefix="" suffix="" suffixOverrides="" prefixOverrides=&quo ...

  5. C语言实现单链表的遍历,逆序,插入,删除

    单链表的遍历,逆序,插入,删除 #include<stdio.h> #include<stdlib.h> #include <string.h> #define b ...

  6. ES6数组的扩展运算符

    一.基本使用 ES6中函数可以使用 rest参数 接收函数的多余参数,组成一个数组,放在形参的最后面. let fn = (a, ...value) => { console.log(a); c ...

  7. [LeetCode] 10. Regular Expression Matching ☆☆☆☆☆

    Implement regular expression matching with support for '.' and '*'. '.' Matches any single character ...

  8. c# 自定义排序Compare

    .net FrameWork 框架博大精深,用着忘着,计划对自己能够想到知识点梳理一下,此篇是对自定义排序的理解: class Program { static void Main(string[] ...

  9. JDBC和Ibatis中的Date,Time,Timestamp处理

    在此前,遇到过使用Ibatis操作Oracle时时间精度丢失的问题,昨天又遇到JDBC操作MySQL时间字段的问题,从网上看到各种式样的解释这些问题的博文/帖子,但多是雾里看花,不得要领. 理解JDB ...

  10. LTC 钱包部署

    基础环境 系统: CentOS 7.x nodejs: v4.6.0 zeromq: 4.x 安装nodejs + zeromq 基础依赖 yum install -y gcc make gcc-c+ ...