Suggestions on Async Operation where each Operation Could Have Different Credentials

Apr 30, 2014 at 12:24 PM
What are your suggestions on how to execute PublishTweet using the Async method where each operation could have different credentials ?

Currently... the execute with credentials it appears only works with each operation having different credentials... if you use the NON async approach.
Coordinator
Apr 30, 2014 at 12:35 PM
Hi there,

Just to be sure we are on the same page. You are saying that when you use TwitterCredentials.ExecuteOperationWithCredentials with an async method it does not use the operation credentials?

If so, I will investigate and let you know what is wrong and how you can solve it.
Would you please confirm that I have understood your problem correctly?

Regards,
Linvi
Apr 30, 2014 at 12:41 PM
Code like the following.. where there are many async methods queued up.... end up with the global static application context... because the context gets rolled back in the method below...


TwitterCredentials.ExecuteOperationWithCredentials(credentials, () =>
                {
                        var tweet = TweetAsync.PublishTweet(message);
                        if (tweet == null)
                        {
                            logger.Error(string.Format("Twitter tweet of message for user id {0} failed, exception description: {1}, status code: {2}, details: {3}.", 
                                accessToken, ExceptionHandler.GetLastException().TwitterDescription,
                                ExceptionHandler.GetLastException().StatusCode, 
                                ExceptionHandler.GetLastException().TwitterExceptionInfos.First().Message ));
                            // just continue with notification processing and log errors about Twitter
                        }
                });

    public void ExecuteOperationWithCredentials(IOAuthCredentials credentials, Action operation)
    {
        var initialCredentials = CurrentThreadCredentials;
        CurrentThreadCredentials = credentials;
        operation();
        __CurrentThreadCredentials = initialCredentials;__
    }
Coordinator
Apr 30, 2014 at 1:08 PM
I am not sure what you are attempting to do there but I think you are not using the async framework correctly.
An Async method returns a Task<Tweet> not a Tweet.

The async method is also returning directly and do not wait for the operation to complete.
If you are willing to do an operation on the Tweet, you need to await the operation to have completed (await the tweet has been created on twitter).

I think you are willing to do the following :
TwitterCredentials.ExecuteOperationWithCredentials(credentials, () =>
{
        var tweetTask = TweetAsync.PublishTweet("message");
        tweetTask.RunSynchronously();
        var tweet = tweetTask.Result;

        if (tweet == null)
        {
            logger.Error(string.Format("Twitter tweet of message for user id {0} failed, exception description: {1}, status code: {2}, details: {3}.", 
                accessToken, ExceptionHandler.GetLastException().TwitterDescription,
                ExceptionHandler.GetLastException().StatusCode, 
                ExceptionHandler.GetLastException().TwitterExceptionInfos.First().Message ));
            // just continue with notification processing and log errors about Twitter
        }
});
Though I think there is a missing feature here and you are right, invoking from an async method and leave right away would cause concurrency between the static credentials of the operation and the backup one.

I will be working on this as my next priority. It will be part of Tweetinvi 0.9.3.0 or potentially available as an update to 0.9.2.3.
A work item has been created here.

Regards,
Linvi
Apr 30, 2014 at 1:14 PM
Based on your work and deep understanding of this framework (creator :))....

