Search code examples
linq-to-twitter

LinqToTwitter Search Never Returns


I am attempting to use LinqToTwitter to search twitter. It works fine as run in an NUnit test but it does not work with ASP.NET or as a WinForm app. I am not sure what Authorizer to use.

public async Task<Search> SearchTwitter(string searchWords)
{
    var twitterCtx = BuildTwitterContext();

    Task<Search> searchResponse = (from search in twitterCtx.Search
                                   where search.Type == SearchType.Search &&
                                         search.Query == searchWords
                                   select search)
        .SingleOrDefaultAsync();

    return await searchResponse;
}

private static TwitterContext BuildTwitterContext()
{
    IAuthorizer authorizer;

    if (HttpContext.Current == null)
        authorizer = new PinAuthorizer();
    else
        authorizer = new AspNetSignInAuthorizer();

    InMemoryCredentialStore credentialStore = new InMemoryCredentialStore();
    credentialStore.ConsumerKey = consumerKey;
    credentialStore.ConsumerSecret = consumerSecret;
    credentialStore.OAuthToken = accessToken;
    credentialStore.OAuthTokenSecret = accessTokenSecret;
    authorizer.CredentialStore = credentialStore;
    var twitterCtx = new TwitterContext(authorizer);
    return twitterCtx;
}

Solution

  • ASP.NET is different because of the page redirections where you start the authorization and then finish after Twitter redirects back. Here's the LINQ to Twitter documentation that will explain how OAuth works and give you a better idea on which authorizers to use:

    https://github.com/JoeMayo/LinqToTwitter/wiki/Learning-to-use-OAuth

    The L2T source code also has demos. Here's an OAuth controller demo:

    https://github.com/JoeMayo/LinqToTwitter/blob/master/New/Linq2TwitterDemos_Mvc/Controllers/OAuthController.cs

    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 it uses a WebAuthorizer/SessionStateCredentials pair and separates the start of authorization with a separate action method (specified via callback) for completion.

    The following demo shows how to perform OAuth in a WinForms app:

    https://github.com/JoeMayo/LinqToTwitter/blob/master/New/Demos/Linq2TwitterDemos_WindowsForms/OAuthForm.cs

    public partial class OAuthForm : Form
    {
        PinAuthorizer pinAuth = new PinAuthorizer();
    
        public OAuthForm()
        {
            InitializeComponent();
        }
    
        async void OAuthForm_Load(object sender, EventArgs e)
        {
            pinAuth = new PinAuthorizer
            {
                // Get the ConsumerKey and ConsumerSecret for your app and load them here.
                CredentialStore = new InMemoryCredentialStore
                {
                    ConsumerKey = ConfigurationManager.AppSettings["consumerKey"],
                    ConsumerSecret = ConfigurationManager.AppSettings["consumerSecret"]
                },
                // Note: GetPin isn't used here because we've broken the authorization
                // process into two parts: begin and complete
                GoToTwitterAuthorization = pageLink => 
                    OAuthWebBrowser.Navigate(new Uri(pageLink, UriKind.Absolute))
            };
    
            await pinAuth.BeginAuthorizeAsync();
        }
    
        async void SubmitPinButton_Click(object sender, EventArgs e)
        {
            await pinAuth.CompleteAuthorizeAsync(PinTextBox.Text);
            SharedState.Authorizer = pinAuth;
    
            // 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 = pinAuth.CredentialStore;
            //string oauthToken = credentials.OAuthToken;
            //string oauthTokenSecret = credentials.OAuthTokenSecret;
            //string screenName = credentials.ScreenName;
            //ulong userID = credentials.UserID;
            //
    
            Close();
        }
    }
    

    In this case, you can use a PinAuthorizer with an InMemoryCredentialStore. If you look at that demo, it uses a Web Browser control to navigate to Twitter and manage the OAuth flow.

    Look at the URL above for the Learning to use OAuth for examples of other IAuthorizer derived types that you can use in different scenarios. Also, download the source code and step through with the debugger to get a feel for the OAuth workflow.