WebForms no longer working

Feb 12, 2014 at 6:15 PM
Edited Feb 12, 2014 at 6:25 PM
I've had the following code working for a year or so in my ASP.NET 4.5 WebForms application using LinqToTwitter 2.1.11.0:
            var authorizer = new SingleUserAuthorizer
            {
                Credentials = new InMemoryCredentials
                {
                    ConsumerKey = Const.ConsumerKey,
                    ConsumerSecret = Const.ConsumerSecret,
                    OAuthToken = Const.OAuthToken,
                    AccessToken = Const.AccessToken,
                },
                UseCompression = true
            };

                var ctx = new TwitterContext(authorizer);

                    var queryResponse =
                        (from tweet in ctx.Status
                            where tweet.Type == StatusType.User && tweet.ScreenName == screenName
                            select tweet);

                    result.AddRange(queryResponse.Select(tweet => new Message
                    {
                            ....
                    }));
I have today upgraded to LinqToTwitter 3.0.2 and adjusted my code like this:

            var authorizer = new SingleUserAuthorizer
            {
                CredentialStore = new InMemoryCredentialStore
                {
                    ConsumerKey = Const.ConsumerKey,
                    ConsumerSecret = Const.ConsumerSecret,
                    OAuthToken = Const.OAuthToken,
                    OAuthTokenSecret = Const.OAuthSecret,
                }
            };

                var ctx = new TwitterContext(authorizer);

                    var queryResponse =
                        (from tweet in ctx.Status
                            where tweet.Type == StatusType.User && tweet.ScreenName == screenName
                            select tweet);

                    result.AddRange(queryResponse.Select(tweet => new Message
                    {
                            ....
                    }));
