本文转自:http://www.cnblogs.com/darrenji/p/4926334.html

本篇体验实现ASP.NET Web API基于OData的增删改查,以及处理实体间的关系。

首先是比较典型的一对多关系,Supplier和Product。

public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
public string Category { get; set; } [ForeignKey("Supplier")]
public int? SupplierId { get; set; }
public virtual Supplier Supplier { get; set; }
} public class Supplier
{
public int Id { get; set; }
public string Name { get; set; } public ICollection<Product> Products { get; set; }
}

Product有一个针对Supplier的外键SupplierId,可以为null。

Entity Framework的配置部分略去。

在WebApiConfig中有关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 }
); //有关OData
//使用ODataConventionModelBuilder创建EDM使用了一些惯例
//如果要对创建EDM有更多的控制,使用ODataModelBuilder
ODataModelBuilder builder = new ODataConventionModelBuilder();
builder.EntitySet<Product>("Products");//创建EntityDataModel(EDM)
builder.EntitySet<Supplier>("Suppliers");
config.MapODataServiceRoute(
routeName: "ODataRoute",
routePrefix: "odata",
model:builder.GetEdmModel());
}
}

有关ProductsController

public class ProductsController : ODataController
{
ProductsContext db = new ProductsContext(); private bool ProductExists(int key)
{
return db.Products.Any(p => p.Id == key);
} protected override void Dispose(bool disposing)
{
db.Dispose();
base.Dispose(disposing);
} ...
}

和OData相关的,都要继承ODataController这个基类。

● 获取所有

[EnableQuery]
public IQueryable<Product> Get()
{
return db.Products;
}

当为某个action配置上[EnableQuery]特性后,就支持OData查询了。

● 根据Product的主键查询

[EnableQuery]
public SingleResult<Product> Get([FromODataUri] int key)
{
IQueryable<Product> query = db.Products.Where(p => p.Id == key);
return SingleResult.Create(query);
}

→[FromODataUri] int key中的key值可以从如下uri中获取:

GET http://localhost:63372/odata/Prodducts(11)

以上的11将赋值给key。

→ SingleResult可以接受0个或1个Entity。

● 根据Product的主键获取其导航属性Supplier

//GET /Products(1)/Supplier
//相当于获取Poduct的导航属性Supplier
//GetSupplier中的Supplier是导航属性的名称,GetSupplier和key的写法都符合惯例
//[EnableQuery(AllowedQueryOptions =System.Web.OData.Query.AllowedQueryOptions.All)]
[EnableQuery]
public SingleResult<Supplier> GetSupplier([FromODataUri] int key)
{
var result = db.Products.Where(p => p.Id == key).Select(m => m.Supplier);
return SingleResult.Create(result);
}

以上,GetSupplier的语法符合惯例,Supplier和Product的导航属性名称保持一致。

● 添加Product

public async Task<IHttpActionResult> Post(Product product)
{
if(!ModelState.IsValid)
{
return BadRequest(ModelState);
}
db.Products.Add(product);
await db.SaveChangesAsync();
return Created(product);
}

以上,首先是验证,然后是添加,最后把新添加的Product放在Create方法中返回给前端。

● Product的部分更新

public async Task<IHttpActionResult> Patch([FromODataUri] int key, Delta<Product> product)
{
if(!ModelState.IsValid)
{
return BadRequest(ModelState);
} var entity = await db.Products.FindAsync(key); if (entity == null)
{
return NotFound();
} product.Patch(entity); try
{
await db.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if(!ProductExists(key))
{
return NotFound();
}
else
{
throw;
}
} return Updated(entity);
}

以上,Delta<Product>这个泛型类可以追踪Product的变化,最后使用其实例方法Patch把变化告知实体entity, Patch成功就把Product放在Updated方法中返回给前端。

● 更新Product

