LINQ To Flickr, 另一种 IQueryable 实现






4.80/5 (28投票s)
本文重点介绍如何使用 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
接口,该接口有两个方法,CreateQuery
、Execute
,因为它实现了 IEnumerable<T>
,你必须实现 GetEnumerator()
,并且 IEnumerable
和 IQueryable
也在 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
这篇文章最初发布在 我的博客 上。