OData的应用

Odata 协议&Odata 应用

Posted by YangLong on April 3, 2017

Why use Odata?

开放数据协议(Open Data Protocol,缩写OData)是一种描述如何创建和访问Restful服务的OASIS标准。该标准由微软发起,前三个版本1.0、2.0、3.0都是微软开放标准。第四个版本4.0于2014年3月17日在OASIS投票通过成为开放工业标准 。

参考

odata 4.0 官方网址 http://www.odata.org
odata 4.0 协议标准内容 http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-protocol.html

How use Odata?

C# .Net Framework 服务端示例

备注

Update OData supports two different semantics for updating an entity, PATCH and PUT.

  • PATCH performs a partial update. The client specifies just the properties to update.
  • PUT replaces the entire entity.
    The disadvantage of PUT is that the client must send values for all of the properties in the entity, including values that are not changing. The OData spec states that PATCH is preferred.

OData支持两种不同语义更新实体,包括PATCH和PUT。

  • PATCH执行一个部分更新,客户端只识别要更新的属性。
  • PUT会替换整个实体
Install OData Package
Install-Package Microsoft.AspNet.Odata
Add Model
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; }
    }
}
use Entity Framework

A.Install-Package EntityFramework B.modify config file

<connectionStrings>
    <add name="ProductsContext" connectionString="Data Source=(localdb)\v11.0; 
        Initial Catalog=ProductsContext; Integrated Security=True; MultipleActiveResultSets=True; 
        AttachDbFilename=|DataDirectory|ProductsContext.mdf"
      providerName="System.Data.SqlClient" />
  </connectionStrings>

C.Create DBContex

    using System.Data.Entity;
    namespace ProductService.Models
    {
        public class ProductsContext : DbContext
        {
            public ProductsContext()  : base("name=ProductsContext") { }
            public DbSet<Product> Products { get; set; }
    }
}
confige the odata endpoint
using ProductService.Models;
using System.Web.OData.Builder;
using System.Web.OData.Extensions;
public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        // New code:
        ODataModelBuilder builder = new ODataConventionModelBuilder();
        builder.EntitySet<Product>("Products");
        config.MapODataServiceRoute(
            routeName: "ODataRoute",
            routePrefix: null,
            model: builder.GetEdmModel());
    }
}
Add Odata Controller
namespace ProductService.Controllers
{
    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);
        }
        //Query
       [EnableQuery]
       public IQueryable<Product> Get()
       {
               return db.Products;
       }
      [EnableQuery]
     public SingleResult<Product> Get([FromODataUri] int key)
     {
         IQueryable<Product> result = db.Products.Where(p => p.Id == key);
         return SingleResult.Create(result);
      }
     //Add
     
    public async Task<IHttpActionResult> Post(Product product)
    { 
    if (!ModelState.IsValid) {   return BadRequest(ModelState);  }
    db.Products.Add(product);
    await db.SaveChangesAsync();
    return Created(product);
    }
//Update
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);
}
public async Task<IHttpActionResult> Put([FromODataUri] int key, Product 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 (!ProductExists(key)) { return NotFound(); }
        else  {   throw;  }
    }
    return Updated(update);
}
//Delete
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);
}
}

测试 服务端内容

测试路径
http://localhost:3471/odata
http://localhost:3471/odata/$metadata service metadata document
http://localhost:3471/odata/User
http://localhost:3471/odata/User(guid’9EEBFD09-82E6-4F4F-90A8-E3C27FD128B4’%20)
参考地址

https://docs.microsoft.com/en-us/aspnet/web-api/overview/odata-support-in-aspnet-web-api/odata-v3/creating-an-odata-endpoint

C# .Net Framework 客户端示例

JavaScript 前段示例

引用Javascript
<script src="path/to/o.min.js"></script>    
创建Handler,并查询数据
var oHandler = o('http://services.odata.org/V4/(S(wptr35qf3bz4kb5oatn432ul))/TripPinServiceRW/People');
oHandler.get(function(data) {
    console.log(data); // data of the TripPinService/People endpoint
});