Attach to previously started async process - c#

So i have an app that works using a token for a user. User can be signed in only on 1 device ( after login previous tokens get expired ). So i came up with an idea to cache some data. So i created a CacheManager that is a singleton.
CacheManager has a Dictionary with previously fetched data in it.
So here is an example:
/// <summary>
/// Tries to get Global settings data from the cache. If it is not present - asks ServiceManager to fetch it.
/// </summary>
/// <returns>Global setting object</returns>
public async Task<GlobalSettingsModel> GetGlobalSettingAsync()
{
GlobalSettingsModel result;
if (!this.cache.ContainsKey("GlobalSettings"))
{
result = await ServiceManager.Instance.RequestGlobalSettingAsync();
if (result != null)
{
this.cache.Add("GlobalSettings", result);
}
// TODO: Figure out what to do in case of null
}
return (GlobalSettingsModel)this.cache["GlobalSettings"];
}
So the question is, how can i modify this method, to handle such case:
For example, method that i call from the server, works longer than user navigated to the page where the data is needed i want to show a loading indicator and hide it when the data has actually been received.
Why do i need it, we have 2 pages - ExtendedSplashScreen and UpdatesPage user can quickly skip them ( 1s ) or stay and read interesting info ( lets say 1m ).
In this time, i have started to fetch the GetGlobalSetting in order to have the process ended or download atleast something ( to minify wait for the user ) when he gets to LoginPage.
On my ExtendedSplashScreen i launched:
CacheManager.Instance.GetGlobalSettingAsync();
For test purposes, i have modified the ServiceManager method:
/// <summary>
/// Fetches the object of Global Settings from the server
/// </summary>
/// <returns>Global setting object</returns>
public async Task<GlobalSettingsModel> RequestGlobalSettingAsync()
{
await Task.Delay(60000);
// Request and response JSONs are here, because we will need them to be logged if any unexpected exceptions will occur
// Response JSON
string responseData = string.Empty;
// Request JSON
string requestData = JsonConvert.SerializeObject(new GlobalSettingsRequestModel());
// Posting list of keys that we want to get from GlobalSettings table
HttpResponseMessage response = await client.PostAsync("ServerMethod", new StringContent(requestData, Encoding.UTF8, "application/json"));
// TODO: HANDLE ALL THE SERVER POSSIBLE ERRORS
Stream receiveStream = await response.Content.ReadAsStreamAsync();
StreamReader readStream = new StreamReader(receiveStream, Encoding.UTF8);
// Read the response data
responseData = readStream.ReadToEnd();
return JsonConvert.DeserializeObject<GlobalSettingsResponseModel>(responseData).GlobalSettings;
}
So, when user gets to the LoginPage i do the:
// The await is here because there is no way without this data further
GlobalSettingsModel settings = await CacheManager.Instance.GetGlobalSettingAsync();
And here, i would like to get the data from the cache if it was already downloaded or that CacheManager would return me the data as soon as it will be finished downloading.

One way would be to cache the Task<GlobalSettingsModel> instead of the GlobalSettingsModel itself. When you obtain it from the cache, you could check if it has completed and then either await or use its result accordingly.

Related

botFramework v4 how to handle dialog response after LUIS call

