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删除)提供一种统一的方式来查询或者操作数据。

ASP.Net Web API同时支持该协议v3和v4版本,你甚至可以在运行v4的端点(Endpoint)时运行v3的端点(Endpoint)。

本课程展示了如何创建OData v4的端点(Endpoint)来提供CRUD操作。

教程里使用的软件版本

教程版本

点击Creating an OData v3 Endpoint可跳转至OData Version 3.

创建Visual Studio工程

打开Visual Studio,在File(文件)菜单中,选择 New  >  Project(新建>项目)。

展开   Installed  >  Templates  >  Visual C#  >  Web(已安装>模板>Visual C#>Web),选择 ASP.NET Web Application(ASP.NET Web应用程序)模板,将工程名称命名为“ ProductService”。

在 New Project(新建项目)对话框里选择Empty(空模板)。在 "Add folders and core references..."(为以下对象添加文件夹和核心引用),择Web API,然后点击OK(确定)。选

安装OData包

在Tools(工具)菜单里,选择 NuGet Package Manager  >  Package Manager Console(NuGet程序包管理>程序包管理器控制台)。在程序包管理器控制台视窗里输入:

1Install-Package Microsoft.AspNet.Odata 2

该命令将会安装最新的OData NuGet包。

添加一个Model 类(模型类)

在你的应用程序里,一个Model(模型)就是一个展现数据实体的对象。

在 Solution Explorer(解决方案资源管理器)里,右击名为Models的文件夹,在右键菜单里,选择 Add  >  Class(添加>类)。

按照惯例,model classes(模型类)会被放入名为Models的文件夹里,不过在你工程里你并不一定要遵循这样的规则。

将该类命名为 Product,在Product.cs文件里,使用以下代码替换样本代码。

1namespace ProductService.Models 2{ 3 public class Product 4 { 5 public int Id { get; set; } 6 public string Name { get; set; } 7 public decimal Price { get; set; } 8 public string Category { get; set; } 9 } 10} 11
1 Id 2

 属性是这个实体的键,客户端可以根据键来查询实体。要查询并得到 Id  是5的 Product,它的资源路径便是 /Products(5),在后台数据库里 Id  也是主键。

启用实体框架

在本次教程里,我们将使用 Entity Framework (EF) Code First来创建后台数据库。

Web API OData并不一定需要EF,你可以使用任何可将数据实体转换为models(模型)的数据访问层。

首先,安装EF的NuGet包,在Tools(工具)菜单里,选择 NuGet Package Manager  >  Package Manager Console(NuGet程序包管理>程序包管理器控制台)。在程序包管理器控制台视窗里输入:

1Install-Package EntityFramework 2

打开 Web.config文件,将以下位于 configuration  节点内, configSections节点后的部分添加至该文件中相应位置。

1<configuration> 2 <configSections> 3 <!-- ... --> 4 </configSections> 5 6 <span style="background-color: rgb(255, 255, 0);"><!-- Add this: --></span> 7 <connectionStrings> 8 <add name="ProductsContext" connectionString="Data Source=(localdb)\v11.0; 9 Initial Catalog=ProductsContext; Integrated Security=True; MultipleActiveResultSets=True; 10 AttachDbFilename=|DataDirectory|ProductsContext.mdf" 11 providerName="System.Data.SqlClient" /> 12 </connectionStrings> 13
1该设置里添加了一个链接LocalDB(本地数据库)的链接字符串。当你在本地运行此程序时,本地数据库将被使用。 2

下一步,在Models文件夹里添加一个名为ProductsContext  的类。

1using System.Data.Entity; 2namespace ProductService.Models 3{ 4 public class ProductsContext : DbContext 5 { 6 public ProductsContext() 7 : base("name=ProductsContext") 8 { 9 } 10 public DbSet<Product> Products { get; set; } 11 } 12} 13
1在构造函数里, 2

"name=ProductsContext"给出了连接字符串的名称。

配置OData Endpoint

打开文件 App_Start/WebApiConfig.cs,添加以下引用语句。

1using ProductService.Models; 2using System.Web.OData.Builder; 3using System.Web.OData.Extensions; 4
1然后添加以下代码至 2

Register方法中:

