OData services入门----使用ASP.NET Web API描述
http://www.cnblogs.com/muyoushui/archive/2013/01/27/2878844.html
ODate 是一种应用层协议,设计它的目的在于提供一组通过HTTP的交互操作。除了提供一些基本的操作(像增删改查),也提供了一些高级的操作类似过滤数据和实体的导航。
下面的文章我将用ODate 提供给ASP.NET Web API 的功能来建立一个小服务。
ODate
你现在可能在想为什么你的web apps需要另外的协议。JSON难道不是很简单吗?XML services 不够好?嗯,事实上,ODate扩展了上述的协议但是不是取代他们。他可以被XML(ATOM)或者JSON取代但是ODate的重要在于它符合REST原则。在某种意义上,它建立在'简单'的REST HTTP 服务上,并且有着清晰的目标——简化和标准化我们操作和查询数据的方式。如果你过去在给你的REST服务创建搜索、过滤、或者分页API的时候感觉很麻烦,那么ODate将是一个不错的选择。
一些ODate查询语法的规则:
- Entity set – /Artists
- Entity by id – /Artists(1)
- Sorting – /Artists?$orderby=Name
- Filtering – /Artists?$filter=Name eq 'Gridlock'
上面的这些只是冰山一角。
概念介绍的差不多了,让我开始项目吧。很幸运,ASP.NET Web API 可以帮我们很方便的创建ODate。
创建项目
首先我们需要创建一个ASP.NET Web API项目。我们是不需要MVC的相关的东西(视图,js库,等)。
ODate的功能是由一个独立的程序集提供的。请注意,现在这个程序集还是在预览版,最新的官方发布版本是0.3 RC (this blogpost 点击链接参看项目信息,这个是英文的哦)。
很不幸,使用最新的ODataLib程序集还有一些方法(例如:过滤)没有实现,不过不用担心最新的更新将会解决这个问题。因为没有必要学习过时的API,我们将使用最新的发布版本在http://www.myget.org/F/aspnetwebstacknightly/,使用nuget。如果你不会使用nuget,可以看看这里here。
一旦你获取了nightly build nuget source。可以使用Manage NuGet Packages安装最新的Web API OData 包,确保你选择'Include Prerelease'在上面的下拉框中,如下图所示。
另外需要注意的是Web API OData 还在开发阶段,还有一些功能还不支持。不过基本功能已经完成。
数据模型
我们需要一些简单的模型去操作,使用Entity Framework 和 SQL CE 4,但是Web API's OData也支持其他的数据库和持久化技术。

CREATE TABLE [Album]
(
[AlbumId] INT NOT NULL IDENTITY,
[Title] NVARCHAR(160) NOT NULL,
[ArtistId] INT NOT NULL,
[GenreId] INT NOT NULL,
[ReleaseDate] DATETIME,
CONSTRAINT [PK_Album] PRIMARY KEY ([AlbumId])
); CREATE TABLE [Artist]
(
[ArtistId] INT NOT NULL IDENTITY,
[Name] NVARCHAR(120),
CONSTRAINT [PK_Artist] PRIMARY KEY ([ArtistId])
); CREATE TABLE [Genre]
(
[GenreId] INT NOT NULL IDENTITY,
[Name] NVARCHAR(120),
[Description] NVARCHAR(1020),
CONSTRAINT [PK_Genre] PRIMARY KEY ([GenreId])
); ALTER TABLE [Album] ADD CONSTRAINT [FK_AlbumArtistId]
FOREIGN KEY ([ArtistId]) REFERENCES [Artist] ([ArtistId])
ON DELETE NO ACTION ON UPDATE NO ACTION; CREATE INDEX [IFK_AlbumArtistId] ON [Album] ([ArtistId]); ALTER TABLE [Album] ADD CONSTRAINT [FK_AlbumGenreId]
FOREIGN KEY ([GenreId]) REFERENCES [Genre] ([GenreId])
ON DELETE NO ACTION ON UPDATE NO ACTION; CREATE INDEX [IFK_AlbumGenreId] ON [Album] ([GenreId]);

你可以创建一个新的SQL CE数据库在App_Data文件夹下面,使用内置的环境去执行SQL代码。请注意执行SQL只能一行一行执行,不支持一下子执行多次语句。一旦数据库表结构完成了我们可以用VS2012向导生成Entity Data Model。
最后我们将得到一个DbContext类去进行数据操作。
$metadata endpoint 和 IEdmModel
正如我之前提到的,ODate标准定义了一个特别的metadata endpoint,它包含一个定义了实体集,关系,实体类型和操作的文档。这些保证了ODate是自描述的,能够让客户端去生成表示服务端类型的客户端代码,简化了服务的访问方式。Metadata endpoint应该在/$metadata下可用。如果你熟悉SOAP服务,你可以把ODate和它类比一下。
GET http://services.odata.org/Northwind/Northwind.svc/$metadata
Metadata文档使用ODate通用架构定义(OData Common Schema Definition Language (CSDL))。很幸运,ASP.NET Web API可以把$metadata endpoint直接暴露给我们,只要我们的模型继承IEdmModel

