Welcome to SpellCoder Sign in | Join | Help

LINQ To Flickr made by Bashmohandes

I had some free time at last, so I implemented a LINQ extension to Flickr, so you can query for photos by tags, creation date, user id or title, the implementation was really easy because I didn't have to use the Flickr API's directly as I found a good open source library for this called FlickrNet which I used as my infrastructure.
So this is a sample of a query
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);

in the last example I retrieved all the photos tagged with "silverkey".
Note, you will need to create Flickr API AppKey, it is free just visit this page and register
http://www.flickr.com/services/api/keys/

The point is implementing the IQueryable<T> interface so you can plug your logic in Linq automatically, so lets see the PhotoQuery class
public class PhotoQuery : IQueryable<FlickrPhoto>

This is the signature of the PhotoQuery class, it implements the IQueryable interface, which has 2 methods, CreateQuery, Execute, and because it implements IEnumerable<T> so you have to implement GetEnumerator(), and also IEnumerable and IQueryable are implemented implicitly in the 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<S> CreateQuery<S>(Expression expression)
{
this._expression = expression;
return (IQueryable<S>)this;
}

public S Execute<S>(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 }
so as you can we didn't implement all the functions we just implemented IQueryable<T>.CreateQuery , IEnumerable.GetEnumerator.
and basically the CreateQuery method just assigns the passed expression parameter to the member _expression and returns the this cased as IQueryable.
The parsing logic is in the IEnumerable.GetEnumerator, currently because this is just a prototype I just implement Where method.
The ParseExpression method which contains the parsing logic
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);
}
The important part is in the MethodCall function which parses expressions of type MethodCallExpression, currently it handles the MemberAccess which is assigning values to properties, then it makes a switch on all the properties which can be used as a query condition like the Title, DateAdded, Tags, UserId, .. etc, and saves the value passed in the query in member variables declared in the PhotoQuery class itself.

The last part which is executing this query is implemented in this function
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();
}
this function is called at the end of the IEnumerable<T>.GetEnumerator() after parsing the query expression, here is where I use the FlickrNet library to search for photos from Flickr.
using the PhotoSearchOptions object I assign the parsed query values stored in the member variables _tags, _title, _userId ... etc, and then perform the search.

The Linq To Flickr code is attached with this post
Have fun.
kick it on DotNetKicks.com
Published Sunday, April 08, 2007 11:48 AM by Mohammed Hossam
Filed Under: ,
Attachment(s): http://bashmohandes.googlepages.com/LinqToFlickr.zip

Comments

# re: LINQ To Flickr made by Bashmohandes

Sunday, April 08, 2007 1:30 PM by Ahmed Essam
Peace be upon you,
how are you mohamed? trully it is amazing, this ideas is amazing, it give u more space to think and do better things,I like ur way :), it is really nice and simple and applicable, u prove everything u do by amazing sample, thanks for your work, but we still need more :D as usual we are information greeds :D, so we are waiting for more, really thanks for you effort and time, and waiting for more.

# re: LINQ To Flickr made by Bashmohandes

Tuesday, April 10, 2007 8:55 AM by Adel
Thanks for the article, down the road i'm expecting to find LINQ to anything..

Could you post that to CodeProject will be more accessible.. just idea.

# re: LINQ To Flickr made by Bashmohandes

Tuesday, April 10, 2007 11:51 AM by Mohammed Hossam
@Adel
I thought of writing this to Code Project, but I don't know why every time I go to codeproject to add a new article I feel bad

# A typo

Tuesday, April 17, 2007 2:38 AM by Soum
var f = from i in new PhoroQuery("SampleAppKey")        where i.Tags == "silverkey"
       select i;

Should be PhotoQuery

# re: LINQ To Flickr made by Bashmohandes

Tuesday, April 17, 2007 4:43 AM by Mohammed Hossam
@Soum
Thank you the typo is corrected.

# re: LINQ To Flickr made by Bashmohandes

Tuesday, October 23, 2007 6:55 AM by Pavan Kulkarni
Amazing stuff!!!

# re: LINQ To Flickr made by Bashmohandes

Monday, December 03, 2007 11:57 PM by Scott Brown
I like it!  Check out my Linq to Google:
http://www.codeplex.com/glinq

I just posted it on codeplex.  It will allow arbitrary queries against Google Base.  You should take a look at how I am parsing the expression tree.

# LINQ to Everything

Monday, April 14, 2008 5:54 PM by Viden.Info
LINQ to Everything
Anonymous comments are disabled