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