使用ASP.NET Web API 2创建OData v4 终结点

简介:

开放数据协议(Open Data Protocol【简称OData】)是用于Web的数据访问协议。OData提供了一种对数据集进行CRUD操作(Create,Read,Update,Delete)的统一方式。
Asp.Net Web API支持该协议的v3 和v4版,甚至可以创建一个和v3终结点并排运行的v4终结点。
该博文演示了如何创建支持CRUD操作的OData v4终结点。

用到的软件版本

  • Web API 2
  • OData v4
  • VS 2013 Update 5
  • EF6
  • .Net 4.5.2

创建VS项目

在VS中创建一个新的Asp.Net Web应用项目,命名为“PersonsService”,如下图:

577014-20160122140755859-1662879820.png

然后继续看下图:

577014-20160122140809797-1705542893.png

安装OData Nuget包

打开Nuget包管理器控制台,输入以下命令:

Install-Package Microsoft.AspNet.Odata

该命令会安装最新版本的OData Nuget 包。

添加Model类

Model类是一个表示应用中的数据实体的对象。

在解决方案资源管理器中的Models文件夹下,创建一个Person类:

按照惯例,model类应该放在Models文件夹下,但是在你自己的项目中可以不这么做。

下面是我的Person类的代码:

namespace PersonsService.Models
{
    public class Person
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public bool Gender { get; set; }
        public string UserName { get; set; }
    }
}

启用Entity Framework

这篇博客,我们使用EF的Code First模式来创建数据库。

Web API OData不要求一定得是EF。只要数据访问层可以将数据库实体转换成model,使用任何数据访问层都可以。

首先,安装EF的Nuget包。在包管理器控制台中使用下面的命令:

Install-Package EntityFramework

打开Web.config文件,在configuration元素中添加下面的connectionStrings节点:

  <connectionStrings>
    <add name="PersonsContext" connectionString="Server=.;Database=PersonsDB;Integrated Security=True" providerName="System.Data.SqlClient"/>
  </connectionStrings>

接下来,在Models文件夹下添加一个PersonsContext类:

using System.Data.Entity;

namespace PersonsService.Models
{
    public class PersonsContext:DbContext
    {
        public PersonsContext()
            : base("name=PersonsContext")
        {
        }

        public DbSet<Person> Persons { get; set; }
    }
}

在构造函数中,"name=PersonsContext"指定了连接字符串的命名。

配置OData终结点

打开App_Start/WebApiConfig.cs文件,配置下面的新代码(删除自动生成的代码):

using System.Web.Http;
using System.Web.OData.Builder;
using System.Web.OData.Extensions;
using PersonsService.Models;

namespace PersonsService
{
    public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {

            //新代码
            ODataModelBuilder builder=new ODataConventionModelBuilder();
            builder.EntitySet<Person>("Persons");
            config.MapODataServiceRoute(
                routeName:"odata",
                routePrefix:"odata",
                model:builder.GetEdmModel()
                );
        }
    }
}

上面的代码做了两件事:

  • 创建了一个实体数据模型(Entity Data Model【简称EDM】)。
  • 添加了一个路由。

EDM是一个抽象的数据模型。EDM用于创建服务元数据文档。ODataConventionModelBuilder类使用默认的命名规范创建了一个EDM。这种方式需要写的代码最少。如果你想更多地控制EDM,那么你可以使用 ODataModelBuilder类来创建EDM类,这样做就要显式添加属性,键和导航属性。

路由(route)会告诉Web API如何将HTTP请求路由到终结点。调用MapODataServiceRoute 扩展方法可以创建一个OData v4路由。

如果你的应用有了多个OData终结点,那么要为每个终结点创建一个单独的路由,给每个路由一个唯一的路由名和前缀(prefix)。

添加OData控制器

控制器是处理HTTP请求的一个类。在OData应用中,应该为每个实体集创建一个单独的控制器。而在这篇博客中,我们只要为Person实体创建一个控制器就行了。

在Controllers文件夹下添加一个控制器,如下:
577014-20160122145847078-1851015606.png

在Controllers文件夹上右键添加控制器,接下来选中上图的选择,因为还没有针对OData v4的基架。

默认已经帮我们生成了下面的代码,基本上我们不需要做什么了,CRUD全都有了,呵呵:

using System.Data.Entity.Infrastructure;
using System.Linq;
using System.Net;
using System.Web.Http;
using System.Web.Http.ModelBinding;
using System.Web.OData;
using PersonsService.Models;

namespace PersonsService.Controllers
{
    /*
    在为此控制器添加路由之前,WebApiConfig 类可能要求你做出其他更改。请适当地将这些语句合并到 WebApiConfig 类的 Register 方法中。请注意 OData URL 区分大小写。

    using System.Web.Http.OData.Builder;
    using System.Web.Http.OData.Extensions;
    using PersonsService.Models;
    ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
    builder.EntitySet<Person>("Persons");
    config.Routes.MapODataServiceRoute("odata", "odata", builder.GetEdmModel());
    */
    public class PersonsController : ODataController
    {
        private PersonsContext db = new PersonsContext();

