OData開放數據協議是微軟針對Google的GData推出的,旨在推廣Web程序數據庫格式標准化的開放數據協議,微軟將 OData 定義為基於 HTTP、AtomPub 和 JSON 的協議,增強各種網頁應用程序之間的數據兼容性,以提供多種應用、服務和數據商店的信息訪問。並且,微軟已經正式推出了 OData SDK,包含了 .NET、Java、PHP、Palm WebOS 和 iPhone 的支持。其中 .Net OData 客戶端基於 Apache 授權開源。微軟多款產品已經支持 OData 包括 SharePoint Server 2010, Excel 2010, Dynamics 等。
微軟第一代數據交換協議叫ODBC(開放數據庫聯接 Open Database Connectivity),目前仍舊可見於window和linux的原生程序開發之中,其目的是為操作系統下的應用程序之間提供統一的數據交互的 API,是函數式的。之後,微軟推出了第二代:OLE DB,帶來了OOP式樣的交互API,以及跨網絡的數據交互的可能性(通過DCOM),OLE DB 標准的具體實現是一組C++ API 函數,就像ODBC 標准中的ODBC API 一樣,不同的是,OLE DB 的API 是符合COM 標准、基於對象的(ODBC API 則是簡單的C API)。使用OLE DB API,可以編寫能夠訪問符合OLE DB 標准的任何數據源的應用程序,也可以編寫針對某種特定數據存儲的查詢處理程序(Query Processor)和游標引擎(Cursor Engine),因此OLE DB 標准實際上是規定了數據使用者和提供者之間的一種應用層的協議(Application-Level Protocol)。在雲計算時代,web應用已經是主流,程序主要通過HTTP Request來表達需求,通過HTTP Response來獲取結果,ODBC和OLE DB都已無法使用。微軟於是開發了其第三代數據交互協議:OData開放數據協議。
在SOA的世界中,最重要的一個概念就是契約(contract)。在雲計算的世界中,有關通信的最重要的概念也是契約。XML具有強大對數據的描述能力,Atom格式和AtomPub都建立在XML之上,在Google和微軟的推動下,也已經成為標准。但是,Atom/AtomPub和ODBC /OLEDB這樣的真正數據交互協議相比較,還有著根本上的欠缺:缺乏數據類型的具體描述,降低了交互性能。缺乏對數據查詢的控制能力,比如返回特定的數據集合的區間,或者說分頁能力等等。微軟基於EDM模型釋出了:OData,這裡也可以看出Entity Framework對於NHibernate這樣的ORM的工具不同的戰略考慮。
在PDC大會上,微軟宣布了一個代號為 “Dallas”的社區技術預覽(CTP),由Windows Azure 和SQL Azure構建的信息服務,能夠讓開發者與信息工作者在任何平台上使用優質的第三方數據集和內容。“Dallas”也可以通過使用微軟技術自助的商務智能與分析存儲的數據集。Dallas所使用的數據交互協議就是OData。
在微軟的解決方案中,是用WCF來處理所有程序間的通信,針對數據通信,WCF Data Services自然是最好的選擇。首先,WCF Data Services是WCF服務,所以你可以使用所有現有的WCF知識。其次,WCF Data Services已經實現了OData拓撲,於是你可以致力於你的數據格式在你的程序中的表示,而不是AtomPub/JSON這些真正在網絡上傳遞的數據格式。再有,WCF Data Services致力於數據傳輸,而不是數據存儲。你的數據可以存放在任何位置:本地的數據庫,雲端的數據庫,外部的web services,xml文件,等等。無論數據是怎麼來的,你都可以用同樣的方式來發布/使用它們。
下面我們就使用WCF Data Service將服務器的Windows應用程序日志向外發布。我們的應用程序可以把日志直接就寫在Windows的日志裡,然後通過使用WCF Data Service非常容易的就將日志想其他需要的用戶公開。WCF Data Service默認使用的是Entity Framework,使用Entity Framework參看文章WCF Data Service QuickStart,還有一個Reflection Provider,可以支持只讀的數據服務,這個例子就是演示使用Reflection Provider,資料參看MSDN:http://msdn.microsoft.com/en-us/library/dd723653(VS.100).aspx,還可以自定義實現一個Provider,參看文章自定義Data Service Providers。
首先定義一個Windows日志的實體,類似於WCF的DataContract,這裡使用的是EDM的映射:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Data.Services.Common;
namespace ReflectionDataServiceDemo
{
[EntityPropertyMappingAttribute("Source",
SyndicationItemProperty.Title,
SyndicationTextContentKind.Plaintext, true)]
[EntityPropertyMapping("Message",
SyndicationItemProperty.Summary,
SyndicationTextContentKind.Plaintext, true)]
[EntityPropertyMapping("TimeGenerated",
SyndicationItemProperty.Updated,
SyndicationTextContentKind.Plaintext, true)]
[DataServiceKey("EventID")]
public class LogEntry
{
public long EventID
{
get;
set;
}
public string Category
{
get;
set;
}
public string Message
{
get;
set;
}
public DateTime TimeGenerateD
{
get;
set;
}
public string Source
{
get;
set;
}
}
}
上面使用一個新特性Friendly feeds 將數據映射到標准ATOM元素,其中DataServiceKey是唯一一個必須的標記,然後使用Reflection Provider實現一個IQueryable接口的數據源:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Diagnostics;
namespace ReflectionDataServiceDemo
{
public class LogDataSource
{
string source;
public LogDataSource(string source)
{
this.source = source;
}
public LogDataSource()
{
}
public IQueryable<LogEntry> LogEntries
{
get { return GetEntries().AsQueryable().OrderBy(e => e.TimeGenerated); }
}
private IEnumerable<LogEntry> GetEntries()
{
var applicationLog = System.Diagnostics.EventLog.GetEventLogs().Where(e => e.Log == "Application")
.FirstOrDefault();
var entries = new List<LogEntry>();
if (applicationLog != null)
{
foreach (EventLogEntry entry in applicationLog.Entries)
{
if (source == null || entry.Source.Equals(source, StringComparison.InvariantCultureIgnoreCase))
{
entries.Add(new LogEntry
{
Category = entry.Category,
EventID = entry.InstanceId,
Message = entry.Message,
TimeGenerated = entry.TimeGenerated,
Source = entry.Source,
});
}
}
}
return entries.OrderByDescending(e => e.TimeGenerated)
.Take(200);
}
}
}
最後添加一個WCF Data Service,代碼非常的簡單,主要的工作就是將上述數據源通過WCF Data Service發布出去:
using System;
using System.Collections.Generic;
using System.Data.Services;
using System.Data.Services.Common;
using System.Linq;
using System.ServiceModel.Web;
using System.Web;
using System.Configuration;
namespace ReflectionDataServiceDemo
{
public class LogDataService : DataService<LogDataSource >
{
// This method is called only once to initialize service-wide policies.
public static void InitializeService(DataServiceConfiguration config)
{
// TODO: set rules to indicate which entity sets and service operations are visible, updatable, etc.
// Examples:
config.SetEntitySetAccessRule("*", EntitySetRights.AllRead);
config.SetServiceOperationAccessRule("*", ServiceOperationRights.All);
config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V2;
}
protected override LogDataSource CreateDataSource()
{
string source = ConfigurationManager.AppSettings["EventLogSource"];
if (source == null)
{
throw new ApplicationException("The EventLogSource appsetting is missing in the configuration file");
}
return new LogDataSource(source);
}
}
}
我們再來寫個簡單控制台客戶端消費這個Service,通過Visual Studio的添加服務引用生成服務的客戶端代碼,還可以使用一個插件Open Data Protocol Visualizer查看服務返回的OData數據數據,這個工具的獲取和安裝可以參看VS2010的擴展。可以通過服務引用的“View in Diagram”進行查看。
客戶端的代碼也非常簡單;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ClientDemo
{
class Program
{
static void Main(string[] args)
{
LogDataService.LogDataSource logSource = new LogDataService.LogDataSource(new Uri("http://localhost:3399/LogDataService.svc/"));
foreach (var item in logSource.LogEntries)
{
Console.WriteLine(string.Format("來自{0}事件{1},內容{2}", item.Source, item.EventID, item.Message));
}
Console.Read();
}
}
}
本文配套源碼