public async Task<IHttpActionResult> Put([FromODataUri] int key, Product product)
{
if(!ModelState.IsValid)
{
return BadRequest(ModelState);
}
if(key != product.Id)
{
return BadRequest();
}
db.Entry(product).State = System.Data.Entity.EntityState.Modified; try
{
await db.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!ProductExists(key))
{
return NotFound();
}
else
{
throw;
}
}
return Updated(product);
}

这里,首先判断实体的ModelState,然后判断从前端传来的Product主键key是否和前端传来的Product的主键相等,在处理Entity Framwork单元提交变化的时候catch一个DbUpdateConcurrencyException异常,防止在更新的时候该Product刚好被删除掉。最终,也把Product放在Updated方法返回给前端。

● 删除Product

public async Task<IHttpActionResult> Delete([FromODataUri] int key)
{
var product = await db.Products.FindAsync(key);
if(product==null)
{
return NotFound();
}
db.Products.Remove(product);
await db.SaveChangesAsync();
return StatusCode(HttpStatusCode.NoContent);
}

● 创建Product与Supplier的实体关系

/// <summary>
/// 创建Product与Supplier的关系
/// 如果为Product.Supplier创建关系,使用PUT请求
/// 如果为Supplier.Products创建关系,使用POST请求
/// </summary>
/// <param name="key">Product的主键</param>
/// <param name="navigationProperty">Product的导航属性</param>
/// <param name="link"></param>
/// <returns></returns>
[AcceptVerbs("POST", "PUT")]
public async Task<IHttpActionResult> CreateRef([FromODataUri] int key, string navigationProperty, [FromBody] Uri link)
{
//现保证Product是存在的
var product = db.Products.SingleOrDefault(p => p.Id == key);
if (product == null)
return NotFound(); switch(navigationProperty)
{
case "Supplier":
//获取Supplier的主键
var supplierId = Helpers.GetKeyFromUri<int>(Request, link);
var supplier = db.Suppliers.SingleOrDefault(s => s.Id == supplierId);
if (supplier == null)
return NotFound();
product.Supplier = supplier;
break;
default:
return StatusCode(HttpStatusCode.NotImplemented);
}
await db.SaveChangesAsync();
return StatusCode(HttpStatusCode.NoContent);
}

以上,如果创建Product的Supplier关系,就使用PUT请求,如果创建Supplier的Products关系,就使用POST请求。

前端发出PUT请求,uri为:http://localhost:54714/odata/Products(1)/Supplier/$ref

意思是说需要为编号为1的Product创建一个Supplier。

需要创建的Supplier来自哪里呢?需要从前端的body中传递过来,格式如下:

{"@odata.id":"http://localhost:54714/odata/Suppliers(2)"}

在CreateRef方法中,形参key用来接收这里的Product主键1, 形参navigationProperty用来接收Supplier,形参link用来接收来自body的有关一个具体Supplier的完整uri,即http://localhost:54714/odata/Suppliers(2)。

$ref放在Products(1)/Supplier/之后,表示现在处理的是编号为1的Product和某个Supplier之间的关系。

Helpers.GetKeyFromUri<int>方法用来取出http://localhost:54714/odata/Suppliers(2)中某个Supplier的主键2。

Helpers.GetKeyFromUri<T>方法如下:

//把uri split成segment,找到key的键值,并转换成合适的类型
public static class Helpers
{
public static TKey GetKeyFromUri<TKey>(HttpRequestMessage request, Uri uri)
{
if (uri == null)
{
throw new ArgumentNullException("uri");
} var urlHelper = request.GetUrlHelper() ?? new UrlHelper(request); string serviceRoot = urlHelper.CreateODataLink(
request.ODataProperties().RouteName,
request.ODataProperties().PathHandler, new List<ODataPathSegment>());
var odataPath = request.ODataProperties().PathHandler.Parse(
request.ODataProperties().Model,
serviceRoot, uri.LocalPath); var keySegment = odataPath.Segments.OfType<KeyValuePathSegment>().FirstOrDefault();
if (keySegment == null)
{
throw new InvalidOperationException("The link does not contain a key.");
} var value = ODataUriUtils.ConvertFromUriLiteral(keySegment.Value, Microsoft.OData.Core.ODataVersion.V4);
return (TKey)value;
} }

