Creating LINQToTwitter library using LinqExtender Thursday, January 1, 2009

In this post, I will show how to cook an LINQToTwitter library with LinqExtender. My target is to create it easily and over existing twitter API. I used Yedda Twitter library. Yedda library basically returns response in XML/JSON/RSS/ATOM format depending on the option provided. I used XML format and serialized it to objects.

Now, with our LINQToTwitter we will be able to get public/user/friend's timeline, sort them via date, screen name. Finally, we will be able to update our status. Twitter response format for status looks like

< statuses > < status > .... .... < user > .... .... </ user > </ status > </ statuses >

For this, I have created a class named Twit that maps to the status and a class name User that is used as an auto property in Twit . These two classes contain some Xml Serialization attributes by which response is serialized to respective properties.

We have our Twitter API in place, the next step is to make them LINQ-able. Prior to the latest release to make an object queryable, it is required to inherit from QueryObjectBase. Using this approach the feed back I got from users that they need to try LINQ in their existing data objects, which is already inherited from a base class and they need to change a lot for using the abstract approach. The new release just solves that with a blank IQueryObject interface, which makes it more easier to port LINQ into existing data object in no time.

Building LINQ providers with LinqExtender requires few easy steps. First, Create or convert an object to be queryable by implementing IQueryObject interface. With our Twit object it looks like

Here, note that I added UniqueIdentifier attribute on top of Id property that makes the object trackable during add, update or delete. Every object should have property that distinguishes entities from each other and should be marked Unique. There is another attribute called Ignore. If we want properties not to be processed by the toolkit we can just put the attribute on top. Once, it is done next we have to create query provider that will be used for making LINQ queries. Let's name it TwitterContext and it needs to inherit Query<Twit> and override few methods depending on support that the library is going to provide.

Now, as I said earlier that we will be doing some queries by timeline and update our status. While adding new items we need to override Query<Twit>.AddItem where we will add object to repository and update any property that we need to get and finally return true/ false depending on the execution status.

Inside Query<Twit>.AddItem it looks like (Partially).

string username = ( string )Bucket.Instance.For.Item(QueryItems.USERNAME).Value; string password = ( string )Bucket.Instance.For.Item(QueryItems.PASSWORD).Value; Validate(username, password); string statusText = ( string )Bucket.Instance.For.Item( "Text" ).Value; string source = ( string )Bucket.Instance.For.Item( "Source" ).Value; Twitter twitter = new Twitter(); if ( string .IsNullOrEmpty(source)) { twitter.Source = source; } string response = twitter.Update(username, password, statusText, Twitter.OutputFormatType.XML); Twit twit = XmlToObject<Twit>.Deserialize(response); Type twitType = twit.GetType(); Bucket.Instance.For.EachItem .Process( delegate (BucketItem item) { item.Value = twitType.GetProperty(item.Name).GetValue(twit, null ); });

The code is pretty simple as we can see that we are getting the required values from bucket object , setting them into Twitter API, updating the result object (Twit) and finally returning TRUE that will tell LinqExtender to update it's collection. For any FALSE or exception toolkit will remove the partial object. There are other methods that we can override like UpdateItem, RemoveItem, GetItem. Due to the API constraint we will skip them now. But you can check them out at the project documentation or in the provided OpenLinqToSql ORM.

Next, we need to override the Query<Twit>.Process which will be handling the LINQ queries we make.

protected override void Process(LinqExtender.Interface.IModify<Twit> items) { string response = string .Empty; Twitter twitter = new Twitter(); /// general select statement with no where clause. if (!Bucket.Instance.IsDirty) { response = twitter.GetPublicTimeline(Twitter.OutputFormatType.XML); } else { string username = ( string )Bucket.Instance.For.Item(QueryItems.USERNAME).Value; string password = ( string )Bucket.Instance.For.Item(QueryItems.PASSWORD).Value; object obj = (Timeline)Bucket.Instance.For.Item(QueryItems.TIMELINE).Value; if (obj == null ) { throw new Exception( "Must provide a valid timeline" ); } Timeline timeline = (Timeline) obj; if (timeline == Timeline.Friends) { Validate(username, password); response = twitter.GetFriendsTimeline(username, password, Twitter.OutputFormatType.XML); } else if (timeline == Timeline.User) { response = twitter.GetUserTimeline(username, password, Twitter.OutputFormatType.XML); } } Twits twits = XmlToObject<Twits>.Deserialize(response); /// do some extra in-memory stuff which are not supported by API IList<Twit> list = GetList(twits); items.AddRange(list, true ); }

Here, we can see that Bucket.Instance.IsDirty is used for public timelines. When Bucket.Instance.IsDirty = false, we can be sure that no where clause is used and as such we can decide to handle the way we want. IModify<T>.AddRange with true tells the toolkit to do in-memory sort when orderby is used. There is another way to do sort (natural sort) by which we can pass in the sort data to source (if supported) and in that case we can either pass false or use the other overload of AddRange or even add items one by one.

This is our simple LINQToTwitter library so far. To start let's do some queries. First, I want to get my friend's timeline sorted by last updated date.

TwitterContext context = new TwitterConext();

var query = from twit in context where twit.Username == "myuser" && twit.Password == "mypass" && twit.Timeline == Timeline.Friends orderby twit.LastUpdated descending select twit; foreach (var t in query) { / // do something useful. }

I also want to update my status. To do so, the following needs to be done.

Twit twitObject = new Twit() { Username = USERNAME, Password = PASSWORD, Text = "Happy new year to all" }; context.Add(twitObject); context.SubmitChanges(); if (! string .IsNullOrEmpty(twitObject.Id)) { / // do something }

There are few other queries that can be made. You can download the source here. I have shown here very few of the options in LinqExtender, to know more check out the word document at project's release page. Also, check the OpenLinqToSql ORM that is made on it and comes out of the box.

Enjoy!!