So I'm trying to use Signalr with the Twitter Streaming API, and specifically, for this I'm using the Tweetinvi C# API (http://tweetinvi.codeplex.com/).
The purpose of the app is to stream tweets to a page in realtime filtered with certain keywords.
The TweetInvi library works a treat, and I have a command line application successfully printing out tweets with certain keywords in.
The basic outline of my usage is as follows:
I have an MVC web app with a single page, with a text input and a button (for updating filters) it then calls the Hub method in the Signalr Hub, to start the stream if there isn't one already present and Stops it on a second button click.
All this is working fine, except when it comes to the signalr part.
public class TweetHub : Hub
{
private IStreamManager _streamManager;
public void AddTweet(String tweet, double lat, double lon)
{
Clients.All.addTweet(tweet, lat, lon);
}
public void StartStream(String[] filters)
{
string accessToken = ConfigurationManager.AppSettings["AccessToken"];
string accessTokenSecret = ConfigurationManager.AppSettings["AccessTokenSecret"];
string consumerKey = ConfigurationManager.AppSettings["ConsumerKey"];
string consumerSecret = ConfigurationManager.AppSettings["ConsumerSecret"];
IToken token = new Token(accessToken, accessTokenSecret, consumerKey, consumerSecret);
if (_streamManager != null && _streamManager.StreamIsOpen())
{
_streamManager.StopStream();
_streamManager.StartStream(token, filters, tweet => AddTweet(tweet.Text, tweet.LocationCoordinates.Lattitude, tweet.LocationCoordinates.Longitude));
}
else if (_streamManager != null && !_streamManager.StreamIsOpen())
{
_streamManager.StartStream(token, filters, tweet => AddTweet(tweet.Text, tweet.LocationCoordinates.Lattitude, tweet.LocationCoordinates.Longitude));
}
else
{
_streamManager = new StreamManager();
_streamManager.StartStream(token, filters, tweet => AddTweet(tweet.Text, tweet.LocationCoordinates.Lattitude, tweet.LocationCoordinates.Longitude));
}
}
public void StopStream()
{
if (_streamManager != null && _streamManager.StreamIsOpen())
{
_streamManager.StopStream();
}
}
}
That is the code for my Signalr Hub. As I said, using js I can trigger the start and stop stream methods fine.
This is the code for my StreamManager class:
public class StreamManager : IStreamManager
{
private StreamClient _streamClient;
private bool _streamOpen = false;
public void StartStream(IToken token, String[] filters, Action<ITweet> action)
{
if (_streamClient == null)
_streamClient = new StreamClient(token, filters, new FilteredStream());
_streamClient.StartStream(action);
_streamOpen = true;
}
public void StopStream()
{
if (_streamClient != null)
{
_streamClient.StopStream();
_streamOpen = false;
}
}
public bool StreamIsOpen()
{
return _streamOpen;
}
public void Dispose()
{
if (_streamOpen)
{
StopStream();
}
_streamClient.Dispose();
_streamClient = null;
}
}
The code for my StreamClient class:
public class StreamClient : IStreamClient
{
private IFilteredStream _filteredStream;
private IToken _token;
private bool _streamOpen = false;
public StreamClient(IToken token, String[] filters, IFilteredStream filteredStream)
{
_token = token;
_filteredStream = filteredStream;
AddFilters(filters);
}
private void AddFilters(String[] filters)
{
for (int i = 0; i < filters.Length; ++i)
{
_filteredStream.AddTrack(filters[i]);
}
}
public void StartStream(Action<ITweet> action)
{
_filteredStream.StartStream(_token, action);
_streamOpen = true;
}
public void StartStream(Func<ITweet, bool> predicateFunc)
{
_filteredStream.StartStream(_token, predicateFunc);
_streamOpen = true;
}
public void StopStream()
{
_filteredStream.StopStream();
_streamOpen = false;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
// free managed resources
if (_streamOpen)
{
_filteredStream.StopStream();
_filteredStream = null;
_token = null;
}
}
}
This code above, is where it makes a call to the Tweetinvi library directly.
My problem is that when I pass the Hub method into the StreamManager's StartStream method as an Action parameter, the AddTweet method never gets hit.
As I said, this all works fine, when using a command prompt application as a client instead, and using this code:
static void Main(string[] args)
{
string accessToken = ConfigurationManager.AppSettings["AccessToken"];
string accessTokenSecret = ConfigurationManager.AppSettings["AccessTokenSecret"];
string consumerKey = ConfigurationManager.AppSettings["ConsumerKey"];
string consumerSecret = ConfigurationManager.AppSettings["ConsumerSecret"];
IToken token = new Token(accessToken, accessTokenSecret, consumerKey, consumerSecret);
String[] filters = new string[2]
{
"test",
"twitter"
};
StreamClient streamClient = new StreamClient(token, filters, new FilteredStream());
streamClient.StartStream(tweet => TestMethod());
}
public static void TestMethod()
{
Console.WriteLine("test");
}
This works perfectly and prints out tweets with those keywords as they are received.
This leads me to believe that is a problem with the way I am using Signalr, that the signalr method is never getting hit, because the stream definitely gets opened, I just have a sneaky suspicion that it is something to do with the lifetime of the hub and the way I am using it.
I suspect this because, although the StartStream Method in my Hub gets called fine, and updates the button being clicked, when I think click again to call StopStream, the StopStream method gets hit, but my "_streamManager" member variable is null, which it shouldn't be IF the hub maintains state during it's lifetime, which I guess it doesn't.
Either that or it's being disposed of and then the stream wouldnt exist anymore anyway.
I don't really have enough experience with Signalr to properly debug.
Thanks in advance for any help.
Instances of a hub class are transient. That means that SignalR creates an instance of the hub class each time a method in that hub class is called or when a connection event occurs (e.g. onConnected, onDisconnected). When the method being called is done doing its work, that hub instance is disposed.
Read this: http://www.asp.net/signalr/overview/signalr-20/hubs-api/hubs-api-guide-server#transience
So you should not try to maintain state information about a connection in your hub class. In your case that would be "_streamManager".
So I think you should move all your business logic to another class (that doesn't derive from Hub).Encapsulate them in methods and call them from your signalR methods.
If you need to call a method in the hub from your sever code, you should get a hub context first.
See how to do that here: http://www.asp.net/signalr/overview/signalr-20/hubs-api/hubs-api-guide-server#callfromoutsidehub
Hope this helps!
Related
I have a situation where there is a synchronous call to a method and based on a parameter we switch the response for sync /async and inside async we start a task as below. The problem is tracer works until its outside the Task() but not inside. Is it happening that Task cannot access the parent thread data ?
RequestProcessor.cs file code:
public classA_Res GetListbyUserRequest(ApiRequest<object> request)
{
if(request.IsAsync)
{
LocalTracer.TraceInformation(AVEventType.Info, "Async call started"); // this works/ gets logged in db
var taskProcessEventsResponseAsync = new Task(() =>
ProcessResponseAsync(validatedInputs, options, grids, userInfo, traceRequest, exportAs, userRequestId, sessionId));
taskProcessEventsResponseAsync.Start();
}
else
{
response=DataManager.Instance.GetListbyUserRequest(); // this gets paginated data for UI
}
//some code for response that request has been put down for export async.
}
private void ProcessResponseAsync(validatedInputs, options, grids, userInfo, traceRequest, exportAs, userRequestId, sessionId)
{
LocalTracer.TraceInformation(AVEventType.Info, "Async call in progress"); // this doesnt works/ doesnt gets logged in db but also doesnt throws any error
//some code for processing data in chunks and creating files on server
}
LocalTracer.cs
public interface ILocalTracer
{
void TraceInformation(AVEventType eventType, DummyParameter dummy = null);
}
public sealed class LocalTracer:ILocalTracer
{
static ILocalTracer _instance = new LocalTracer();
public static ILocalTracer Instance
{
get { return _instance; }
set { _instance = value; }
}
private LocalTracer()
{
}
public static void TraceInformation(AVEventType eventType, string sMessage = "", string
userName = "", string ipAddress = "",.....)
{
//tracer code
}
public void TraceInformation(AVEventType eventType, DummyParameter dummy = null)
{
TraceInformation(eventType, "");
}
}
Please assume all this code in proper try catch blocks.
Sorry, if this is a stupid question but I don't find any useful information in the internet.
Has anyone ever tried to implement the observer pattern in C# using gRPC as communication?
If yes, please show me the link.
Many thanks in advance and best regards.
I have implemented a client convenience class wrapper to turn server streaming calls into regular events for a project I am working. Not sure if this is what you are after. Here is a simple gRPC server that just publishes the time as a string once every second.
syntax = "proto3";
package SimpleTime;
service SimpleTimeService
{
rpc MonitorTime(EmptyRequest) returns (stream TimeResponse);
}
message EmptyRequest{}
message TimeResponse
{
string time = 1;
}
The server implementation, which just loops once a second returning the string representation of the current time until canceled, is as follows
public override async Task MonitorTime(EmptyRequest request, IServerStreamWriter<TimeResponse> responseStream, ServerCallContext context)
{
try
{
while (!context.CancellationToken.IsCancellationRequested)
{
var response = new TimeResponse
{
Time = DateTime.Now.ToString()
};
await responseStream.WriteAsync(response);
await Task.Delay(1000);
}
}
catch (Exception)
{
Console.WriteLine("Exception on Server");
}
}
For the client, I created a class that contains the gRPC client and exposes the results of the server streaming MonitorTime call as a plain ole .net event.
public class SimpleTimeEventClient
{
private SimpleTime.SimpleTimeService.SimpleTimeServiceClient mClient = null;
private CancellationTokenSource mCancellationTokenSource = null;
private Task mMonitorTask = null;
public event EventHandler<string> OnTimeReceived;
public SimpleTimeEventClient()
{
Channel channel = new Channel("127.0.0.1:50051", ChannelCredentials.Insecure);
mClient = new SimpleTime.SimpleTimeService.SimpleTimeServiceClient(channel);
}
public void Startup()
{
mCancellationTokenSource = new CancellationTokenSource();
mMonitorTask = Task.Run(() => MonitorTimeServer(mCancellationTokenSource.Token));
}
public void Shutdown()
{
mCancellationTokenSource.Cancel();
mMonitorTask.Wait(10000);
}
private async Task MonitorTimeServer(CancellationToken token)
{
try
{
using (var call = mClient.MonitorTime(new SimpleTime.EmptyRequest()))
{
while(await call.ResponseStream.MoveNext(token))
{
var timeResult = call.ResponseStream.Current;
OnTimeReceived?.Invoke(this, timeResult.Time);
}
}
}
catch(Exception e)
{
Console.WriteLine($"Exception encountered in MonitorTimeServer:{e.Message}");
}
}
}
Now create the client and subscribe to the event.
static void Main(string[] args)
{
SimpleTimeEventClient client = new SimpleTimeEventClient();
client.OnTimeReceived += OnTimeReceivedEventHandler;
client.Startup();
Console.WriteLine("Press any key to exit");
Console.ReadKey();
client.Shutdown();
}
private static void OnTimeReceivedEventHandler(object sender, string e)
{
Console.WriteLine($"Time: {e}");
}
Which when run produces
I have left out a lot of error checking and such to make the example smaller. One thing I have done is for gRPC interfaces with many server streaming calls that may or may not be of interest to call clients, is to implement the event accessor (add,remove) to only call the server side streaming method if there is a client that has subscribed to the wrapped event. Hope this is helpful
There is a need to receive user data using a token. Hello. There is a need to receive user data using a token. I have a web api + websockets, websockets connection via a web browser.
var webSocket = new WebSocket(handlerUrl);
//Open connection handler.
webSocket.onopen = function () {
webSocket.send("{\"type\":\"LOGIN\",\"access_token\":\"Bearer HIDDEN\"}");
};
Once connected, I immediately send token.
On the server side, it looks as follows:
public class SocketClientController: ApiController
{
public HttpResponseMessage Get ()
{
HttpContext.Current.AcceptWebSocketRequest (new WebSocketHandler ());
return Request.CreateResponse (HttpStatusCode.SwitchingProtocols);
}
<miss>
Socket class:
<miss>
private void Login(string access_token)
{
// here i want get user info
}
public override void OnMessage(string input)
{
dynamic data = JObject.Parse(input);
switch ((string)data.type)
{
case "LOGIN":
Login((string)data.access_token);
break;
}
}
I use Identity, a variant with a token when you first received from the client suits me the data. Tell me how you can get the user input without going through the login and password, and use [Authorize].
Sorry for my english.
I decided my task! Below is the code:
public class MachineKeyProtector : Microsoft.Owin.Security.DataProtection.IDataProtector
{
private readonly string[] _purpose =
{
typeof(OAuthAuthorizationServerMiddleware).Namespace,
"Access_Token",
"v1"
};
public byte[] Protect(byte[] userData)
{
throw new NotImplementedException();
}
public byte[] Unprotect(byte[] protectedData)
{
return System.Web.Security.MachineKey.Unprotect(protectedData, _purpose);
}
}
Use:
var secureDataFormat = new TicketDataFormat(new Providers.MachineKeyProtector());
AuthenticationTicket ticket = secureDataFormat.Unprotect(access_token);
var userId = ticket.Identity.GetUserId();
An example of the implementation for the ADAL library for Windows Phone 8.1 can be found on GitHub
To display the Azure Login page for the user you call the AuthenticationContext.AcquireTokenAndContinue method. After the login process it should do a callback to the AuthenticationContextDelegate.
AuthenticationContext _authenticationContext = AuthenticationContext.CreateAsync(authority).GetResults();
_authenticationContext.AcquireTokenAndContinue(resource, clientId, redirectUri, authenticationContextDelegate);
AuthenticationContextDelegate authenticationContextDelegate= new AuthenticationContextDelegate(AuthContextDelegateMethod);
public static void AuthContextDelegateMethod(AuthenticationResult result)
{
// Never called
}
The AuthContextDelegateMethod is not getting called even after a successful login.
Is there any reason why the AuthenticationContextDelegate is not called, and any way to fix that?
I had this same issue with the ADAL library not working and not calling the AuthContextDelegateMethod
The firts thing was to understand how the AndContinue methods work, once you understand this the rest make a lot of sense.
App.xaml.cs
In your App.xaml.cs you need to add the OnActivate method which works with the ContinuationManager; this is where the app enters after the Azure login returns.
ContinuationManager _continuationManager;
protected override void OnActivated(IActivatedEventArgs e)
{
base.OnActivated(e);
_continuationManager = new ContinuationManager();
var continuationEventArgs = e as IContinuationActivatedEventArgs;
if (continuationEventArgs != null)
{
IWebAuthenticationContinuable azure = GetIWebAuthenticationContinuableInstance();
_continuationManager.Continue(continuationEventArgs, azure);
}
}
The GetIWebAuthenticationContinuableInstance should return the same instance of the IWebAuthenticationContinuable that called AuthenticationContext.AcquireTokenAndContinue; I had some issues when you try to give it a new instance (didn't work), in this answer it is called AzureLoginClass below.
ContinuationManager
Then in the ContinuationManager I added the following function (which is an adaptation of Continue(IContinuationActivatedEventArgs args, Frame rootFrame)):
internal void Continue(IContinuationActivatedEventArgs args, IWebAuthenticationContinuable wabPage)
{
if (args == null)
throw new ArgumentNullException("args");
if (this.args != null && !handled)
throw new InvalidOperationException("Can't set args more than once");
this.args = args;
this.handled = false;
this.id = Guid.NewGuid();
if (wabPage == null)
return;
switch (args.Kind)
{
case ActivationKind.PickFileContinuation:
break;
case ActivationKind.PickSaveFileContinuation:
break;
case ActivationKind.PickFolderContinuation:
break;
case ActivationKind.WebAuthenticationBrokerContinuation:
if (wabPage != null)
{
wabPage.ContinueWebAuthentication(args as WebAuthenticationBrokerContinuationEventArgs);
}
break;
}
}
I needed to do this because I implemented my IWebAuthenticationContinuable as part of a class and the given implementations give preference to an AppicationPage.
AzureLoginClass
Then in your implementation of IWebAuthenticationContinuable's ContinueWebAuthentication you need to call the ContinueAcquireTokenAsync
public class AzureLoginClass : IWebAuthenticationContinuable
{
public void CallAzureLogin
{
_authenticationContext.AcquireTokenAndContinue(resource, clientId, redirectUri, AuthContextDelegateMethod);
}
private void AuthContextDelegateMethod(AuthenticationResult result)
{
// Is now called when ContinueAcquireTokenAsync is called
}
public async void ContinueWebAuthentication(WebAuthenticationBrokerContinuationEventArgs args)
{
AuthenticationResult result = await _authenticationContext .ContinueAcquireTokenAsync(args);
}
}
When ContinueAcquireTokenAsync is called it gives a callback to AuthContextDelegateMethod. This is however not called if result.ErrorDescription is "User canceled authentication", so if you need this you should cater extra for it.
I am trying to implement a simple Windows 8 C# XAML application where there are two calls made to access single web service one from project to load and display data and other to display notification.
Since there are two calls made for same web service I want that if one call is already made to the service the other call should wait and use the same response from the first call.
How can i achieve this kind of functionality? I have not added any code since there is no code that i have written for this. I am just trying to think first and then will I code.
Please let me know I can get some help for this kind of project structure?
You can do this by caching the Task that's currently downloading and not starting the download again if there is a cached Task:
private volatile Task<string> m_cachedWebServiceTask;
async Task<string> AccessWebServiceAsync()
{
var task = m_cachedWebServiceTask;
if (task == null)
task = m_cachedWebServiceTask = DownloadFromWebServiceAsync();
try
{
return await task;
}
finally
{
m_cachedWebServiceTask = null;
}
}
Note that this code has a race condition: if you call AccessWebServiceAsync() twice at the same time, there is a small chance DownloadFromWebServiceAsync() will be called twice too. But since this in only an optimization, I think that shouldn't be a problem. If it is a problem for you, you would need to guard the access to the field by a lock.
As I had the feeling that this problem needs further attention and it's solution could still be optimized, I decided to post another approach. The OP is mostly a problem about leveraging the following 3 scopes of requirements: user experience within the application, the application's internal requirements and the web service's loading with multiple requests.
The application needs to make an initial request to load the data.
When he asks for it, the user expects to get the results with the latest updates.
On the other side, it makes no initiate a large series of calls to the web service within a very short moment of time.
So, managing what happens in this very short moment of time it's actually the solution to the problem.
On the client side, the Service1Client class:
public partial class Service1Client
{
// default time buffer value
int _timeBuffer = 100;
// a time buffer used for returning the same response
public int TimeBuffer
{
get { return _timeBuffer; }
set { _timeBuffer = value; }
}
// the start and end time of the web service request
static DateTime _start, _end;
// a volatile static variable to store the response in the buffering time
volatile static string _response;
// used for blocking other threads until the current thread finishes it's job
object _locker = new object();
public async Task<string> GetResponseData()
{
return await Task.Factory.StartNew<string>(() =>
{
lock (_locker)
{
if (DateTime.Now >= _end.AddMilliseconds(TimeBuffer))
{
_start = DateTime.Now;
var async = GetDataAsync();
_response = async.Result;
_end = DateTime.Now;
}
}
return _response;
});
}
}
The console application used for testing:
class Program
{
static void Main(string[] args)
{
while (true)
{
var client = new ServiceReference1.Service1Client();
client.TimeBuffer = 150;
Console.WriteLine(client.GetResponseData().Result);
if (Console.ReadKey().Key == ConsoleKey.Enter)
break;
}
}
}
As a remark, note that, for the reason of a clear sample, I decided to change the returned type of the GetDate WCF service's method from DateTime to string.
[ServiceContract]
public interface IService1
{
[OperationContract]
string GetData();
}
public class Service1 : IService1
{
public string GetData()
{
System.Threading.Thread.Sleep(5000);
return DateTime.Now.ToString();
}
}
For your scenario, a feasible idea would be to extend the service class.
The IService1 interface definition:
[ServiceContract]
public interface IService1
{
[OperationContract]
DateTime GetData();
}
The Service1 class definition:
public class Service1 : IService1
{
public DateTime GetData()
{
System.Threading.Thread.Sleep(5000);
return DateTime.Now;
}
}
On the client side, extend the Service1Client class definition and add a new method:
public partial class Service1Client
{
static bool _isOpen;
static DateTime? _cachedResponse;
object _locker = new object();
public DateTime GetResponseData()
{
if (!_isOpen)
{
if (!_cachedResponse.HasValue)
{
lock (_locker)
{
_isOpen = true;
_cachedResponse = GetData();
_isOpen = false;
}
return _cachedResponse.Value;
}
else
{
Task.Factory.StartNew<DateTime>(() =>
{
lock (_locker)
{
_isOpen = true;
_cachedResponse = GetData();
_isOpen = false;
}
return _cachedResponse.Value;
});
}
}
return _cachedResponse.Value;
}
}
Test it:
class Program
{
static void Main(string[] args)
{
while (true)
{
var client = new ServiceReference1.Service1Client();
Console.WriteLine(client.GetResponseData());
if (Console.ReadKey().Key == ConsoleKey.Enter)
break;
}
}
}