OData是一個非常靈活的RESTful API,如果要做出強大的查詢API,那麼OData就強烈推薦了。http://www.odata.org/ OData的特點就是可以根據傳入參數動態生成Entity Framework的查詢,最終實現動態的SQL的查詢。但是在項目有時我們並沒有採用Entit ...
OData是一個非常靈活的RESTful API,如果要做出強大的查詢API,那麼OData就強烈推薦了。http://www.odata.org/
OData的特點就是可以根據傳入參數動態生成Entity Framework的查詢,最終實現動態的SQL的查詢。但是在項目有時我們並沒有採用Entity Framework,而是採用的NHibernate,那麼該怎麼用OData呢?
經過一段時間的Google和研究,終於找到了一個好的方案。
在OData API查詢時,用戶前端是url跟參數,但是在伺服器端,我們是接收到的是一個ODataQueryOptions<T>對象,其實我們需要做的就是把這個對象進行解析,生成NHibernate能夠理解的查詢形式,比如HQL。網上找到微軟官方已經寫了這麼個轉換方法,主要是對ODataQueryOptions對象下的Filter和OrderBy進行轉換,另外兩個參數Top和Skip很簡單,就是一個整數。
public static string ToHql(this ODataQueryOptions query,out int top,out int skip){
string queryString = "from " + query.Context.ElementClrType.Name + " $it" + Environment.NewLine;
if (query.Filter != null)
{
// convert $filter to HQL where clause.
string where = ToString(query.Filter);
queryString += where;
}
if(query.OrderBy!=null)
{
// convert $orderby to HQL orderby clause.
string orderBy = ToString(query.OrderBy);
// create a query using the where clause and the orderby clause.
queryString += orderBy;
}
top = query.Top?.Value ?? 0;
skip = query.Skip?.Value ?? 0;
return queryString;
}
ODataQueryOptions轉換為HQL的項目在這裡:
Filter和OrderBy屬性都會被轉換成HQL,然後我們就需要進行NHibernate的查詢了。
public QueryResult<T> FindByPaging(string hql, int top, int skip){
bool paging = top > 0;
var query = Session.CreateQuery(hql);
var querys = Session.CreateMultiQuery();
if (paging)
{
query = query.SetFirstResult(skip).SetMaxResults(top);
}
querys.Add(query);
if (paging)
{
var countQuery = Session.CreateQuery("select count(*) " + hql);
querys.Add(countQuery);
}
var queryResults = querys.List();
var result = new QueryResult<T>();
result.TotalCount = paging
? Convert.ToInt32( ((IList) queryResults[1])[0])
: ((IList) queryResults[0]).Count;
result.ResultSet = ((IList) queryResults[0]).Cast<T>().ToList();
return result;
}
對於一般的分頁查詢來說,我們應該會有兩個查詢,一個是查詢滿足條件的數據總條數,另一個是返回當前頁的數據集。但是似乎OData並不支持返回這樣的數據類型,OData支持的是Entity的List,如果我們重新定義了一個對象QueryResult:
[DataContract]public class QueryResult<T>
{
[DataMember]
public int TotalCount { get; set; }
[DataMember]
public IList<T> ResultSet { get; set; }
public QueryResult()
{ }
public QueryResult(int count, IList<T> list)
{
this.TotalCount = count;
this.ResultSet = list;
}
}
然後在Controller中返回QueryResult,那麼系統就會報406的錯誤。其實系統給我們提供了一個專門分頁返回的對象System.Web.Http.OData.PageResult<T>,我們可以將Service返回的QueryResult封裝成PageResult再返回即可。
PageResult裡面有個NextPage的URI參數,我們可以傳Null。