I have a WPF C# application, which sends out a message to both Facebook and Twitter using the API. To do so I have one main static class where I can send the string message and a few parameters. The code works fine when the user only sends out announcements intermittently. But when the user sends out multiple announcements at once it doesn't work. What I want is:
I have looked into using BlockingCollection, but not had much luck understanding how to make it work.
This is my current code and I would like it as close to this as possible:
class PublishAnnouncement {
//This function is callled upon in many parts of the program and acts as a general publisher
public static void PostAnnoucment(string message, string TwAccountKey, string FbAccountKey, string[] JourneyRefID, double latness, MainWindow mainWindow) {
//First it is published to facebook
BackgroundWorker FacebookWorker = new BackgroundWorker();
FacebookWorker.DoWork += (obj, e) => FacebookDoWork(message, FbAccountKey);
FacebookWorker.RunWorkerAsync();
//Then it is published to twitter - This is where it appears to fail
BackgroundWorker TwitterWorker = new BackgroundWorker();
TwitterWorker.DoWork += (obj, e) => TwitterDoWork(message, TwAccountKey, JourneyRefID, latness, mainWindow, 0);
TwitterWorker.RunWorkerAsync();
}
private static void FacebookDoWork(string message, string FbAccountKey) {
//STAGE 1 - Facebook
//First the program will attempt to post a Facebook post.
try {
//If it is to be posted by one of the additional Facebook Pages and
//not by the default page.
var client = new RestClient("https://graph.facebook.com/v3.0/");
var request = new RestRequest("{pageId}/feed", Method.POST);
request.AddParameter("message", message); // adds to POST or URL querystring based on Method
request.AddParameter("access_token", Properties.Settings.Default.FBPageAccessToken);
request.AddUrlSegment("pageId", Properties.Settings.Default.FBPageID); // replaces matching token in request.Resource
IRestResponse response = client.Execute(request);
if (response.IsSuccessful == false) {
Console.WriteLine(response.Content);
Console.WriteLine("");
}
} catch (Exception ex) {
Console.WriteLine(ex.ToString());
}
}
private static void TwitterDoWork(string message, string TwAccountKey, string[] JourneyRefID, double latness, MainWindow mainWindow, int Attempts) {
//STAGE 2 - Twitter
//Once a Facebook post has/has not been posted the program will attempt to send a tweet.
try {
Auth.SetUserCredentials(Properties.Settings.Default.TwConsumerKey, Properties.Settings.Default.TwConsumerSecret, Properties.Settings.Default.TwUserAccessToken, Properties.Settings.Default.TwUserAccessSecret);
var tweet = Tweet.PublishTweet(message);
foreach(var ID in JourneyRefID)
AddTweetID(tweet.Id, ID, latness, mainWindow);
} catch (Exception ex) {
foreach(var ID in JourneyRefID)
AddTweetID(0, ID, latness, mainWindow);
Console.WriteLine(ex.Message);
}
}
}
I would suggest using a Mutex in PostAnnouncement. See the accepted answer here - use the lock example: Usage of Mutex in c#
class PublishAnnouncement {
private static readonly object syncLock = new object();
public static void PostAnnoucment(string message, string TwAccountKey, string FbAccountKey, string[] JourneyRefID, double latness, MainWindow mainWindow) {
lock(syncLock) {
//First it is published to facebook
BackgroundWorker FacebookWorker = new BackgroundWorker();
FacebookWorker.DoWork += (obj, e) => FacebookDoWork(message, FbAccountKey);
FacebookWorker.RunWorkerAsync();
//Then it is published to twitter - This is where it appears to fail
BackgroundWorker TwitterWorker = new BackgroundWorker();
TwitterWorker.DoWork += (obj, e) => TwitterDoWork(message, TwAccountKey, JourneyRefID, latness, mainWindow, 0);
TwitterWorker.RunWorkerAsync();
//etc
}
}
}
the lock will allow only one thread through at a time, stacking up all threads. Note I've made the lock static. (no matter how many instances of the class are in use at any one time, only one lock object is made and referenced by all calling threads).
If you want to move the locks into the background threads:
class PublishAnnouncement {
private static readonly object syncLockForTwitter = new object();
private static readonly object syncLockForFacebook = new object();
//This function is callled upon in many parts of the program and acts as a general publisher
public static void PostAnnoucment(string message, string TwAccountKey, string FbAccountKey, string[] JourneyRefID, double latness, MainWindow mainWindow) {
//First it is published to facebook
BackgroundWorker FacebookWorker = new BackgroundWorker();
FacebookWorker.DoWork += (obj, e) => FacebookDoWork(message, FbAccountKey);
FacebookWorker.RunWorkerAsync();
//Then it is published to twitter - This is where it appears to fail
BackgroundWorker TwitterWorker = new BackgroundWorker();
TwitterWorker.DoWork += (obj, e) => TwitterDoWork(message, TwAccountKey, JourneyRefID, latness, mainWindow, 0);
TwitterWorker.RunWorkerAsync();
}
private static void FacebookDoWork(string message, string FbAccountKey) {
lock(syncLockForFacebook) {
//STAGE 1 - Facebook
//First the program will attempt to post a Facebook post.
try {
//If it is to be posted by one of the additional Facebook Pages and
//not by the default page.
var client = new RestClient("https://graph.facebook.com/v3.0/");
var request = new RestRequest("{pageId}/feed", Method.POST);
request.AddParameter("message", message); // adds to POST or URL querystring based on Method
request.AddParameter("access_token", Properties.Settings.Default.FBPageAccessToken);
request.AddUrlSegment("pageId", Properties.Settings.Default.FBPageID); // replaces matching token in request.Resource
IRestResponse response = client.Execute(request);
if (response.IsSuccessful == false) {
Console.WriteLine(response.Content);
Console.WriteLine("");
}
} catch (Exception ex) {
Console.WriteLine(ex.ToString());
}
}
}
private static void TwitterDoWork(string message, string TwAccountKey, string[] JourneyRefID, double latness, MainWindow mainWindow, int Attempts) {
lock(syncLockForTwitter) {
//STAGE 2 - Twitter
//Once a Facebook post has/has not been posted the program will attempt to send a tweet.
try {
Auth.SetUserCredentials(Properties.Settings.Default.TwConsumerKey, Properties.Settings.Default.TwConsumerSecret, Properties.Settings.Default.TwUserAccessToken, Properties.Settings.Default.TwUserAccessSecret);
var tweet = Tweet.PublishTweet(message);
foreach(var ID in JourneyRefID)
AddTweetID(tweet.Id, ID, latness, mainWindow);
} catch (Exception ex) {
foreach(var ID in JourneyRefID)
AddTweetID(0, ID, latness, mainWindow);
Console.WriteLine(ex.Message);
}
}
}
}