[转]ASP.NET Web API基于OData的增删改查,以及处理实体间关系
本文转自: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的增删改查,以及处理实体间关系的更多相关文章
- ASP.NET Web API基于OData的增删改查,以及处理实体间关系
本篇体验实现ASP.NET Web API基于OData的增删改查,以及处理实体间的关系. 首先是比较典型的一对多关系,Supplier和Product. public class Product { ...
- 【转载】ASP.NET MVC Web API 学习笔记---联系人增删改查
本章节简单介绍一下使用ASP.NET MVC Web API 做增删改查.目前很多Http服务还是通过REST或者类似RESP的模型来进行数据操作的.下面我们通过创建一个简单的Web API来管理联系 ...
- ASP.NET MVC Web API 学习笔记---联系人增删改查
本章节简单介绍一下使用ASP.NET MVC Web API 做增删改查. 目前很多Http服务还是通过REST或者类似RESP的模型来进行数据操作的. 下面我们通过创建一个简单的Web API来管理 ...
- [转]ASP.NET web API 2 OData enhancements
本文转自:https://www.pluralsight.com/blog/tutorials/asp-net-web-api-2-odata-enhancements Along with the ...
- 在ASP.NET MVC4中实现同页面增删改查,无弹出框02,增删改查界面设计
在上一篇"在ASP.NET MVC4中实现同页面增删改查,无弹出框01,Repository的搭建"中,已经搭建好了Repository层,本篇就剩下增删改查的界面了......今 ...
- Android 系统API实现数据库的增删改查和SQLite3工具的使用
在<Android SQL语句实现数据库的增删改查>中介绍了使用sql语句来实现数据库的增删改查操作,本文介绍Android 系统API实现数据库的增删改查和SQLite3工具的使用. 系 ...
- Mybatis_3.基于注解的增删改查
1.实体类User.java public class User { private int id; private String name; private int age; //getter.se ...
- Java API实现Hadoop文件系统增删改查
Java API实现Hadoop文件系统增删改查 Hadoop文件系统可以通过shell命令hadoop fs -xx进行操作,同时也提供了Java编程接口 maven配置 <project x ...
- [转]ASP.NET Web API对OData的支持
http://www.cnblogs.com/shanyou/archive/2013/06/11/3131583.html 在SOA的世界中,最重要的一个概念就是契约(contract).在云计算的 ...
随机推荐
- Spring Boot - 依赖注入
@Autowired 查找被注解的变量类型,找到所有此类型的构建或此类型子类的构建 如果一个也没有找到,看required参数,false则用null,true则失败(默认,即spring会启动失败) ...
- 如何为 smartraiden 贡献代码
如何为 smartRaiden 贡献代码 1.Fork 项目 登录 github 账号,并访问https://github.com/SmartMeshFoundation/SmartRaiden,然后 ...
- HAOI2010 订货
题目链接:戳我 费用流. 将每天分成早上和晚上两个点.源点向早上连容量INF,费用为进货量的边.早上向汇点连容量供货量,费用0.早上向晚上连容量为S,费用为0的边.晚上向第二天早上连容量S,费用0.之 ...
- PHP如何将多维数组中的数据批量插入数据库?
PHP将多维数组中的数据批量插入到数据库中,顾名思义,需要用循环来插入. 1.循环insert into 语句,逐渐查询 <?php /* www.qSyz.net */ @mysql_conn ...
- [ActionScript 3.0] AS3 socket示例(官方示例)
下例对套接字执行读写操作,并输出在套接字事件期间传输的信息. 该示例的要点遵循: 该构造函数创建名为 socket 的 CustomSocket 实例,并将主机名 localhost 和端口 80 作 ...
- [Flex] 组件Tree系列 —— 利用firstVisibleItem属性,设置或取得第一个显示节点
mxml: <?xml version="1.0" encoding="utf-8"?> <!--功能描述: 利用firstVisibleIt ...
- [Swift]扩展String类:extension String
请参考本博客另一篇技术博文: <[Swift]字符串(String类.NSString类)常用操作> extension String { //获取字符串首字符 var first: St ...
- Windows下磁盘无损重新分配
打开百度(www.baidu.com),找到我们的分区助手.exe 单击“普通下载”,会下载一个安装包. 这时候,我们进行安装.安装完成如下. 对着那个盘容量较大的盘右键,分配空闲空间. 服务器多次重 ...
- redis 3.0 集群__使用
参考文档 http://redis.readthedocs.org/en/latest/topic/cluster-tutorial.html#id5 http://blog.csdn.net/myr ...
- 「案例」重新设计 Adobe 的文件类型图标
Adobe 的品牌设计团队负责为公司旗下桌面端.移动端和 web 端的产品进行品牌设计.品牌元素的形式很多,可以是两个字母的产品 logo,应用启动界面,产品里的图标等等. 一个很常见却常被忽视的品牌 ...