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 = 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);
        }
时间: 2024-11-05 13:39:14

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

《机电传动控制》学习笔记09

<机电传动控制>学习笔记09 胡恒谦 机卓1301 1. 电力电子器件 电力电子器件根据其导通和关断可控性的不同可以分为以下三类. (1)不可控型器件,即导通与关断都不能控制的器件.仅整流二极管是不可控器件. (2)半控型器件,即只能控制其导通,不能控制其关断的器件.普通晶闸管SCR及其派生器件属于半控型器件. (3)全控型器件,即导通与关断都可以控制的器件.GTR.GTO.IGBT等都属于全控型器件. 2. 晶闸管 晶闸管导通条件为:加正向电压且门极有触发电流:其派生器件有:快速晶闸管,双向

C++ GUI Qt4学习笔记09

C++ GUI Qt4学习笔记09 qtc++ 本章介绍Qt中的拖放 拖放是一个应用程序内或者多个应用程序之间传递信息的一种直观的现代操作方式.除了剪贴板提供支持外,通常它还提供数据移动和复制的功能. QMimeData是一个可以提供不同格式数据的类. 9.1使拖放生效 拖放操作有两个动作:拖动和放下.Qt窗口部件可以作为拖动点.放下点或者同时作为拖动点和放下点. 9.2支持自定义拖动类型 9.3剪贴板处理技术 多数应用程序都通过某一种或者几种方式来使用Qt的内置剪贴板处理技术. C++ GUI

Asp.Net Core WebApi学习笔记(四)-- Middleware

Asp.Net Core WebApi学习笔记(四)-- Middleware 本文记录了Asp.Net管道模型和Asp.Net Core的Middleware模型的对比,并在上一篇的基础上增加Middleware功能支持. 在演示Middleware功能之前,先要了解一下Asp.Net管道模型发生了什么样的变化. 第一部分:管道模型 1. Asp.Net管道 在之前的Asp.Net里,主要的管道模型流程如下图所示: 请求进入Asp.Net工作进程后,由进程创建HttpWorkRequest对象

[学习笔记]面向对象开发中的一些思想和原则

摘自<Java与模式>作者阎宏 面向对象的可复用设计的第一块基石:开闭原则(一个软件实体应当对扩展开放,对修改关闭) "开-闭"原则的手段和工具: 1)里氏代换原则:任何基类出现的地方,子类一定可以出现: 2)依赖倒转原则:要依赖于抽象,不要依赖于实现: 3)合成\聚合复用原则:要尽量使用合成\聚合,而不是继承关系达到复用的目的: 4)迪米特法则:一个软件实体应该与尽可能少的其它实体发生相互作用: 5)接口隔离原则:应当为客户端提供尽可能小的单独的接口,而不要提供大的总接口

MVC4 学习笔记 之 URL中存在编译的空格 20%20%

/Config/Edit/QQCC%20%20%20%20%20%20%20 原因是: 通过EF直接添加了空格? NO 是因为你的数据库字段设计问题,因为你当然设计如>:sID nchar(10) 那你一定想输入10个字符,但实际你只输入了必个,所以后面数据自动帮你补空了. 从EF数据库取出数据,在绑定的时候为安全,编码了,所以显示%20,代表一个空格. MVC4 学习笔记 之 URL中存在编译的空格 20%20%,布布扣,bubuko.com

Guava学习笔记:guava中的Preconditions使用

Guava学习笔记:guava中的Preconditions使用 转载:http://outofmemory.cn/java/guava/base/Preconditions google guava的base包中提供的Preconditions类用来方便的做参数的校验,他主要提供如下方法: checkArgument 接受一个boolean类型的参数和一个可选的errorMsg参数,这个方法用来判断参数是否符合某种条件,符合什么条件google guava不关心,在不符合条件时会抛出Illeg

【学习笔记】jQuery中的动画与效果

1.基本效果 匹配元素从左上角开始变浓变大或缩小到左上角变淡变小 ①隐藏元素 除了可以设置匹配元素的display:none外,可以用以下函数 hide(speed,[callback])  返回值:jQuery  参数-speed:三种预订速度之一的字符串String(slow,normal,fast)或表示动画时长的毫秒数Number  callback:在完成动画时执行的函数,每个匹配元素执行一次 slow=600毫秒  normal=400毫秒  fast=200毫秒 以优雅的动画隐藏所

Guava学习笔记:guava中对字符串的操作

Guava学习笔记:guava中对字符串的操作 转载:http://outofmemory.cn/java/guava/base/Strings 在google guava中为字符串操作提供了很大的便利,有老牌的判断字符串是否为空字符串或者为null,用指定字符填充字符串,以及拆分合并字符串,字符串匹配的判断等等. 下面我们逐一了解这些操作: 1. 使用com.google.common.base.Strings类的isNullOrEmpty(input)方法判断字符串是否为空        

Linux学习笔记——vmware plarer中安装ubuntu

1.前言 学习了很长时间ubuntu,在旧笔记中安装过lubuntu,也使用过他人安装好的ubuntu虚拟机(contiki2.6和contiki2.7).熟悉了ubuntu之后,决定自己尝试通过vmware player安装ubuntu. [1]vmware plaryer是免费软件,不存在破解问题.如果用来学习ubuntu完全足够了. [2]建议在虚拟机种学习ubuntu,等完全熟练之后再摆脱windows.ubuntu现在还没有有道笔记,QQ等工具,总感觉网上世界少了点什么. [3]在虚拟