前言

很久没更新了,之前有很多事情,所以拖了很久,非常抱歉。好了,废话不多说,下面开始正题。本篇仍然使用上一季的的项目背景(系列地址http://www.cnblogs.com/fzrain/p/3490137.html)来演示OData服务,因此我们可以直接使用之前建好的数据访问层。但是不是说一定要看到之前的所有内容,我们只是借用数据库访问层,对于数据库的模型构建移步(使用Entity Framework Code First构建数据库模型)。

有了数据访问的基础,我们可以开始构建OData服务了。

第一步:创建空的Web Api项目

打开项目的解决方案,新建web项目,选择空的web api项目(如上图所示),记得选择.Net Framework 4.5。建好项目之后需要添加对“EntityFramework”和“Learning.Data”(我们的数据访问层)的引用。

第二步:添加OData引用

在默认的情况下,Web Api是无法支持OData的,因此我们需要添加对“Microsoft.ASP.NET Web API 2.1 OData”的引用——打开NuGet如下图所示:

第三步:配置OData路由

打开“App_Start”文件夹中系统帮我们创建的“WebApiConfig”类,在这里有一个Register方法并注册路由规则。我们需要配置的OData也是写在这里,代码如下:

public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API 配置和服务 // Web API 路由
config.MapHttpAttributeRoutes(); config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
config.Routes.MapODataRoute("elearningOData", "OData", GenerateEdmModel());
}
private static IEdmModel GenerateEdmModel()
{
var builder = new ODataConventionModelBuilder();
builder.EntitySet<Course>("Courses");
builder.EntitySet<Enrollment>("Enrollments");
builder.EntitySet<Subject>("Subjects");
builder.EntitySet<Tutor>("Tutors"); return builder.GetEdmModel();
}
}

虽然我们只是使用OData服务,但我并没有移除默认的配置因为这两种路由规则是可以共存的。

上面为我们的OData服务配置了路由规则以及实体数据模型(EDM)

EDM主要是定义数据类型、导航属性和方法来适应OData的格式。有2种方式来定义EDM,第一种是基于公约的方式,我们将使用“ODataConventionModelBuilder”类,另一种则是使用“ODataModelBuilder”。

在这里我们将使用“ODataConventionModelBuilder ”因为它会根据我们定义的导航属性来生成关联集合的链接。相比来说写的代码比较少。如果你想在关联集合间有更多的控制,那么你可以使用“ODataModelBuilder”。

我们在builder对象中添加了4个不同的实体,注意:字符串参数“Courses”定义的实体集合名字必须与Controller的名字保持一致,也就是说我们的controller的名字必须是“CoursesController”。

MapODataRoute”是一个扩展方法,当我们添加对OData引用时就可以使用了。它主要为OData服务来定义路由规则的:第一个参数指定一个名字,这个名字客户端是不会用到的;第二个参数是指对应OData终结点的URI前缀(在我们的案列中访问Courses资源的URI就应该是:http://hostname/odata/Courses)。你可以在同一个应用程序中拥有多个OData终结点,只需要调用“MapODataRoute”方法指定不同的前缀就行了。

第四步:添加第一个只读的OData控制器

现在我们创建一个Web Api控制器来处理OData URI类似“/odata/Courses”的HTTP请求。右击controller文件夹->新增->选择“空的API控制器”模板并命名“CoursesController”。

创建好之后,首先将我们的基类改成“System.Web.Http.OData.EntitySetController”。这个泛型基类需要2个参数:第一个指映射到这个controller对应的实体类型;第二个参数是指这个实体主键的类型,下面上代码:

public class CoursesController : EntitySetController<Course, int>
{
LearningContext ctx = new LearningContext(); [Queryable(PageSize = 10)]
public override IQueryable<Course> Get()
{
return ctx.Courses.AsQueryable();
} protected override Course GetEntityByKey(int key)
{
return ctx.Courses.Find(key);
}
}

“EntitySetController”类定义了很多抽象和可重写的方法来更新和查询实体,因此你会发现你可以重写很多的方法例如:Get(),GetEntityByKey(),CreateEntrty(),PatchEntity(),UpdateEntity(),etc…

正如我前面提到的,我们将创建一个只读的控制器,这就意味着我们只实现读取的操作,解释一下上面代码的实现:

1.重写Get()方法并附上[Queryable]特性,这意味着我们允许客户端发送HTTP的Get到我们的终结点并在URI参数值支持filter,order by,pagination的操作。Queryable特性是一个action过滤器,主要转换和校验查询的URI及相应参数,当客户端查询将花费很多时间或者大量数据时候这个特性将会有意想不到的作用(举个例子:设置PageSize属性,这样一次性只会给客户端返回10条数据)

2.重写GetEntityByKey(int key)方法将支持客户端发送HTTP访问单个资源,形式类似于“/odata/Courses(5)”。注:这里的key代表对应实体的主键。

第五步:测试Courses控制器

现在我们开始测试我们的controller,对于所有的请求我们都将accept header指定为“application/json”,因此我们将获得轻量JSON数据,你也可以去指定accept header为“application/json;odata=verbose”或者“application/atom+xml”来查看结果。

我们演示一下场景:

1.$filter:我们查询所有时长超过4小时的课程:发送Get请求http://{hostname}/OData/Courses?$filter=Duration gt 4

2.$orderby, $take:我们需要根据课程名排序并获取前5条记录:发送Get请求http://{hostname}/OData/Courses?$orderby=Name&$top=5

3.$select:我们仅需要获取Name和Duration字段的值:发送Get请求http://{hostname}/OData/Courses?$select=Name,Duration

4.$expand:我们需要获取每个课程对应的主题和讲师并根据课程名倒序排列:发送Get请求http://{hostname}/OData/Courses?$expand=CourseTutor,CourseSubject&$orderby=Name desc

通过刚刚4个例子我们可以看到在我们的返回结果中包含了UserName和Password这两个字段,但个信息是没必要给客户端的。,

十分幸运的是我们只需要去配置一下EDM就可以在返回结果中不包含这两个字段了,具体做法在WebApiConfig类中的GenerateEdmModel()方法里加入如下代码:

private static IEdmModel GenerateEdmModel()
{
var builder = new ODataConventionModelBuilder();
builder.EntitySet<Course>("Courses");
builder.EntitySet<Enrollment>("Enrollments");
builder.EntitySet<Subject>("Subjects");
builder.EntitySet<Tutor>("Tutors");
var tutorsEntitySet = builder.EntitySet<Tutor>("Tutors"); tutorsEntitySet.EntityType.Ignore(s => s.UserName);
tutorsEntitySet.EntityType.Ignore(s => s.Password); return builder.GetEdmModel();
}

源码地址:https://github.com/fzrain/WebApi.eLearning

 

作者:FZRAIN
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

Web Api系列教程第2季(OData篇)(二)——使用Web Api创建只读的OData服务的更多相关文章

  1. Web Api系列教程第2季(OData篇)(一)——OData简介和一个小应用

    第一季的链接以及系列导航:http://www.cnblogs.com/fzrain/p/3490137.html 在这里,首先要感谢Taiseer Joudeh不断的为我们带来最新的技术分享,楼主对 ...

  2. [转]Web Api系列教程第2季(OData篇)(二)——使用Web Api创建只读的OData服务

    本文转自:http://www.cnblogs.com/fzrain/p/3923727.html 前言 很久没更新了,之前有很多事情,所以拖了很久,非常抱歉.好了,废话不多说,下面开始正题.本篇仍然 ...

  3. ASP.NET Web API系列教程目录

    ASP.NET Web API系列教程目录 Introduction:What's This New Web API?引子:新的Web API是什么? Chapter 1: Getting Start ...

  4. ASP.NET Web API系列教程(目录)(转)

    注:微软随ASP.NET MVC 4一起还发布了一个框架,叫做ASP.NET Web API.这是一个用来在.NET平台上建立HTTP服务的Web API框架,是微软的又一项令人振奋的技术.目前,国内 ...

  5. [转]ASP.NET Web API系列教程(目录)

    本文转自:http://www.cnblogs.com/r01cn/archive/2012/11/11/2765432.html 注:微软随ASP.NET MVC 4一起还发布了一个框架,叫做ASP ...

  6. Web攻防系列教程之文件上传攻防解析(转载)

    Web攻防系列教程之文件上传攻防解析: 文件上传是WEB应用很常见的一种功能,本身是一项正常的业务需求,不存在什么问题.但如果在上传时没有对文件进行正确处理,则很可能会发生安全问题.本文将对文件上传的 ...

  7. 源码学习系列之SpringBoot自动配置(篇二)

    源码学习系列之SpringBoot自动配置(篇二)之HttpEncodingAutoConfiguration 源码分析 继上一篇博客源码学习系列之SpringBoot自动配置(篇一)之后,本博客继续 ...

  8. SpringBoot系列之profles配置多环境(篇二)

    SpringBoot系列之profles配置多环境(篇二) 继续上篇博客SpringBoot系列之profles配置多环境(篇一)之后,继续写一篇博客进行补充 写Spring项目时,在测试环境是一套数 ...

  9. 【转】ASP.NET WEB API系列教程

    from: 西瓜小强 http://www.cnblogs.com/risk/category/406988.html ASP.NET Web API教程(六) 安全与身份认证 摘要: 在实际的项目应 ...

随机推荐

  1. 安卓---Toast工具类,有点懒

    package com.liunan.myfirstapp.util; import android.content.Context; import android.widget.Toast; /** ...

  2. IT软件的编程方向 - 进阶者系列 - 学习者系列文章

    IT软件经过了这么些年的发展,已经形成了很多的软件公司和开发团队,而且当前编程语言也有很多种,让开发人员能够有很大的选择.现在国际上的开发阵营基本分为ASP.NET.JAVA和PHP三种,但是每种开发 ...

  3. although 和 although 的区别

    作为连词的时候,although 和 though 是可以互换的.Although 一般被认为更加正式一些.比如,以下的这些句子: Growth in Europe is maintaining mo ...

  4. 安装TFS(2015)工作组模式代理服务器(Agent)

    TFS的代理服务器(agent)用于持续集成编译和发布,为开发.测试团队和运维团队带来的非常便捷高效的发布和测试速度,许多企业和研发团队都在自己的研发测试平台中广泛使用这一技术. 在部署TFS代理服务 ...

  5. Android LayoutInflater.inflate(int resource, ViewGroup root, boolean attachToRoot)的参数理解

    方法inflate(int resource, ViewGroup root, boolean attachToRoot) 中 第一个参数传入布局的资源ID,生成fragment视图,第二个参数是视图 ...

  6. Ioc和Ao使用扩展

    一.Bean作用域 spring容器创建的时候,会将所有配置的bean对象创建出来,默认bean都是单例的.代码通过getBean()方法从容器获取指定的bean实例,容器首先会调用Bean类的无参构 ...

  7. PHP代码

    1 <html>  <head>       <meta http-equiv="content-type" content="text/h ...

  8. JS中字符串的true转化为boolean类型的true

    var a="True"; a = eval(a.toLowerCase()); alert(typeof a); //boolean alert(a);//true 正解,eva ...

  9. [LeetCode] Spiral Matrix 螺旋矩阵

    Given a matrix of m x n elements (m rows, n columns), return all elements of the matrix in spiral or ...

  10. JS组件系列——BootstrapTable+KnockoutJS实现增删改查解决方案(三):两个Viewmodel搞定增删改查

    前言:之前博主分享过knockoutJS和BootstrapTable的一些基础用法,都是写基础应用,根本谈不上封装,仅仅是避免了html控件的取值和赋值,远远没有将MVVM的精妙展现出来.最近项目打 ...