public class ModelBuilder
{
public IEdmModel Build()
{
ODataModelBuilder modelBuilder = new ODataConventionModelBuilder();
modelBuilder.EntitySet<Album>("Albums");
modelBuilder.EntitySet<Artist>("Artists");
modelBuilder.EntitySet<Genre>("Genres"); return modelBuilder.GetEdmModel();
}
}


public IEdmModel BuildExplicitly()
{
ODataModelBuilder modelBuilder = new ODataModelBuilder();
EntitySetConfiguration<Genre> genres = modelBuilder.EntitySet<Genre>("Genres");
EntityTypeConfiguration<Genre> genre = genres.EntityType;
genre.HasKey(g => g.GenreId);
genre.Property(g => g.Name);
genre.Property(g => g.Description); //(...) return modelBuilder.GetEdmModel();
}

使用ODate
Microsoft.AspNet.WebApi.OData提供可一系列的类扩展了Web API

public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
var modelBuilder = new ModelBuilder();
IEdmModel model = modelBuilder.Build();
config.Routes.MapODataRoute("OData", null, model);
config.EnableQuerySupport();
}
}

这些代码(Global.asax.cs)做了两件事情:
- 通过路由注册我们的模型表示方法-
- 使查询可用
现在我们的服务应该自动知道怎么去处理OData ~/$metadata 请求。很酷,不是吗?

<edmx:Edmx xmlns:edmx="http://schemas.microsoft.com/ado/2007/06/edmx" Version="1.0">
<edmx:DataServices xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata" m:DataServiceVersion="1.0">
<Schema xmlns="http://schemas.microsoft.com/ado/2009/11/edm" Namespace="Piotr.ODataWebApiService.Service.Models">
<EntityType Name="Album">...</EntityType>
<EntityType Name="Artist">...</EntityType>
<EntityType Name="Genre">...</EntityType>
<Association
Name="Piotr_ODataWebApiService_Service_Models_Album_Artist_Piotr_ODataWebApiService_Service_Models_Artist_ArtistPartner">...</Association>
<Association Name="Piotr_ODataWebApiService_Service_Models_Album_Genre_Piotr_ODataWebApiService_Service_Models_Genre_GenrePartner">...</Association>
<Association Name="Piotr_ODataWebApiService_Service_Models_Artist_Albums_Piotr_ODataWebApiService_Service_Models_Album_AlbumsPartner">...</Association>
<Association Name="Piotr_ODataWebApiService_Service_Models_Genre_Albums_Piotr_ODataWebApiService_Service_Models_Album_AlbumsPartner">...</Association>
</Schema>
<Schema xmlns="http://schemas.microsoft.com/ado/2009/11/edm" Namespace="Default">...</Schema>
</edmx:DataServices>
</edmx:Edmx>

控制器
现在是时候去检测一下我们的成果了。我们应该添加一个暴露ODate资源的控制器。正如你将看到的,这和一个平常的CRUD控制器很不一样。暴露一个ODate实体非常容易。

[ODataRouting]
[ODataFormatting]
public class ArtistsController : ApiController
{
private AlbumsContext db = new AlbumsContext(); // GET /Artists
// GET /Artists?$filter=startswith(Name,'Grid')
[Queryable]
public IQueryable<Artist> Get()
{
return db.Artists;
} protected override void Dispose(bool disposing)
{
db.Dispose();
base.Dispose(disposing);
}
}