● 删除Product与Supplier的实体关系

/// <summary>
/// 删除Product与Supplier的关系
/// </summary>
/// <param name="key">Product主键</param>
/// <param name="navigationProperty">Product的导航属性</param>
/// <param name="link">Suppliers(1)的所在地址</param>
/// <returns></returns>
[HttpDelete]
public async Task<IHttpActionResult> DeleteRef([FromODataUri] int key, string navigationProperty, [FromBody] Uri link)
{
var product = db.Products.SingleOrDefault(p => p.Id == key);
if (product == null)
return NotFound(); switch(navigationProperty)
{
case "Supplier":
product.Supplier = null;
break;
default:
return StatusCode(HttpStatusCode.NotImplemented);
}
await db.SaveChangesAsync();
return StatusCode(HttpStatusCode.NoContent);
}

前端发出DELETE请求:http://localhost:54714/odata/Products(1)/Supplier/$ref

DeleteRef方法中,形参key用来接收Product的主键1,形参navigationProperty用来接收Supplier。

SuppliersController,与Product类似

public class SuppliersController : ODataController
{
ProductsContext db = new ProductsContext(); [EnableQuery]
public IQueryable<Product> GetProducts([FromODataUri] int key)
{
return db.Suppliers.Where(m => m.Id.Equals(key)).SelectMany(m => m.Products);
} [EnableQuery]
public IQueryable<Supplier> Get()
{
return db.Suppliers;
} [EnableQuery]
public SingleResult<Supplier> Get([FromODataUri] int key)
{
IQueryable<Supplier> result = db.Suppliers.Where(s => s.Id == key);
return SingleResult.Create(result);
} /// <summary>
/// 删除某个Supplier与某个Product之间的关系
/// DELETE http://host/Suppliers(1)/Products/$ref?$id=http://host/Products(1)
/// </summary>
/// <param name="key">Supplier的主键</param>
/// <param name="relatedKey">Product的主键字符串</param>
/// <param name="navigationProperty">Supplier的导航属性</param>
/// <returns></returns>
[HttpDelete]
public async Task<IHttpActionResult> DeleteRef([FromODataUri] int key, [FromODataUri] string relatedKey, string navigationProperty)
{
var supplier = db.Suppliers.SingleOrDefault(p => p.Id == key);
if (supplier == null)
return NotFound(); switch(navigationProperty)
{
case "Products":
var productId = Convert.ToInt32(relatedKey);
var product = db.Products.SingleOrDefault(p => p.Id == productId);
if (product == null)
return NotFound();
product.Supplier = null;
break;
default:
return StatusCode(HttpStatusCode.NotImplemented);
}
await db.SaveChangesAsync();
return StatusCode(HttpStatusCode.NoContent);
} protected override void Dispose(bool disposing)
{
db.Dispose();
base.Dispose(disposing);
}
}
 
 

