OData – the best way to REST–實例講解ASP.NET WebAPI OData (V4) Service & Client
一、概念介紹
1.1,什么是OData?
還是看OData官網的簡單說明:
An open protocol to allow the creation and consumption of queryable and interoperable RESTful APIs in a simple andstandard way.
這是一個開放的數據查詢和服務協議,目前已經有眾多廠商和平台支持,已經形成了完整的生態鏈,這應該是未來數據查詢的標准,參見官網說明。
OData的意義還在於,它能夠大大簡小SOA架構里面服務的粒度,只需要提供一個OData數據源,而查詢工作交給客戶端去做即可,這將大大減少服務端服務方法定義的數量。
OData的版本現在已經是V4了,之前的很多類庫都是基於V1-V3版本的。現在的V4版本已經很完善了,而且成為了工業標准,所以現在可以放心的在項目中使用了。
1.2,OData on .NET
OData 的前身是WCF Data Service,后來演變成跨平台的數據查詢協議,現在,除了WCF支持OData,ASP.NET WebAPI 已經內置支持OData了,這將獲得一種輕量級的,Rest架構的OData訪問方案,本文將講解如何在VS 2013上搭建一個OData 服務和客戶端程序。
1.3,參考資源
在閱讀本文之前,首先建議你參考下面的OData 學習資源,本文也是根據這里的資源整理而成,之所以要重新整理一次,是因為原文說的並不清楚,給出的示例程序有些小小的問題,而且國內有關OData的文章介紹非常少。
http://www.odata.org/blog/how-to-use-web-api-odata-to-build-an-odata-v4-service-without-entity-framework/
http://www.asp.net/web-api/overview/odata-support-in-aspnet-web-api/odata-v4/create-an-odata-v4-endpoint
http://www.asp.net/web-api/overview/odata-support-in-aspnet-web-api/odata-v4/create-an-odata-v4-client-app
本文的實例程序相關代碼請在此下載。
二、OData WebAPI搭建
2.1,創建項目
新建一個ASP.NET WebAPI 項目,名字是 ODataWebApplication ,如下圖:
注意選擇一個空項目,並購選WebAPI,單擊確定。
2.2,添加Asp.Net OData 支持
打開VS2013的程序包管理器控制台,在下面輸入 Install-Package Microsoft.AspNet.OData 命令,如下圖:
這里會添加很多附屬的程序集文件,下面是一個詳細的清單列表,如果你的符合下面的內容,就表示安裝成功了:

