Click here to Skip to main content
Click here to Skip to main content

LINQ To Flickr, Another IQueryable Implementation

By , 23 Oct 2007
 

Introduction

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 APIs directly as I found a good open source library for this called FlickrNet which I used as my infrastructure. 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.

How To Implement This ?

The point is implementing the IQueryable<T> interface so you can plug your logic in LINQ automatically. Let's look at the PhotoQuery class:

public class PhotoQuery : IQueryable<FlickrPhoto>

This is the signature of the PhotoQuery class. It implements the IQueryable interface, which has two methods, CreateQuery, Execute, and because it implements IEnumerable<T>, you have to implement GetEnumerator(), and also IEnumerable and IQueryable are implemented implicitly in 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
}

As you can see, we didn't implement all the functions. We just implemented IQueryable<T>.CreateQuery, IEnumerable.GetEnumerator. Basically the CreateQuery method just assigns the passed expression parameter to the MemberExpression and returns this cased as IQueryable. The parsing logic is in the IEnumerable.GetEnumerator, currently because this is just a prototype I implement where the ParseExpression method 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.

History

  • 23rd October, 2007: Initial post to The Code Project

This article was originally posted to my blog.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Mohamed H. Elsherif
Software Developer Hulu, LLC
United States United States
Member
Mohamed Elsherif
Sr. SDE,
Hulu, LLC
Los Angeles, CA
Blogs: www.Bashmohandes.com

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
Generaljust what i was looking for.memberLeblanc Meneses10 Nov '08 - 9:18 
you know of any projects besides:
http://www.codeplex.com/glinq/Thread/View.aspx?ThreadId=21327
 
that are implementing similar solutions?
 
tonight i'm going try implementing this on my own personnel website:
            public Tree<comment> Comments(CommentQuery query)
            {
                    
            }
 
thanks,
 
-lm
Generalyes interestingmemberBlaiseBraye29 Oct '07 - 22:15 
Hello, I think all of it is really interesting (for the fonctional aspect even more)
but I don't trust in LINQ to Sql yet...
 
the problem is with the phylosophy of it.
 
I mean, I have spent a lot of time (in general) to decuple my queries from my applications layers;
I try a maximum to never reference the real database from my application by having a mapping to the tables name,... of the true database; and this in order to be able to modify the database without to much implications in the aplication (I don't like the usage of the sql views even i know it can save a lot of time)
 

anyway the LINQ style tends the be in the oposite way; may be I am wrong then you "must" explain to me where I am Wink | ;)
 

 
Let's make code sharing our goal...
Blaise Braye

GeneralRe: yes interestingmemberMohammad Tayseer30 Oct '07 - 1:37 
LINQ is designed to provide higher level APIs and syntax for data handling. LINQ to SQL is just an implementation of LINQ that maps to SQL.
 
There is nothing wrong with LINQ itself. LINQ to SQL is not the only option for connecting to a DB. I think all ORMs will provide LINQ APIs soon.
 
Nothing forces you to use LINQ directly in your business logic layer. You can write wrappers on top of it. I guess now you are doing the same thing by wrapping ADO.net, right??
GeneralRe: yes interestingmemberBlaiseBraye30 Oct '07 - 2:28 
right.
Thanks for light;
I was making a big mistake, one more time Wink | ;)
 
Let's make code sharing our goal...
Blaise Braye

GeneralRe: yes interestingmemberMohammed H. El-Sherief30 Oct '07 - 2:31 
Exactly LINQ is not limited to any tier in your application, it is just like while, for, if, ... statements, use it wherever you want, the different implementations are giving you more options in terms of what to query.
 
/////////////////////////
Eng: Mohammed Hossam
////////////////////////

Generalextremely goodmemberSacha Barber23 Oct '07 - 20:15 
extremely good
 
love it
 
Sacha Barber
A Modern Geek - I cook, I clean, I drink, I Program. Modern or what?
 
My Blog : sachabarber.net

GeneralRe: extremely goodmemberMohammed H. El-Sherief23 Oct '07 - 22:07 
Thanks I hope it was clear enough
 
/////////////////////////
Eng: Mohammed Hossam
////////////////////////

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web02 | 2.6.130523.1 | Last Updated 23 Oct 2007
Article Copyright 2007 by Mohamed H. Elsherif
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid