Summary
Upon deciding to start this series of blogs I decided that in order for me to consume the Blippr API I would attempt to “try” and make my life easy. I decided that the easiest way would be to start writing a LINQ to Blippr provider (first ever try) so that it was possible to easily consume the Blippr platform in any .NET application I wrote or for that matter anyone in the .net community. The project itself is hosted on CodePlex and can be found at LINQ to Blippr. The project implements the wonderful LINQ Extender container to make life easy when attempting to create your own LINQ providers.
I also have to give a massive appreciation and shout out to Chris Heald at Blippr for amending the API for me as I have been developing the LINQ provider.
Using LINQ to Blippr
Download the latest alpha release from LINQ to Blippr on CodePlex. Just to note that the provider is still in development as we speak and there are many features in testing. Operations that are available within the Alpha release should work correctly.
Once downloaded add a reference to both the LINQ Extender and LinqToBlippr assembly.
You will also need an API key for write operations which can be obtained by visiting and logging into the following page: http://www.blippr.com/api
The Titles Query
To search for titles that are available on Blippr you can now easily do this for example to search for all Matrix titles across all media types you can do the following (you will notice that you are not required to supply an API key to perform searching on titles):
1: BlipprContext context = new BlipprContext();
2: var result = context.Titles.Where(t => t.Name == "Matrix");
Creating a New Title
Creating a new title for Blippr is also now very easy. The simplistic process is to construct a new Title object add it to the Titles query and submit the changes for example (you will need to initialize the context with an API key):
1: BlipprContext context = new BlipprContext(“YOUR-API-KEY”);
2:
3: Title matrixRampage2 = new Title(MediaType.Games, "Matrix Rampage 2");
4: context.Titles.Add(matrixRampage2);
5: context.Titles.SubmitChanges();
It can take a little while for the Blippr site cache to refresh so you probably will not see your title added immediately.
Filtering
The Blippr API covers the following Media Types which you can also add to your expression:
1: MediaType.Apps
2: MediaType.Books
3: MediaType.Games
4: MediaType.Movies
5: MediaType.Music
Using the previous example we can now get all matrix games by specifying a media type:
1: BlipprContext context = new BlipprContext();
2: var result = context.Titles.Where(t => t.Name == "Matrix"
3: && t.MediaType == MediaType.Games);
You can also add to your expression a filter on score and the total amount of blips posted against the titles. As a quick reference I have provided some examples below:
Score
An expression that returns all “matrix” titles of any media type that has a score less than or equal to 70.
1: BlipprContext context = new BlipprContext();
2:
3: var result = context.Titles.Where(t => t.Name == "Matrix" &&
4: t.Score <= 70);
Total Blips
An expression that returns all “matrix” titles of any media type that has a total number of blips greater than 10.
1: BlipprContext context = new BlipprContext();
2:
3: var result = context.Titles.Where(t => t.Name == "Matrix" &&
4: t.TotalBlips > 10);
Ordering
Ordering by a specific property such as title is as simple as:
1: BlipprContext context = new BlipprContext();
2:
3: var result = context.Titles.Where(t => t.Name == "Matrix" &&
4: t.TotalBlips > 10)
5: .OrderByDescending(t => t.Name);
If the Blippr API has a supporting order by clause then this will be done when requesting the xml data. If supporting functionality does not exist then ordering will be done in memory.
Other Features....
- Comments Query (BlipprContext.Comments)
Add comments against blips.
Get comments by blip id.
- Blips Query (BlipprContext.Blips)
Add new blip against a title.
Get blips currently posted against a title.
Delete a blip that you own.
As the core element of Blippr is to “Blip” against titles you can follow the same syntax style to post a Blip against a title once you have searched and got your title id (Red Alert 3 is 345210):
1: BlipprContext context = new BlipprContext(APIKEY);
2: Blip redAlert3Blip = new Blip(345210, Rating.LoveIt, "Play this game every Wednesday with work mates. Great game!");
3:
4: context.Blips.Add(redAlert3Blip);
5: context.Blips.SubmitChanges();
Under The Hood
As previously mentioned the LINQ to Blippr library uses LINQ Extender. LINQ Extender provides a very easy to use framework for creating your own LINQ providers.
In order to start creating LINQ to Blippr we first need to create a context class and then a LINQ Extender query; we will focus on the TitlesQuery only. Going forward any additional queries will follow a similar pattern.
Lets Begin…
As previously stated to begin we require a context class in order to initialize and access the relevant queries to return the data we require. For the LINQ to Blippr context there are a couple of constructors one that allows the API key to be passed through for operations that require authentication and one without.
Lastly we have a property that returns a TitlesQuery which will allow us to execute expressions aginst the query to return the data we want.
1: namespace Linq.Blippr
2: {
3: using Factories;
4: using Queries;
5:
6: /// <summary>
7: /// Defines the Blippr context.
8: /// </summary>
9: public class BlipprContext
10: {
11: /// <summary>
12: /// The factory to return queries.
13: /// </summary>
14: private readonly QueryFactory queryFactory;
15:
16: /// <summary>
17: /// The api key.
18: /// </summary>
19: private readonly string apiKey;
20:
21: /// <summary>
22: /// The global titles query.
23: /// </summary>
24: private TitlesQuery titlesQuery;
25:
26: /// <summary>
27: /// Initializes a new instance of the <see cref="BlipprContext"/> class.
28: /// </summary>
29: /// <param name="apiKey">The API key.</param>
30: public BlipprContext(string apiKey)
31: {
32: this.apiKey = apiKey;
33: this.queryFactory = new QueryFactory();
34: }
35:
36: /// <summary>
37: /// Initializes a new instance of the <see cref="BlipprContext"/> class.
38: /// </summary>
39: public BlipprContext()
40: {
41: this.queryFactory = new QueryFactory();
42: }
43: /// <summary>
44: /// Gets the global titles query.
45: /// </summary>
46: public TitlesQuery Titles
47: {
48: get
49: {
50: if (this.titlesQuery == null)
51: {
52: this.titlesQuery = this.queryFactory.CreateTitlesQuery(this.apiKey);
53: }
54:
55: return this.titlesQuery;
56: }
57: }
58: }
Titles Query
We now have our Blippr context class it is now time to create our TitlesQuery that the property exposes. Firstly to create this we need to inherit from the Generic Query<T> class provided to us from the LINQ Extender assembly. We need to pass in the type of object that will be returned for the final result set.
1: public class TitlesQuery : Query<Title>
As the Blippr platform API can return either JSON, XML or YAML I decided to head down the de-serialization route and request Xml for results. The Title class is a plain object decorated with the .NET frameworks serialization Xml Attributes so that later on when we get the data and de-serialize it those properties will be populated. Clean, simple and readable. Below shows the Title object:
1: namespace Linq.Blippr.CommonTypes
2: {
3: using System;
4: using System.Xml.Schema;
5: using System.Xml.Serialization;
6: using Enums;
7: using LinqExtender.Attributes;
8:
9: /// <summary>
10: /// Determines a blippr title object.
11: /// </summary>
12: [Serializable]
13: [XmlRootAttribute(ElementName = "title", Namespace = "", IsNullable = false)]
14: public class Title
15: {
16: /// <summary>
17: /// Initializes a new instance of the <see cref="Title"/> class.
18: /// </summary>
19: public Title()
20: {
21: }
22:
23: /// <summary>
24: /// Initializes a new instance of the <see cref="Title"/> class.
25: /// </summary>
26: /// <param name="mediaType">Type of the media.</param>
27: /// <param name="name">The name of the title.</param>
28: public Title(MediaType mediaType, string name)
29: {
30: if (mediaType == MediaType.Titles)
31: {
32: throw new Exception("Invalid media type please only use apps, books, games, movies or music.");
33: }
34:
35: this.MediaType = mediaType;
36: this.Name = name;
37: }
38:
39: /// <summary>
40: /// Gets or sets the unique id.
41: /// </summary>
42: [XmlAttribute(AttributeName = "id", Form = XmlSchemaForm.Unqualified)]
43: [UniqueIdentifier]
44: public long Id { get; set; }
45:
46: /// <summary>
47: /// Gets or sets the url link.
48: /// </summary>
49: [XmlAttribute(AttributeName = "link", Form = XmlSchemaForm.Unqualified)]
50: public string Link { get; set; }
51:
52: /// <summary>
53: /// Gets or sets the name.
54: /// </summary>
55: [XmlAttribute(AttributeName = "name", Form = XmlSchemaForm.Unqualified)]
56: public string Name { get; set; }
57:
58: /// <summary>
59: /// Gets or sets the total amount of reviews.
60: /// </summary>
61: [XmlAttribute(AttributeName = "reviews_count", Form = XmlSchemaForm.Unqualified)]
62: public long TotalBlips { get; set; }
63:
64: /// <summary>
65: /// Gets or sets the score rating.
66: /// </summary>
67: [XmlAttribute(AttributeName = "score", Form = XmlSchemaForm.Unqualified)]
68: public float Score { get; set; }
69:
70: /// <summary>
71: /// Gets or sets the images.
72: /// </summary>
73: [XmlElement(ElementName = "images", Form = XmlSchemaForm.Unqualified)]
74: public Images Images { get; set; }
75:
76: /// <summary>
77: /// Gets or sets a value indicating whether [media type].
78: /// </summary>
79: /// <value><c>true</c> if [media type]; otherwise, <c>false</c>.</value>
80: [XmlAttribute(AttributeName = "media_type", Form = XmlSchemaForm.Unqualified)]
81: public MediaType MediaType { get; set; }
82: }
83: }
Now on to the fun part where we can now override relevant methods in out Titles query to return the data. The following methods are overridden within the TitlesQuery class:
1: protected override void SelectItem(IBucket bucket, IModify<Title> items)
Processes a complex expression and adds results to the items IModify<Title> result set.
1: protected override bool AddItem(IBucket bucket)
Adds an item when submitting changes to the query.
1: protected override Title GetItem(IBucket bucket)
Retrieves a single item by its unique identifier.
You will notice that commonly the IBucket is provided to each method signature. IBucket exposes information for the actual expression being executed. This allows us to handle and build up the request URL to send to the Blippr API. It contains all items by field name and the values for the expression along with the relation type (being Greater Than, Equal, Less Than…) and other information you can use.
In LINQ to Blippr currently the logic has been separated out into individual repository classes. Let’s take AddItem(IBucket bucket) as an example as this is a simple operation to review:
1: /// <summary>
2: /// Adds a new the item.
3: /// </summary>
4: /// <param name="bucket">The bucket.</param>
5: /// <returns><c>true</c> if addition was a success.</returns>
6: protected override bool AddItem(IBucket bucket)
7: {
8: if (string.IsNullOrEmpty(this.apiKey))
9: {
10: throw new NullReferenceException("Authentication is required, initialize the Blippr Context with your API key.");
11: }
12:
13: return this.titleRepository.Add(bucket);
14: }
15:
Then in our TitleRepository we can do the necessary implementation to add a new item via the Blippr API:
1: /// <summary>
2: /// Adds a title to blippr.
3: /// </summary>
4: /// <param name="bucket">The linq bucket.</param>
5: /// <returns><c>true</c> if post was a success.</returns>
6: public bool Add(IBucket bucket)
7: {
8: if (bucket.Items[TitleFields.MediaType].Value != null)
9: {
10: this.mediaType = (MediaType)bucket.Items[TitleFields.MediaType].Value;
11: }
12:
13: string query = string.Format(
14: UrlTemplates.POSTNewTitle,
15: this.mediaType.ToString().ToLowerInvariant(),
16: OutputFormat.Xml.ToString().ToLowerInvariant(),
17: bucket.Items[TitleFields.Name].Value,
18: this.ApiKey);
19:
20: bool result = this.PostData(query);
21:
22: return result;
23: }
The above grabs the expression information from the bucket builds up the URL in the correct format to post to Blippr and then returns a true or false value if the request was successful. Let’s break down an example:
Say our code is the following:
1: BlipprContext context = new BlipprContext(“YOUR-API-KEY”);
2:
3: Title matrixRampage2 = new Title(MediaType.Games, "Matrix Rampage 2");
4: context.Titles.Add(matrixRampage2);
5: context.Titles.SubmitChanges();
The IBucket items will store the values for each field by keys meaning:
bucket.Items[“Title”] would represent matrixRampage2.Title value.
bucket.Items[“MediaType”] would represent matrixRampage2.MediaType value.
You can then take this further when building up expressions that select items with a Where clause. For example when we wish to query a title against the total number of blips we have the following code executed within the SelectItem function:
1: BucketItem totalBlipsBucketItem = bucket.Items[TitleFields.TotalBlips];
2:
3: if (totalBlipsBucketItem.Value != null)
4: {
5: int totalBlips;
6:
7: if (!int.TryParse(totalBlipsBucketItem.Value.ToString(), out totalBlips))
8: {
9: throw new InvalidCastException("Total blips must not be of type " + totalBlipsBucketItem.PropertyType.Name);
10: }
11:
12: switch (totalBlipsBucketItem.RelationType)
13: {
14: case RelationType.GreaterThan:
15: query.Append("&min_reviews=" + (totalBlips + 1));
16: break;
17: case RelationType.GreaterThanEqual:
18: query.Append("&min_reviews=" + totalBlips);
19: break;
20: case RelationType.LessThan:
21: query.Append("&max_reviews=" + (totalBlips - 1));
22: break;
23: case RelationType.LessThanEqual:
24: query.Append("&max_reviews=" + totalBlips);
25: break;
26: default:
27: query.Append("&min_reviews=" + totalBlips + "&max_reviews=" + totalBlips);
28: break;
29: }
30: }
You will notice we use totalBlipsBucketItem.RelationType to determine how to handle each of the different relation types. For example below you will find that the above code deals with the t.TotalBlips > 1 part of expression below:
1: var titles = context.Titles.Where(t => t.Name == "Iron Man" &&
2: t.MediaType == MediaType.Movies &&
3: t.TotalBlips > 1);
Feedback and Comments
Hopefully this gives you a good idea on how you can go about starting to create your own LINQ provider with LINQ Extender. I will announce the stable releases for LINQ to Blippr via Twitter, CodePlex and this blog… so stay tuned.
If you have any creative ideas or begin to start using LINQ to Blippr in you applications I would love to hear about it. Alternatively there is twitter trend you can use #LinqToBlippr.
I welcome any comments either about the blog content itself or suggestions in steering what you would like covered in the coming posts. You can leave a comment on my blog, drop me a mail or tweet me @jsharratt.
Lastly if you want to contribute to this project please free to get in
touch.
Blippr Blog Series
- Blippr Series Part 1 - Introduction
- Blippr Series Part 2 - Linq To Blippr
- Blippr Series Part 3 - Windows 7 Mobile Blippr Application
- Blippr Series Part 4 - Titanium Mobile Blippr Application