What potential issues would I have if I added something like the following class below.... and then switched the code to actually make the outer part async... and used the sync operation for the inner operation ??. I want to insure the credentials are passed all the way down on the actual web query building:
                TwitterCredentialsAsync.ExecuteOperationWithCredentials(credentials, () =>
                {
                        var tweet = Tweet.PublishTweet(message);
}


using System;
using Tweetinvi.Core.Interfaces.Credentials;
using Tweetinvi.Core.Interfaces.Factories;
using Tweetinvi.Core.Interfaces.oAuth;
using System.Threading.Tasks;

namespace Tweetinvi
{
public class TwitterCredentialsAsync
{
    private static readonly ICredentialsFactory _credentialsFactory;

    [ThreadStatic]
    private static ICredentialsAccessor _credentialsAccessor;
    public static ICredentialsAccessor CredentialsAccessor
    {
        get
        {
            if (_credentialsAccessor == null)
            {
                Initialize();
            }

            return _credentialsAccessor;
        }
    }

    static TwitterCredentialsAsync()
    {
        _credentialsFactory = TweetinviContainer.Resolve<ICredentialsFactory>();
    }

    private static void Initialize()
    {
        _credentialsAccessor = TweetinviContainer.Resolve<ICredentialsAccessor>();
    }

    public static IOAuthCredentials ApplicationCredentials
    {
        get { return CredentialsAccessor.ApplicationCredentials; }
        set { CredentialsAccessor.ApplicationCredentials = value; }
    }

    public static IOAuthCredentials Credentials
    {
        get { return CredentialsAccessor.CurrentThreadCredentials; }
        set { CredentialsAccessor.CurrentThreadCredentials = value; }
    }

    public static IOAuthCredentials CreateCredentials(string userAccessToken, string userAccessSecret, string consumerKey, string consumerSecret)
    {
        return _credentialsFactory.CreateOAuthCredentials(userAccessToken, userAccessSecret, consumerKey, consumerSecret);
    }

    public static void SetCredentials(string userAccessToken, string userAccessSecret, string consumerKey, string consumerSecret)
    {
        Credentials = CreateCredentials(userAccessToken, userAccessSecret, consumerKey, consumerSecret);
    }

    public static void SetCredentials(IOAuthCredentials credentials)
    {
        Credentials = credentials;
    }

    public static async Task<T> ExecuteOperationWithCredentials<T>(IOAuthCredentials credentials, Func<T> operation)
    {
        return await Sync.ExecuteTaskAsync(() => CredentialsAccessor.ExecuteOperationWithCredentials(credentials, operation));
    }

    public static async Task ExecuteOperationWithCredentials(IOAuthCredentials credentials, Action operation)
    {
        await Sync.ExecuteTaskAsync(() => CredentialsAccessor.ExecuteOperationWithCredentials(credentials, operation));
    }
}
}
Marked as answer by linvi on 5/4/2014 at 12:10 PM
Coordinator
Apr 30, 2014 at 9:05 PM
Hi,

Yes, I think it will work properly if you do that. Very simple but I think I will add more features in Tweetinvi so that developers can pass an async action and still make it work.

In the meantime feel free to use your class it looks perfectly fine to me.

Linvi
Apr 30, 2014 at 9:07 PM
Ok... really appreciate your response... and it seems to be working... just wanted to see if you saw any issues.

We will use your method / support when completed.

Thanks

James
Coordinator
May 22, 2014 at 6:07 PM
Edited May 22, 2014 at 8:16 PM
Hi James,

I found that concurrency can make your solution fail in a very rare cases.
Please find the code I came up with and that will be available with Tweetinvi 0.9.3.0.

I hope you will appreciate it.
public static async Task ExecuteTaskAsync(Action action)
{
    // We store the credentials at the time of the call within the local memory
    var credentialsAtInvokeTime = CredentialsAccessor.CurrentThreadCredentials;
    CredentialsAccessor.CurrentThreadCredentials = null;

    // The lambda expression will store 'credentialsAtInvokeTime' within a generated class
    // In order to keep the reference to the credentials at the time of invocation
    var operationRunWithSpecificCredentials = new Action(() =>
    {
        // We get the newly created credentialsAccessor for the async thread (CredentialsAccessor are Thread specific)
        var credentialsAccessor = TweetinviContainer.Resolve<ICredentialsAccessor>();

        // We now use credentials of the lambda expression local variables to perform our operation
        credentialsAccessor.ExecuteOperationWithCredentials(credentialsAtInvokeTime, action);
    });

    await _taskFactory.ExecuteTaskAsync(operationRunWithSpecificCredentials);
}
Regards,
Linvi
Marked as answer by linvi on 5/22/2014 at 11:07 AM
May 22, 2014 at 6:08 PM

Thank you very much…… we will get it

James

James L. Harris

TriPerta, LLC (creators of RaceJoy)

Altamonte Springs, Fla.

www.triperta.com

407 921 4589

racejoy_200

www.racejoy.com

Aug 14, 2014 at 7:23 PM

Hi,

We have conducted a round of debugging and testing with V9.3.4 with the change to allow DIFFERENT credentials on an Async Tweet request and are going to use this code in a production event this weekend. We changed from our code to your official changes in 9.3.4.

We are doing code like the following. Just wanted to give you feedback that we are moving to the new code and also please let us know if any issues on this code.. and our approach below. Note.. we process exceptions outside this in a service loop.

var credentials = TwitterCredentials.CreateCredentials(accessToken, accessSecret, twConsumerKey, twConsumerSecret);

// just queue them up and continue..

TwitterCredentials.ExecuteOperationWithCredentials(credentials, () =>

{

var tweet = TweetAsync.PublishTweet(message);

}

Thank you for your code,

James

James L. Harris

TriPerta, LLC (creators of RaceJoy)

Altamonte Springs, Fla.

www.triperta.com

407 921 4589

racejoy_200

www.racejoy.com

Coordinator
Aug 21, 2014 at 9:26 AM
Thank you lepert,

I am happy to hear that you successfully switched to the latest version of Tweetinvi and that the async credentials work like you wanted.
Feel free to come back again, your input was very useful and allowed me to improve Tweetinvi greatly.

Linvi