Geeks With Blogs
Joe Mayo

LINQ to Twitter recently released as v4.x, where one of its main goals was to support Universal Windows Platform development. LINQ to Twitter has supported many platforms and UWP is a natural evolution. One of the driving forces in the new version is the fact that UWP has it’s own HTTP client stack, which isn’t compatible with PCL. In retrospect, this was an opportunity because the generic nature of PCL means that you don’t inherently have platform-specific fidelity. (note: that said – there are techniques to achieve this, but because of the nature of PCL, it doesn’t come out of the box) This platform-specific motivation and its associated design might be a fun subject of another blog post, but this post is about achieving the primary goal of 4.x – to support UWP.

As I mentioned, UWP has it’s own HTTP stack and developers learned this quickly when LINQ to Twitter wasn’t compatible. Jose Fajardo created an early fork of LINQ to Twitter, modified to support UWP on GitHub. This was useful because not only did it identify where the problems were, it helped scope the amount of work required for the UWP solution. Thanks to Jose Fajardo. This was great, it worked, but LINQ to Twitter needed to support multiple platforms. So, v4.x was born.

As a LINQ to Twitter developer, you rarely come into contact with the HTTP client libraries that communicate with the Twitter API. That low-level communication is abstracted through convenience classes. Your LINQ to Twitter queries don’t change between v3.x and v4.x either. However, what does change is the OAuth authorization and you’ll learn how in this blog post. I’ll use a sample app that’s part of the LINQ to Twitter source code on GitHub and show you how to authorize, tweet, and query.

About Authorization

Twitter API security is based on OAuth, a security protocol that gives the user the ability to authorize an application to work on their behalf. The application that you write will expose an interface where a user will authorize your application to operate under that user’s credentials with Twitter. In case it isn’t clear, OAuth is user-centric, in that it gives the user control to protect themselves from abusive programs.

In this spirit, LINQ to Twitter offers a framework for Twitter’s flavor of OAuth and supplies wrappers for different technologies and Twitter API OAuth capabilities. Particular to UWP, v4.x introduces the UniversalAuthorizer. In v3.x, a Windows RT app would use a WinRtAuthorizer, but a Windows Phone app would use a PinAuthorizer that interacted with a Web control. The WinRtAuthorizer used the Windows WebAuthenticationBroker, which was a very nice way to support OAuth, but wasn’t available to Windows Phone. With UWP, the programming model is unified (was that a pun?), with the benefit that WebAuthenticationBroker works for Windows Apps on desktops/tablets, phones, and other devices. So, introducing UniversalAuthorizer facilitates that move, making Windows Phone a 1st class citizen in LINQ to Twitter v4.x authorization.

The Authorizer

The UniversalAuthorizer derives from LinqToTwitter.AuthorizerBase and implements IAuthorizer. You can use these as customization points or derive from UniversalAuthorizer yourself for additional functionality in your app. You can examine the source code, but behind the scenes, UniversalAuthorizer is using WebAuthenticationBroker, which is very convenient and works well. The following listing shows how to instantiate a new UniversalAuthorizer:

            var authorizer = new UniversalAuthorizer
            {
                CredentialStore = new InMemoryCredentialStore
                {
                    ConsumerKey = "",
                    ConsumerSecret = ""
                },
                Callback = "http://github.com/JoeMayo/LinqToTwitter"
            };

To create an authorizer (including UniversalAuthorizer), instantiate it as shown above. Notice that I also instantiate an InMemoryCredentialStore and assign it to the UniversalAuthorizer.CredentialStore property. You’ll need to give the ConsumerKey and ConsumerSecret properties their corresponding keys from your Twitter App page. The Callback property is required – though not used and you can set it to any Url you like – yes, it’s dumb and I have an open issue to research why. InMemoryCredentialStore implements LinqToTwitter.ICredentialStore and is another extensibility point. If you wanted, write a custom ICredentialStore implementation to manage values and storage and then plug it into the authorizer by assigning your ICredentialStore instance to the CredentialStore property. You can visit the Security Wiki in the LINQ to Twitter documentation for more information on available authorizers.

