• 由浅入深表达式树(完结篇)重磅打造 Linq To 博客园


      一个多月之后,由浅入深表达式系列的最后一篇终于要问世了。想对所有关注的朋友说声:“对不起,我来晚了!” 希望最后一篇的内容对得起这一个月时间的等待。在学习完表达式树的创建和遍历之后,我们要利用它的特性来写一个我们自己的Linq Provider。人家都有Linq to Amazon为什么我们不能有Linq to cnblogs呢?今天我们就来一步一步的打造自己的Linq Provider,文章未尾已附上源码下载地址。如果对于表达式树的创建和遍历还是熟悉的话,建议先看前面两篇:

      创建表达式树
          http://www.cnblogs.com/jesse2013/p/expressiontree-part1.html

      遍历表达式树
        http://www.cnblogs.com/jesse2013/p/expressiontree-part2.html

      更新:之前没有描述清楚本篇博客的意图,导致很多朋友的误解表示抱歉。本系列重在理解表达式目录树,以及Linq Provider。最后一篇是Linq Provider的实现,之所有会写这么多的代码去做一件简单的事(拉取博客园首页文章列表)完全是为了有一个生动的例子去展示如何实现自己的Linq Provider。和我们项目中的三层架构,或者直接序列化到本地是没有可比性的。

      当然,表达式目录树以及Linq Provider的强大也远非这个小小的Demo能体现得了的,如果你真正知道Linq Provider和表达式树目录树是什么,用来干什么的,也许你就能明白本篇博客的意图了。如果不了解的,建议读完前面两篇之后再做评论。因为你在自己不理解的情况下就直接去评论其它的领域,你就失去了一个了解它的机会。:)

    目录

    实现目标

      我们实现的目标就像Linq to SQL一样,可以用Linq查询语句来查询数据,我们这里面的数据用到了博客园官方的Service去查询到最新的发布到首页的博客信息。看下面的代码:

    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

      我们如何在博客园公开Service的基础上加一层实现条件查询呢?主要思路是这样的:

    • 为文章建立实体类(Post)
    • 将博客园Service返回的数据解析成Post的集合,我们可以加上自己的缓存机制,可以采用1分钟才到博客园取一次数据
    • 把我们上面创建的post集合当作数据库,建立查询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参数的定义:

      

    利用JsonResult 返回json数据来创建我们的Service

      作为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是可以直接在浏览器里面看数据的)。但是我没有做任何安全性的措施,希望大侠高抬贵手,别把网站整挂了就行。

    认识IQueryable和IQueryProvider接口

       有了上面的Service之后,我们要做的事情就简单多了,但是在我们真正开始动手写自己的Linq Provider之前,先来看看IQueryable和IQueryProvider这两个重要的接口。

    IQueryable

      IQueryable本身并没有包含多少的东西,它只有三个属性:

      

    • ElementType 代表当然这个Query所对应的类型
    • Expression 包含了我们当然Query所执行的所有查询或者是其它的操作
    • IQueryProvider则是负责处理上面的Expression的实现

      更为重要的是,在IQueryable这个接口之上,.net为我们提供了很多的扩展方法:

      

      我们平常用到的Where,Select,Max,Any都包括在其中,具体的方法大家可以到System.Linq.Queryable这个静态类下去看。大家注意一下,传给Where方法的正是我们现在学习的Expression。

        在另外一个很重要的接口IEnumrable下,也有着同样的扩展方法:

      

      这些扩展方法来自System.Linq.Enumrable这个静态类下。我们可以看到两组扩展方法的不同之处在于IQueryable下传入的Expression类型,而IEnumrable下传入的是委托。这样做的用意是什么呢?您请接着往下看。

    IQueryProvider

      我们上面讲到了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的思路。

    Linq to cnblogs的实现

      有了前面的数据准备和一些实现的大致思路以后,我们就可以着手开始实现我们的CnblogsQueryProvider了。我们的思路大致是这样的:

    1. 实现自己的ExpressionVisitor类去访问表达式目录数,将其翻译成可以访问Service的Url
    2. 调用WebRequest去访问这个Url
    3. 将上面返回的Response解析成我们要的对象

    实现PostExpressionVisitor

      关于表达式树的访问,我们在第二篇中已经有了比较详细的介绍。如果对于表达式树的遍历不清楚的,可以去第二篇《遍历表达式》中查阅。在这里,我们创建一个我们自己的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 }
    View Code

     实现CnblogsQueryProvider

      有了上面的访问类之后,我们的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 }
    View Code

      我们里面只覆盖了基类的两个方法,GetQueryText和Execute。

    • GetQueryText根据访问类得到的SearchCriteria去和成访问Service的Url
    • Execute访问则负责去请求这个Url拿到数据返回即可

    PostHelper请求数据

      我们这里面有一个帮助类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 }
    View Code

      因为我们的Service是返回的Json数据,所以这里,我们借助了Json.Net将其转成我们所要的List<Post>的数据。

      就是这么简单,我们的Linq to cnblogs就大功告成了。

        点击这里下载源码:http://pan.baidu.com/s/1gd85l1T 

  • 相关阅读:
    windows10更新导致中文乱码
    优化国际网站从一分钟到4~6秒
    修改elementUI组件自带的提示文字并支持国际化
    Python钉钉报警及Zabbix集成钉钉报警
    Go热门开源项目大全
    CentOS7基于ss5搭建Socks5代理服务器
    sass map !default 属性覆盖
    Linux下mv命令高级用法
    设置与查看Linux系统中的环境变量
    Linux下more命令高级用法
  • 原文地址:https://www.cnblogs.com/jesse2013/p/expressiontree-Linq-to-cnblogs.html
Copyright © 2020-2023  润新知