65.9K
CodeProject 正在变化。 阅读更多。
Home

LINQ To Flickr, 另一种 IQueryable 实现

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.80/5 (28投票s)

2007 年 10 月 23 日

CPOL

2分钟阅读

viewsIcon

58758

downloadIcon

311

本文重点介绍如何使用 IQueryable 的实现思路将强大的 LINQ 技术扩展到其他领域。

引言

我终于有了一些空闲时间,所以我实现了一个 LINQ 扩展到 Flickr,因此您可以通过标签、创建日期、用户 ID 或标题查询照片。

这个实现非常简单,因为我不需要直接使用 Flickr API,因为我找到了一个很好的开源库 FlickrNet,我将其用作我的基础设施。 这是一个查询示例

var f = from i in new PhotoQuery("SampleAppKey")
        where i.Tags == "silverkey"
        select i;
foreach(var x in f)
    Console.WriteLine("Title <{0}>, Url {1}", x.Title, x.Url);

在最后一个例子中,我检索了所有带有“silverkey”标签的照片。
请注意,您需要创建 Flickr API AppKey,它是免费的。 只需访问 此页面 并注册。

如何实现这个?

关键是实现 IQueryable<T> 接口,这样你就可以自动将你的逻辑插入到 LINQ 中。 让我们看看 PhotoQuery

public class PhotoQuery : IQueryable<FlickrPhoto>

这是 PhotoQuery 类的签名。 它实现了 IQueryable 接口,该接口有两个方法,CreateQueryExecute,因为它实现了 IEnumerable<T>,你必须实现 GetEnumerator(),并且 IEnumerableIQueryable 也在 IQueryable<T> 中隐式实现。

public class PhotoQuery : IQueryable<FlickrPhoto>
{
    string AppKey;
    Expression _expression;
    string _tags;
    string _title;
    string _photoId;
    DateTime? _dateAdded;
    string _userId;

    public PhotoQuery(string AppKey)
    {
        this.AppKey = AppKey;
    }
    #region IQueryable<FlickrPhoto> Members

    public IQueryable<T> CreateQuery<T>(Expression expression)
    {
        this._expression = expression;
        return (IQueryable<T>)this;
    }

    public T Execute<T>(Expression expression)
    {
        throw new NotImplementedException();
    }

    #endregion

    #region IEnumerable<FlickrPhoto> Members

    IEnumerator<FlickrPhoto> IEnumerable<FlickrPhoto>.GetEnumerator()
    {
        return (IEnumerator<FlickrPhoto>)((IEnumerable)this).GetEnumerator();
    }

    #endregion

    #region IQueryable Members

    public IQueryable CreateQuery(Expression expression)
    {
        return CreateQuery<FlickrPhoto>(expression);
    }

    public Type ElementType
    {
        get { return typeof(FlickrPhoto); }
    }

    public object Execute(Expression expression)
    {
        throw new NotImplementedException();
    }

    public Expression Expression
    {
        get { return System.Expressions.Expression.Constant(this); }
    }

    #endregion

    #region IEnumerable
    IEnumerator IEnumerable.GetEnumerator()
    {
        MethodCallExpression methodCall = _expression as MethodCallExpression;
        if ((methodCall == null) || (methodCall.Method.Name != "Where"))
            throw new NotImplementedException();
        foreach (var param in methodCall.Parameters)
        {
            ParseExpression(param);
        }
        return QueryPhotoBLOCKED EXPRESSION;
    }
    #endregion
}

正如你所看到的,我们没有实现所有的函数。 我们只实现了 IQueryable<T>.CreateQuery, IEnumerable.GetEnumerator。 基本上,CreateQuery 方法只是将传递的表达式参数分配给 MemberExpression 并将其作为 IQueryable 返回。 解析逻辑在 IEnumerable.GetEnumerator 中,目前因为这只是一个原型,我实现在哪里 ParseExpression 方法包含解析逻辑

private void ParseExpression(Expression param)
{
    switch (param.NodeType)
    {
        case ExpressionType.AndAlso:
            AndAlso(param as BinaryExpression);
            break;
        case ExpressionType.Lambda:
            ParseExpression(((LambdaExpression)param).Body);
            break;
        case ExpressionType.MethodCall:
            MethodCall(param as MethodCallExpression);
            break;
        default:
            break;
    }
}

private void MethodCall(MethodCallExpression methodCallExpression)
{
    switch (methodCallExpression.Method.Name)
    {
        case "op_Equality":
            //Photo.PhotoId == ???
            //Photo.Title == ???
            if (methodCallExpression.Parameters[0].NodeType == 
                ExpressionType.MemberAccess)
            {
                MemberExpression memberExpr = 
                methodCallExpression.Parameters[0] as MemberExpression;
                if (memberExpr.Expression.Type == typeof(FlickrPhoto))
                {
                    if (methodCallExpression.Parameters[1].NodeType == 
                        ExpressionType.Constant)
                    {
                        ConstantExpression constant = 
                    methodCallExpression.Parameters[1] as ConstantExpression;
                        switch (memberExpr.Member.Name)
                        {
                            case "Title":
                                _title = constant.Value.ToString();
                                break;
                            case "Tags":
                                _tags = constant.Value.ToString();
                                break;
                            case "PhotId":
                                _photoId = constant.Value.ToString();
                                break;
                            case "DateAdded":
                                _dateAdded = (DateTime?)constant.Value;
                                break;
                            case "UserId":
                                _userId = constant.Value.ToString();
                                break;
                            default:
                                break;
                        }
                    }
                }
            }
            break;
        default:
            break;
    }
}

private void AndAlso(BinaryExpression binaryExpression)
{
    ParseExpression(binaryExpression.Left);
    ParseExpression(binaryExpression.Right);
}

重要的是 MethodCall 函数,它解析类型为 MethodCallExpression 的表达式。 目前它处理 MemberAccess,它将值分配给属性,然后切换所有可以用作查询条件的属性,如 Title, DateAdded, Tags, UserId 等,并将查询中传递的值保存在 PhotoQuery 类本身中声明的成员变量中。 执行此查询的最后一部分在此函数中实现

private IEnumerator<FlickrPhoto> QueryPhotoBLOCKED EXPRESSION
{
    Flickr flickr = new Flickr(AppKey);
    PhotoSearchOptions options = new PhotoSearchOptions();
    options.Tags = _tags;
    options.Text = _title;
    options.UserId = _userId;
    if (_dateAdded.HasValue)
        options.MinUploadDate = _dateAdded.Value;
    Photos photos = flickr.PhotosSearch(options);
    var photoList = new List<FlickrPhoto>(photos.PhotoCollection.Count);
    foreach (Photo p in photos.PhotoCollection)
    {
        photoList.Add(new FlickrPhoto(p));
    }
    return photoList.GetEnumerator();
}

在解析查询表达式后,此函数在 IEnumerable<T>.GetEnumerator() 的末尾被调用。 在这里我使用 FlickrNet 库从 Flickr 搜索照片。 使用 PhotoSearchOptions 对象,我将解析后的查询值分配给存储在成员变量 _tags_title_userId 等中,然后执行搜索。 LINQ to Flickr 代码附在此帖子上。 玩得开心。

历史

  • 2007 年 10 月 23 日:首次发布到 Code Project

这篇文章最初发布在 我的博客 上。

© . All rights reserved.