Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

LINQ to FQL (Facebook Query Language)

0.00/5 (No votes)
21 May 2009 1  
A library that enables typed queries to Facebook

LinqToFqlAddon/LinqToFql_logo.png

Facebook Developer Toolkit LINQ to FQL Addon

Preface

LINQ to FQL is an open source project. Sources, binaries and samples can be found at CodePlex. This library extends the Facebook Developer Toolkit.

Introduction

This library enables developers to use LINQ (.NET Language-Integrated Query) with Facebook instead of string based FQL queries. The main benefits of using this library are:

  • Type safety: queries are now typed and their syntax is verified during compilation
  • Auto-complete now functions when composing queries in Visual Studio
  • Using Facebook / anonymous objects for query results

Example: The following sample code retrieves the names and pictures of all of your friends.

var db = new FacebookDataContext();
var friendIDs = from friend in db.friend_info where friend.uid1 == 
					db.uid select friend.uid2;
var friendDetails = from user in db.user where friendIDs.Contains(user.uid) 
			select new { Name = user.name, Picture = user.pic_small };

Background

While the LINQ infrastructure is extensible, the LINQ to SQL (formerly known as DLINQ) infrastructure is not. Its implementation is internal and supports SQL Server only. In order to natively support FQL, it was required to mimic the LINQ to SQL infrastructure from scratch, as follows:

  • FqlDataContext represents a data source that supports FQL, and consists of FqlTable instances.
  • FqlTable represents a table in the model, and provides as the target for LINQ queries.
  • FqlDataQuery represents a LINQ query that is executed over an FqlTable.
  • FqlProvider manages the transformation and execution of queries.
  • FqlQueryBuilder transforms LINQ expressions to FQL strings.
  • FacebookDataContext derives from FqlDataContext, and exposes the Facebook Developer Toolkit model as tables.

LinqToFql_diagram.png - Click to enlarge image

The flow of a LINQ to FQL query looks like this:

  1. A table is queried using LINQ (in FqlTable.CreateQuery)
  2. The LINQ expression is analyzed and transformed into a valid FQL text (in FqlQueryBuilder.BuildQuery)
  3. The FQL text is sent through the Facebook API (in API.linq.query)
  4. The XML result is returned, and deserialized back into proper objects (in FqlQueryResultEnumerator.GetEnumerator)

Transforming LINQ to FQL

The LINQ to FQL transformation is done through the FqlQueryBuilder class. This class takes an expression tree, that is, an instance of System.Linq.Expressions.Expression, and recursively iterates its sub expressions. During this iteration, each sub expression is identified and converted to FQL text by its expression data and the current state of the builder. For example:

  • Query clauses (SELECT, FROM, WHERE, ORDERBY, etc...) are identified by MethodCall expressions to well known LINQ extension methods, for example, the SELECT clause should match the System.Linq.Queryable.Select method.
  • Tables are identified by MemberExpressions when their member type implements the IFqlTable interface.
  • Columns are identified by MemberExpressions when their member has the proper custom attributes.
  • Sub queries are identified by MemberExpressions when their member type implements the IFqlDataQuery interface.

Integrating with LINQ

Several classes and interfaces are used to integrate with LINQ:

  • IEnumerable, IEnumerable<T>, IEnumerator, IEnumerator<T> are used to return physical iteratable results.
  • IQueryable, IQueryable<T> are used to create classes that have more control over LINQ queries that are applied on it.
  • IQueryProvider is used for rewriting and executing queries.
  • The System.Linq.Expressions namespace is used as the basic model of LINQ queries.

The main class that serves as a target for handling LINQ queries in this library is the FqlTable<T> class which implements both the IQueryable and IQueryProvider interfaces.

public class FqlTable<T> : IQueryable, IQueryable<T>, IQueryProvider
{
  //IQueryable
  public IQueryProvider Provider
  {
    get { return this; }
  }
 
  //IQueryProvider
  public IQueryable<TResult> CreateQuery<TResult>(Expression expression)
  {
    return new FqlDataQuery<TResult>(this.context, expression, typeof(T));
  }
}

Running the Sample

This sample is available online, simply click the Run button to run the sample, or click on any of the source files to view or edit them.

In order to run the sample locally, please follow these steps:

  • Open the SampleQuery.sln file in Visual Studio.NET 2008.
  • When you are prompted to create a virtual directory at http://localhost/SimpleQuery for the application, click yes.

You may run the application, note that it will use a predefined generic Facebook application (courtesy of CodeRun) that will redirect Facebook to your local sample. In order to use your own application, continue to these steps:

  • Go to the Developer application in your Facebook account.
  • Click on "Setup new application".
  • Give your application the name SimpleQuery.
  • Set the callback URL to: http://localhost/SimpleQuery/facebookcallback.ashx.
  • In the canvas tab, set the Render Method to IFrame.
  • Click on "Save changes".
  • In the next page - copy and paste the API Key, Application Secret and Application ID to the sample project's web.config file.
<appSettings>
	<add key="Facebook.Linq.ApplicationID" value="xxxxxxxx" />
 
	<add key="Facebook.Linq.APIKey" value="xxxxxxxxxxxxxxxxxxxxxx" />
	<add key="Facebook.Linq.Secret" value="xxxxxxxxxxxxxxxxxxxxxx" />
</appSettings>

Run the application by clicking Run in Visual Studio or here.

Using the Code

The main entry point to use LINQ queries is the FacebookDataContext class. This class should contain all of Facebook's tables, and each table is queryable using LINQ. To use it - create a new instance of FacebookDataContext, and use LINQ on any table property on it.

var db = new FacebookDataContext();
var myUser = db.user.Where(t => t.uid==db.uid).FirstOrDefault();

It is also possible to specify the exact connection to Facebook to be used, using the facebook.API class (located in the Facebook Developer Toolkit assembly).

var api = new API { ApplicationKey = "MyAppKey", 
	AuthToken = "MyAuthToken", SessionKey = "MySessionKey", Secret = "MySecret" };
var db = new FacebookDataContext(api);

Example 1: Syntax

There are two types of syntax to use LINQ, one is called LINQ Query Syntax and the other is LINQ Method Syntax. The LINQ Query Syntax is a new and native implementation as part of the C# 3.0 language. It is not extensible and therefore it has some limitations. The good news is that both types of syntax can be mixed in the same query and get the same results.

var db = new FacebookDataContext();
//LINQ Query Syntax
var friendsIDs = from friend in db.friend_info where friend.uid1 == 
					db.uid select friend.uid2;
//LINQ Method Syntax (the same query)
var friendsIDs2 = db.friend_info.Where(t => t.uid1 == db.uid).Select(t => t.uid2);

Example 2: Nested Queries

FQL supports the use of only one table in each query, this means that table joins are not supported. On the upside, FQL allows the use of more than one table using nested queries and so does LINQ to FQL. In the future, this library should compensate this drawback by transforming LINQ expressions that use the join clause to nested or multiple FQL queries.

var db = new FacebookDataContext();
var friendIDs = from friend in db.friend_info where friend.uid1 == 
					db.uid select friend.uid2;
var friendDetails = from user in db.user where friendIDs.Contains(user.uid) select user;
//Or in a single line
var friendDetails2 = from user in db.user where 
	(from friend in db.friend_info where friend.uid1 == 
	db.uid select friend.uid2).Contains(user.uid) select user;

Show Me.

Example 3: Retrieving Specific Properties

Retrieving specific properties using LINQ is done using the select clause, it is an important part in query optimization because it minimizes the data returned from Facebook. The Select clause usually requires you to have another object in which you can put your selective data, but no worries, Anonymous Types makes the job very simple. Anonymous types allow you to implicitly define a new class with a certain set of read-only properties. In this example, an anonymous type will be created with two properties - Name and Picture, both will be of type String.

var db = new FacebookDataContext();
var friendIDs = from friend in db.friend_info where friend.uid1 == 
					db.uid select friend.uid2;
var friendDetails = from user in db.user where friendIDs.Contains(user.uid) 
		select new { Name = user.name, Picture = user.pic_small };

Show Me.

Example 4: Paging and Sorting

Sorting is performed using the orderby clause (using query syntax), or the OrderBy method (using method syntax). Paging is performed using the Skip and Take methods, note that paging does not have a LINQ Query Syntax alternative. Paging is also an important part in query optimization because it limits the number of rows that will be returned from Facebook.

var db = new FacebookDataContext();
var friendIDs = from friend in db.friend_info where friend.uid1 == 
					db.uid select friend.uid2;
var friendDetails = (from user in db.user where friendIDs.Contains(user.uid) 
	orderby user.name select new { Name = user.name, 
	Picture = user.pic_small }).Skip(4).Take(5);

Show Me.

The facebook.Web Namespace

The facebook.Web namespace contains some useful classes designed to help with Facebook Integration. However, they are not crucial to the LINQ to FQL infrastructure. For example, when you use the FacebookDataContext empty constructor, a contextual facebook.API object will be used. This context is accessible through the FacebookContext.Current property or the FacebookContext.Get(httpContext) method, and contains all the relevant Facebook contextual information such as the currently logged in user and the application info. The default application that will be used by the FacebookDataContext class is defined in your web.config file.

<appSettings>
 <add key="Facebook.Linq.ApplicationID" value="xxxxxxxx" />
 <add key="Facebook.Linq.APIKey" value="xxxxxxxxxxxxxxxxxxxxxx" />
 
 <add key="Facebook.Linq.Secret" value="xxxxxxxxxxxxxxxxxxxxxx" />
</appSettings>

For a full support of the FacebookContext, you should also register the FacebookCallback handler in your web.config file. The FacebookCallback handler automates the redirection to and callback from Facebook, it also automates the login process.

<system.web>
 <httpHandlers>
 <add verb="*" path="FacebookCallback.ashx" type="Facebook.Web.FacebookCallback, 
	Facebook.Linq, Version=1.0.0.0, Culture=neutral, 
	PublicKeyToken=null" validate="false"/>
 
 </httpHandlers>
</system.web>

Show Me.

Using this technique, it is easier to verify that the user is logged in, if he's not, he will be redirected to the Facebook login page.

if (FacebookContext.Current.TryAuthenticating(true))
{
 var db = new FacebookDataContext(); //in here the FacebookDataContext 
		// object will use the currently authenticated user connection info.
}

Show Me.

Custom Facebook Models

While the FqlDataContext is a generic implementation designed to work with any model, the FacebookDataContext class derives from it, and implements a specific Facebook model. This gives you the flexibility to create your own Facebook models and map them to FQL using custom attributes. This library actually supports the same custom attributes as the native LINQ to SQL implementation, these are TableAttribute and ColumnAttribute. For example, consider this custom, more user friendly Facebook model:

[Table(Name="album")]
public class Album
{
 [Column(Name="aid", IsPrimaryKey=true)]
 public long ID { get; set; }
 
 [Column(Name="cover_pid")]
 public long CoverPictureID {get;set;}
}
 
public class FacebookFriendlyDataContext : FqlDataContext
{
	public FqlTable<Album> Albums
	{
 get
 {
 return GetTable<Album>();
 }
 }
}

Performance Guidelines

The LINQ to FQL is a powerful tool and should be used with care. It is important to remember that your application is not communicating with a local database, but a remote service. Any data that your application receives through this pipe travels through the internet as an XML message. So, it is very important to try and get only the data that your application really needs. The most common ways to do that are:

  • Select only the columns you need to process or show.
  • Filter your queries using the LINQ to FQL infrastructure and not in memory.
  • Use paging - if you're not planning to show or process the entire query result, do not retrieve it all.

Points of Interest

Useful Links

Future Versions

  • Complete coverage of the Facebook model
  • Data Store API support (Personal Facebook databases)
  • Associations support (userid=>user)
  • Entity Caching

History

  • 9th March, 2009: Initial post
  • 10th March, 2009: Updated source code
  • 13th May, 2009: Updated article
  • 21st May, 2009: Updated article

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here