OData - Open Data Protocol,是一個設計和使用RESTful API的標準。REST本身只是一個構建web服務的思想和理念,其沒有規定一個統一的標準來限制開發人員該如何設計RESTful API。其實我們實際開發中的確也沒有遵循某個統一的標準去設計WebAPI。因爲大多數場景下,遵循一個統一的標準並不是必要的。但在某些場景下,有這樣一個標準卻能帶來很大的好處。
OData的理想是, 無論哪個組織構建的RESTful API,只要其符合OData標準。其他組織就可以按照OData標準中定義的方式去使用這個API獲取/修改資源。這個可以類比SQL標準之於RDBMS關係。無論什麼關係型數據庫,如果其聲稱支持SQL標準,任何人就可以使用標準SQL查詢語句來查詢數據。
標準化的另一個好處:可以將Odata協議實現到一個通用的類庫中,通過這個類庫去創建和訪問RESTful API可以減少開發人員的工作量。
一、新建一個空的WebApi項目
二、添加包Microsoft.AspNet.OData包
1、工具菜單--NuGet包管理器--程序包管理器控制檯:
2、輸入Install-Package Microsoft.AspNet.OData 命令
三、添加EntityFramework包(如何已經有數據則可以使用自己的數據源,直接跳過)
1、輸入Install-Package EntityFramework 命令
2、創建一個實體對象
設置數據庫連接
public class Product
{
[Key]
public int ID { get; set; }
public String Name { get; set; }
// [IgnoreDataMember] 屬性表示在EDM中不可見
[IgnoreDataMember]
public string Category { get; set; }
}
三、配置OData終結點
1、打開WebApiConfig配置EDM
private static IEdmModel GenerateEdmModel()
{
var builder = new ODataConventionModelBuilder
{
Namespace = "OdataModel",
ContainerName = "DefaultContainer",
};
builder.EntitySet<Product>("Products");
return builder.GetEdmModel();
}
2、添加路由並設置查詢參數
public static void Register(HttpConfiguration config)
{
// Web API 配置和服務
config.MapODataServiceRoute("ProductOData", null, GenerateEdmModel());
//設置查詢參數方式一
//config.Select().OrderBy().Filter().Expand().MaxTop(100).Count();
//設置查詢參數方式二
DefaultQuerySettings query = new DefaultQuerySettings()
{
EnableCount = true,
EnableExpand = true,
EnableFilter = true,
EnableOrderBy = true,
EnableSelect = true,
MaxTop = 100
};
config.SetDefaultQuerySettings(query);
}
四、添加OData控制器
public class ODataAPIContext : DbContext
{
public ODataAPIContext() : base("ODataDB")
{ }
public DbSet<Product> Products { get; set; }
static ODataAPIContext()
{
//關閉數據庫初始化操作
Database.SetInitializer<ODataAPIContext>(null);
}
}
數據庫連接
public class ProductsController : ODataController
{
private ODataAPIContext db = new ODataAPIContext();
// GET Products
//[Queryable]
[EnableQuery]
public IQueryable<Product> GetProducts()
{
return db.Products;
}
// GET Products(5)
//[Queryable]
[EnableQuery]
public SingleResult<Product> GetProduct([FromODataUri] int key)
{
return SingleResult.Create(db.Products.Where(product => product.ID == key));
}
/// <summary>
/// 更新、 修改
/// </summary>
/// <param name="key"></param>
/// <param name="product"></param>
/// <returns></returns>
// PUT Products(5)
public async Task<IHttpActionResult> Put([FromODataUri] int key, Product product)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
if (key != product.ID)
{
return BadRequest();
}
db.Entry(product).State = EntityState.Modified;
try
{
await db.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!ProductExists(key))
{
return NotFound();
}
else
{
throw;
}
}
return Updated(product);
}
/// <summary>
/// 新增
/// </summary>
/// <param name="product"></param>
/// <returns></returns>
// POST Products
public async Task<IHttpActionResult> Post(Product product)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
db.Products.Add(product);
await db.SaveChangesAsync();
return Created(product);
}
/// <summary>
///
/// </summary>
/// <param name="key"></param>
/// <param name="patch"></param>
/// <returns></returns>
// PATCH Products(5)
[AcceptVerbs("PATCH", "MERGE")]
public async Task<IHttpActionResult> Patch([FromODataUri] int key, Delta<Product> patch)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
Product product = await db.Products.FindAsync(key);
if (product == null)
{
return NotFound();
}
patch.Patch(product);
try
{
await db.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!ProductExists(key))
{
return NotFound();
}
else
{
throw;
}
}
return Updated(product);
}
/// <summary>
/// 刪除
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
// DELETE Products(5)
public async Task<IHttpActionResult> Delete([FromODataUri] int key)
{
Product product = await db.Products.FindAsync(key);
if (product == null)
{
return NotFound();
}
db.Products.Remove(product);
await db.SaveChangesAsync();
return StatusCode(HttpStatusCode.NoContent);
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
db.Dispose();
}
base.Dispose(disposing);
}
private bool ProductExists(int key)
{
return db.Products.Count(e => e.ID == key) > 0;
}
}
運行查看
2、查看$metadata
3、查看集合
五、添加OData客戶端
1、工具--擴展和更新--安裝“OData v4 Client Code Generator”
2、添加控制檯程序
3、添加新建項
4、在ODataClient.tt文件中修改MetadataDocumentUri路徑
5、保存或者在文件上面右擊運行自定義工具,生成相應文件
6、執行
class Program
{
static void Main(string[] args)
{
const string serviceUri = "http://localhost:57244/";
var container = new DefaultContainer(new Uri(serviceUri));
foreach (var p in container.Products)
{
Console.WriteLine("{0} {1}", p.ID, p.Name);
}
Console.Read();
}
}