1public static class WebApiConfig 2{ 3 public static void Register(HttpConfiguration config) 4 { 5 // New code: 6 ODataModelBuilder builder = new ODataConventionModelBuilder(); 7 builder.EntitySet<Product>("Products"); 8 config.MapODataServiceRoute( 9 routeName: "ODataRoute", 10 routePrefix: null, 11 model: builder.GetEdmModel()); 12 } 13} 14
1这段代码做了两件事情 2
  • 创建

Entity Data Model(EDM实体数据模型)

  • 增加一个Route(路由)

EDM是数据的抽象模型,EDM被用来创建服务的元数据文件。 ODataConventionModelBuilder使用默认的命名规则创建EDM。这种方法需要少量的代码。如果你想更多支配EDM的话,你可以使用通过明确地添加属性、键和导航属性来创建EDM。

Route告诉了Web API应该怎样路由Http请求至Endpoint(端点)中。你可以通过调用 MapODataServiceRoute  扩展的方法来创建 OData v4 route(路由)。

如果你的程序里有多个OData Endpoint,你需要为它们分别创建一个route,且给它们不同的Route路由名称和前缀。

添加OData控制器

控制器是处理Http请求的类。你可以为每一个OData服务的实体创建不同的控制器。在本教程里,你将为Product  实体创建一个控制器。

在 Solution Explorer(解决方案资源管理器)里,右击名为 Controllers 的文件夹,在右键菜单里,选择 Add  >  Class(添加>类),将其命名为 ProductsController 。

在OData v3教程里我们使用了 Add Controller对话框,但目前还没有OData v4版的。

在 ProductsController.cs中用以下代码替换样本代码。

1using ProductService.Models; 2using System.Data.Entity; 3using System.Data.Entity.Infrastructure; 4using System.Linq; 5using System.Net; 6using System.Threading.Tasks; 7using System.Web.Http; 8using System.Web.OData; 9namespace ProductService.Controllers 10{ 11 public class ProductsController : ODataController 12 { 13 ProductsContext db = new ProductsContext(); 14 private bool ProductExists(int key) 15 { 16 return db.Products.Any(p => p.Id == key); 17 } 18 protected override void Dispose(bool disposing) 19 { 20 db.Dispose(); 21 base.Dispose(disposing); 22 } 23 } 24} 25

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

这是控制的开始。下一步,我们将为所有的CRUD操作添加方法。


Querying the Entity Set  查询实体集

添加以下代码至 ProductsController。

1[EnableQuery] 2public IQueryable<Product> Get() 3{ 4 return db.Products; 5} 6[EnableQuery] 7public SingleResult<Product> Get([FromODataUri] int key) 8{ 9 IQueryable<Product> result = db.Products.Where(p => p.Id == key); 10 return SingleResult.Create(result); 11} 12
1其中一个没有参数的 Get 2

 方法返回 Product  的实体集合。而有一个参数的 Get  方法则是通过 Product的键来查找相应的 Product(在这种情况下,键就是 Id属性)。

[EnableQuery]标签通过使用查询设置 $filter, $sort,  $page 来修改查询。详细信息请点击Supporting OData Query Options


Adding an Entity to the Entity Set  添加实体至实体集

为了使客户端可以添加新Product  至数据库中,我们添加如下方法至 ProductsController。

1public async Task<IHttpActionResult> Post(Product product) 2{ 3 if (!ModelState.IsValid) 4 { 5 return BadRequest(ModelState); 6 } 7 db.Products.Add(product); 8 await db.SaveChangesAsync(); 9 return Created(product); 10} 11
1 **Updating an Entity  2

更新实体**

OData支持两种不同的语法来更新实体, PATCH 和 PUT。

  • PATCH

执行部分更新。客户端只需指定需要更新的属性。

  • PUT 

替换整个实体。

PUT 的不利之处是客户端必须发送实体所有属性的值,包括那些没有改变的值。OData 规范书推荐使用 PATCH。

不管怎样,以下是 PATCH和 PUT 方法的代码:

1public async Task<IHttpActionResult> Patch([FromODataUri] int key, Delta<Product> product) 2{ 3 if (!ModelState.IsValid) 4 { 5 return BadRequest(ModelState); 6 } 7 var entity = await db.Products.FindAsync(key); 8 if (entity == null) 9 { 10 return NotFound(); 11 } 12 product.Patch(entity); 13 try 14 { 15 await db.SaveChangesAsync(); 16 } 17 catch (DbUpdateConcurrencyException) 18 { 19 if (!ProductExists(key)) 20 { 21 return NotFound(); 22 } 23 else 24 { 25 throw; 26 } 27 } 28 return Updated(entity); 29} 30public async Task<IHttpActionResult> Put([FromODataUri] int key, Product update) 31{ 32 if (!ModelState.IsValid) 33 { 34 return BadRequest(ModelState); 35 } 36 if (key != update.Id) 37 { 38 return BadRequest(); 39 } 40 db.Entry(update).State = EntityState.Modified; 41 try 42 { 43 await db.SaveChangesAsync(); 44 } 45 catch (DbUpdateConcurrencyException) 46 { 47 if (!ProductExists(key)) 48 { 49 return NotFound(); 50 } 51 else 52 { 53 throw; 54 } 55 } 56 return Updated(update); 57} 58
12

PATCH情况下,控制器使用 Delta<T>来跟踪实体的改变。


Deleting an Entity  删除实体

为了能让客户端从数据库中删除实体,我们可以在 ProductsController中添加以下方法。

1public async Task<IHttpActionResult> Delete([FromODataUri] int key) 2{ 3 var product = await db.Products.FindAsync(key); 4 if (product == null) 5 { 6 return NotFound(); 7 } 8 db.Products.Remove(product); 9 await db.SaveChangesAsync(); 10 return StatusCode(HttpStatusCode.NoContent); 11} 12

原文地址链接地址

代码交流 2021