I have a bot written in C# that is using LUIS to determine intents. I have a method that makes a call to the LUIS service and then looks for an 'Open_Case' intent. The model has a CaseNumber entity defined which may or may not be included in the response from the LUIS service.
If the response doesn't have a case number entity I start a dialog to ask the user for the case number.
Once I have a case number I then want to return a card with case information.
Here's the code I have:-
/// <summary>
/// Dispatches the turn to the requested LUIS model.
/// </summary>
private async Task DispatchToLuisModelAsync(ITurnContext context, string appName, DialogContext dc, CancellationToken cancellationToken =
default (CancellationToken)) {
var result = await botServices.LuisServices[appName].RecognizeAsync(context, cancellationToken);
var intent = result.Intents ? .FirstOrDefault();
string caseNumber = null;
if (intent ? .Key == "Open_Case") {
if (!result.Entities.ContainsKey("Case_CaseNumber")) {
var dialogResult = await dc.BeginDialogAsync(CaseNumberDialogId, null, cancellationToken);
} else {
caseNumber = (string)((Newtonsoft.Json.Linq.JValue) result.Entities["Case_CaseNumber"].First).Value;
var cardAttachment = botServices.CaseInfoServices.LookupCase(caseNumber);
var reply = context.Activity.CreateReply();
reply.Attachments = new List < Attachment > () {
cardAttachment
};
await context.SendActivityAsync(reply, cancellationToken);
}
}
}
What I'm struggling with is where the code send the card response should sit.
In the code I currently have I send the card if the number was returned in the LUIS response, but if there was no number and I start the dialog then I only get access to the number either in the final step of the dialog or in the dialog result in the root turn handler. I've currently duplicated the reply inside the final step in the dialog, but it feels wrong and inelegant.
I'm sure there must be a way that I can collect the number from LUIS or the dialog and THEN send the response from a single place instead of duplicating code.
Any suggestions gratefully received...
I came to the conclusion that I need to put the code that displays the card into a method on the bot class, then call it from the else in code snippet and also from the turn handler when the dialogTurnStatus is equal to Complete

Azure Mobile Service Offline Sync in Xamarin.Forms

I've gone through instructions In this documentation To implement offline sync on my Xamarin.Forms client But when I pull data using sync table, I don't get the data presently in the cloud, Instead when I Read data using the normal table, I actually receive data normally, I don't understand, Here is my code to get data Using SYncTable :
/// <summary>
/// Initialize offline sync
/// </summary>
/// <returns></returns>
public async Task InitializeAsync()
{
if(!_client.SyncContext.IsInitialized)
{
_store.DefineTable<T>();
await _client.SyncContext.InitializeAsync(_store, new MobileServiceSyncHandler());
await SyncOfflineCacheAsync();
}
}
public async Task SyncOfflineCacheAsync()
{
try
{
Debug.WriteLine("SyncOfflineCacheAsync: Initializing...");
await InitializeAsync();
// Push the Operations Queue to the mobile backend
Debug.WriteLine("SyncOfflineCacheAsync: Pushing Changes");
await _client.SyncContext.PushAsync();
// Pull each sync table
Debug.WriteLine("SyncOfflineCacheAsync: Pulling tags table");
_table = _client.GetSyncTable<T>();
string queryName = $"incsync_{typeof(T).Name}";
await _table.PullAsync(queryName, _table.CreateQuery());
}
catch (MobileServicePushFailedException e )
{
if (e.PushResult != null)
{
foreach (var error in e.PushResult.Errors)
{
await ResolveConflictAsync(error);
}
}
}
catch(Exception e)
{
throw e ;
}
}
I get no data previously added online
But when I get data without offline sync, it functions well
var data = await baseAzureMobileService.NormalTable.ReadAsync();
Try calling PullAsync with null in place of queryName, that will force it to fetch all the records instead of trying to do an incremental sync.
AFAIK, the Incremental Sync request would look like this:
Get https://{your-app-name}.azurewebsites.net/tables/TodoItem?$filter=(updatedAt%20ge%20datetimeoffset'2017-11-03T06%3A56%3A44.4590000%2B00%3A00')&$orderby=updatedAt&$skip=0&$top=50&__includeDeleted=true
For Incremental Sync, the updatedAt timestamp of the results returned from your latest pull operation would be stored in the __config table of your local SQLite db as follows:
Note: The format for the value under the id column equals deltaToken|{table-name}|{query-name}.
I would recommend you capture the network traces and check the synced records under your local table to narrow this issue. Since incremental sync has optimized the requests instead of retrieving all records each time, I would recommend you leverage this feature. If your data set is small or you do not care the bandwidth, you could just opt out of incremental sync.

Shared object among different requests