[ODataRouting]
[ODataFormatting]
public class ArtistsController : ApiController
{
private AlbumsContext _db = new AlbumsContext(); // GET /Artists
// GET /Artists?$filter=startswith(Name,'Grid')
[Queryable]
public IQueryable<Artist> Get()
{
return _db.Artists;
} // GET /Artists(2)
public HttpResponseMessage Get([FromODataUri]int id)
{
Artist artist = _db.Artists.SingleOrDefault(b => b.ArtistId == id);
if (artist == null)
{
return Request.CreateResponse(HttpStatusCode.NotFound);
} return Request.CreateResponse(HttpStatusCode.OK, artist);
} public HttpResponseMessage Put([FromODataUri] int id, Artist artist)
{
if (!_db.Artists.Any(a => a.ArtistId == id))
{
return Request.CreateResponse(HttpStatusCode.NotFound);
}
//overwrite any existing id, as url is more explicit
artist.ArtistId = id;
_db.Entry(artist).State = EntityState.Modified; try
{
_db.SaveChanges();
}
catch (DbUpdateConcurrencyException)
{
return Request.CreateResponse(HttpStatusCode.NotFound);
} return Request.CreateResponse(HttpStatusCode.NoContent);
} public HttpResponseMessage Post(Artist artist)
{
var odataPath = Request.GetODataPath();
if (odataPath == null)
{
return Request.CreateErrorResponse(HttpStatusCode.BadRequest,
"ODataPath not present in the request.");
} var entitySetPathSegment
= odataPath.Segments.FirstOrDefault() as EntitySetPathSegment; if (entitySetPathSegment == null)
{
return Request.CreateErrorResponse(HttpStatusCode.BadRequest,
"ODataPath does not start with entity set path segment");
} Artist addedArtist = _db.Artists.Add(artist);
_db.SaveChanges();
var response = Request
.CreateResponse(HttpStatusCode.Created, addedArtist); response.Headers.Location = new Uri(Url.ODataLink(
entitySetPathSegment,
new KeyValuePathSegment(ODataUriUtils
.ConvertToUriLiteral(addedArtist.ArtistId
, ODataVersion.V3))));
return response;
} public HttpResponseMessage Patch([FromODataUri] int id,
Delta<Artist> artistPatch)
{
Artist artist = _db.Artists
.SingleOrDefault(p => p.ArtistId == id);
if (artist == null)
{
throw new HttpResponseException(HttpStatusCode.NotFound);
} artistPatch.Patch(artist);
_db.SaveChanges(); return Request.CreateResponse(HttpStatusCode.NoContent);
} public HttpResponseMessage Delete([FromODataUri] int id)
{
Artist artist = _db.Artists.Find(id);
if (artist == null)
{
return Request.CreateResponse(HttpStatusCode.NotFound);
} _db.Artists.Remove(artist); _db.SaveChanges();
return Request.CreateResponse(HttpStatusCode.Accepted);
} protected override void Dispose(bool disposing)
{
_db.Dispose();
base.Dispose(disposing);
}
}