        // GET: odata/Persons
        [EnableQuery]
        public IQueryable<Person> GetPersons()
        {
            return db.Persons;
        }

        // GET: odata/Persons(5)
        [EnableQuery]
        public SingleResult<Person> GetPerson([FromODataUri] int key)
        {
            return SingleResult.Create(db.Persons.Where(person => person.Id == key));
        }

        // PUT: odata/Persons(5)
        public IHttpActionResult Put([FromODataUri] int key, Delta<Person> patch)
        {
            Validate(patch.GetEntity());

            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }

            Person person = db.Persons.Find(key);
            if (person == null)
            {
                return NotFound();
            }

            patch.Put(person);

            try
            {
                db.SaveChanges();
            }
            catch (DbUpdateConcurrencyException)
            {
                if (!PersonExists(key))
                {
                    return NotFound();
                }
                else
                {
                    throw;
                }
            }

            return Updated(person);
        }

        // POST: odata/Persons
        public IHttpActionResult Post(Person person)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }

            db.Persons.Add(person);
            db.SaveChanges();

            return Created(person);
        }

        // PATCH: odata/Persons(5)
        [AcceptVerbs("PATCH", "MERGE")]
        public IHttpActionResult Patch([FromODataUri] int key, Delta<Person> patch)
        {
            Validate(patch.GetEntity());

            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }

            Person person = db.Persons.Find(key);
            if (person == null)
            {
                return NotFound();
            }

            patch.Patch(person);

            try
            {
                db.SaveChanges();
            }
            catch (DbUpdateConcurrencyException)
            {
                if (!PersonExists(key))
                {
                    return NotFound();
                }
                else
                {
                    throw;
                }
            }

            return Updated(person);
        }

        // DELETE: odata/Persons(5)
        public IHttpActionResult Delete([FromODataUri] int key)
        {
            Person person = db.Persons.Find(key);
            if (person == null)
            {
                return NotFound();
            }

            db.Persons.Remove(person);
            db.SaveChanges();

            return StatusCode(HttpStatusCode.NoContent);
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                db.Dispose();
            }
            base.Dispose(disposing);
        }

        private bool PersonExists(int key)
        {
            return db.Persons.Count(e => e.Id == key) > 0;
        }
    }
}

该控制器借助EF,使用PersonsContext类来访问数据库。注意控制器重写了Dispose方法来释放 PersonsContext

生成数据库

通过Code First的Migration生成数据库,然后填充数据。关于如何使用CodeFirst生成数据库不是本节的重点,所以这里一笔带过。下面是我生成的数据库已经填充的数据:

577014-20160122161828437-190692613.png

查询实体集

自动生成的查询操作如下:

 // GET: odata/Persons
        [EnableQuery]
        public IQueryable<Person> GetPersons()
        {
            return db.Persons;
        }

        // GET: odata/Persons(5)
        [EnableQuery]
        public SingleResult<Person> GetPerson([FromODataUri] int key)
        {
            return SingleResult.Create(db.Persons.Where(person => person.Id == key));
        }

无参数的GetPersons()方法会返回整个Person表的集合。
GetPerson([FromODataUri] int key)方法会返回指定Id的Person。

[EnableQuery]特性允许客户端使用查询选项(如$filter,$sort和$page)修改查询。

操作演示

577014-20160122161345047-860283141.png

577014-20160122161439625-1644473315.png

新增实体

允许客户端将一个新的Person实体添加到数据库中:

// POST: odata/Persons
        public IHttpActionResult Post(Person person)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }

            db.Persons.Add(person);
            db.SaveChanges();

            return Created(person);
        }

更新实体

OData支持两种不同语义更新实体,包括PATCH和PUT。

  • PATCH执行一个部分更新,客户端只识别要更新的属性。
  • PUT会替换整个实体。

PUT的劣势在于客户端必须发送实体的所有属性,包括没有改变的值。
OData说明书陈述了PATCH是首选。

下面是生成的代码:

// PATCH: odata/Persons(5)
        [AcceptVerbs("PATCH", "MERGE")]
        public IHttpActionResult Patch([FromODataUri] int key, Delta<Person> patch)
        {
            Validate(patch.GetEntity());

            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }

            Person person = db.Persons.Find(key);
            if (person == null)
            {
                return NotFound();
            }

            patch.Patch(person);

            try
            {
                db.SaveChanges();
            }
            catch (DbUpdateConcurrencyException)
            {
                if (!PersonExists(key))
                {
                    return NotFound();
                }
                else
                {
                    throw;
                }
            }

            return Updated(person);
        }

在PATCH中,控制器使用了Delta类型来跟踪改变。

删除实体

允许客户端从数据库删除一个Person:

// DELETE: odata/Persons(5)
        public IHttpActionResult Delete([FromODataUri] int key)
        {
            Person person = db.Persons.Find(key);
            if (person == null)
            {
                return NotFound();
            }

            db.Persons.Remove(person);
            db.SaveChanges();

            return StatusCode(HttpStatusCode.NoContent);
        }







本文转自tkbSimplest博客园博客,原文链接:http://www.cnblogs.com/farb/p/ODataAspNetWebAPI.html,如需转载请自行联系原作者

相关实践学习
基于Hologres轻松玩转一站式实时仓库
本场景介绍如何利用阿里云MaxCompute、实时计算Flink和交互式分析服务Hologres开发离线、实时数据融合分析的数据大屏应用。
阿里云实时数仓实战 - 项目介绍及架构设计
课程简介 1)学习搭建一个数据仓库的过程,理解数据在整个数仓架构的从采集、存储、计算、输出、展示的整个业务流程。 2)整个数仓体系完全搭建在阿里云架构上,理解并学会运用各个服务组件,了解各个组件之间如何配合联动。 3&nbsp;)前置知识要求 &nbsp; 课程大纲 第一章&nbsp;了解数据仓库概念 初步了解数据仓库是干什么的 第二章&nbsp;按照企业开发的标准去搭建一个数据仓库 数据仓库的需求是什么 架构 怎么选型怎么购买服务器 第三章&nbsp;数据生成模块 用户形成数据的一个准备 按照企业的标准,准备了十一张用户行为表 方便使用 第四章&nbsp;采集模块的搭建 购买阿里云服务器 安装 JDK 安装 Flume 第五章&nbsp;用户行为数据仓库 严格按照企业的标准开发 第六章&nbsp;搭建业务数仓理论基础和对表的分类同步 第七章&nbsp;业务数仓的搭建&nbsp; 业务行为数仓效果图&nbsp;&nbsp;
目录
相关文章
|
1月前
|
JSON API 数据库
解释如何在 Python 中实现 Web 服务(RESTful API)。
解释如何在 Python 中实现 Web 服务(RESTful API)。
26 0
|
1月前
|
XML JSON API
通过Flask框架创建灵活的、可扩展的Web Restful API服务
通过Flask框架创建灵活的、可扩展的Web Restful API服务
|
1月前
|
缓存 监控 API
Python Web框架FastAPI——一个比Flask和Tornada更高性能的API框架
Python Web框架FastAPI——一个比Flask和Tornada更高性能的API框架
58 0
|
1月前
|
JSON API 数据格式
构建高效Python Web应用:Flask框架与RESTful API设计实践
【2月更文挑战第17天】在现代Web开发中,轻量级框架与RESTful API设计成为了提升应用性能和可维护性的关键。本文将深入探讨如何使用Python的Flask框架来构建高效的Web服务,并通过具体实例分析RESTful API的设计原则及其实现过程。我们将从基本的应用架构出发,逐步介绍如何利用Flask的灵活性进行模块化开发,并结合请求处理、数据验证以及安全性考虑,打造出一个既符合标准又易于扩展的Web应用。
646 4
|
1月前
|
存储 PHP 数据库
使用Net2FTP轻松打造免费的Web文件管理器并公网远程访问
使用Net2FTP轻松打造免费的Web文件管理器并公网远程访问
|
2月前
|
前端开发 JavaScript API
前端秘法番外篇----学完Web API,前端才能算真正的入门
前端秘法番外篇----学完Web API,前端才能算真正的入门
|
2月前
|
API 网络架构
解释 RESTful API,以及如何使用它构建 web 应用程序。
解释 RESTful API,以及如何使用它构建 web 应用程序。
88 0
|
2月前
|
存储 前端开发 搜索推荐
前端开发中值得关注的三个Web API
【2月更文挑战第4天】Web API是前端开发中非常重要的一部分,它们为开发者提供了众多的功能和特性,帮助我们构建更加高效、优美的Web应用。本文将介绍三个值得关注的Web API,包括Web Storage、Geolocation和Web Notifications,希望能够对前端开发者有所帮助。
|
20天前
|
监控 JavaScript 前端开发
《理解 WebSocket:Java Web 开发的实时通信技术》
【4月更文挑战第4天】WebSocket是Java Web实时通信的关键技术,提供双向持久连接,实现低延迟、高效率的实时交互。适用于聊天应用、在线游戏、数据监控和即时通知。开发涉及服务器端实现、客户端连接及数据协议定义,注意安全、错误处理、性能和兼容性。随着实时应用需求增加,WebSocket在Java Web开发中的地位将更加重要。
|
1月前
|
Web App开发 前端开发 开发工具
介绍Web开发的基础知识
介绍Web开发的基础知识
29 7