博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
ASP.NET Web API基于OData的增删改查,以及处理实体间关系
阅读量:5269 次
发布时间:2019-06-14

本文共 10204 字,大约阅读时间需要 34 分钟。

 

本篇体验实现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
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
("Products");//创建EntityDataModel(EDM) builder.EntitySet
("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
Get(){ return db.Products;}

 

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

 

● 根据Product的主键查询

 

[EnableQuery]public SingleResult
Get([FromODataUri] int key){ IQueryable
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
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
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
Patch([FromODataUri] int key, Delta
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
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
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的实体关系

 

/// /// 创建Product与Supplier的关系/// 如果为Product.Supplier创建关系,使用PUT请求/// 如果为Supplier.Products创建关系,使用POST请求/// /// Product的主键/// Product的导航属性/// /// 
[AcceptVerbs("POST", "PUT")]public async Task
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
(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
(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
()); var odataPath = request.ODataProperties().PathHandler.Parse( request.ODataProperties().Model, serviceRoot, uri.LocalPath); var keySegment = odataPath.Segments.OfType
().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的实体关系

 

/// /// 删除Product与Supplier的关系/// /// Product主键/// Product的导航属性/// Suppliers(1)的所在地址/// 
[HttpDelete]public async Task
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
GetProducts([FromODataUri] int key) { return db.Suppliers.Where(m => m.Id.Equals(key)).SelectMany(m => m.Products); } [EnableQuery] public IQueryable
Get() { return db.Suppliers; } [EnableQuery] public SingleResult
Get([FromODataUri] int key) { IQueryable
result = db.Suppliers.Where(s => s.Id == key); return SingleResult.Create(result); } ///
/// 删除某个Supplier与某个Product之间的关系 /// DELETE http://host/Suppliers(1)/Products/$ref?$id=http://host/Products(1) /// ///
Supplier的主键 ///
Product的主键字符串 ///
Supplier的导航属性 ///
[HttpDelete] public async Task
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); }}

 

转载于:https://www.cnblogs.com/darrenji/p/4926334.html

你可能感兴趣的文章
hdu1255扫描线计算覆盖两次面积
查看>>
hdu1565 用搜索代替枚举找可能状态或者轮廓线解(较优),参考poj2411
查看>>
bzoj3224 splay板子
查看>>
程序存储问题
查看>>
Mac版OBS设置详解
查看>>
优雅地书写回调——Promise
查看>>
android主流开源库
查看>>
AX 2009 Grid控件下多选行
查看>>
PHP的配置
查看>>
Struts框架----进度1
查看>>
Round B APAC Test 2017
查看>>
MySQL 字符编码问题详细解释
查看>>
Ubuntu下面安装eclipse for c++
查看>>
让IE浏览器支持CSS3圆角属性的方法
查看>>
Windows 2003全面优化
查看>>
URAL 1002 Phone Numbers(KMP+最短路orDP)
查看>>
web_day4_css_宽度
查看>>
electron入门心得
查看>>
格而知之2:UIView的autoresizingMask属性探究
查看>>
我的Hook学习笔记
查看>>