[水煮 ASP.NET Web API2 方法论](12-1)创建 OData
问题
怎样用在 Web API 中创建 OData 服务。
解决方案
对于我们来说,在 Web API 中使用 OData最简单的方式就是使用 ASP.NET 模板来创建Odata Controller。在 Controllers 文件夹上鼠标右键->添加->新建项。

显示一个如图 12-1 的对话框,在这里我们可以选择两个 “Web API 2 OData” 相关的模板。Vistual Studio将会生成相关的 OData Controller,同时,从 NuGet 上下载 OData 需要的所有程序集。
图 12-1. 使用模板添加 OData Controller

不过,这个模板仅仅对于 WEB Host (ASP.NET Web API 托管在 ASP.NET Web 应用程序中)是可以用。对于 Web API 托管在其他地方,我们可以通过 NuGet 手动安装 OData Microsoft.AspNet.OData 来开启我们的OData 开发之旅。
工作原理
OData 是一种通过 HTTP 公开丰富 API的标准化协议。OData 4.0 已经被 OASIS 国际开放标准联盟批准,也被认为是 Web 界的 ODBC。
Open Data Protocol(OData )可以创建基于REST 的数据服务,可以是资源,使用 URL 和定义的数据模型,可以通过 Web 客户端使用简单的 HTTP 消息来发布和编辑。
OData 4.0
http://docs.oasis-open.org/odata/odata/v4.0/odata-v4.0-part1-protocol.html
小提示 OData 主页 www.odata.org 里面有所有感兴趣的资源,他可以帮助我们了解 Odato 协议。
ASP.NET WWB API 2.2 支持 OData 4.0(Microsoft.AspNet.OData NuGet 包),然而,之前的 Web API 支持的OData 3.0。如果我们要是指定引用 Mircosoft.Aspnet.WebApi.OData NuGet 包,还是可以使用 OData 3.0。
OData Controller 应该继承自 ODataController 基类,而不是常规的ApiController。ASP.NET Web API 允许我们在一个项目中混合使用 OData 的Controller 和 传统的 Controller,所以,我们可以在提供 OData Api 的同时提供常规 Api。
Controller 继承 ODataController 是有框架进行不同配置的。被称为 ODataActionSelector 的 Odata IHttpAcionSelector 的实现类,是基于 Odata 路由的约定,以及一组特定的媒体类型格式化也是被默认替换的。所有的 OData 格式化程序都是 ODataMediaTypeFormatter 的变种,他可以处理 OData 指定的请求和相应格式,XML 和 JSON。
代码演示
清单 12-1 展示了一个完成的功能,而且很典型的 ODataController 的 CRUD。在这样的情况下,会通过ASP.NET 的模板根据 Player 实体和 EF 数据上下文生成 Controller。
清单 12-1 典型的 ODataController
| 
 1 
2 
3 
4 
5 
6 
7 
8 
9 
 | 
namespace BoiledCode.WebApi.Recipe.ODataDemo.Models{    public class Player    {        public int Id { get; set; }        public string Name { get; set; }        public string Team { get; set; }    }} | 

| 
 1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
24 
25 
26 
27 
28 
29 
30 
31 
32 
33 
34 
35 
36 
37 
38 
39 
40 
41 
42 
43 
44 
45 
46 
47 
48 
49 
50 
51 
52 
53 
54 
55 
56 
57 
58 
59 
60 
61 
62 
63 
64 
65 
66 
67 
68 
69 
70 
71 
72 
73 
74 
75 
76 
77 
78 
79 
80 
81 
82 
83 
84 
85 
86 
87 
88 
89 
90 
91 
92 
93 
94 
95 
96 
97 
98 
99 
100 
101 
102 
103 
104 
105 
106 
107 
108 
109 
110 
111 
112 
113 
114 
115 
116 
117 
118 
119 
120 
121 
122 
123 
124 
125 
126 
127 
128 
129 
130 
131 
132 
133 
134 
135 
136 
137 
138 
139 
140 
141 
142 
143 
144 
145 
146 
147 
148 
149 
150 
151 
 | 