With a UniversalAuthorizer instance, you can start the authorization process.

Authorizing

Since LINQ to Twitter is async, you must await the AuthorizeAsync method. A typical symptom of forgetting to await an async method is a stack trace showing that the application died in the middle of the state machine with a NullReferenceException. Here’s an example of how to call AuthorizeAsync:

            await authorizer.AuthorizeAsync();

Once you call AuthorizeAsync via authorizer, LINQ to Twitter takes care of all the OAuth protocol details and you’ll see a login screen like this:

TwitterLogin

This is from Windows Phone and other devices will be similar. Fill in your login credentials, authorize via the Twitter Authorization page, and control returns to your app.

If you debug and examine authorizer.CredentialStore, you’ll see all 4 keys for that particular user, along with UserID and ScreenName. This is the time for you to read those values and save them, associated with the user using your app. The next time that user wants to perform any operation, retrieve these keys and populate CredentialStore. This will allow the user to use your application without needing to go through the log-in/Authorization process again. You can do this because Twitter does not change these keys, meaning you can save and reuse them for the next time the user wants you to access Twitter on their behalf.

Tip: If you re-populate all 4 keys, the user won’t need to go through the authorization process, which is convenient. However, UserID and ScreenName populate as part of the authorization process, so you should grab them the first time the user authorizes. If you need these values again later, because sometimes a ScreenName changes, you can do an Account/VerifyCredentials query.

After authorization success, or loading all 4 keys in the credential store, instantiate a TwitterContext, passing in the authorizer instance, like this:

            var ctx = new TwitterContext(authorizer);

You’ll see other libraries and examples where a library constructor accepts keys directly, without a special authorization object. That approach presumes your authorization strategy, of which there are several that Twitter supports – as does LINQ to Twitter. Such a simplification would lead new developers down a path of failure too often, so I opted for a safer, albeit slightly more complex, approach that also demonstrates how much freedom the developer has with authorization. e.g. you could use SingleUserAuthorizer to simplify server applications that only operate on behalf of your company user or ApplicationOnlyAuthorizer if your application doesn’t operate on behalf of a user at all. Instantiate the authorizer, authorize, and pass the authorizer (instance of LinqToTwitter.IAuthorizer) to TwitterContext.

Once you have a TwitterContext instance, you can tweet as explained next.

Tweeting

You can tweet and perform other LINQ to Twitter commands via the TwitterContext instance (ctx in this example):

            Status tweet = await ctx.TweetAsync(userInput);

The userInput variable is some text you want to tweet. As I mentioned earlier, remember that LINQ to Twitter is async and you must await commands and queries.

The response from the Twitter API contains the new tweet details. In particular for this example, tweet contains a StatusIDResponse property, which contains the ID of the new status. Since this Status type is the same one used for queries, it has a StatusID, used for input, and StatusIDResponse, used for output. Anytime there’s an input where twitter returns an output of the same name, the convention is for the output to have a “Response” suffix.

The previous examples explained authorization and how to tweet and you still haven’t seen any LINQ. I’ll fix that in the next section that shows how to perform a query.

Querying

The next set of examples show how to create a different type of authorizer, perform a query, and consume the results of that query.  The particular query is a Twitter Search. A search doesn’t operate on behalf of a user, so you don’t need an authorizer that requires user authorization. This example, shown next, instantiates an ApplicationOnlyAuthorizer:

            var authorizer = new ApplicationOnlyAuthorizer
            {
                CredentialStore = new InMemoryCredentialStore
                {
                    ConsumerKey = "",
                    ConsumerSecret = ""
                }
            };

            await authorizer.AuthorizeAsync();
            var ctx = new TwitterContext(authorizer);

