[转]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

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值