Leveraging a Custom Twitter Service

,

A custom Twitter service is used for all communication with the Twitter web API. The service also provides the capability to cache the results of calls to Twitter using a local database.

The viewmodel, which presents the Twitter timeline information, consumes the Twitter service using an ITwitterService interface.

Image

The ITwitterService contains two methods designed to execute asynchronously. They are as follows:

Image GetTimeline(string screenName,
                        Action<ResultEventArgs<TwitterUser>> completeAction)

Image GetCachedTimeline(
                        string screenName,
                        Action<ResultEventArgs<TwitterUser>> completeAction)

A caller of either method provides an Action argument, which, on completion of the method, is invoked, notifying the caller of the result.

The ResultEventArgs class either contains the result produced by the operation or an Exception object. ResultEventArgs is presented in Chapter 27, “Communicating with Network Services.”

GetTimeline uses the Twitter web API to retrieve a Twitter user’s details and list of status updates. After a response is received from the Web, the result is converted into entity objects representing the Twitter user and the status updates (TimelineItem objects).

GetCachedTimeline is used to retrieve a TwitterUser from the database. This method does not produce a result until GetTimeline has successfully completed at least once, because until then no data has been placed in the database.

The default implementation of the ITwitterService is the TwitterService class. TwitterService relies on the TwitterDatabaseUtility. A TwitterDataContext is created when the TwitterService is instantiated; as shown:

public class TwitterService : ITwitterService
{
    readonly TwitterDatabaseUtility twitterDatabaseUtility
                                       = new TwitterDatabaseUtility();
    readonly TwitterDataContext dataContext;

    public TwitterService()
    {
        dataContext = twitterDatabaseUtility.CreateContext();
    }
...
}

The public GetTimeline method queues the Twitter call on a ThreadPool thread. To demonstrate population using the local database, the ThreadPool thread is made to wait for 3 seconds before calling the main GetTimelineCore method. If an exception is raised, the complete action is called, as shown:

public void GetTimeline(
    string screenName, Action<ResultEventArgs<TwitterUser>> completeAction)
{
    ArgumentValidator.AssertNotNullOrWhiteSpace(screenName, "userName");
    ArgumentValidator.AssertNotNull(completeAction, "completeAction");

    ThreadPool.QueueUserWorkItem(
        delegate
        {
            try
            {
                /*Wait for a moment to demonstrate the database retrieval.*/
                Wait(3000); /* Equivalent to Thread.Sleep(3000); */
                GetTimelineCore(screenName, completeAction);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
                completeAction(new ResultEventArgs<TwitterUser>(null, ex));
            }
        });
}

GetTimelineCore uses the Twitter web API (see Listing 29.4). A WebClient is used to dispatch an HTTP request to the site. When the call returns, it is handled within a lambda expression. The result of the call is an XML fragment, which we convert to an XElement using the XElement.Parse method.

The user information is then extracted from the root element, before building a list of TimelineItems. Finally, the data context is used to store the TwitterUser and its associated TimelineItems in the database.

LISTING 29.4. GetTimelineCore Method


void GetTimelineCore(
        string screenName, Action<ResultEventArgs<TwitterUser>> completeAction)
{
    WebClient twitterClient = new WebClient();
    twitterClient.DownloadStringCompleted
        += (o, args) =>
        {
            if (args.Error != null)
            {
                completeAction(
                        new ResultEventArgs<TwitterUser>(null, args.Error));
                return;
            }

            try
            {
                XElement rootElement = XElement.Parse(args.Result);

                XElement element = rootElement.Descendants("status").First();
                XElement userElement = element.Descendants("user").First();
                var twitterUser = new TwitterUser
                {
                    Id = userElement.Element("id").Value,
                    ScreenName = userElement.Element("screen_name").Value,
                    Description = userElement.Element("description").Value,
                    ImageUrl = userElement.Element("profile_image_url").Value,
                };

                IEnumerable<TimelineItem> items = from statusElement
                                    in rootElement.Descendants("status")
                            select new TimelineItem
                            {
                                Id = statusElement.Element("id").Value,
                                ReceivedTime = ConvertToDateTime(
                                    statusElement.Element("created_at").Value),
                                Text = statusElement.Element("text").Value
                            };
                TwitterUser storedUser = dataContext.TwitterUsers.Where(
                    user => user.ScreenName == screenName).FirstOrDefault();

                if (storedUser != null)
                {
                    dataContext.TimelineItems.DeleteAllOnSubmit(
                        dataContext.TimelineItems.Where(
                            item => item.TwitterUserId == storedUser.Id));
                    dataContext.TwitterUsers.DeleteOnSubmit(storedUser);
                    dataContext.SubmitChanges();
                }

                twitterUser.TimelineItems.AddRange(items);
                dataContext.TwitterUsers.InsertOnSubmit(twitterUser);

                dataContext.SubmitChanges();

                completeAction(new ResultEventArgs<TwitterUser>(twitterUser));
            }
            catch (Exception ex)
            {
                Debug.WriteLine("Unable to get timeline. " + ex);
                completeAction(new ResultEventArgs<TwitterUser>(null, ex));
                return;
            }
        };

    twitterClient.DownloadStringAsync(new Uri(
        "http://api.twitter.com/1/statuses/user_timeline.xml?screen_name="
        + screenName));
}


After the Twitter web API has been queried and the result stored in the database, the cached data can be retrieved from the database. This allows the Twitter timeline information to remain viewable when no network connection is available.


Note

Changes to the entity model are not persisted to the database until DataContext.SubmitChanges is called. New entity objects that have been added to the data context are not inserted into the database until SubmitChanges is called, and entities that have been removed from the data context are not deleted from the database until SubmitChanges is called.


Retrieving the cached data from the local database is the task of the TwitterService.GetCachedTimeline method.

You see the same pattern applied as earlier; the principle GetCachedTimelineCore method is called from a ThreadPool thread, as shown:

public void GetCachedTimeline(
    string screenName, Action<ResultEventArgs<TwitterUser>> completeAction)
{
    ArgumentValidator.AssertNotNullOrWhiteSpace(screenName, "userName");
    ArgumentValidator.AssertNotNull(completeAction, "completeAction");

    ThreadPool.QueueUserWorkItem(
        delegate
        {
            try
            {
                GetCachedTimelineCore(screenName, completeAction);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
                completeAction(new ResultEventArgs<TwitterUser>(null, ex));
            }
        });
}

The GetCachedTimelineCore method uses the data context to retrieve the Twitter user with the specified screen name, shown in the following excerpt:

void GetCachedTimelineCore(
      string userName, Action<ResultEventArgs<TwitterUser>> completeAction)
{
    TwitterUser storedUser = dataContext.TwitterUsers.Where(
            user => user.ScreenName == userName).FirstOrDefault();

    completeAction(new ResultEventArgs<TwitterUser>(storedUser));
}

You saw how the TwitterService is able to retrieve live data from the Twitter web API and then cache it using a local database. Subsequent sections look at presenting this data to the user.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.147.6.10