when it reaches the line queryResponse.Select(.... it just freezes up, no error, nothing at all. I have to kill the application and restart.

I've tried using the async model and the same thing happens. It never returns when you execute the .ToListAsync().

These calls are being made during an .ASPX page load event if that is any help.

I should also add that if I copy the exact same code out of the WebForms project and paste into a console application it works fine, both async and sync.

I've also tried using the AspNetAuthorizer and the SessionStateCredentialStore to no avail.

Have you any ideas why this is no longer working in my web form page?
Coordinator
Feb 12, 2014 at 6:49 PM
Hi,

There's a sample project named Linq2TwitterDemos_WebForms in the downloadable source code. Maybe you can get that working and then compare differences.

@JoeMayo
Feb 12, 2014 at 8:27 PM
Downloading it now. I'll let you know. Thanks.
Feb 12, 2014 at 9:14 PM
Ok, this part of your demo is pretty much the same code as I have:
            var auth = new AspNetAuthorizer
            {  CredentialStore = new InMemoryCredentialStore
                {
                ConsumerKey = ConfigurationManager.AppSettings["consumerKey"],
                ConsumerSecret = ConfigurationManager.AppSettings["consumerSecret"],
                OAuthToken = ConfigurationManager.AppSettings["accessToken"],
                OAuthTokenSecret = ConfigurationManager.AppSettings["accessTokenSecret"],
                  }, 
                GoToTwitterAuthorization = twitterUrl => { }
            };

            var ctx = new TwitterContext(auth);

            var tweets =
                await
                (from tweet in ctx.Status
                 where tweet.Type == StatusType.Home
                 select tweet)
                .ToListAsync();

            TwitterListView.DataSource = tweets;
            TwitterListView.DataBind();
It works fine. I guess it suggests something is clashing somewhere but I've no idea how to find out what is causing it to fail. I suppose a start would be to compile the LinqToTwitter source and reference it directly see if I can spot something.

Any other ideas would be appreciated!

Thanks.
Feb 12, 2014 at 9:43 PM
Hmm, ok I've download the latest source code "commit 2e4bde5ee94e, Jan 21, 2014" and that works however that is not the latest 3.0.2 release so I am unable to get any further.

Are you planning on releasing the source for 3.0.2 so I can try and debug my problem?

Thanks.
Coordinator
Feb 13, 2014 at 5:26 AM
If the demo code is working, could the problem be related to your use of async? e.g. is your method async void?

LinqToTwitterPcl.dll is 3.0.2, but I didn't version the demos.
Feb 13, 2014 at 5:31 AM
Interestingly enough, I'm having the same issue today, but with a slightly different set of circumstances. I too am attempting to use SingleUserAuthorizer, however mine works... in a console application. The second I move that same code into a web project, I get the same symptom x6tus describes on the same type of query:

mentionsList =
(from mention in twitterCtx.Status
where mention.Type == StatusType.Mentions
select mention).ToList()


Under "configuring oauth", I did recall seeing this nugget of wisdom:

_I always set application type to Browser. This gives you the full OAuth flow. If you accidentally set it to Client, then only desktop applications, like Console, Windows Forms, and WPF apps will be able to run. Setting it to Browser lets Web apps run too. In the past, I've fielded questions where someone can get my Console examples to work, but the Web example won't work; the answer being that they needed to reconfigure their app to use Browser.
_



So I went back and started to make a new twitter app, only to find that I can't find that option/setting anywhere....

The questions that arise for me are:
  1. Is the "browser" application type no longer relevant? If it's not, then :
  2. What's different between a console application and a web site/app that might cause this?
  3. Is SingleUserAuthorizer not designed for websites? The documentation around that authorization doesn't say anything that makes me believe it's invalid to use it (at least that I've seen). What I'm writing is a single user app, so this seems like the appropriate authorization mechanism for me. I've also never done oauth before, so was avoiding it.
Feb 13, 2014 at 10:14 AM
Edited Feb 13, 2014 at 6:11 PM
Ok, I think from these posts it is deadlocking.

http://stackoverflow.com/questions/10343632/httpclient-getasync-never-returns-when-using-await-async/10351400#10351400

http://blog.stephencleary.com/2012/07/dont-block-on-async-code.html

My code is being called from a WebPage model binding event on a repeater control like this...


.aspx
        <asp:Repeater ID="rpTopicsList" runat="server"
            ItemType="Shared.Data.Forum.Topic" SelectMethod="GetTopics">
.aspx.cs
        public IEnumerable<Topic> GetTopics([Control("lbTopicID")] int? topicID)
        {
                var tweets = GetTweetsAsync(_twitterUserName);
                ......
         }
I don't think I fully understand how to call an async control from a web page.

In fact I don't think this can be done within a model binding call :(
Feb 13, 2014 at 11:50 AM
Unfortunately I've not been able to resolve my issues with my web forms so I've had to revert to version 2.1.11.0 :(
Feb 13, 2014 at 3:06 PM
x6tus, your research is interesting. I tried an asynchronous approach and it didn't work, but after reading the links you posted I wouldn't be surprised if I ran into a similar problem as what was described in the stephencleary blog. I guess it poses another question that I've had for a couple days now, that being "why do we need or want asynchronous calls in linqtotwitter?" I understand the performance related benefits to using asynch calls, but it feels like it's more of a "requirement" for this project? Is that the case? The working samples I've been able to write were standard, non-async.
Feb 13, 2014 at 3:44 PM
Yes indeed I've happily had the non-async working in our production system for well over a year with no issues. I didn't want to upgrade to async if I could help it but I don't think there is an option now.
Coordinator
Feb 13, 2014 at 4:25 PM
Edited Feb 13, 2014 at 4:28 PM
jbart08,
  1. It seems Twitter has changed their Application page since I wrote the bit about "browser". Thanks, I've updated my docs.
  2. The SynchronizationContext is unique for each technology - Steven Cleary's posts and articles are good resources. Here's another: http://msdn.microsoft.com/en-us/magazine/gg598924.aspx
  3. SingleUserAuthorizer should work everywhere. Just give it all 4 credentials (from your Twitter App page).
jbart08 wrote:
The questions that arise for me are:
  1. Is the "browser" application type no longer relevant? If it's not, then :
  2. What's different between a console application and a web site/app that might cause this?
  3. Is SingleUserAuthorizer not designed for websites? The documentation around that authorization doesn't say anything that makes me believe it's invalid to use it (at least that I've seen). What I'm writing is a single user app, so this seems like the appropriate authorization mechanism for me. I've also never done oauth before, so was avoiding it.
Coordinator
Feb 13, 2014 at 4:39 PM
x6tus,

This is tricky and I'll have to look at that scenario more. In Console apps, you can do something like:
Task tweetsTask = GetTweetsAsync(_twitterUserName);
tweetsTask.Wait();
var tweets = tweetsTask.Result;
But I haven't tried that in a control callback like this yet.
Coordinator
Feb 13, 2014 at 4:51 PM
Async can be useful in different scenarios and necessary with some technologies like windows phone. I've received a lot of feedback from people who wanted it and are pleased that it's there. Clearly, there are some wrinkles and I'll be using this feedback to move forward and improve or try to find the best possible work-arounds for different scenarios.
Feb 13, 2014 at 6:11 PM
Edited Feb 13, 2014 at 6:15 PM
Hi Joe, thanks for your input. Actually in my case the async would benefit my system if I could get it working.

I have a repeater nested in a repeater. The parent repeater model binds to a list of companies. The child repeater model binds to the tweets for those companies. So I have to make a call to LinqToTwitter for each of the parent rows and get all that data back before the page displays. At the moment for about 20-50 companies and 20 tweets each it isn't really noticeable. But I can imagine if the site gets heavily used that we'd start to see an impact. I'd imagine that the async methods would help reduce this.

(In reality I cache the tweets for each company for a certain amount of time to help with performance but for the purposes of getting this working I am ignoring that).

I tried pushing the async back up the the chain by trying something like this:
  public async Task<IEnumerable<Topic>> GetTopicsAsync([Control("lbTopicID")] int? topicID)
        {
                var tweets = GetTweetsAsync(_twitterUserName);

               // get topic data here

               // append tweets to topic data

                ......
         }
But obviously the repeater model binding doesn't work with such a call as it expects an IEnumerable of type Topic not a task.

Also the nesting makes my situation more complex as I'm accessing a control within the parent repeater (lbTopicID) to get the relevant ID for the nest-ED repeater to get data from and add the tweets to.
Feb 14, 2014 at 2:31 AM
Gentlemen,

Here's the basic codebehind for a web form that hangs in the new release for me:
private const string consumerKey = "xxxxxxxxx";
private const string consumerSecret = "xxxxxxxxx";
private const string accessToken = "xxxxxxxxx";
private const string accessTokenSecret = "xxxxxxxxx";
protected void Page_Load(object sender, EventArgs e)
{
    var auth = new SingleUserAuthorizer
    {
        CredentialStore = new SingleUserInMemoryCredentialStore
        {
            ConsumerKey = consumerKey,
            ConsumerSecret = consumerSecret,
            AccessToken = accessToken,
            AccessTokenSecret = accessTokenSecret
        }
    };

    var twitterCtx = new TwitterContext(auth);

    var mentionsList =
        from mention in twitterCtx.Status
        where mention.Type == StatusType.Mentions
        select mention;

    foreach (Status s in mentionsList)
    {
        Response.Write(s.CreatedAt.ToString() + " : " + s.Text + "<br/>");
    }
}
}
Feb 14, 2014 at 11:00 AM
jbart08, that's pretty much my scenario too, just without the repeaters.
Feb 15, 2014 at 1:48 AM
I've played with this a couple ways now. I was able to get an asynchronous version of the console application working, but when I ported that over to a website it still hangs in the same area. One thing I did notice different between my console app and my web app is that the console app only has the LinqToTwitterPCL dll, but my website has the additional ASP dll. Made me wonder if it was possible the aspnet version has an issue that the pcl dll doesn't? I tried removing the aspnet dll from my web app, but unfortunately that didn't have any change in behavior.
Feb 15, 2014 at 2:02 AM
SUCCESS.


After a night of rest, a fresh set of eyes, and my 47th pass over the 3.0 demos, I was able to find the parts I was missing to make this work. Here's my code from above, written asynchronously, and working. X6tus, hopefully this helps you out!


private const string consumerKey = "xxxxxxxxx";
private const string consumerSecret = "xxxxxxxxx";
private const string accessToken = "xxxxxxxxx";
private const string accessTokenSecret = "xxxxxxxxx";
protected async void Page_Load(object sender, EventArgs e)
{
    var auth = new SingleUserAuthorizer
    {
        CredentialStore = new SingleUserInMemoryCredentialStore
        {
            ConsumerKey = consumerKey,
            ConsumerSecret = consumerSecret,
            AccessToken = accessToken,
            AccessTokenSecret = accessTokenSecret
        }
    };

    await auth.AuthorizeAsync();

    using (var twitterCtx = new TwitterContext(auth))
    {
        var mentionsList = await
        (from mention in twitterCtx.Status
        where mention.Type == StatusType.Mentions
        select mention).ToListAsync();

        foreach (Status s in mentionsList)
        {
            Response.Write(s.CreatedAt.ToString() + " : " + s.Text + "<br/>");
        }

    } ;
}
Feb 15, 2014 at 2:28 PM
Fraid not, because I'm using model binding on a repeater I can't async all the way up the chain so I think it deadlocks still. Potentially I could rewrite my code to not use the select method on the repeater and call DataBind myself but I don't really want to go and change working production code.

Here's my example of non-working code:

WebForm1.aspx
%@ Page Language="C#" AutoEventWireup="true" CodeBehind="WebForm1.aspx.cs" Inherits="LinqToTwitterTest.WebForm1" %>

<!DOCTYPE html>
<html>
<head>
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
        <asp:Repeater runat="server" SelectMethod="GetTweets" ItemType="LinqToTwitterTest.Tweet">
            <ItemTemplate>
                <%#: Item.Text %>
            </ItemTemplate>
        </asp:Repeater>
    </form>
</body>
</html>
WebForm1.aspx.cs
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using LinqToTwitter;

namespace LinqToTwitterTest
{
    public partial class WebForm1 : System.Web.UI.Page
    {
        private const string ConsumerKey = "wwwwww";
        private const string ConsumerSecret = "xxxxx";
        private const string AccessToken = "yyyyyy";
        private const string AccessTokenSecret = "zzzzzz";

        public IEnumerable<Tweet> GetTweets()
        {
            return GetTweetsInternal().Result;
        }

        private async Task<IEnumerable<Tweet>> GetTweetsInternal()
        {
            var authorizer = new SingleUserAuthorizer
            {
                CredentialStore = new SingleUserInMemoryCredentialStore
                {
                    ConsumerKey = ConsumerKey,
                    ConsumerSecret = ConsumerSecret,
                    AccessToken = AccessToken,
                    AccessTokenSecret = AccessTokenSecret
                }
            };

            await authorizer.AuthorizeAsync();

            using (var context = new TwitterContext(authorizer))
            {
                var tweetList = await
                    (from mention in context.Status
                        where mention.Type == StatusType.Mentions
                        select mention).ToListAsync();

                return tweetList.Select(item => new Tweet {Text = item.Text}).ToList();
            }
        }
    }

    public class Tweet
    {
        public string Text { get; set; }
    }
}
Feb 15, 2014 at 2:35 PM
Edited Feb 15, 2014 at 2:35 PM
Also for what it's worth I don't think you should be doing:
protected async void Page_Load(object sender, EventArgs e)
According to hanselman...
Using async with voids is not stable or reliable. However, all you have to do is call Page.RegisterAyncTask - it's not any trouble and you'll be in a better more flexible place.
http://www.hanselman.com/blog/TheMagicOfUsingAsynchronousMethodsInASPNET45PlusAnImportantGotcha.aspx

So it should be:
public void Page_Load(object sender, EventArgs e)
{
    RegisterAsyncTask(new PageAsyncTask(LoadSomeData));
}
 
public async Task LoadSomeData()
{
Thank you for your help though!
Feb 15, 2014 at 5:00 PM
Edited Feb 18, 2014 at 9:41 AM
I tweeted Scott Hanselman about my problem and he said this:
I think that's technically possible but didn't make it in. @DamianEdwards ?
it being, .NET 4.5 WebForms.
Feb 18, 2014 at 9:44 AM
To which Damian Edwards has now replied...

@shanselman @GarethParris not in the box, we might ship a package to enable though, prototype of support here though https://t.co/RcZFWyGbyR

So for me I will not be able to use the new async LinqToTwitter unless I rewrite the code. As this is a production system it isn't going to happen in the short-term so I'll have to stick with v2.1.11 :(
Mar 14, 2014 at 7:52 AM
Guys Im having the same problem and the one thing that makes it work is turning async off in web.config
<add key="aspnet:UseTaskFriendlySynchronizationContext" value="false" />

But seeing as this is not an option for scaling in the future. Id like to get any ideas you guys have on how to fix this.

This is my code
public class Auth
    {
        //Linq2Twitter
        public async static Task<TwitterContext> GetTwitterContext() {
            var auth = new ApplicationOnlyAuthorizer {
                CredentialStore = new InMemoryCredentialStore { ConsumerKey = Auth.ConsumerKey, ConsumerSecret = Auth.ConsumerSecret }
            };
            await auth.AuthorizeAsync();
            return new TwitterContext( auth );
        }
}

  public static IEnumerable GetTweets( string ScreenName ) {
            if ( !string.IsNullOrWhiteSpace( ScreenName ) ) {
                var task = Auth.GetTwitterContext();
                if ( task.IsCompleted ) {
                    using ( var twitter = task.Result ) {
                        return twitter.Status.Where( tweet => tweet.Type == StatusType.User && tweet.ScreenName == ScreenName )
                                             .Select( t => new {
                                                 t.ScreenName, t.User.Name, 
                                                 ProfileImageUrl = t.User.ProfileImageUrl
                                             } ).ToList();
                    }
                }
                return null;
            }
            else return null;
}

and the calling code in the page load


                TwitterR.DataSource = NightlifeService.GetTweets( TwitterHandle );
                TwitterR.DataBind();
Now this all works perfectly when I turn off Async in web.config

When I remove that line in web.config it just deadlocks and the browser keeps saying Waiting for localhost indefinitely.