I'm working with .NET 3.5 with a simple handler for http requests. Right now, on each http request my handler opens a tcp connection with 3 remote servers in order to receive some information from them. Then closes the sockets and writes the server status back to Context.Response.
However, I would prefer to have a separate object that every 5 minutes connects to the remote servers via tcp, gets the information and keeps it. So the HttpRequest, on each request would be much faster just asking this object for the information.
So my questions here are, how to keep a shared global object in memory all the time that can also "wake" an do those tcp connections even when no http requests are coming and have the object accesible to the http request handler.
A service may be overkill for this.
You can create a global object in your application start and have it create a background thread that does the query every 5 minutes. Take the response (or what you process from the response) and put it into a separate class, creating a new instance of that class with each response, and use System.Threading.Interlocked.Exchange to replace a static instance each time the response is retrieved. When you want to look the the response, simply copy a reference the static instance to a stack reference and you will have the most recent data.
Keep in mind, however, that ASP.NET will kill your application whenever there are no requests for a certain amount of time (idle time), so your application will stop and restart, causing your global object to get destroyed and recreated.
You may read elsewhere that you can't or shouldn't do background stuff in ASP.NET, but that's not true--you just have to understand the implications. I have similar code to the following example working on an ASP.NET site that handles over 1000 req/sec peak (across multiple servers).
For example, in global.asax.cs:
public class BackgroundResult
{
public string Response; // for simplicity, just use a public field for this example--for a real implementation, public fields are probably bad
}
class BackgroundQuery
{
private BackgroundResult _result; // interlocked
private readonly Thread _thread;
public BackgroundQuery()
{
_thread = new Thread(new ThreadStart(BackgroundThread));
_thread.IsBackground = true; // allow the application to shut down without errors even while this thread is still running
_thread.Name = "Background Query Thread";
_thread.Start();
// maybe you want to get the first result here immediately?? Otherwise, the first result may not be available for a bit
}
/// <summary>
/// Gets the latest result. Note that the result could change at any time, so do expect to reference this directly and get the same object back every time--for example, if you write code like: if (LatestResult.IsFoo) { LatestResult.Bar }, the object returned to check IsFoo could be different from the one used to get the Bar property.
/// </summary>
public BackgroundResult LatestResult { get { return _result; } }
private void BackgroundThread()
{
try
{
while (true)
{
try
{
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create("http://example.com/samplepath?query=query");
request.Method = "GET";
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
{
using (StreamReader reader = new StreamReader(response.GetResponseStream(), System.Text.Encoding.UTF8))
{
// get what I need here (just the entire contents as a string for this example)
string result = reader.ReadToEnd();
// put it into the results
BackgroundResult backgroundResult = new BackgroundResult { Response = result };
System.Threading.Interlocked.Exchange(ref _result, backgroundResult);
}
}
}
catch (Exception ex)
{
// the request failed--cath here and notify us somehow, but keep looping
System.Diagnostics.Trace.WriteLine("Exception doing background web request:" + ex.ToString());
}
// wait for five minutes before we query again. Note that this is five minutes between the END of one request and the start of another--if you want 5 minutes between the START of each request, this will need to change a little.
System.Threading.Thread.Sleep(5 * 60 * 1000);
}
}
catch (Exception ex)
{
// we need to get notified of this error here somehow by logging it or something...
System.Diagnostics.Trace.WriteLine("Error in BackgroundQuery.BackgroundThread:" + ex.ToString());
}
}
}
private static BackgroundQuery _BackgroundQuerier; // set only during application startup
protected void Application_Start(object sender, EventArgs e)
{
// other initialization here...
_BackgroundQuerier = new BackgroundQuery();
// get the value here (it may or may not be set quite yet at this point)
BackgroundResult result = _BackgroundQuerier.LatestResult;
// other initialization here...
}

Coded Web Test: Close a stream after a given amount of time

