Search code examples
c#asp.nettwitterlinq-to-twitter

How to search tweets using linqtotwitter on an MVC project


How do I search for tweets using linqtotwitter on an MVC project, converting this from console to MVC.

var searchResponse =
            await
            (from search in twitterCtx.Search
             where search.Type == SearchType.Search &&
                   search.Query == "\"LINQ to Twitter\""
             select search)
            .SingleOrDefaultAsync();

        if (searchResponse != null && searchResponse.Statuses != null)
            searchResponse.Statuses.ForEach(tweet =>
                Console.WriteLine(
                    "User: {0}, Tweet: {1}", 
                    tweet.User.ScreenNameResponse,
                    tweet.Text));

Solution

  • The main thing with MVC is to use the right authorizer. The technique I use is a separate controller to handle OAuth, like this:

    using System;
    using System.Configuration;
    using System.Linq;
    using System.Threading.Tasks;
    using System.Web.Mvc;
    using LinqToTwitter;
    
    namespace MvcDemo.Controllers
    {
        public class OAuthController : AsyncController
        {
            public ActionResult Index()
            {
                return View();
            }
    
            public async Task<ActionResult> BeginAsync()
            {
                //var auth = new MvcSignInAuthorizer
                var auth = new MvcAuthorizer
                {
                    CredentialStore = new SessionStateCredentialStore
                    {
                        ConsumerKey = ConfigurationManager.AppSettings["consumerKey"],
                        ConsumerSecret = ConfigurationManager.AppSettings["consumerSecret"]
                    }
                };
    
                string twitterCallbackUrl = Request.Url.ToString().Replace("Begin", "Complete");
                return await auth.BeginAuthorizationAsync(new Uri(twitterCallbackUrl));
            }
    
            public async Task<ActionResult> CompleteAsync()
            {
                var auth = new MvcAuthorizer
                {
                    CredentialStore = new SessionStateCredentialStore()
                };
    
                await auth.CompleteAuthorizeAsync(Request.Url);
    
                // This is how you access credentials after authorization.
                // The oauthToken and oauthTokenSecret do not expire.
                // You can use the userID to associate the credentials with the user.
                // You can save credentials any way you want - database, 
                //   isolated storage, etc. - it's up to you.
                // You can retrieve and load all 4 credentials on subsequent 
                //   queries to avoid the need to re-authorize.
                // When you've loaded all 4 credentials, LINQ to Twitter will let 
                //   you make queries without re-authorizing.
                //
                //var credentials = auth.CredentialStore;
                //string oauthToken = credentials.OAuthToken;
                //string oauthTokenSecret = credentials.OAuthTokenSecret;
                //string screenName = credentials.ScreenName;
                //ulong userID = credentials.UserID;
                //
    
                return RedirectToAction("Index", "Home");
            }
        }
    }
    

    Notice that this is an MvcAuthorizer with a SessionStateCredentialStore. Also, remember to ensure your State Server is running so it doesn't arbitrarily dump your session state during IIS recycles. So now, when you need to detect if a user isn't authorized, send them to the OAuthController, like this:

    using LinqToTwitter;
    using System.Web.Mvc;
    
    namespace MvcDemo.Controllers
    {
        public class HomeController : Controller
        {
            public ActionResult Index()
            {
                if (!new SessionStateCredentialStore().HasAllCredentials())
                    return RedirectToAction("Index", "OAuth");
    
                return View();
            }
        }
    }
    

    Once the user is authorized, you can perform any LINQ to Twitter query in an action method, like this:

    using System;
    using System.Linq;
    using System.Threading.Tasks;
    using System.Web.Mvc;
    using MvcDemo.Models;
    using LinqToTwitter;
    using System.Collections.Generic;
    
    namespace MvcDemo.Controllers
    {
        public class StatusDemosController : Controller
        {
            public ActionResult Index()
            {
                return View();
            }
    
            public ActionResult Tweet()
            {
                var sendTweetVM = new SendTweetViewModel
                {
                    Text = "Testing async LINQ to Twitter in MVC - " + DateTime.Now.ToString()
                };
    
                return View(sendTweetVM);
            }
    
            [HttpPost]
            [ActionName("Tweet")]
            public async Task<ActionResult> TweetAsync(SendTweetViewModel tweet)
            {
                var auth = new MvcAuthorizer
                {
                    CredentialStore = new SessionStateCredentialStore()
                };
    
                var ctx = new TwitterContext(auth);
    
                Status responseTweet = await ctx.TweetAsync(tweet.Text);
    
                var responseTweetVM = new SendTweetViewModel
                {
                    Text = "Testing async LINQ to Twitter in MVC - " + DateTime.Now.ToString(),
                    Response = "Tweet successful! Response from Twitter: " + responseTweet.Text
                };
    
                return View(responseTweetVM);
            }
    
            [ActionName("HomeTimeline")]
            public async Task<ActionResult> HomeTimelineAsync()
            {
                var auth = new MvcAuthorizer
                {
                    CredentialStore = new SessionStateCredentialStore()
                };
    
                var ctx = new TwitterContext(auth);
    
                var tweets =
                    await
                    (from tweet in ctx.Status
                     where tweet.Type == StatusType.Home
                     select new TweetViewModel
                     {
                         ImageUrl = tweet.User.ProfileImageUrl,
                         ScreenName = tweet.User.ScreenNameResponse,
                         Text = tweet.Text
                     })
                    .ToListAsync();
    
                return View(tweets);
            }
    
            [ActionName("Search")]
            public async Task<ActionResult> SearchAsync()
            {
                var auth = new MvcAuthorizer
                {
                    CredentialStore = new SessionStateCredentialStore()
                };
    
                var ctx = new TwitterContext(auth);
    
                var searchResponse =
                    await
                    (from search in twitterCtx.Search
                     where search.Type == SearchType.Search &&
                           search.Query == "\"LINQ to Twitter\""
                     select search)
                    .SingleOrDefaultAsync();
    
                return View(searchResponse.Statuses);
            }
    
            [ActionName("UploadImage")]
            public async Task<ActionResult> UploadImageAsync()
            {
                var auth = new MvcAuthorizer
                {
                    CredentialStore = new SessionStateCredentialStore()
                };
    
                var twitterCtx = new TwitterContext(auth);
    
                string status = $"Testing multi-image tweet #Linq2Twitter £ {DateTime.Now}";
                string mediaCategory = "tweet_image";
    
                string path = Server.MapPath("..\\Content\\200xColor_2.png");
                var imageUploadTasks =
                    new List<Task<Media>>
                    {
                        twitterCtx.UploadMediaAsync(System.IO.File.ReadAllBytes(path), "image/jpg", mediaCategory),
                    };
    
                await Task.WhenAll(imageUploadTasks);
    
                List<ulong> mediaIds =
                    (from tsk in imageUploadTasks
                     select tsk.Result.MediaID)
                    .ToList();
    
                Status tweet = await twitterCtx.TweetAsync(status, mediaIds);
    
                return View(
                    new TweetViewModel
                    {
                        ImageUrl = tweet.User.ProfileImageUrl,
                        ScreenName = tweet.User.ScreenNameResponse,
                        Text = tweet.Text
                    });
            }
        }
    }
    

    I hand-coded your Search query in there, but see how that uses an MvcAuthorizer. The SessionStateCredentialStore pulls the previously obtained credentials from Session state.

    If you don't like Session state, implement ICredentialStore for where you want to persist credentials.

    You can also find the code for a working MVC demo on the LINQ to Twitter GitHub site.