using System.Data.Entity.Infrastructure;using System.Linq;using System.Net;using System.Web.Http;using System.Web.OData;using BoiledCode.WebApi.Recipe.ODataDemo.Models;  namespace BoiledCode.WebApi.Recipe.ODataDemo.Controllers{    /*    The WebApiConfig class may require additional changes to add a route for this controller. Merge these statements into the Register method of the WebApiConfig class as applicable. Note that OData URLs are case sensitive.      using System.Web.OData.Builder;    using System.Web.OData.Extensions;    using BoiledCode.WebApi.Recipe.ODataDemo.Models;    ODataConventionModelBuilder builder = new ODataConventionModelBuilder();    builder.EntitySet<Player>("Players");    config.MapODataServiceRoute("odata", "odata", builder.GetEdmModel());    */      public class PlayersController : ODataController    {        private readonly ApplicationDbContext db = new ApplicationDbContext();          // GET: odata/Players        [EnableQuery]        public IQueryable<Player> GetPlayers()        {            return db.Players;        }          // GET: odata/Players(5)        [EnableQuery]        public SingleResult<Player> GetPlayer([FromODataUri] int key)        {            return SingleResult.Create(db.Players.Where(player => player.Id == key));        }          // PUT: odata/Players(5)        public IHttpActionResult Put([FromODataUri] int key, Delta<Player> patch)        {            Validate(patch.GetEntity());              if (!ModelState.IsValid)            {                return BadRequest(ModelState);            }              var player = db.Players.Find(key);            if (player == null)            {                return NotFound();            }              patch.Put(player);              try            {                db.SaveChanges();            }            catch (DbUpdateConcurrencyException)            {                if (!PlayerExists(key))                {                    return NotFound();                }                throw;            }              return Updated(player);        }          // POST: odata/Players        public IHttpActionResult Post(Player player)        {            if (!ModelState.IsValid)            {                return BadRequest(ModelState);            }              db.Players.Add(player);            db.SaveChanges();              return Created(player);        }          // PATCH: odata/Players(5)        [AcceptVerbs("PATCH", "MERGE")]        public IHttpActionResult Patch([FromODataUri] int key, Delta<Player> patch)        {            Validate(patch.GetEntity());              if (!ModelState.IsValid)            {                return BadRequest(ModelState);            }              var player = db.Players.Find(key);            if (player == null)            {                return NotFound();            }              patch.Patch(player);              try            {                db.SaveChanges();            }            catch (DbUpdateConcurrencyException)            {                if (!PlayerExists(key))                {                    return NotFound();                }                throw;            }              return Updated(player);        }          // DELETE: odata/Players(5)        public IHttpActionResult Delete([FromODataUri] int key)        {            var player = db.Players.Find(key);            if (player == null)            {                return NotFound();            }              db.Players.Remove(player);            db.SaveChanges();              return StatusCode(HttpStatusCode.NoContent);        }          protected override void Dispose(bool disposing)        {            if (disposing)            {                db.Dispose();            }            base.Dispose(disposing);        }          private bool PlayerExists(int key)        {            return db.Players.Count(e => e.Id == key) > 0;        }    }} | 
这个控制器和正常的 Controller 非常相似,只有几个地方是需要强调
OData 查询语法是通过 EnableQueryAttribute 来启用的。我们将在 12-3 来继续讨论。
OData 查询语法不仅可以用在集合上也可以用在单个实体上,用在单个实体上的时候,只要实体使用 SingleResult<T> 就可以。关于这个我们也是在 12-3 来详细介绍。
从 URI 绑定的时候,需要使用 FromODataUriAttribute,而不是传统的 Web API FormUriAttribute。
OData Controller 一般是允许部分实体的更新。这个例子上,是通过 HTTP 的 PATCH 和 Delta<T>来实现部分更新。Delta<T> 是一种特殊的类型,可以用于比较两个实体之间的差异,但是,他仅仅适用于 ODataMediaTypeFormatters 类型。
很显然,控制器并非万能的。使用 OData 的最小要求就是为OData 创建一个实体数据模型(EDM)和 设置OData 路由。这些最终操作的都是 Web API HttpConfiguration 的实例。如清单 12-2 所示,我们会在下一次(12-2)来介绍 OData 路由。EDM 是用来为我们的服务定义 URI,以及提供语义描述(元数据)。
清单 12-1. 设置 EDM 和 OData 路由
| 
 1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
 | 
        public void SettingUpEdmRoyte()        {            var config = new HttpConfiguration();            //配置 Web API            var builder = new ODataConventionModelBuilder();            builder.EntitySet<Player>("Players");            // 第一个参数:路由名称,第二个参数:OData 路由前缀            // players 资源可以被 /odata/players 访问            config.MapODataServiceRoute("odata", "odata", builder.GetEdmModel());        } | 
这个 ODataConventionModelBuilder 类可以帮我们创建一个 EDM,我们不需要不必担心名称转换,导航属性,主键。如果我们需要自定义这些默认关系,那么,我们就需要使用它的基类 ODataModelBuilder,而不是ODataConventionModelBuilder。
EntitySet方法添加实体并设置为 EDM 同时定义指定的 ODataController 来处理相应资源的 HTTP 请求,在我们的例子中就是 PlayersController。
[水煮 ASP.NET Web API2 方法论](12-1)创建 OData的更多相关文章
- [水煮 ASP.NET Web API2 方法论](12-3)OData 查询
		
问题 Web API 怎么支持通用的 OData 系统查询项,例如 $select 或 $filter. 解决方案 为了在 Web API 中启用查询项,我们需要在 Action 上使用 Enable ...
 - [水煮 ASP.NET Web API2 方法论](12-4)OData 支持的 Function 和 Action
		
问题 在 Web API 中使用 OData Function 和 Action. 解决方案 可以通过 ODataModelBuilder,使用 OData 构建 ASP.NET Web API, E ...
 - [水煮 ASP.NET Web API2 方法论](3-9)空气路由的设置
		
阅读导航 问题 解决方案 工作原理 代码演示 在此解释一下,空气路由,是本人臆想出来,觉着更能表达 IgnoreRoute 的意图,如果看着辣眼睛^^,请见谅. 问题 我们在之定义过集中式路由,集中式 ...
 - [水煮 ASP.NET Web API2 方法论](12-2)管理 OData 路由
		
问题 如何控制 OData 路由 解决方案 为了注册路由,可以使用 HttpConfigurationExtension 类中 MapODataServiceRoute 的扩展方法.对于单一路由这样 ...
 - [水煮 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 ...
 - [水煮 ASP.NET Web API2 方法论](1-5)ASP.NET Web API Scaffolding(模板)
		
问题 我们想快速启动一个 ASP.NET Web API 解决方案. 解决方案 APS.NET 模板一开始就支持 ASP.NET Web API.使用模板往我们的项目中添加 Controller,在我 ...
 - [水煮 ASP.NET Web API2 方法论](1-4)从 MVC Controller 链接到 API Controller 以及反向链接
		
问题 想创建一个从 ASP.NET MVC controller 到 ASP.NET Web API controller 的直接链接,或者反向链接. 解决方案 可以使用 System.Web.Htt ...
 - [水煮 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 自动地整合 ...
 - [水煮 ASP.NET Web API2 方法论](1-3)如何接收 HTML 表单请求
		
问题 我们想创建一个能够处理 HTML表单的 ASP.NET Web API 应用程序(使用 application/x-www-form-urlencoded 方式提交数据). 解决方案 我们可以创 ...
 - [水煮 ASP.NET Web API2 方法论](1-6)Model Validation
		
问题 想要 ASP.NET Web API 执行模型验证,同时可以和 ASP.NET MVC 共享一些验证逻辑. 解决方案 ASP.NET Web API 与 ASP.NET MVC 支持一样的验证机 ...
 
随机推荐
- UIScrollView控件及其三个常用属性:contentSize、contentInset和contentOffset
			
如果您对UIScrollView控件感到难以理解,下面是本人自己对UIScrollView控件的理解方式,按照我的思路,理解UIScrollView控件非常容易! 我对UIScrollView的构成理 ...
 - [sed]记录
			
sed的括号本身没有特殊意义,如果要保留通配内容,需要转义. echo "1) host" |sed 's/1) ([a-z])/$1) $1/' 有两处有问题. 1. 首先是上 ...
 - js获取当前页面的参数,带完善~~~
			
let url = window.location.href; let id = url.slice(url.indexOf('?') + 4);
 - [技巧篇]13.从今天开始做一个有理想的人,放弃alter的调试,拥抱console.log
			
在js前端开发时,为了调试经常会加上 console.log.但是在有的浏览器(比如IE)中会报错,怎么办呢?好像10之后也开始支持了!如果以防方一,可以使用如下方式 在js文件最前面添加如下js代码 ...
 - [洛谷P2596] [ZJOI2006]书架
			
洛谷题目链接:书架 题目描述 小T有一个很大的书柜.这个书柜的构造有些独特,即书柜里的书是从上至下堆放成一列.她用1到n的正整数给每本书都编了号. 小T在看书的时候,每次取出一本书,看完后放回书柜然后 ...
 - [Luogu 3224] HNOI2012 永无乡
			
[Luogu 3224] HNOI2012 永无乡 特别水一个平衡树题. 不认真的代价是调试时间指数增长. 我写的 SBT,因为 Treap 的 rand() 实在写够了. 用并查集维护这些点的关系, ...
 - bzoj 1452: [JSOI2009]Count ——二维树状数组
			
escription Input Output Sample Input Sample Output 1 2 HINT ———————————————————————————————————————— ...
 - Vuejs - 花式渲染目标元素
			
Vue.js是什么 摘自官方文档: Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架.与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用.Vue 的核心库 ...
 - for in、each;  for 、forEach、map
			
1.jQuery.each(object, [callback]) 用于例遍任何对象.回调函数拥有两个参数:第一个为对象的成员或数组的索引,第二个为对应变量或内容.如果需要退出 each 循环可使回调 ...
 - 模型验证与模型集成(Ensemble)
			
作者:吴晓军 原文:https://zhuanlan.zhihu.com/p/27424282 模型验证(Validation) 在Test Data的标签未知的情况下,我们需要自己构造测试数据来验证 ...