[转]ASP.NET Web API基于OData的增删改查,以及处理实体间关系的更多相关文章

  1. ASP.NET Web API基于OData的增删改查,以及处理实体间关系

    本篇体验实现ASP.NET Web API基于OData的增删改查,以及处理实体间的关系. 首先是比较典型的一对多关系,Supplier和Product. public class Product { ...

  2. 【转载】ASP.NET MVC Web API 学习笔记---联系人增删改查

    本章节简单介绍一下使用ASP.NET MVC Web API 做增删改查.目前很多Http服务还是通过REST或者类似RESP的模型来进行数据操作的.下面我们通过创建一个简单的Web API来管理联系 ...

  3. ASP.NET MVC Web API 学习笔记---联系人增删改查

    本章节简单介绍一下使用ASP.NET MVC Web API 做增删改查. 目前很多Http服务还是通过REST或者类似RESP的模型来进行数据操作的. 下面我们通过创建一个简单的Web API来管理 ...

  4. [转]ASP.NET web API 2 OData enhancements

    本文转自:https://www.pluralsight.com/blog/tutorials/asp-net-web-api-2-odata-enhancements Along with the ...

  5. 在ASP.NET MVC4中实现同页面增删改查,无弹出框02,增删改查界面设计

    在上一篇"在ASP.NET MVC4中实现同页面增删改查,无弹出框01,Repository的搭建"中,已经搭建好了Repository层,本篇就剩下增删改查的界面了......今 ...

  6. Android 系统API实现数据库的增删改查和SQLite3工具的使用

    在<Android SQL语句实现数据库的增删改查>中介绍了使用sql语句来实现数据库的增删改查操作,本文介绍Android 系统API实现数据库的增删改查和SQLite3工具的使用. 系 ...

  7. Mybatis_3.基于注解的增删改查

    1.实体类User.java public class User { private int id; private String name; private int age; //getter.se ...

  8. Java API实现Hadoop文件系统增删改查

    Java API实现Hadoop文件系统增删改查 Hadoop文件系统可以通过shell命令hadoop fs -xx进行操作,同时也提供了Java编程接口 maven配置 <project x ...

  9. [转]ASP.NET Web API对OData的支持

    http://www.cnblogs.com/shanyou/archive/2013/06/11/3131583.html 在SOA的世界中,最重要的一个概念就是契约(contract).在云计算的 ...

随机推荐

  1. javaScript prototype实例(正则) 自定义日期格式化方法

    一个JS自定义日期格式化方法,包括了不少知识点,以下方法来自jQuery DataTable中文的官方参考 //return (new Date(data)).Format("yyyy-MM ...

  2. python学习之路 六 :装饰器

    本节重点: 掌握装饰器相关知识 ​ python装饰器就是用于拓展原来函数功能的一种函数,这个函数的特殊之处在于它的返回值也是一个函数,使用python装饰器的好处就是在不用更改原函数的代码前提下给函 ...

  3. python IDE安装-mac

    mac 配置Python集成开发环境(Eclipse +Python+Pydev)   1.下载Mac版64位的Eclipse. 进入到Eclipse官方网站的下载页面(http://www.ecli ...

  4. iOS开发应用结构化资源储备

    1.常用跳转 class 1.1 工程配置及项目初始化 1.2 超常用代码规范 1.3 一种传统的程序模块化马甲包开发架构 2.UI显示部分 UIButton UILabel UIImageView ...

  5. 微信小程序遇到的问题与解决

    1.微信开发工具报错 400 (Bad Request) 解决方法: 注:因为开发工具升级 content-type的写法变了 如下代码: header:{     "Content-Typ ...

  6. oracle无监听程序的解决方法(PLSQL)Oracle ORA12514 监听程序当前无法识别连接描述符中请求的服务

    \PLSQL\instantclient_11_2 listener.ora # listener.ora Network Configuration File: E:\software\PLSQL\ ...

  7. 使用bluebird解决promise兼容性问题

    //引入promiseif(!Promise){ var Promise = require("bluebird"); // Configure Promise.config({ ...

  8. php中签名公钥、私钥(SHA1withRSA签名)以及AES(AES/ECB/PKCS5Padding)加密解密详解

    由于http请求是无状态,所以我们不知道请求方到底是谁.于是就诞生了签名,接收方和请求方协商一种签名方式进行验证,来取得互相信任,进行下一步业务逻辑交流. 其中签名用得很多的就是公钥私钥,用私钥签名, ...

  9. loj2497 [PA2017]Banany(动态淀粉质)

    link 给定一棵树,点有点权,边有边权,你每次修改一个点点权或者是修改一个边边权 你一开始在1号点,你每次改节点之后你需要移动到另一个节点,满足这个节点权值减去路径长度最大(下一次从这个节点移动)如 ...

  10. 读取Excel表格日期类型数据的时候

    用POI读取Excel数据:(版本号:POI3.7) 1.读取Excel 2.Excel数据处理: Excel存储日期.时间均以数值类型进行存储,读取时POI先判断是是否是数值类型,再进行判断转化 1 ...