文章出處

問題

  怎樣用在 Web API 中創建 OData 服務。

 

解決方案

  對于我們來說,在 Web API 中使用 OData最簡單的方式就是使用 ASP.NET 模板來創建Odata Controller。在 Controllers 文件夾上鼠標右鍵->添加->新建項。

wKiom1glDKCzoR0BAAEYtJ6ZIOQ942.png

 

  顯示一個如圖 12-1 的對話框,在這里我們可以選擇兩個 Web API 2 OData 相關的模板。Vistual Studio將會生成相關的 OData Controller,同時,從 NuGet 上下載 OData 需要的所有程序集。

 12-1. 使用模板添加 OData Controller

wKioL1glDMaxZzIIAAC5fDASuZ4568.png

 

  不過,這個模板僅僅對于 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 ProtocolOData )可以創建基于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.0Microsoft.AspNet.OData NuGet 包),然而,之前的 Web API 支持的OData 3.0。如果我們要是指定引用 Mircosoft.Aspnet.WebApi.OData NuGet ,還是可以使用 OData 3.0

OData Controller 應該繼承自 ODataController 基類,而不是常規的ApiControllerASP.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 { getset; }
        public string Name { getset; }
        public string Team { getset; }
    }
}

 

wKioL1glDN-jaix-AAAmSQFySzs693.png-wh_50

 

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


文章列表




Avast logo

Avast 防毒軟體已檢查此封電子郵件的病毒。
www.avast.com


arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()