我正在编写一个类库,它连接到Twitter Streaming API并实时处理连续的JSON流。我想在每次从API收到一条新的推文时引发一个事件,这样我就可以在调用者类中使用lambda方法,如下所示:
var stream = new Stream(customerKey,customerSecret,accessToken,accessTokenSecret);
stream.Start();
// Handler
stream.TweetReceivedEvent += (sender, tweetargs) =>
{
Console.WriteLine(tweetargs.Tweet.ToString());
};
但是,我不确定如何做到这一点。目前,我已经创建了一个名为Stream
的类,其中保存了与连接到Twitter Streaming API相关联的逻辑(为了简洁起见,下面仅使用此类中的Start()
方法):
public async Task Start()
{
//Twitter Streaming API
string stream_url = "https://stream.twitter.com/1.1/statuses/filter.json";
string trackKeywords = "twitter";
string followUserId = "";
string locationCoord = "";
string postparameters = (trackKeywords.Length == 0 ? string.Empty : "&track=" + trackKeywords) +
(followUserId.Length == 0 ? string.Empty : "&follow=" + followUserId) +
(locationCoord.Length == 0 ? string.Empty : "&locations=" + locationCoord);
if (!string.IsNullOrEmpty(postparameters))
{
if (postparameters.IndexOf('&') == 0)
postparameters = postparameters.Remove(0, 1).Replace("#", "%23");
}
//Connect
webRequest = (HttpWebRequest) WebRequest.Create(stream_url);
webRequest.Timeout = -1;
webRequest.Headers.Add("Authorization", GetAuthHeader(stream_url + "?" + postparameters));
Encoding encode = Encoding.GetEncoding("utf-8");
if (postparameters.Length > 0)
{
webRequest.Method = "POST";
webRequest.ContentType = "application/x-www-form-urlencoded";
byte[] _twitterTrack = encode.GetBytes(postparameters);
webRequest.ContentLength = _twitterTrack.Length;
var _twitterPost = webRequest.GetRequestStream();
_twitterPost.Write(_twitterTrack, 0, _twitterTrack.Length);
_twitterPost.Close();
}
webRequest.BeginGetResponse(ar =>
{
var req = (WebRequest)ar.AsyncState;
using (var response = req.EndGetResponse(ar))
{
using (var reader = new StreamReader(response.GetResponseStream()))
{
while (!reader.EndOfStream)
{
// Deserialize the JSON obj to type Tweet
var jsonObj = JsonConvert.DeserializeObject<Tweet>(reader.ReadLine(), new JsonSerializerSettings());
Console.WriteLine(jsonObj.Text);
Raise(TweetReceivedEvent, new TweetReceivedEventArgs(jsonObj));
}
}
}
}, webRequest);
}
在同一个类中,我创建了一个事件和一个委托,如下所示:
public event TweetReceivedHandler TweetReceivedEvent;
public delegate void TweetReceivedHandler(TwitterStreamClient s, TweetEventArgs e);
Raise
方法调用我的测试人员类中的EventHandler方法:
public void Raise(TweetReceivedHandler handler, TweetEventArgs e)
{
if (handler != null)
{
handler(this, e);
}
}
但是,当我调试并逐步执行Raise
方法时,Handler始终为null。我在这里错过了什么?正如您所看到的,我已采取一些步骤使此方法异步并返回任务,但我不确定这是正确的操作过程。
如果您需要任何澄清,请随时提出要求,如果您能解释我需要做什么,我将永远感激您!如果我完全弄错了棍子的末端,请提前道歉!
如果您想查看完整代码,可以在https://github.com/adaam2/APoorMansTwitterStreamingClient/blob/master/TwitterClient/Infrastructure/Utility/TwitterStreamClient.cs存在此类(稍微更旧)的版本。
答案 0 :(得分:1)
正如Hans建议的那样,看到引发事件的关键是确保你在之前订阅它可能会引发事件。否则,在您有机会订阅之前,它可能会被提升。 E.g:
var stream = new Stream(customerKey,customerSecret,accessToken,accessTokenSecret);
// Handler
stream.TweetReceivedEvent += (sender, tweetargs) =>
{
Console.WriteLine(tweetargs.Tweet.ToString());
};
stream.Start();
就修复Start()
方法而言,编写async
方法的关键是确保在其中使用await
。理想情况下,在方法中启动的所有异步操作都是等待的,因此您可以在整个过程中使用简化的await
语法。
在你的例子中,我会推荐更像这样的东西:
public async Task Start()
{
//Twitter Streaming API
string stream_url = "https://stream.twitter.com/1.1/statuses/filter.json";
string trackKeywords = "twitter";
string followUserId = "";
string locationCoord = "";
string postparameters = (trackKeywords.Length == 0 ? string.Empty : "&track=" + trackKeywords) +
(followUserId.Length == 0 ? string.Empty : "&follow=" + followUserId) +
(locationCoord.Length == 0 ? string.Empty : "&locations=" + locationCoord);
if (!string.IsNullOrEmpty(postparameters))
{
if (postparameters.IndexOf('&') == 0)
postparameters = postparameters.Remove(0, 1).Replace("#", "%23");
}
//Connect
webRequest = (HttpWebRequest) WebRequest.Create(stream_url);
webRequest.Timeout = -1;
webRequest.Headers.Add("Authorization", GetAuthHeader(stream_url + "?" + postparameters));
Encoding encode = Encoding.GetEncoding("utf-8");
if (postparameters.Length > 0)
{
webRequest.Method = "POST";
webRequest.ContentType = "application/x-www-form-urlencoded";
byte[] _twitterTrack = encode.GetBytes(postparameters);
webRequest.ContentLength = _twitterTrack.Length;
var _twitterPost = await webRequest.GetRequestStreamAsync();
await _twitterPost.WriteAsync(_twitterTrack, 0, _twitterTrack.Length);
_twitterPost.Close();
}
using (var response = await webRequest.GetResponseAsync())
{
using (var reader = new StreamReader(response.GetResponseStream()))
{
while (!reader.EndOfStream)
{
// Deserialize the JSON obj to type Tweet
var jsonObj = JsonConvert.DeserializeObject<Tweet>(await reader.ReadLineAsync(), new JsonSerializerSettings());
Console.WriteLine(jsonObj.Text);
Raise(TweetReceivedEvent, new TweetReceivedEventArgs(jsonObj));
}
}
}
}
请注意,我在上面的四个地方使用了...Async()
方法:获取请求流,写入请求流,获取响应以及处理响应流。通过这种方式,该方法的逻辑可以以直接的,逐步的方式编写,但仍然允许异步操作。也就是说,当这些异步操作正在进行时,该方法将返回并执行当前线程,并且该方法将在完成后继续执行。
最重要的是,这样做可以确保方法返回的Task
对象本身不会完成,直到整个操作完成。通过这种方式,您实际上可以完全删除事件,并依赖Task
对象本身来表示完成。那么你的通话网站就是这样的:
var stream = new Stream(customerKey,customerSecret,accessToken,accessTokenSecret);
await stream.Start();
Console.WriteLine(tweetargs.Tweet.ToString());
最后,还有一个建议:我强烈建议您为班级使用Stream
以外的名称。实际上保证名称Stream
在尝试读取和维护代码时会引起混淆。