1. 程式人生 > 其它 >OData – the best way to REST–例項講解ASP.NET WebAPI OData (V4) Service & Client

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 and standard 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 控制檯程式

按照下面連結文章的內容,新建一個控制檯程式:

http://www.asp.net/web-api/overview/odata-support-in-aspnet-web-api/odata-v4/create-an-odata-v4-client-app

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:UsersdthDocumentsVisual Studio 2013ProjectsODataWebApplicationDemo.ConsoleClientODataClient1.ttinclude:行號 125 
   在 Microsoft.VisualStudio.TextTemplating0CFD4DFE54AE767D51F4D19A4964CA0B33E6E87CF53FFF790646D6E3F14916C4544A62E4C781ABB55BC19EE5225FDBA7E28D9767E56040F8F9A9EF5278D6B7CA.GeneratedTextTransformation.ApplyParametersFromConfigurationClass() 位置 c:UsersdthDocumentsVisual Studio 2013ProjectsODataWebApplicationDemo.ConsoleClientODataClient1.ttinclude:行號 313 
   在 Microsoft.VisualStudio.TextTemplating0CFD4DFE54AE767D51F4D19A4964CA0B33E6E87CF53FFF790646D6E3F14916C4544A62E4C781ABB55BC19EE5225FDBA7E28D9767E56040F8F9A9EF5278D6B7CA.GeneratedTextTransformation.TransformText() 位置 c:UsersdthDocumentsVisual Studio 2013ProjectsODataWebApplicationDemo.ConsoleClientODataClient1.ttinclude:行號 58    c:UsersdthDocumentsVisual Studio 2013ProjectsODataWebApplicationDemo.ConsoleClientODataClient1.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框架,相關程式碼請在此下載。

注:本文說的 ODataV4ContextBase OData客戶端基類程式已經整合到了PDF.NET框架中,在上面的下載裡面即可看到。