The ApplicationOnlyAuthorizer only needs ConsumerKey and ConsumerSecret. Since it’s based on the application, there isn’t a user authorization process and you don’t have the intermediate step of re-directing to the Twitter Authorization page and back. Like normal, call AuthorizeAsync and pass the authorizer during instantiation of TwitterContext. Now you can perform a search, like this:

            Search searchResponse =
                await
                (from search in ctx.Search
                 where search.Type == SearchType.Search &&
                       search.Query == searchString
                 select search)
                .SingleOrDefaultAsync();

You can see that this is LINQ syntax and there are some features that are particularly interesting in this LINQ to Twitter query: entity, type, and async. TwitterContext contains several IQueryable<T> properties that refer to different types of queries that you can make. These categories roughly map to similar categories in the Twitter API and because the Twitter API has evolved, the categories don’t always match cleanly. That said, the entities and categories they represent are mostly semantically equivalent and help in the readability of the code. The LINQ to Twitter documentation contains a map between APIs and documentation for each LINQ to Twitter command or links in query documentation to corresponding Twitter API endpoints in case you need the extra help. For each entity, there is a type, which helps organize the queries. For Search, there is only one SearchType.Search, but other entities have many types that you can query. Each query has properties, that correspond to Twitter API parameters. These parameters are documented in the LINQ to Twitter Documentation. Remember to only use parameters listed in the documentation – the others correspond to output values and won’t work.

Tip: The Twitter API is a REST endpoint that only accepts parameters that the API specifies. You are using LINQ, but it’s a Twitter API specific dialect and you can’t perform the same operations you would with a SQL database. This is the inherent nature of the data source. The typical work-around for manipulating data is to perform the query, pulling in all the data you’ll need and then using LINQ to Objects once you have that data in memory.

As mentioned earlier, LINQ to Twitter is async, so you await queries too. LINQ to Twitter was the first 3rd party LINQ provider, outside of Microsoft, to support async. I waited a while to see how Microsoft would implement this and received an answer when they added async to the Entity Framework (EF). The LINQ to Twitter async implementation is syntactically similar to EF in that you await the query and materialize it with a standard operator containing the “Async” suffix. You must use the standard operator overload with the “Async”suffix because it returns a Task<T>, where T is Search in this example. The entity type is IQueryable<Search> and queries on it return a List<Search> response type. The Search query returns a single instance of the Search type, so SingleOrDefaultAsync works well. Other queries return a collection and you’ll want to use ToListAsync in those cases. Each type is different and you can find out about their contents via the LINQ to twitter documentation for entities. In the case of a search, here’s how you can access the information returned from the Twitter API:

            List<TweetViewModel> tweets =
                (from tweet in searchResponse.Statuses
                 select new TweetViewModel
                 {
                     ImageUrl = tweet.User.ProfileImageUrl,
                     ScreenName = tweet.User.ScreenNameResponse,
                     Text = tweet.Text
                 })
                .ToList();

The Search instance, searchResponse, has a Statuses property that is a List<Status>. After the query materializes, you can use LINQ to Objects to manipulate the collection of results any way you want. This example projected into a List<TweetViewModel> for UI presentation.

The source code is in the Samples folder of the LINQ to Twitter GitHub repository.

Summary

The primary support for UWP was with a new HttpClient and the unification of the authorization model via UniversalAuthorizer. UniversalAuthorizer works for Desktop/Tablet and Windows Phone apps. The authorization types are extensible through IAuthorizer and ICredentialStore, but you can use the implementations that LINQ to Twitter provides too. Remember that LINQ to Twitter is async and bad things happen when you forget to await commands and queries. Once you’ve authorized and instantiated a TwitterContext, use that TwitterContext instance to perform commands and queries with the Twitter API. If you need help, there’s extensive documentation in the LINQ to Twitter GitHub Wiki and you can ask questions on StackOverflow with the linq-to-twitter and twitter tags (tip: sometimes including the C# tag gets a quick answer).

 

@JoeMayo

Posted on Wednesday, March 2, 2016 8:25 AM | Back to top

Copyright © Joe Mayo | Powered by: GeeksWithBlogs.net