一個多月以後,由淺入深表達式系列的最後一篇終於要問世了。想對全部關注的朋友說聲:「對不起,我來晚了!」 但願最後一篇的內容對得起這一個月時間的等待。在學習完表達式樹的建立和遍歷以後,咱們要利用它的特性來寫一個咱們本身的Linq Provider。人家都有Linq to Amazon爲何咱們不能有Linq to cnblogs呢?今天咱們就來一步一步的打造本身的Linq Provider,文章未尾已附上源碼下載地址。若是對於表達式樹的建立和遍歷仍是熟悉的話,建議先看前面兩篇:html
建立表達式樹
http://www.cnblogs.com/jesse2013/p/expressiontree-part1.html程序員
遍歷表達式樹
http://www.cnblogs.com/jesse2013/p/expressiontree-part2.htmlweb
更新:以前沒有描述清楚本篇博客的意圖,致使不少朋友的誤解表示抱歉。本系列重在理解表達式目錄樹,以及Linq Provider。最後一篇是Linq Provider的實現,之全部會寫這麼多的代碼去作一件簡單的事(拉取博客園首頁文章列表)徹底是爲了有一個生動的例子去展現如何實現本身的Linq Provider。和咱們項目中的三層架構,或者直接序列化到本地是沒有可比性的。
數據庫
固然,表達式目錄樹以及Linq Provider的強大也遠非這個小小的Demo能體現得了的,若是你真正知道Linq Provider和表達式樹目錄樹是什麼,用來幹什麼的,也許你就能明白本篇博客的意圖了。若是不瞭解的,建議讀完前面兩篇以後再作評論。由於你在本身不理解的狀況下就直接去評論其它的領域,你就失去了一個瞭解它的機會。:)express
咱們實現的目標就像Linq to SQL同樣,能夠用Linq查詢語句來查詢數據,咱們這裏面的數據用到了博客園官方的Service去查詢到最新的發佈到首頁的博客信息。看下面的代碼:json
var provider = new CnblogsQueryProvider(); var queryable = new Query<Post>(provider); var query = from p in queryable where p.Diggs >= 10 && p.Comments > 10 && p.Views > 10 && p.Comments < 20 select p; var list = query.ToList();
做爲實時訪問遠程的Service,咱們還應該具體如下幾點要求:瀏覽器
最後實現的結果:緩存
根據博客園公開的API顯示,獲取首頁文章列表很是容易,你們能夠點下面的URL來體檢一把。咱們最後給的參數是100000,固然真實返回確定是沒有那麼多的,咱們是但願把可以取回來的都取回來。安全
http://wcf.open.cnblogs.com/blog/sitehome/recent/100000 架構
點擊完上面的URL以後呢,問題就來了,它只有一個參數。我並不能傳給它查詢條件,好比說根據標題來搜索,或者根據評論數,瀏覽量來過濾。難道個人計劃就此要泡湯了麼,剛開始我很不開心,爲何博客園就不能提供靈活一點的Service呢?可是事實就是這樣,咋是程序員呀,需求擺在這,怎麼着還得實現是不?沒有辦法,我給它封裝了一層。在它的基礎上作了一個本身的Service。
咱們如何在博客園公開Service的基礎上加一層實現條件查詢呢?主要思路是這樣的:
咱們首先要作的就是爲博客園的博客創建實體類。
public class Post { // Id public int Id { get; set; } // 標題 public string Title { get; set; } // 發佈時間 public DateTime Published { get; set; } // 推薦數據 public int Diggs { get; set; } // 訪問人數 public int Views { get; set; } // 評論數據 public int Comments { get; set; } // 做者 public string Author { get; set; } // 博客連接 public string Href { get; set; } // 摘要 public string Summary { get; set; } }
接着,咱們要將博客園返回給咱們的XML數據轉換成咱們要的post集合,因此咱們要用到Linq to XML。
_list = new List<CnblogsLinqProvider.Post>(); var document = XDocument.Load( "http://wcf.open.cnblogs.com/blog/sitehome/recent/100000" ); var elements = document.Root.Elements(); var result = from entry in elements where entry.HasElements == true select new CnblogsLinqProvider.Post { Id = Convert.ToInt32(entry.Elements() .SingleOrDefault(x => x.Name.LocalName == "id").Value), Title = entry.Elements() .SingleOrDefault(x => x.Name.LocalName == "title").Value, Published = Convert.ToDateTime(entry.Elements() .SingleOrDefault(x => x.Name.LocalName == "published").Value), Diggs = Convert.ToInt32(entry.Elements() .SingleOrDefault(x => x.Name.LocalName == "diggs").Value), Views = Convert.ToInt32(entry.Elements() .SingleOrDefault(x => x.Name.LocalName == "views").Value), Comments = Convert.ToInt32(entry.Elements() .SingleOrDefault(x => x.Name.LocalName == "comments").Value), Summary = entry.Elements() .SingleOrDefault(x=>x.Name.LocalName=="summary").Value, Href = entry.Elements() .SingleOrDefault(x => x.Name.LocalName == "link") .Attribute("href").Value, Author = entry.Elements() .SingleOrDefault(x => x.Name.LocalName == "author") .Elements() .SingleOrDefault(x => x.Name.LocalName == "name").Value }; _list.AddRange(result);
而後就到了咱們的查詢了,實際上咱們有了IEnumrable<Post>的數據就能夠直接在本地用Linq去查詢它了。可是這不是咱們想要的,由於咱們上面的步驟是把全部的數據一次性所有下載下來了,而不是根據咱們的需求返回數據。另外咱們這裏面是在博客園Service的基礎上作一層封裝,實現經過Url直接查詢首頁的文章。爲何要經過Url來查詢?由於咱們最後會經過咱們本身的LinqProvider將Linq查詢語句直接翻譯成Url這樣就可以實現遠程的返回數據了。來看看咱們對Url參數的定義:
做爲Service,咱們返回Json或者XML格式的數據都是能夠的。固然實現這個需求的方法有不少種,咱們這裏面有選了一種最簡單方便又比較適合咱們需求方式。不須要WCF Service也不須要Web API,直接用MVC裏面的Action返回JsonResult就能夠了。
[HttpGet] public JsonResult Index(SearchCriteria criteria = null) { var result = PostManager.Posts; if (criteria != null) { if (!string.IsNullOrEmpty(criteria.Title)) result = result.Where( p => p.Title.IndexOf(criteria.Title, StringComparison.OrdinalIgnoreCase) >= 0); if (!string.IsNullOrEmpty(criteria.Author)) result = result.Where(p => p.Author.IndexOf(criteria.Author, StringComparison.OrdinalIgnoreCase) >= 0); if (criteria.Start.HasValue) result = result.Where(p => p.Published >= criteria.Start.Value); if (criteria.End.HasValue) result = result.Where(p => p.Published <= criteria.End.Value); if (criteria.MinComments > 0) result = result.Where(p => p.Comments >= criteria.MinComments); if (criteria.MinDiggs > 0) result = result.Where(p => p.Diggs >= criteria.MinDiggs); if (criteria.MinViews > 0) result = result.Where(p => p.Diggs >= criteria.MinViews); if (criteria.MaxComments > 0) result = result.Where(p => p.Comments <= criteria.MaxComments); if (criteria.MaxDiggs > 0) result = result.Where(p => p.Diggs <= criteria.MaxDiggs); if (criteria.MaxViews > 0) result = result.Where(p => p.Diggs <= criteria.MaxViews); } return Json(result, JsonRequestBehavior.AllowGet); }
利用Action來作這種Service還有一個好處就是咱們不須要一個一個的聲明查詢參數,只須要把全部的參數放到一個model中就能夠了。剩下的事就交給Model Binder吧。
public class SearchCriteria { public string Title { get; set; } public string Author { get; set; } public DateTime? Start { get; set; } public DateTime? End { get; set; } public int MinDiggs { get; set; } public int MaxDiggs { get; set; } public int MinViews { get; set; } public int MaxViews { get; set; } public int MinComments { get; set; } public int MaxComments { get; set; } }
若是你們想更熟悉這個Service的功能,能夠參考上面的參數本身去體驗一下(用IE會直接下載.json的文件,用Chrom是能夠直接在瀏覽器裏面看數據的)。可是我沒有作任何安全性的措施,但願大俠高擡貴手,別把網站整掛了就行。
有了上面的Service以後,咱們要作的事情就簡單多了,可是在咱們真正開始動手寫本身的Linq Provider以前,先來看看IQueryable和IQueryProvider這兩個重要的接口。
IQueryable自己並無包含多少的東西,它只有三個屬性:
更爲重要的是,在IQueryable這個接口之上,.net爲咱們提供了不少的擴展方法:
咱們日常用到的Where,Select,Max,Any都包括在其中,具體的方法你們能夠到System.Linq.Queryable這個靜態類下去看。你們注意一下,傳給Where方法的正是咱們如今學習的Expression。
在另一個很重要的接口IEnumrable下,也有着一樣的擴展方法:
這些擴展方法來自System.Linq.Enumrable這個靜態類下。咱們能夠看到兩組擴展方法的不一樣之處在於IQueryable下傳入的Expression類型,而IEnumrable下傳入的是委託。這樣作的用意是什麼呢?您請接着往下看。
咱們上面講到了Enumrable和Queryable這兩個靜態類下的擴展方法,對於Enumrable下的擴展方法來講他們傳入的是委託,對於委託而言直接執行就能夠了。
public static IEnumerable<T> Where<T>(this IEnumerable<T> list, Func<T, bool> predicate) { var result = new List<T>(); foreach (var element in list) { // 調用委託是驗證這個元素是否符合條件 if(predicate(element)) result.Add(element); } return result; }
上面的代碼給你們做一個參考,相信不難理解,因此Enumrable下的靜態方法都是操做本地數據的。而對於Queryable下的靜態方法而言,他們接收的是表達式,還記得表達式的最大特徵嗎?能夠在運行時去遍歷解釋而後執行,那麼這樣就能夠將表達式轉換成各類其它的方式去獲取數據,偉大的Linq to SQL就是這麼實現的。而這背後的大功臣就是咱們的Linq Provider了,而IQueryProvider就是LinqProvider的接口。
IQueryProvider只有兩個操做,CreateQuery和Execute分別有泛型版本和非泛型版本。 CreatQuery用於構造一個IQueryable<T>的對象,這個類其實沒有任何實現,只是繼承了IQueryable和IEnumrable接口。主要用於計算指定表達式目錄樹所表示的查詢,返回的結果是一個可枚舉的類型。 而Execute會執行指定表達式目錄樹所表示的查詢,返回指定的結果。全部的內幕就在這個Execute方法裏面,拿咱們要進行的Linq to cnblogs方法來舉例,咱們將把傳入的表達式目錄樹翻譯成一個URL就是指向咱們封裝好的Service的URL,經過發起web request到這個URL,拿到response進行解析,最終獲得咱們所要的數據,這就是咱們Linq to cnblogs的思路。
有了前面的數據準備和一些實現的大體思路之後,咱們就能夠着手開始實現咱們的CnblogsQueryProvider了。咱們的思路大體是這樣的:
關於表達式樹的訪問,咱們在第二篇中已經有了比較詳細的介紹。若是對於表達式樹的遍歷不清楚的,能夠去第二篇《遍歷表達式》中查閱。在這裏,咱們建立一個咱們本身的ExpressionVisitor類,去遍歷表達式樹。咱們暫時只須要生成一個SearchCriteria(咱們上面已經定義好了,對於查詢條件建的模)對象便可。
1 public class PostExpressionVisitor 2 { 3 private SearchCriteria _criteria; 4 5 // 入口方法 6 public SearchCriteria ProcessExpression(Expression expression) 7 { 8 _criteria = new SearchCriteria(); 9 VisitExpression(expression); 10 return _criteria; 11 } 12 13 private void VisitExpression(Expression expression) 14 { 15 switch (expression.NodeType) 16 { 17 // 訪問 && 18 case ExpressionType.AndAlso: 19 VisitAndAlso((BinaryExpression)expression); 20 break; 21 // 訪問 等於 22 case ExpressionType.Equal: 23 VisitEqual((BinaryExpression)expression); 24 break; 25 // 訪問 小於和小於等於 26 case ExpressionType.LessThan: 27 case ExpressionType.LessThanOrEqual: 28 VisitLessThanOrEqual((BinaryExpression)expression); 29 break; 30 // 訪問大於和大於等於 31 case ExpressionType.GreaterThan: 32 case ExpressionType.GreaterThanOrEqual: 33 GreaterThanOrEqual((BinaryExpression)expression); 34 break; 35 // 訪問調用方法,主要有於解析Contains方法,咱們的Title會用到 36 case ExpressionType.Call: 37 VisitMethodCall((MethodCallExpression)expression); 38 break; 39 // 訪問Lambda表達式 40 case ExpressionType.Lambda: 41 VisitExpression(((LambdaExpression)expression).Body); 42 break; 43 } 44 } 45 46 // 訪問 && 47 private void VisitAndAlso(BinaryExpression andAlso) 48 { 49 VisitExpression(andAlso.Left); 50 VisitExpression(andAlso.Right); 51 } 52 53 // 訪問 等於 54 private void VisitEqual(BinaryExpression expression) 55 { 56 // 咱們這裏面只處理在Author上的等於操做 57 // Views, Comments, 和 Diggs 咱們都是用的大於等於,或者小於等於 58 if ((expression.Left.NodeType == ExpressionType.MemberAccess) && 59 (((MemberExpression)expression.Left).Member.Name == "Author")) 60 { 61 if (expression.Right.NodeType == ExpressionType.Constant) 62 _criteria.Author = 63 (String)((ConstantExpression)expression.Right).Value; 64 65 else if (expression.Right.NodeType == ExpressionType.MemberAccess) 66 _criteria.Author = 67 (String)GetMemberValue((MemberExpression)expression.Right); 68 69 else 70 throw new NotSupportedException("Expression type not supported for author: " + 71 expression.Right.NodeType.ToString()); 72 } 73 } 74 75 // 訪問大於等於 76 private void GreaterThanOrEqual(BinaryExpression expression) 77 { 78 // 處理 Diggs >= n 推薦人數 79 if ((expression.Left.NodeType == ExpressionType.MemberAccess) && 80 (((MemberExpression)expression.Left).Member.Name == "Diggs")) 81 { 82 if (expression.Right.NodeType == ExpressionType.Constant) 83 _criteria.MinDiggs = 84 (int)((ConstantExpression)expression.Right).Value; 85 86 else if (expression.Right.NodeType == ExpressionType.MemberAccess) 87 _criteria.MinDiggs = 88 (int)GetMemberValue((MemberExpression)expression.Right); 89 90 else 91 throw new NotSupportedException("Expression type not supported for Diggs:" 92 + expression.Right.NodeType.ToString()); 93 } 94 // 處理 Views>= n 訪問人數 95 else if ((expression.Left.NodeType == ExpressionType.MemberAccess) && 96 (((MemberExpression)expression.Left).Member.Name == "Views")) 97 { 98 if (expression.Right.NodeType == ExpressionType.Constant) 99 _criteria.MinViews = 100 (int)((ConstantExpression)expression.Right).Value; 101 102 else if (expression.Right.NodeType == ExpressionType.MemberAccess) 103 _criteria.MinViews = 104 (int)GetMemberValue((MemberExpression)expression.Right); 105 106 else 107 throw new NotSupportedException("Expression type not supported for Views: " 108 + expression.Right.NodeType.ToString()); 109 } 110 // 處理 comments >= n 評論數 111 else if ((expression.Left.NodeType == ExpressionType.MemberAccess) && 112 (((MemberExpression)expression.Left).Member.Name == "Comments")) 113 { 114 if (expression.Right.NodeType == ExpressionType.Constant) 115 _criteria.MinComments = 116 (int)((ConstantExpression)expression.Right).Value; 117 118 else if (expression.Right.NodeType == ExpressionType.MemberAccess) 119 _criteria.MinComments = 120 (int)GetMemberValue((MemberExpression)expression.Right); 121 122 else 123 throw new NotSupportedException("Expression type not supported for Comments: " 124 + expression.Right.NodeType.ToString()); 125 } 126 // 處理 發佈時間>= 127 else if ((expression.Left.NodeType == ExpressionType.MemberAccess) && 128 (((MemberExpression)expression.Left).Member.Name == "Published")) 129 { 130 if (expression.Right.NodeType == ExpressionType.Constant) 131 _criteria.Start = 132 (DateTime)((ConstantExpression)expression.Right).Value; 133 134 else if (expression.Right.NodeType == ExpressionType.MemberAccess) 135 _criteria.Start = 136 (DateTime)GetMemberValue((MemberExpression)expression.Right); 137 138 else 139 throw new NotSupportedException("Expression type not supported for Published: " 140 + expression.Right.NodeType.ToString()); 141 } 142 } 143 144 // 訪問 小於和小於等於 145 private void VisitLessThanOrEqual(BinaryExpression expression) 146 { 147 // 處理 Diggs <= n 推薦人數 148 if ((expression.Left.NodeType == ExpressionType.MemberAccess) && 149 (((MemberExpression)expression.Left).Member.Name == "Diggs")) 150 { 151 if (expression.Right.NodeType == ExpressionType.Constant) 152 _criteria.MaxDiggs = 153 (int)((ConstantExpression)expression.Right).Value; 154 155 else if (expression.Right.NodeType == ExpressionType.MemberAccess) 156 _criteria.MaxDiggs = 157 (int)GetMemberValue((MemberExpression)expression.Right); 158 159 else 160 throw new NotSupportedException("Expression type not supported for Diggs: " 161 + expression.Right.NodeType.ToString()); 162 } 163 // 處理 Views<= n 訪問人數 164 else if ((expression.Left.NodeType == ExpressionType.MemberAccess) && 165 (((MemberExpression)expression.Left).Member.Name == "Views")) 166 { 167 if (expression.Right.NodeType == ExpressionType.Constant) 168 _criteria.MaxViews = 169 (int)((ConstantExpression)expression.Right).Value; 170 171 else if (expression.Right.NodeType == ExpressionType.MemberAccess) 172 _criteria.MaxViews = 173 (int)GetMemberValue((MemberExpression)expression.Right); 174 175 else 176 throw new NotSupportedException("Expression type not supported for Views: " 177 + expression.Right.NodeType.ToString()); 178 } 179 // 處理 comments <= n 評論數 180 else if ((expression.Left.NodeType == ExpressionType.MemberAccess) && 181 (((MemberExpression)expression.Left).Member.Name == "Comments")) 182 { 183 if (expression.Right.NodeType == ExpressionType.Constant) 184 _criteria.MaxComments = 185 (int)((ConstantExpression)expression.Right).Value; 186 187 else if (expression.Right.NodeType == ExpressionType.MemberAccess) 188 _criteria.MaxComments = 189 (int)GetMemberValue((MemberExpression)expression.Right); 190 191 else 192 throw new NotSupportedException("Expression type not supported for Comments: " 193 + expression.Right.NodeType.ToString()); 194 } 195 196 // 處理髮布時間 <= 197 else if ((expression.Left.NodeType == ExpressionType.MemberAccess) && 198 (((MemberExpression)expression.Left).Member.Name == "Published")) 199 { 200 if (expression.Right.NodeType == ExpressionType.Constant) 201 _criteria.End = 202 (DateTime)((ConstantExpression)expression.Right).Value; 203 204 else if (expression.Right.NodeType == ExpressionType.MemberAccess) 205 _criteria.End = 206 (DateTime)GetMemberValue((MemberExpression)expression.Right); 207 208 else 209 throw new NotSupportedException("Expression type not supported for Published: " 210 + expression.Right.NodeType.ToString()); 211 } 212 } 213 214 // 訪問 方法調用 215 private void VisitMethodCall(MethodCallExpression expression) 216 { 217 if ((expression.Method.DeclaringType == typeof(Queryable)) && 218 (expression.Method.Name == "Where")) 219 { 220 VisitExpression(((UnaryExpression)expression.Arguments[1]).Operand); 221 } 222 else if ((expression.Method.DeclaringType == typeof(String)) && 223 (expression.Method.Name == "Contains")) 224 { 225 // Handle Title.Contains("") 226 if (expression.Object.NodeType == ExpressionType.MemberAccess) 227 { 228 MemberExpression memberExpr = (MemberExpression)expression.Object; 229 if (memberExpr.Expression.Type == typeof(Post)) 230 { 231 if (memberExpr.Member.Name == "Title") 232 { 233 Expression argument; 234 argument = expression.Arguments[0]; 235 if (argument.NodeType == ExpressionType.Constant) 236 { 237 _criteria.Title = 238 (String)((ConstantExpression)argument).Value; 239 } 240 else if (argument.NodeType == ExpressionType.MemberAccess) 241 { 242 _criteria.Title = 243 (String)GetMemberValue((MemberExpression)argument); 244 } 245 else 246 { 247 throw new NotSupportedException("Expression type not supported: " 248 + argument.NodeType.ToString()); 249 } 250 } 251 } 252 } 253 } 254 else 255 { 256 throw new NotSupportedException("Method not supported: " 257 + expression.Method.Name); 258 } 259 } 260 261 // 獲取屬性值 262 private Object GetMemberValue(MemberExpression memberExpression) 263 { 264 MemberInfo memberInfo; 265 Object obj; 266 267 if (memberExpression == null) 268 throw new ArgumentNullException("memberExpression"); 269 270 271 if (memberExpression.Expression is ConstantExpression) 272 obj = ((ConstantExpression)memberExpression.Expression).Value; 273 else if (memberExpression.Expression is MemberExpression) 274 obj = GetMemberValue((MemberExpression)memberExpression.Expression); 275 else 276 throw new NotSupportedException("Expression type not supported: " 277 + memberExpression.Expression.GetType().FullName); 278 279 memberInfo = memberExpression.Member; 280 if (memberInfo is PropertyInfo) 281 { 282 PropertyInfo property = (PropertyInfo)memberInfo; 283 return property.GetValue(obj, null); 284 } 285 else if (memberInfo is FieldInfo) 286 { 287 FieldInfo field = (FieldInfo)memberInfo; 288 return field.GetValue(obj); 289 } 290 else 291 { 292 throw new NotSupportedException("MemberInfo type not supported: " 293 + memberInfo.GetType().FullName); 294 } 295 } 296 }
有了上面的訪問類以後,咱們的CnblogsQueryProvider就很是簡單了。
1 public class CnblogsQueryProvider:QueryProvider 2 { 3 public override String GetQueryText(Expression expression) 4 { 5 SearchCriteria criteria; 6 7 // 翻譯查詢條件 8 criteria = new PostExpressionVisitor().ProcessExpression(expression); 9 10 // 生成URL 11 String url = PostHelper.BuildUrl(criteria); 12 13 return url; 14 } 15 16 public override object Execute(Expression expression) 17 { 18 String url = GetQueryText(expression); 19 IEnumerable<Post> results = PostHelper.PerformWebQuery(url); 20 return results; 21 } 22 }
咱們裏面只覆蓋了基類的兩個方法,GetQueryText和Execute。
咱們這裏面有一個幫助類PostHelper,就負責了根據criteria生成Url以及請求Url獲取數據的功能。
1 static internal string BuildUrl(SearchCriteria criteria,string url=null) 2 { 3 if (criteria == null) 4 throw new ArgumentNullException("criteria"); 5 6 var sbUrl = new StringBuilder(url ?? "http://linqtocnblogs.cloudapp.net/"); 7 var sbParameter = new StringBuilder(); 8 9 if (!String.IsNullOrEmpty(criteria.Title)) 10 AppendParameter(sbParameter, "Title", criteria.Title); 11 12 if (!String.IsNullOrEmpty(criteria.Author)) 13 AppendParameter(sbParameter, "Author", criteria.Author); 14 15 if (criteria.Start.HasValue) 16 AppendParameter(sbParameter, "Start", criteria.Start.Value.ToString()); 17 18 if (criteria.End.HasValue) 19 AppendParameter(sbParameter, "End", criteria.End.Value.ToString()); 20 21 if (criteria.MinDiggs > 0) 22 AppendParameter(sbParameter, "MinDiggs", criteria.MinDiggs.ToString()); 23 24 if (criteria.MinViews > 0) 25 AppendParameter(sbParameter, "MinViews", criteria.MinViews.ToString()); 26 27 if (criteria.MinComments> 0) 28 AppendParameter(sbParameter, "MinComments", 29 criteria.MinComments.ToString()); 30 31 if (criteria.MaxDiggs > 0) 32 AppendParameter(sbParameter, "MaxDiggs", criteria.MaxDiggs.ToString()); 33 34 if (criteria.MaxViews > 0) 35 AppendParameter(sbParameter, "MaxViews", criteria.MaxViews.ToString()); 36 37 if (criteria.MaxComments > 0) 38 AppendParameter(sbParameter, "MaxComments", 39 criteria.MaxComments.ToString()); 40 41 if (sbParameter.Length > 0) 42 sbUrl.AppendFormat("?{0}", sbParameter.ToString()); 43 44 return sbUrl.ToString(); 45 } 46 47 static internal void AppendParameter(StringBuilder sb,string name,string value) 48 { 49 if (sb.Length > 0) 50 sb.Append("&"); 51 52 sb.AppendFormat("{0}={1}",name,value); 53 } 54 55 static internal IEnumerable<Post> PerformWebQuery(string url) 56 { 57 var request = WebRequest.Create(url); 58 request.Credentials = CredentialCache.DefaultCredentials; 59 60 var response = (HttpWebResponse)request.GetResponse(); 61 using(var reader= new StreamReader(response.GetResponseStream())) 62 { 63 var body = reader.ReadToEnd(); 64 return JsonConvert.DeserializeObject<List<Post>>(body); 65 } 66 } 67 }
由於咱們的Service是返回的Json數據,因此這裏,咱們藉助了Json.Net將其轉成咱們所要的List<Post>的數據。
就是這麼簡單,咱們的Linq to cnblogs就大功告成了。
點擊這裏下載源碼:http://pan.baidu.com/s/1gd85l1T