I am looking for a way to close a WebTest response stream (JSON Object) without having to use the timeout property as it makes the test fail and not always works, the reason to do this is that the stream ticks infinitely unless it is closed by the client, right now my tests just time out because I haven't found a way to close them from code.
The JSON object doesn't need to be valid, but an example of an object and how it looks like when streamed can be found here: http://tradestation.github.io/webapi-docs/en/stream/
My load test parses an IISLog and then it sends the Web API requests it finds as WebTestRequests, some of those requests are answered with JSON objects that stream endlessly and I need to close those streams based on the time it took the request to complete in the IISlog.
public class WebTest1Coded : WebTest
{
public WebTest1Coded()
{
this.PreAuthenticate = true;
}
public override IEnumerator<WebTestRequest> GetRequestEnumerator()
{
//Substitute the highlighted path with the path of the iis log file
IISLogReader reader = new IISLogReader(#"C:\IisLogsToWebPerfTest\TestData\log.log");
foreach (WebTestRequest request in reader.GetRequests())
{
if (this.LastResponse != null) {
this.LastResponse.HtmlDocument.ToString();
}
yield return request;
}
}
}
Thanks!

YouTube C# API V3, how do you resume an interrupted upload?

I can't work out how to resume an interrupted upload in V3 of the C# YouTube API.
My existing code uses V1 and works fine but I'm switching to V3.
If I call UploadAsync() without changing anything, it starts from the beginning. Using Fiddler, I can see the protocol given here is not followed and the upload restarts.
I've tried setting the position within the stream as per V1 but there is no ResumeAsync() method available.
The Python example uses NextChunk but the SendNextChunk method is protected and not available in C#.
In the code below, both UploadVideo() and Resume() work fine if I leave them to completion but the entire video is uploaded instead of just the remaining parts.
How do I resume an interrupted upload using google.apis.youtube.v3?
Here is the C# code I have tried so far.
private ResumableUpload<Video> UploadVideo(
YouTubeService youTubeService, Video video, Stream stream, UserCredential userCredentials)
{
var resumableUpload = youTubeService.Videos.Insert(video,
"snippet,status,contentDetails", stream, "video/*");
resumableUpload.OauthToken = userCredentials.Token.AccessToken;
resumableUpload.ChunkSize = 256 * 1024;
resumableUpload.ProgressChanged += resumableUpload_ProgressChanged;
resumableUpload.ResponseReceived += resumableUpload_ResponseReceived;
resumableUpload.UploadAsync();
return resumableUpload;
}
private void Resume(ResumableUpload<Video> resumableUpload)
{
//I tried seeking like V1 but it doesn't work
//if (resumableUpload.ContentStream.CanSeek)
// resumableUpload.ContentStream.Seek(resumableUpload.ContentStream.Position, SeekOrigin.Begin);
resumableUpload.UploadAsync(); // <----This restarts the upload
}
void resumableUpload_ResponseReceived(Video obj)
{
Debug.WriteLine("Video status: {0}", obj.Status.UploadStatus);
}
void resumableUpload_ProgressChanged(IUploadProgress obj)
{
Debug.WriteLine("Position: {0}", (resumableUploadTest == null) ? 0 : resumableUploadTest.ContentStream.Position);
Debug.WriteLine("Status: {0}", obj.Status);
Debug.WriteLine("Bytes sent: {0}", obj.BytesSent);
}
private void button2_Click(object sender, EventArgs e)
{
Resume(resumableUploadTest);
}
Any solution/suggestion/demo or a link to the "google.apis.youtube.v3" source code will be very helpful.
Thanks in Advance !
EDIT: New information
I'm still working on this and I believe the API simply isn't finished. Either that or I'm missing something simple.
I still can't find the "google.apis.youtube.v3" source code so I downloaded the latest "google-api-dotnet-client" source code. This contains the ResumableUpload class used by the YouTube API.
I managed to successfully continue an upload by skipping the first four lines of code in the UploadAsync() method. I created a new method called ResumeAsync(), a copy of UploadAsync() with the first four lines of initialization code removed. Everything worked and the upload resumed from where it was and completed.
I'd rather not be changing code in the API so if anyone knows how I should be using this, let me know.
I'll keep plugging away and see if I can work it out.
This is the original UploadAsync() method and my ResumeAsync() hack.
public async Task<IUploadProgress> UploadAsync(CancellationToken cancellationToken)
{
try
{
BytesServerReceived = 0;
UpdateProgress(new ResumableUploadProgress(UploadStatus.Starting, 0));
// Check if the stream length is known.
StreamLength = ContentStream.CanSeek ? ContentStream.Length : UnknownSize;
UploadUri = await InitializeUpload(cancellationToken).ConfigureAwait(false);
Logger.Debug("MediaUpload[{0}] - Start uploading...", UploadUri);
using (var callback = new ServerErrorCallback(this))
{
while (!await SendNextChunkAsync(ContentStream, cancellationToken).ConfigureAwait(false))
{
UpdateProgress(new ResumableUploadProgress(UploadStatus.Uploading, BytesServerReceived));
}
UpdateProgress(new ResumableUploadProgress(UploadStatus.Completed, BytesServerReceived));
}
}
catch (TaskCanceledException ex)
{
Logger.Error(ex, "MediaUpload[{0}] - Task was canceled", UploadUri);
UpdateProgress(new ResumableUploadProgress(ex, BytesServerReceived));
throw ex;
}
catch (Exception ex)
{
Logger.Error(ex, "MediaUpload[{0}] - Exception occurred while uploading media", UploadUri);
UpdateProgress(new ResumableUploadProgress(ex, BytesServerReceived));
}
return Progress;
}
public async Task<IUploadProgress> ResumeAsync(CancellationToken cancellationToken)
{
try
{
using (var callback = new ServerErrorCallback(this))
{
while (!await SendNextChunkAsync(ContentStream, cancellationToken).ConfigureAwait(false))
{
UpdateProgress(new ResumableUploadProgress(UploadStatus.Uploading, BytesServerReceived));
}
UpdateProgress(new ResumableUploadProgress(UploadStatus.Completed, BytesServerReceived));
}
}
catch (TaskCanceledException ex)
{
UpdateProgress(new ResumableUploadProgress(ex, BytesServerReceived));
throw ex;
}
catch (Exception ex)
{
UpdateProgress(new ResumableUploadProgress(ex, BytesServerReceived));
}
return Progress;
}
These are the Fiddler records showing the upload resuming.
After a fair bit of deliberation, I've decided to modify the API code. My solution maintains backwards compatibility.
I've documented my changes below but I don't recommend using them.
In the UploadAsync() method in the ResumableUpload Class in "Google.Apis.Upload", I replaced this code.
BytesServerReceived = 0;
UpdateProgress(new ResumableUploadProgress(UploadStatus.Starting, 0));
// Check if the stream length is known.
StreamLength = ContentStream.CanSeek ? ContentStream.Length : UnknownSize;
UploadUri = await InitializeUpload(cancellationToken).ConfigureAwait(false);
with this code
UpdateProgress(new ResumableUploadProgress(
BytesServerReceived == 0 ? UploadStatus.Starting : UploadStatus.Resuming, BytesServerReceived));
StreamLength = ContentStream.CanSeek ? ContentStream.Length : UnknownSize;
if (UploadUri == null) UploadUri = await InitializeUpload(cancellationToken).ConfigureAwait(false);
I also made the UploadUri and BytesServerReceived properties public. This allows an upload to be continued after the ResumableUpload object has been destroyed or after an application restart.
You just recreate the ResumableUpload as per normal, set these two fields and call UploadAsync() to resume an upload. Both fields need to be saved during the original upload.
public Uri UploadUri { get; set; }
public long BytesServerReceived { get; set; }
I also added "Resuming" to the UploadStatus enum in the IUploadProgress class.
public enum UploadStatus
{
/// <summary>
/// The upload has not started.
/// </summary>
NotStarted,
/// <summary>
/// The upload is initializing.
/// </summary>
Starting,
/// <summary>
/// Data is being uploaded.
/// </summary>
Uploading,
/// <summary>
/// Upload is being resumed.
/// </summary>
Resuming,
/// <summary>
/// The upload completed successfully.
/// </summary>
Completed,
/// <summary>
/// The upload failed.
/// </summary>
Failed
};
Nothing has changed for starting an upload.
Provided the ResumableUpload Oject and streams have not been destroyed, call UploadAsync() again to resume an interrupted upload.
If they have been destroyed, create new objects and set the UploadUri and BytesServerReceived properties. These two properties can be saved during the original upload. The video details and content stream can be configured as per normal.
These few changes allow an upload to be resumed even after restarting your application or rebooting. I'm not sure how long before an upload expires but I'll report back when I've done some more testing with my real application.
Just for completeness, this is the test code I've been using, which happily resumes an interrupted upload after restarting the application multiple times during an upload. The only difference between resuming and restarting, is setting the UploadUri and BytesServerReceived properties.
resumableUploadTest = youTubeService.Videos.Insert(video, "snippet,status,contentDetails", fileStream, "video/*");
if (resume)
{
resumableUploadTest.UploadUri = Settings.Default.UploadUri;
resumableUploadTest.BytesServerReceived = Settings.Default.BytesServerReceived;
}
resumableUploadTest.ChunkSize = ResumableUpload<Video>.MinimumChunkSize;
resumableUploadTest.ProgressChanged += resumableUpload_ProgressChanged;
resumableUploadTest.UploadAsync();
I hope this helps someone. It took me much longer than expected to work it out and I'm still hoping I've missed something simple. I messed around for ages trying to add my own error handlers but the API does all that for you. The API does recover from minor short hiccups but not from an application restart, reboot or prolonged outage.
Cheers. Mick.
This issue has been resolved in version "1.8.0.960-rc" of the Google.Apis.YouTube.v3 Client Library.
They've added a new method called ResumeAsync and it works fine. I wish I'd known they were working on it.
One minor issue I needed to resolve was resuming an upload after restarting the application or rebooting. The current api does not allow for this but two minor changes resolved the issue.
I added a new signature for the ResumeAsync method, which accepts and sets the original UploadUri. The StreamLength property needs to be initialised to avoid an overflow error.
public Task<IUploadProgress> ResumeAsync(Uri uploadUri, CancellationToken cancellationToken)
{
UploadUri = uploadUri;
StreamLength = ContentStream.CanSeek ? ContentStream.Length : UnknownSize;
return ResumeAsync(cancellationToken);
}
I also exposed the getter for UploadUri so it can be saved from the calling application.
public Uri UploadUri { get; private set; }
I've managed to get this to work using reflection and avoided the need to modify the API at all. For completeness, I'll document the process but it isn't recommended. Setting private properties in the resumable upload object is not a great idea.
When your resumeable upload object has been destroyed after an application restart or reboot, you can still resume an upload using version "1.8.0.960-rc" of the Google.Apis.YouTube.v3 Client Library.
private static void SetPrivateProperty<T>(Object obj, string propertyName, object value)
{
var propertyInfo = typeof(T).GetProperty(propertyName, BindingFlags.NonPublic | BindingFlags.Instance);
if (propertyInfo == null) return;
propertyInfo.SetValue(obj, value, null);
}
private static object GetPrivateProperty<T>(Object obj, string propertyName)
{
if (obj == null) return null;
var propertyInfo = typeof(T).GetProperty(propertyName, BindingFlags.NonPublic | BindingFlags.Instance);
return propertyInfo == null ? null : propertyInfo.GetValue(obj, null);
}
You need to save the UploadUri during the ProgressChanged event.
Upload.ResumeUri = GetPrivateProperty<ResumableUpload<Video>>(InsertMediaUpload, "UploadUri") as Uri;
You need to set the UploadUri and StreamLength before calling ResumeAsync.
private const long UnknownSize = -1;
SetPrivateProperty<ResumableUpload<Video>>(InsertMediaUpload, "UploadUri", Upload.ResumeUri);
SetPrivateProperty<ResumableUpload<Video>>(InsertMediaUpload, "StreamLength", fileStream.CanSeek ? fileStream.Length : Constants.UnknownSize);
Task = InsertMediaUpload.ResumeAsync(CancellationTokenSource.Token);

Categories

Resources