首页 > 代码库 > WebApi学习笔记09:OData中的实体关系

WebApi学习笔记09:OData中的实体关系

1.概述

本例是在学习系列07介绍的项目基础上进行演练……

2.添加实体

在Models文件夹下,添加Supplier.cs类,代码:

using System.Collections.Generic;namespace ProductService.Models{    public class Supplier    {        public int Id { get; set; }        public string Name { get; set; }        public ICollection<Product> Products { get; set; }    }}

修改Models\Product.cs,代码(添加外键和导航属性):

using System.ComponentModel.DataAnnotations.Schema;namespace ProductService.Models{    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; }    }}

修改Models\EFContext.cs,代码(添加EF实体集):

using System.Data.Entity;namespace ProductService.Models{    public class EFContext : DbContext    {        public EFContext() : base("name=ProductContext") { }        public DbSet<Product> Products { get; set; }        public DbSet<Supplier> Suppliers { get; set; }    }}

修改App_Start\WebApiConfig.cs,代码(添加实体数据模型):

using ProductService.Models;using System.Web.Http;using System.Web.OData.Builder;using System.Web.OData.Extensions;namespace ProductService{    public static class WebApiConfig    {        public static void Register(HttpConfiguration config)        {            // Web API 配置和服务            //创建实体数据模型 (EDM)            ODataModelBuilder builder = new ODataConventionModelBuilder();            builder.EntitySet<Product>("Products");            builder.EntitySet<Supplier>("Suppliers");            //添加路由            config.MapODataServiceRoute(routeName: "ODataRoute", routePrefix: null, model: builder.GetEdmModel());            // Web API 路由            //config.MapHttpAttributeRoutes();            //config.Routes.MapHttpRoute(            //    name: "DefaultApi",            //    routeTemplate: "api/{controller}/{id}",            //    defaults: new { id = RouteParameter.Optional }            //);        }    }}

3.添加控制器
在Controllers下,添加SuppliersController.cs,其代码(参照ProductsControllers.cs CRUD写法):

using ProductService.Models;using System.Data.Entity;using System.Data.Entity.Infrastructure;using System.Linq;using System.Net;using System.Threading.Tasks;using System.Web.Http;using System.Web.OData;namespace ProductService.Controllers{    public class SuppliersController : ODataController    {        EFContext db = new EFContext();        [EnableQuery]        public IQueryable<Supplier> Get()        {            return db.Suppliers;        }        [EnableQuery]        public SingleResult<Supplier> Get([FromODataUri] int key)        {            IQueryable<Supplier> result = db.Suppliers.Where(p => p.Id == key);            return SingleResult.Create(result);        }        public async Task<IHttpActionResult> Post(Supplier supplier)        {            if (!ModelState.IsValid)            {                return BadRequest(ModelState);            }            db.Suppliers.Add(supplier);            await db.SaveChangesAsync();            return Created(supplier);        }        public async Task<IHttpActionResult> Patch([FromODataUri] int key, Delta<Supplier> supplier)        {            if (!ModelState.IsValid)            {                return BadRequest(ModelState);            }            var entity = await db.Suppliers.FindAsync(key);            if (entity == null)            {                return NotFound();            }            supplier.Patch(entity);            try            {                await db.SaveChangesAsync();            }            catch (DbUpdateConcurrencyException)            {                if (!SupplierExists(key))                {                    return NotFound();                }                else                {                    throw;                }            }            return Updated(entity);        }        public async Task<IHttpActionResult> Put([FromODataUri] int key, Supplier update)        {            if (!ModelState.IsValid)            {                return BadRequest(ModelState);            }            if (key != update.Id)            {                return BadRequest();            }            db.Entry(update).State = EntityState.Modified;            try            {                await db.SaveChangesAsync();            }            catch (DbUpdateConcurrencyException)            {                if (!SupplierExists(key))                {                    return NotFound();                }                else                {                    throw;                }            }            return Updated(update);        }        public async Task<IHttpActionResult> Delete([FromODataUri] int key)        {            var supplier = await db.Suppliers.FindAsync(key);            if (supplier == null)            {                return NotFound();            }            db.Suppliers.Remove(supplier);            await db.SaveChangesAsync();            return StatusCode(HttpStatusCode.NoContent);        }        private bool SupplierExists(int key)        {            return db.Suppliers.Any(p => p.Id == key);        }        protected override void Dispose(bool disposing)        {            db.Dispose();            base.Dispose(disposing);        }    }}

4.修改控制器
打开Controllers\ProductsController.cs,添加下面方法(根据产品获取其供应商):

        //一个产品有一家供应商        //获取产品供应商        // GET /Products(1)/Supplier        [EnableQuery]        public SingleResult<Supplier> GetSupplier([FromODataUri] int key)        {            var result = db.Products.Where(m => m.Id == key).Select(m => m.Supplier);            return SingleResult.Create(result);        }

打开Controllers\SuppliersController.cs,添加下面方法(根据供应商获取其所有产品):

        //导航属性可以返回一个集合。        //获取供应商的产品        // GET /Suppliers(1)/Products        [EnableQuery]        public IQueryable<Product> GetProducts([FromODataUri] int key)        {            return db.Suppliers.Where(m => m.Id.Equals(key)).SelectMany(m => m.Products);        }

新建Commons文件夹,在里面添加Helpers.cs类:

若要查找供应商,我们需要 ID (或键),这是链接参数的一部分,写个帮助方法

using Microsoft.OData.Core;using Microsoft.OData.Core.UriParser;using System;using System.Collections.Generic;using System.Linq;using System.Net.Http;using System.Web.Http.Routing;using System.Web.OData.Extensions;using System.Web.OData.Routing;namespace ProductService.Commons{    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 =http://www.mamicode.com/ ODataUriUtils.ConvertFromUriLiteral(keySegment.Value, ODataVersion.V4);            return (TKey)value;        }    }}

创建实体间添加关系:

打开Controllers\ProductsController.cs,添加下面方法:

(解决产品和其供应商之间的引用的 URI:/Products(1)/Supplier/$ref

        [AcceptVerbs("POST", "PUT")]        public async Task<IHttpActionResult> CreateRef([FromODataUri] int key, string navigationProperty, [FromBody] Uri link)        {            var product = await db.Products.SingleOrDefaultAsync(p => p.Id == key);            if (product == null)            {                return NotFound();            }            switch (navigationProperty)            {                case "Supplier":                    // Note: The code for GetKeyFromUri is shown later in this topic.                    var relatedKey = Helpers.GetKeyFromUri<int>(Request, link);                    var supplier = await db.Suppliers.SingleOrDefaultAsync(f => f.Id == relatedKey);                    if (supplier == null)                    {                        return NotFound();                    }                    product.Supplier = supplier;                    break;                default:                    return StatusCode(HttpStatusCode.NotImplemented);            }            await db.SaveChangesAsync();            return StatusCode(HttpStatusCode.NoContent);        }

NavigationProperty参数指定要设置的关系。(如果在实体上有不止一个导航属性,你可以添加更多的case声明)。

链接参数包含供应商的 URI。Web API 自动解析请求正文来获取此参数的值。

删除实体间关系(DELETE http://host/Products(1)/Supplier/$ref):

打开Controllers\ProductsController.cs,添加下面方法(删除一个产品和供应商之间的关系):

        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);        }

打开Controllers\SuppliersController.cs,添加下面方法

        public async Task<IHttpActionResult> DeleteRef([FromODataUri] int key,        [FromODataUri] string relatedKey, string navigationProperty)        {            var supplier = await db.Suppliers.SingleOrDefaultAsync(p => p.Id == key);            if (supplier == null)            {                return StatusCode(HttpStatusCode.NotFound);            }            switch (navigationProperty)            {                case "Products":                    var productId = Convert.ToInt32(relatedKey);                    var product = await db.Products.SingleOrDefaultAsync(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);        }

 

WebApi学习笔记09:OData中的实体关系