测试这个服务
我将使用Fiddler去测试这个服务。
注意 Content-Type: application/json这个头。应该加一个新的类型。如果我们想要更新实体的一部分,如下图
现在这个类别为id=3将更新描述。
最后,我们进行一个文章实体排序操作如下图,http://localhost:2537/Artists?$orderby=Name&$inlinecount=allpages
正如你以上看到的,我们没有写任何一个特别的逻辑去支持这些功能,全部都由框架来提供的,当然如果你愿意,也可以自己写控制器不限于ODate指定的CRUD操作。
ODate毫无疑问是一个有趣的协议。我感觉它更像一个加强的REST服务。
本文的源代码在bitbucket
译后语:
原文地址:http://www.piotrwalat.net/getting-started-with-odata-services-in-asp-net-web-api/
OData services作为一种最新的协议,将来可能会大规模使用,可能就没有未来。但是关注一点新的技术总没什么害处吧,万一以后你的公司用了,你可以说一句"貌似我以前看过一点",与时俱进嘛。
翻译中遇到的一些好玩,好用的句型:
a tip of an iceberg冰山一角
reap the reward 获得奖励
on steroids 加强的,这个单词词典的翻译是"类固醇",太坑爹,这里完全不是这个意思,后来看了很多例句才体会出加强这个意思。
-------------------------------------------------
!!!作者:木由水 http://www.cnblogs.com/muyoushui
OData services入门----使用ASP.NET Web API描述的更多相关文章
- [转]Supporting OData Query Options in ASP.NET Web API 2
本文转自:https://docs.microsoft.com/en-us/aspnet/web-api/overview/odata-support-in-aspnet-web-api/suppor ...
- Web API 2 入门——使用ASP.NET Web API和Angular.js构建单页应用程序(SPA)(谷歌翻译)
在这篇文章中 概观 演习 概要 由网络营 下载网络营训练包 在传统的Web应用程序中,客户机(浏览器)通过请求页面启动与服务器的通信.然后,服务器处理请求,并将页面的HTML发送给客户端.在与页面的后 ...
- Create an OData v4 Endpoint Using ASP.NET Web API 2.2(使用ASP.NET Web API 2.2创建OData v4端点)
开放数据协议Open Data Protocol(OData)是web的一种数据存取协议,OData通过设置CRUD操作(Create创建.Read读取.Update更新,Delete删除)提供一种统 ...
- Web API 2 入门——创建ASP.NET Web API的帮助页面(谷歌翻译)
在这篇文章中 创建API帮助页面 将帮助页面添加到现有项目 添加API文档 在敞篷下 下一步 作者:Mike Wasson 创建Web API时,创建帮助页面通常很有用,以便其他开发人员知道如何调用A ...
- 杂项:ASP.NET Web API
ylbtech-杂项:ASP.NET Web API ASP.NET Web API 是一种框架,用于轻松构建可以访问多种客户端(包括浏览器和移动设备)的 HTTP 服务. ASP.NET Web A ...
- [转]Web API Introduction to OData Services using ASP.NET Web API
本文转自:http://mahedee.net/tag/web-api/ What is OData? OData Stands for Open Data Protocol. It is a dat ...
- 水果项目第3集-asp.net web api开发入门
app后台开发,可以用asp.net webservice技术. 也有一种重量级一点的叫WCF,也可以用来做app后台开发. 现在可以用asp.net web api来开发app后台. Asp.net ...
- 【ASP.NET Web API教程】1 ASP.NET Web API入门
原文 [ASP.NET Web API教程]1 ASP.NET Web API入门 Getting Started with ASP.NET Web API第1章 ASP.NET Web API入门 ...
- 对一个前端AngularJS,后端OData,ASP.NET Web API案例的理解
依然chsakell,他写了一篇前端AngularJS,后端OData,ASP.NET Web API的Demo,关于OData在ASP.NET Web API中的正删改查没有什么特别之处,但在前端调 ...
随机推荐
- 四、CCSprite
在介绍CCSprite之前,先要理解游戏开发中的一个核心概念:精灵.精灵也称为游戏对象,它可以用来表示游戏中的任何物体,比如敌人.子弹.甚至是一个背景图片.一段文字.CCSprite可以说是在coco ...
- PAT (Basic Level) Practise:1006. 换个格式输出整数
[题目链接] 让我们用字母B来表示“百”.字母S表示“十”,用“12...n”来表示个位数字n(<10),换个格式来输出任一个不超过3位的正整数.例如234应该被输出为BBSSS1234,因为它 ...
- UVa 12558 - Egyptian Fractions (HARD version)
题目大意: 给出一个真分数,把它分解成最少的埃及分数的和.同时给出了k个数,不能作为分母出现,要求解的最小的分数的分母尽量大. 分析: 迭代加深搜索,求埃及分数的基础上,加上禁用限制就可以了.具体可以 ...
- 快速对字符转义,避免跨站攻击XSS
XSS已经成为非常流行的网站攻击方式,为了安全起见,尽量避免用户的输入.可是有些情况下不仅不避免,反而要求鼓励输入,比如写博客.博客园开放性很高,可以运行手写的JS.之前比较著名的例子就是,凡是看到某 ...
- 图像特征提取三大法宝:HOG特征,LBP特征,Haar特征(转载)
(一)HOG特征 1.HOG特征: 方向梯度直方图(Histogram of Oriented Gradient, HOG)特征是一种在计算机视觉和图像处理中用来进行物体检测的特征描述子.它通过计算和 ...
- 行为识别笔记:improved dense trajectories算法(iDT算法)(转载)
iDT算法是行为识别领域中非常经典的一种算法,在深度学习应用于该领域前也是效果最好的算法.由INRIA的IEAR实验室于2013年发表于ICCV.目前基于深度学习的行为识别算法效果已经超过了iDT算法 ...
- H5标签-canvas实现颜色拾取功能
HTML5 <canvas> 标签是用于绘制图像,不过,<canvas> 元素本身并没有绘制能力(它仅仅是图形的容器),必须使用脚本(通常是 JS)来完成实际的绘图任务. &l ...
- spring ioc 原理 spring aop原理
大家一直都说spring的IOC如何如何的强大,其实我倒觉得不是IOC如何的强大,说白了IOC其实也非常的简单.我们先从IOC说起,这个概念其实是从我们平常new一个对象的对立面来说的,我们平常使用对 ...
- JavaWeb学习记录(四)——日期和数字的格式转换
一.Date转为String (1) public class DateUtil { private static SimpleDateFormat sdf = new SimpleDateFo ...
- 【NOIP2013】【P1441】花匠
又一次看错题…… 原题: 花匠栋栋种了一排花,每株花都有自己的高度.花儿越长越大,也越来越挤.栋栋决定把这排中的一部分花移走,将剩下的留在原地,使得剩下的花能有空间长大,同时,栋栋希望剩下的花排列得比 ...