鍵入“get-help NuGet”以查看所有可用的 NuGet 命令。 PM> Install-Package Microsoft.AspNet.OData 正在嘗試解析依賴項“Microsoft.AspNet.WebApi.Client (≥ 5.2.2)”。 正在嘗試解析依賴項“Newtonsoft.Json (≥ 6.0.4)”。 正在嘗試解析依賴項“Microsoft.AspNet.WebApi.Core (≥ 5.2.2 && < 5.3.0)”。 正在嘗試解析依賴項“Microsoft.OData.Core (≥ 6.10.0 && < 7.0.0)”。 正在嘗試解析依賴項“Microsoft.Spatial (= 6.10.0)”。 正在嘗試解析依賴項“Microsoft.OData.Edm (= 6.10.0)”。 正在安裝“Newtonsoft.Json 6.0.8”。 已成功安裝“Newtonsoft.Json 6.0.8”。 正在安裝“Microsoft.AspNet.WebApi.Client 5.2.3”。 您正在從 Microsoft 下載 Microsoft.AspNet.WebApi.Client,有關此程序包的許可協議在 http://www.microsoft.com/web/webpi/eula/net_library_eula_ENU.htm 上提供。請檢查此程序包是否有其他依賴項,這些依賴項可能帶有各自的許可協議。您若使用程序包及依賴項,即構成您接受其許可協議。如果您不接受這些許可協議,請從您的設備中刪除相關組件。 已成功安裝“Microsoft.AspNet.WebApi.Client 5.2.3”。 正在安裝“Microsoft.AspNet.WebApi.Core 5.2.3”。 您正在從 Microsoft 下載 Microsoft.AspNet.WebApi.Core,有關此程序包的許可協議在 http://www.microsoft.com/web/webpi/eula/net_library_eula_ENU.htm 上提供。請檢查此程序包是否有其他依賴項,這些依賴項可能帶有各自的許可協議。您若使用程序包及依賴項,即構成您接受其許可協議。如果您不接受這些許可協議,請從您的設備中刪除相關組件。 已成功安裝“Microsoft.AspNet.WebApi.Core 5.2.3”。 正在安裝“Microsoft.Spatial 6.10.0”。 您正在從 Microsoft Corporation 下載 Microsoft.Spatial,有關此程序包的許可協議在 http://go.microsoft.com/?linkid=9809688 上提供。請檢查此程序包是否有其他依賴項,這些依賴項可能帶有各自的許可協議。您若使用程序包及依賴項,即構成您接受其許可協議。如果您不接受這些許可協議,請從您的設備中刪除相關組件。 已成功安裝“Microsoft.Spatial 6.10.0”。 正在安裝“Microsoft.OData.Edm 6.10.0”。 您正在從 Microsoft Corporation 下載 Microsoft.OData.Edm,有關此程序包的許可協議在 http://go.microsoft.com/?linkid=9809688 上提供。請檢查此程序包是否有其他依賴項,這些依賴項可能帶有各自的許可協議。您若使用程序包及依賴項,即構成您接受其許可協議。如果您不接受這些許可協議,請從您的設備中刪除相關組件。 已成功安裝“Microsoft.OData.Edm 6.10.0”。 正在安裝“Microsoft.OData.Core 6.10.0”。 您正在從 Microsoft Corporation 下載 Microsoft.OData.Core,有關此程序包的許可協議在 http://go.microsoft.com/?linkid=9809688 上提供。請檢查此程序包是否有其他依賴項,這些依賴項可能帶有各自的許可協議。您若使用程序包及依賴項,即構成您接受其許可協議。如果您不接受這些許可協議,請從您的設備中刪除相關組件。 已成功安裝“Microsoft.OData.Core 6.10.0”。 正在安裝“Microsoft.AspNet.OData 5.5.0”。 您正在從 Microsoft 下載 Microsoft.AspNet.OData,有關此程序包的許可協議在 http://www.microsoft.com/web/webpi/eula/net_library_eula_ENU.htm 上提供。請檢查此程序包是否有其他依賴項,這些依賴項可能帶有各自的許可協議。您若使用程序包及依賴項,即構成您接受其許可協議。如果您不接受這些許可協議,請從您的設備中刪除相關組件。 已成功安裝“Microsoft.AspNet.OData 5.5.0”。 正在從 ODataWebApplication 刪除“Microsoft.AspNet.WebApi.Client.zh-Hans 5.0.0”。 已成功將“Microsoft.AspNet.WebApi.Client.zh-Hans 5.0.0”從 ODataWebApplication 中刪除。 正在從 ODataWebApplication 刪除“Microsoft.AspNet.WebApi.Core.zh-Hans 5.0.0”。 已成功將“Microsoft.AspNet.WebApi.Core.zh-Hans 5.0.0”從 ODataWebApplication 中刪除。 正在從 ODataWebApplication 刪除“Microsoft.AspNet.WebApi.Client 5.0.0”。 已成功將“Microsoft.AspNet.WebApi.Client 5.0.0”從 ODataWebApplication 中刪除。 正在從 ODataWebApplication 刪除“Newtonsoft.Json 5.0.6”。 已成功將“Newtonsoft.Json 5.0.6”從 ODataWebApplication 中刪除。 正在將“Newtonsoft.Json 6.0.8”添加到 ODataWebApplication。 已成功將“Newtonsoft.Json 6.0.8”添加到 ODataWebApplication。 正在將“Microsoft.AspNet.WebApi.Client 5.2.3”添加到 ODataWebApplication。 已成功將“Microsoft.AspNet.WebApi.Client 5.2.3”添加到 ODataWebApplication。 正在從 ODataWebApplication 刪除“Microsoft.AspNet.WebApi.Core 5.0.0”。 已成功將“Microsoft.AspNet.WebApi.Core 5.0.0”從 ODataWebApplication 中刪除。 正在將“Microsoft.AspNet.WebApi.Core 5.2.3”添加到 ODataWebApplication。 已成功將“Microsoft.AspNet.WebApi.Core 5.2.3”添加到 ODataWebApplication。 正在將“Microsoft.Spatial 6.10.0”添加到 ODataWebApplication。 已成功將“Microsoft.Spatial 6.10.0”添加到 ODataWebApplication。 正在將“Microsoft.OData.Edm 6.10.0”添加到 ODataWebApplication。 已成功將“Microsoft.OData.Edm 6.10.0”添加到 ODataWebApplication。 正在將“Microsoft.OData.Core 6.10.0”添加到 ODataWebApplication。 已成功將“Microsoft.OData.Core 6.10.0”添加到 ODataWebApplication。 正在將“Microsoft.AspNet.OData 5.5.0”添加到 ODataWebApplication。 已成功將“Microsoft.AspNet.OData 5.5.0”添加到 ODataWebApplication。 正在將“Microsoft.AspNet.WebApi.Client.zh-Hans 5.2.3”添加到 ODataWebApplication。 正在安裝“Microsoft.AspNet.WebApi.Client.zh-Hans 5.2.3”。 您正在從 Microsoft 下載 Microsoft.AspNet.WebApi.Client.zh-Hans,有關此程序包的許可協議在 http://www.microsoft.com/web/webpi/eula/net_library_eula_CHS.htm 上提供。請檢查此程序包是否有其他依賴項,這些依賴項可能帶有各自的許可協議。您若使用程序包及依賴項,即構成您接受其許可協議。如果您不接受這些許可協議,請從您的設備中刪除相關組件。 已成功安裝“Microsoft.AspNet.WebApi.Client.zh-Hans 5.2.3”。 已成功將“Microsoft.AspNet.WebApi.Client.zh-Hans 5.2.3”添加到 ODataWebApplication。 正在將“Microsoft.AspNet.WebApi.Core.zh-Hans 5.2.3”添加到 ODataWebApplication。 正在安裝“Microsoft.AspNet.WebApi.Core.zh-Hans 5.2.3”。 您正在從 Microsoft 下載 Microsoft.AspNet.WebApi.Core.zh-Hans,有關此程序包的許可協議在 http://www.microsoft.com/web/webpi/eula/net_library_eula_CHS.htm 上提供。請檢查此程序包是否有其他依賴項,這些依賴項可能帶有各自的許可協議。您若使用程序包及依賴項,即構成您接受其許可協議。如果您不接受這些許可協議,請從您的設備中刪除相關組件。 已成功安裝“Microsoft.AspNet.WebApi.Core.zh-Hans 5.2.3”。 已成功將“Microsoft.AspNet.WebApi.Core.zh-Hans 5.2.3”添加到 ODataWebApplication。 正在卸載“Microsoft.AspNet.WebApi.Client.zh-Hans 5.0.0”。 已成功卸載“Microsoft.AspNet.WebApi.Client.zh-Hans 5.0.0”。 正在卸載“Microsoft.AspNet.WebApi.Core.zh-Hans 5.0.0”。 已成功卸載“Microsoft.AspNet.WebApi.Core.zh-Hans 5.0.0”。 正在卸載“Microsoft.AspNet.WebApi.Client 5.0.0”。 已成功卸載“Microsoft.AspNet.WebApi.Client 5.0.0”。 正在卸載“Newtonsoft.Json 5.0.6”。 已成功卸載“Newtonsoft.Json 5.0.6”。 正在卸載“Microsoft.AspNet.WebApi.Core 5.0.0”。 已成功卸載“Microsoft.AspNet.WebApi.Core 5.0.0”。 PM>
2.3,添加Model和控制器
按照 http://www.odata.org/blog/how-to-use-web-api-odata-to-build-an-odata-v4-service-without-entity-framework/ 這個鏈接內容的文章,添加Model和控制器,具體過程請參考原文。在本篇文章的實例中,為了更好的重用Model,我將它放到了一個獨立的Demo.Models 項目中。
注意,添加控制器的時候選擇空的 WebAPI 控制器,不要選擇帶OData 的。
下面是添加完整后的項目目錄結構:
2.4,解決程序集沖突
立刻運行這個項目,發現報下面的錯誤:
未能加載文件或程序集“System.Web.Http, Version=5.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35”或它的某一個依賴項。找到的程序集清單定義與程序集引用不匹配。 (異常來自 HRESULT:0x80131040)
此時需要在Web.config 文件中加入下面的配置內容:
<system.web> <compilation debug="true" targetFramework="4.5"/> <httpRuntime targetFramework="4.5"/> </system.web> <runtime> <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> <dependentAssembly> <assemblyIdentity name="System.Web.Http" publicKeyToken="31BF3856AD364E35" culture="neutral"/> <bindingRedirect oldVersion="0.0.0.0-5.2.3.0" newVersion="5.2.3.0"/> </dependentAssembly> <dependentAssembly> <assemblyIdentity name="System.Net.Http.Formatting" publicKeyToken="31BF3856AD364E35" culture="neutral"/> <bindingRedirect oldVersion="0.0.0.0-5.2.3.0" newVersion="5.2.3.0"/> </dependentAssembly> </assemblyBinding> </runtime>
也可以在編譯項目的時候,注意查看“輸出窗口”,單擊“警告”的文字內容,會有下面的提示詢問,回答確定即可自動為你添加上面的內容:
2.5,正確的OData 服務程序
再次運行,程序不報錯了,用谷歌瀏覽器來打開本程序,出現了下面的內容,就表示ASP.NET WebAPI OData V4 已經成功了:
{ "@odata.context":"http://localhost:20491/$metadata#People","value":[ { "ID":"001","Name":"Angel","Description":null },{ "ID":"002","Name":"Clyde","Description":"Contrary to popular belief, Lorem Ipsum is not simply random text." },{ "ID":"003","Name":"Elaine","Description":"It has roots in a piece of classical Latin literature from 45 BC, making Lorems over 2000 years old." } ] }
三、添加OData客戶端
3.1,添加OData 控制台程序
按照下面鏈接文章的內容,新建一個控制台程序:
3.2,為VS添加OData客戶端工具
按照上面鏈接文章的內容,添加此工具,如下圖:
下載好該工具后讓你安裝運行,最后會讓你重啟動VS。
3.3,添加OData客戶端T4程序
重新啟動后,如果看到下面的內容,表示該工具安裝成功了。
該工具運行后,會在項目下面添加一個OData Client T4 文件,添加后,運行該項目,會報下面的錯誤:
錯誤 1 正在運行轉換: System.ArgumentException: The value "" is not a valid MetadataDocumentUri because is it not a valid absolute Uri. The MetadataDocumentUri must be set to an absolute Uri referencing the $metadata endpoint of an OData service. 在 Microsoft.VisualStudio.TextTemplating0CFD4DFE54AE767D51F4D19A4964CA0B33E6E87CF53FFF790646D6E3F14916C4544A62E4C781ABB55BC19EE5225FDBA7E28D9767E56040F8F9A9EF5278D6B7CA.GeneratedTextTransformation.set_MetadataDocumentUri(String value) 位置 c:\Users\dth\Documents\Visual Studio 2013\Projects\ODataWebApplication\Demo.ConsoleClient\ODataClient1.ttinclude:行號 125 在 Microsoft.VisualStudio.TextTemplating0CFD4DFE54AE767D51F4D19A4964CA0B33E6E87CF53FFF790646D6E3F14916C4544A62E4C781ABB55BC19EE5225FDBA7E28D9767E56040F8F9A9EF5278D6B7CA.GeneratedTextTransformation.ApplyParametersFromConfigurationClass() 位置 c:\Users\dth\Documents\Visual Studio 2013\Projects\ODataWebApplication\Demo.ConsoleClient\ODataClient1.ttinclude:行號 313 在 Microsoft.VisualStudio.TextTemplating0CFD4DFE54AE767D51F4D19A4964CA0B33E6E87CF53FFF790646D6E3F14916C4544A62E4C781ABB55BC19EE5225FDBA7E28D9767E56040F8F9A9EF5278D6B7CA.GeneratedTextTransformation.TransformText() 位置 c:\Users\dth\Documents\Visual Studio 2013\Projects\ODataWebApplication\Demo.ConsoleClient\ODataClient1.ttinclude:行號 58 c:\Users\dth\Documents\Visual Studio 2013\Projects\ODataWebApplication\Demo.ConsoleClient\ODataClient1.ttinclude 125 1 Demo.ConsoleClient
3.4,配置OData Client T4 信息
根據錯誤信息,找到T4文件錯誤的位置,將前面的OData WebAPI項目的地址,寫在文件里面,如下所示:
// The URI of the metadata document. The value must be set to a valid service document URI or a local file path // eg : "http://services.odata.org/V4/OData/OData.svc/", "File:///C:/Odata.edmx", or @"C:\Odata.edmx" // ### Notice ### If the OData service requires authentication for accessing the metadata document, the value of // MetadataDocumentUri has to be set to a local file path, or the client code generation process will fail. public const string MetadataDocumentUri = "http://localhost:20491/";
3.5,生成OData Client 代理類
此時再次運行該T4文件,我們發現OData Client 代理類文件生成了,內容很多,這里就不貼了。
3.6,使用OData Client 代理類
添加下面的代碼,調用OData Client 代理類並運行:
class Program { static void Main(string[] args) { // TODO: Replace with your local URI. string serviceUri = "http://localhost:20491/"; var container = new DefaultContainer(new Uri(serviceUri)); foreach (var p in container.People) { Console.WriteLine("{0} {1} {2}", p.ID, p.Name, p.Description); } Console.Read(); } }
如果看到下面的運行結果,表示OData Client 程序成功了:
至此,OData WebAPI Serivce & Client 的工作就全部完成了。
四、不使用OData客戶端工具訪問OData 服務
4.1,封裝OData Client 類庫
經過前面的過程我們看到,依托於OData 客戶端工具生成OData 代理類的過程還是比較麻煩的,當然好處也有,但缺點就是沒有手工操控的那么靈活自由。
仔細研究下前面的代理類,我們發現這里關鍵依賴於 Microsoft.OData.Client 程序集的DataServiceContext 對象,將代理類進行抽取封裝就可以完成我們手工的代理類了。
創建一個類庫項目,新建一個 ODataV4ContextBase.cs 文件,
接着為該項目添加Nuget 依賴的包:
Install-Package Microsoft.OData.Client
然后在項目下增加了一個文件 packages.config,里面有如下內容:
<?xml version="1.0" encoding="utf-8"?> <packages> <package id="Microsoft.OData.Client" version="6.11.0" targetFramework="net40" /> <package id="Microsoft.OData.Core" version="6.11.0" targetFramework="net40" /> <package id="Microsoft.OData.Edm" version="6.11.0" targetFramework="net40" /> <package id="Microsoft.Spatial" version="6.11.0" targetFramework="net40" /> </packages>
然后,編寫ODataV4ContextBase 類的具體內容:
using Microsoft.OData.Client; using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace PWMIS.OData.Client { /// <summary> /// OData V4 Version ASP.NET WebAPI OData RestFull Client Context /// <remarks>v1.0 2015.4.1 http://www.pwmis.com/sqlmap </remarks> /// </summary> public class ODataV4ContextBase : DataServiceContext { /// <summary> /// V4 OData Init /// </summary> /// <param name="serviceRoot">V4 OData ASP.NET WebAPI url base</param> public ODataV4ContextBase(string serviceRoot) : base(new System.Uri( serviceRoot), ODataProtocolVersion.V4) { if (!serviceRoot.EndsWith("/")) serviceRoot = serviceRoot + "/"; GeneratedEdmModel gem = new GeneratedEdmModel(serviceRoot); this.Format.LoadServiceModel = gem.GetEdmModel; this.Format.UseJson(); } public IQueryable<T> CreateNewQuery<T>(string name) where T : class { return base.CreateQuery<T>(name); } class GeneratedEdmModel { private string ServiceRootUrl; public GeneratedEdmModel(string serviceRootUrl) { this.ServiceRootUrl = serviceRootUrl; } public Microsoft.OData.Edm.IEdmModel GetEdmModel() { string metadataUrl = ServiceRootUrl + "$metadata"; return LoadModelFromUrl(metadataUrl); } private Microsoft.OData.Edm.IEdmModel LoadModelFromUrl(string metadataUrl) { System.Xml.XmlReader reader = CreateXmlReaderFromUrl(metadataUrl); try { return Microsoft.OData.Edm.Csdl.EdmxReader.Parse(reader); } finally { ((System.IDisposable)(reader)).Dispose(); } } private static System.Xml.XmlReader CreateXmlReaderFromUrl(string inputUri) { return System.Xml.XmlReader.Create(inputUri); } } } }
4.2 編寫ODataClient客戶端
在解決方案里面添加一個WinForm項目,在項目里面添加一個ODataContainer.cs 文件,內容如下:
class ODataContainer : PWMIS.OData.Client.ODataV4ContextBase { public ODataContainer(string serviceRoot):base(serviceRoot) { } public IQueryable<Person> People { get { return base.CreateNewQuery<Person>("People"); } } }
非常的簡單,這里只是添加了一個屬性 People。
然后,在窗體代碼中調用:
private void button1_Click(object sender, EventArgs e) { string serviceUri = "http://localhost:20491/"; var container = new ODataContainer(serviceUri); var query = container.People.Where(p => p.Description!=null); this.dataGridView1.DataSource = query.ToList(); }
最后運行該程序,出現下面的界面,就表示成功了:
至此,一個不依賴於EF的全內存的OData 應用程序就完全做好了,更多OData的研究,請大家一起來做吧。
感謝支持 PDF.NET SOD框架,相關代碼請在此下載。