I'm new here and I hope you can help me with a problem I've got.
I'm trying to call some API's from Unity UWP, but every time on the third call I've got an Task Cancellation Exception, no matter what the order is of calling the api's.
Here is my piece of code:
public class DashboardManager : MonoBehaviour
{
public TMP_Text Status;
async Task Awake()
{
await GetPerformanceInfo().ConfigureAwait(false);
await GetProductionInfo().ConfigureAwait(false);
await GetChecklistInfo().ConfigureAwait(false);
}
private async Task GetChecklistInfo()
{
try
{
HttpClient httpClient = new HttpClient((HttpMessageHandler)ApiHelpers.HttpClientHandler);
httpClient.BaseAddress = new Uri(ApiHelpers.PlantApiBaseAddress.CheckBaseUrl(true));
var plantApiClient = new PlantApiClient(httpClient, default); // await ProgramManager.ServiceProvider.GetService<PlantApiClient>()
var checklists = await plantApiClient.GetChecklistWidgetInfo(UserSettings.Location);
//Status.text = "Checklist OK";
}
catch (LogoutException ex)
{
SceneManager.LoadScene("LoginScene");
}
catch (TaskCanceledException ex)
{
Status.text = "Checklist: " + ex.Message;
}
catch (Exception ex)
{
Status.text = "Checklist: " + ex.Message;
}
}
public void OnClickButton(string guid)
{
GlobalManager.CurrentChecklistID = guid;
SceneManager.LoadScene("ChecklistsScene");
}
private async Task GetPerformanceInfo()
{
try
{
HttpClient httpClient = new HttpClient((HttpMessageHandler)ApiHelpers.HttpClientHandler);
httpClient.BaseAddress = new Uri(ApiHelpers.PlantApiBaseAddress.CheckBaseUrl(true));
var plantApiClient = new PlantApiClient(httpClient, default); // await ProgramManager.ServiceProvider.GetService<PlantApiClient>()
var perfInfo = await plantApiClient.GetPerformanceWidgetInfo(UserSettings.Location);
//Status.text = "Performance OK";
}
catch (LogoutException ex)
{
SceneManager.LoadScene("LoginScene");
}
catch (TaskCanceledException ex)
{
Status.text = "Performance: " + ex.Message;
}
catch (Exception ex)
{
Status.text = "Performance: " + ex.Message;
}
}
private async Task GetProductionInfo()
{
try
{
HttpClient httpClient = new HttpClient((HttpMessageHandler)ApiHelpers.HttpClientHandler);
httpClient.BaseAddress = new Uri(ApiHelpers.PlantApiBaseAddress.CheckBaseUrl(true));
var plantApiClient = new PlantApiClient(httpClient, default); // await ProgramManager.ServiceProvider.GetService<PlantApiClient>()
var prodInfo = await plantApiClient.GetProductionWidgetInfo(UserSettings.Location);
//Status.text = "Production OK";
}
catch (LogoutException ex)
{
SceneManager.LoadScene("LoginScene");
}
catch (TaskCanceledException ex)
{
Status.text = "Production: " + ex.Message;
}
catch (Exception ex)
{
Status.text = "Production: " + ex.Message;
}
}
}
In the Awake Task, the 3rd call always fails with an TaskCancellation Exception.
If I change the order, it always fails on the third call.
There is nothing wrong with the api calls, because they are working as expected. If I just call one or two, everything works fine, but when I call a 3rd one, it fails.
What's happening here, I hope somebody can help me.
Damn, this took me 2 wasted days, just to figure out this was a bug in Unity 2020.3.11f1.
I installed version 2021.2.8f1 and everything is working fine now.
Related
I would like to know if the code I produced is good practice and does not produce leaks, I have more than 7000 objects Participant which I will push individually and Handle the Response to save the "external" id in our database. First I use the Parallel ForEach on the list pPartcipant:
Parallel.ForEach(pParticipant, participant =>
{
try
{
//Create
if (participant.id == null)
{
ExecuteRequestCreate(res, participant);
}
else
{//Update
ExecuteRequestUpdate(res, participant);
}
}
catch (Exception ex)
{
LogHelper.Log("Fail Parallel ", ex);
}
});
Then I do a classic (not async request), but after I need to "handle" the response (print in the console, save in a text file in async mode, and Update in my database)
private async void ExecuteRequestCreate(Uri pRes, ParticipantDo pParticipant)
{
try
{
var request = SetRequest(pParticipant);
//lTaskAll.Add(Task.Run(() => { ExecuteAll(request, pRes, pParticipant); }));
//Task.Run(() => ExecuteAll(request, pRes, pParticipant));
var result = RestExecute(request, pRes);
await HandleResult(result, pParticipant);
//lTaskHandle.Add(Task.Run(() => { HandleResult(result, pParticipant); }));
}
catch (Exception e)
{
lTaskLog.Add(LogHelper.Log(e.Message + " " + e.InnerException));
}
}
Should I run a new task for handeling the result (as commented) ? Will it improve the performance ?
In comment you can see that I created a list of tasks so I can wait all at the end (tasklog is all my task to write in a textfile) :
int nbtask = lTaskHandle.Count;
try
{
Task.WhenAll(lTaskHandle).Wait();
Task.WhenAll(lTaskLog).Wait();
}
catch (Exception ex)
{
LogHelper.Log("Fail on API calls tasks", ex);
}
I don't have any interface it is a console program.
I would like to know if the code I produced is good practice
No; you should avoid async void and also avoid Parallel for async work.
Here's a similar top-level method that uses asynchronous concurrency instead of Parallel:
var tasks = pParticipant
.Select(participant =>
{
try
{
//Create
if (participant.id == null)
{
await ExecuteRequestCreateAsync(res, participant);
}
else
{//Update
await ExecuteRequestUpdateAsync(res, participant);
}
}
catch (Exception ex)
{
LogHelper.Log("Fail Parallel ", ex);
}
})
.ToList();
await Task.WhenAll(tasks);
And your work methods should be async Task instead of async void:
private async Task ExecuteRequestCreateAsync(Uri pRes, ParticipantDo pParticipant)
{
try
{
var request = SetRequest(pParticipant);
var result = await RestExecuteAsync(request, pRes);
await HandleResult(result, pParticipant);
}
catch (Exception e)
{
LogHelper.Log(e.Message + " " + e.InnerException);
}
}
Catching an UnauthorizedAccessException works fine, but it doesn't when I make the File.WriteAllBytes -> await Task.Run(() => File.WriteAllBytes(Path + vid.FullName, vid.GetBytes()));
When I do this the exception doesn't get caught. What's the best way to catch this exception, so that the MessageBox shows? Thanks.
(NOTE: This code is clearly a YouTube converter, but it is for my own personal use)
public async Task ConvertVideoAndDownloadToFolder(string link, string MP3Name = null)
{
try
{
var youtube = YouTube.Default;
var vid = await Task.Run(() => youtube.GetVideo(link));
await Task.Run(() => File.WriteAllBytes(Path + vid.FullName, vid.GetBytes()));
var inputFile = new MediaFile { Filename = Path + vid.FullName };
MediaFile outputFile;
//Creates mp4 and mp3
if (String.IsNullOrEmpty(MP3Name)) { outputFile = new MediaFile { Filename = $"{Path + vid.FullName}.mp3" }; }
//only creates MP4
else { outputFile = new MediaFile { Filename = $"{MP3Name}.mp3" }; }
using (var engine = new Engine())
{
await Task.Run(() => engine.GetMetadata(inputFile));
await Task.Run(() => engine.Convert(inputFile, outputFile));
}
}
catch (Exception ex)
{
if(ex is UnauthorizedAccessException)
{
MessageBox.Show($"Can't write on '{Path}' as it is protected.\rPlease choose another folder. ", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
}
else if(ex is ArgumentException)
{
MessageBox.Show($"'{link}' is not a valid YouTube link, \rMake sure the video still works, or check the link for typos. ", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
}
else
{
throw;
}
}
}
In Asynchronous programming it is very easy for exceptions to be swallowed as they typically run on another thread. This needs to be carefully managed
AggregateException stores all the exceptions that happen in different threads and therefore you do not lose any exception
catch (AggregateException aex)
{
foreach(var ex in aex.InnerExceptions) //This will consolidate t
{
Console.WriteLine(ex.Message);
}
}
Reference
I don't understand why but I'm receiving Flurl Exceptions and those are not being caught by the try/catch block. Any ideas on why that's happening?
Here's the code:
try
{
var x = await Utils.Sales.GetUrl()
.PostJsonAsync(new Sale
{
MerchantId = Constants.Sandbox.MerchantId
})
.ReceiveJson<Sale>();
var b = x;
}
catch (FlurlHttpTimeoutException)
{
//LogError("Timed out!"); //todo:
}
catch (FlurlHttpException ex)
{
var x = ex.Message;
//todo:
//if (ex.Call.Response != null)
// LogError("Failed with response code " + call.Response.StatusCode);
//else
// LogError("Totally failed before getting a response! " + ex.Message);
}
catch (Exception ex)
{
var a = ex.Message;
}
Here's the output (the only reason why I know the exception is being thrown):
Maybe this page will help https://msdn.microsoft.com/zh-cn/library/jj619227.aspx
Sorry don't have a english version, you can try google translate it.
It's someting wrong with you catch exception type or await code.
Try this way catch your exception:
```
try
{
await t1;
}
catch (AggregateException ex)
{
var innerEx = ex.InnerExceptions[0];
if (innerEx is NotSupportedException)
{
...
}
else if (innerEx is NotImplementedException)
{
...
}
else
{
...
}
}
```
This is killing me. I'm new to android/Xamarin. I can't find anything on the web that explains what is going on here.
I'm using xamarin forms for my application. I have a page that synchronizes the device with a web service.
The method simply retrieves 100 records at a time from the web service and updates a sqlite table on the device. I randomly get this error. I'm running 5000 records for my test sample.
Here is the button click:
public async void OnSyncCachedData_Clicked(object sender, EventArgs e)
{
activityIndicatorAll.IsRunning = true;
try
{
actIndSyncItems.IsRunning = true;
SyncAllFinished += SynchAllFinishedProcessing;
await Task.Run(async () => await App.ItemsRepo.LoadCacheDataFromCacheAsync(DbPath).ConfigureAwait(false));
}
catch (BAL.Exceptions.NetworkException nex)
{
await DisplayAlert(Messages.TitleError, nex.Message, Messages.MsgOk);
}
catch (Exception ex)
{
await DisplayAlert(Messages.TitleError, string.Format(Messages.MsgAreYouConnectedParm1, ex.Message), Messages.MsgOk);
}
finally
{
EventHandler handler = SyncAllFinished;
if (handler != null)
{
handler(this, new EventArgs());
}
SyncAllFinished -= SynchAllFinishedProcessing;
}
}
the main worker method:
public async Task<bool> LoadCacheDataFromCacheAsync(string dbPath)
{
WebSvcManagers.ItemsManager itemsWebServiceManager = new WebSvcManagers.ItemsManager();
List<Models.WebServiceItems> consumedRecords = new List<Models.WebServiceItems>() { };
int bufferSize = 100;
Log.Debug(TAG, "LoadCacheDataFromCacheAsync starting");
try
{
{
int lastID = 0;
IEnumerable<Models.WebServiceItems> remoteRecords = await BAL.DataAccessHelper.GetItemsFromGPCacheAsync(App.Login, lastID, bufferSize, itemsWebServiceManager).ConfigureAwait(false);
while (remoteRecords.Count() != 0)
{
foreach (Models.WebServiceItems remoteItem in remoteRecords)
{
// DbActionTypes dbAction = (DbActionTypes)remoteItem.DbAction;
Models.Items itemRecord = new Models.Items() { ItemNumber = remoteItem.ItemNumber.ToUpper().Trim(), Description = remoteItem.Description.Trim() };
Log.Debug(TAG, "Processing {0}", remoteItem.ItemNumber.Trim());
bool success = await AddRecordAsync(itemRecord).ConfigureAwait(false);
if (success)
consumedRecords.Add(remoteItem);
}
lastID = remoteRecords.Max(r => r.RecordID) + 1;
remoteRecords = await BAL.DataAccessHelper.GetItemsFromGPCacheAsync(App.Login, lastID, bufferSize, itemsWebServiceManager).ConfigureAwait(false);
}
}
// await UpdateConsumedRecords(consumedRecords).ConfigureAwait(false);
return true;
}
catch (Exception ex)
{
this.StatusMessage = ex.Message;
Log.Debug(TAG, "Error Catch: {0}", StatusMessage);
return false;
}
finally
{
itemsWebServiceManager = null;
HandleSyncFinished(this, new EventArgs());
SyncAllFinished -= HandleSyncFinished;
}
}
My simple webservice manager:
public static async Task<IEnumerable<Models.WebServiceItems>> GetItemsFromGPCacheAsync(Models.Login login, int offset, int bufferCount, WebSvcManagers.ItemsManager manager)
{
try
{
return await manager.GetCacheRecordsAsync(login, offset, bufferCount).ConfigureAwait(false);
}
catch (Exception)
{
throw;
}
}
And the code for the interaction with the web service:
const int bufferSize = 100;
public async Task<IEnumerable<Models.WebServiceItems>> GetCacheRecordsAsync(Models.Login login, int offSet, int bufferCount)
{
string deviceID = App.ConfigSettings.DeviceID.ToString("D");
try
{
///* Testing start */
//return await DataStore(bufferCount, offSet).ConfigureAwait(false);
///* Testing end */
if (!App.IsConnected)
throw new BAL.Exceptions.NetworkException(Messages.ExceptionNetworkConnection);
string user = login.UserName;
string password = login.Password;
HttpClient client = HttpClientExtensions.CreateHttpClient(user, password);
try
{
List<Models.WebServiceItems> items = new List<Models.WebServiceItems>() { };
int lastID = offSet;
int i = 0;
string uri = string.Format("{0}", string.Format(Messages.WebRequestItemsCacheParms3, deviceID, lastID, Math.Min(bufferCount, bufferSize)));
Log.Debug(TAG, string.Format("Webservice {0}", uri));
string response = await client.GetStringAsync(uri).ConfigureAwait(false);
while (i < bufferCount && response != null && response != "[]")
{
while (response != null && response != "[]")
{
dynamic array = JsonConvert.DeserializeObject(response);
foreach (var item in array)
{
i++;
items.Add(new Models.WebServiceItems()
{
ItemNumber = item["ITEMNMBR"].Value.Trim(),
Description = item["ITEMDESC"].Value.Trim(),
DbAction = (int)(item["DbAction"].Value),
RecordID = (int)(item["DEX_ROW_ID"].Value),
});
lastID = (int)(item["DEX_ROW_ID"].Value);
Log.Debug(TAG, string.Format("Webservice {0}", item["ITEMNMBR"].Value.Trim()));
}
if (i < Math.Min(bufferCount, bufferSize))
{
uri = string.Format("{0}", string.Format(Messages.WebRequestItemsCacheParms3, deviceID, lastID + 1, Math.Min(bufferCount, bufferSize)));
Log.Debug(TAG, string.Format("Webservice {0}", uri));
response = await client.GetStringAsync(uri).ConfigureAwait(false);
}
else
break;
}
}
Log.Debug(TAG, string.Format("Webservice return {0} items", items.Count()));
return items;
}
catch (Exception ex)
{
Log.Debug(TAG, "Error Catch: {0}", ex.Message);
throw ex;
}
}
catch (System.Net.Http.HttpRequestException nex)
{
throw new Exception(string.Format(Messages.ExceptionWebServiceLoginParm1, nex.Message));
}
catch (Exception)
{
throw;
}
}
I've had assistance from a Xamarin instructor and we thought we got it, (because this is random), but it clearly is not swatted. I've been hitting my head up against a wall for over 3 weeks on this. It smells like a memory leak, but I have no idea how to find it with such a generic error message that doesn't appear on web searches.
Somebody out there with some major brains, Please help!
Error:
08-03 12:41:11.281 D/X:ItemsRepositiory(16980): UpdateRecordAsync 65702-40710
08-03 12:41:11.306 D/X:ItemsManager(16980): Webservice DeleteCacheRecordAsync 20497
08-03 12:41:11.406 D/X:ItemsManager(16980): Webservice api/InventoryItems?DeviceID=7c5bb45d-2ea0-45b9-ae50-92f2e25a2983&OffSet=20498&Max=100&cached=true
Thread finished: <Thread Pool> #7 08-03 12:41:11.521 E/art (16980): Nested signal detected - original signal being reported The thread 'Unknown' (0x7) has exited with code 0 (0x0).
This might be a problem with the VS Android Emulator. As we have shown in tests, this is not reproducible on the Google Android Emulators, nor on Xamarin Android Player, nor on a physical Android device.
I've got a Post method in my webapi 2 controller that does an insert into a database but often has to retry for many seconds before it succeeds. Basically, that causes a lot of sleeps between the retries. Effectively, it is running the code below. My question is, is this code correct so that I can have thousands of these running at the same time and not using up my iis page pool?
public async Task<HttpResponseMessage> Post()
{
try
{
HttpContent requestContent = Request.Content;
string json = await requestContent.ReadAsStringAsync();
Thread.Sleep(30000);
//InsertInTable(json);
}
catch (Exception ex)
{
throw ex;
}
return new HttpResponseMessage(HttpStatusCode.OK);
}
* Added By Peter As To Try Stephens's suggestion of Await.Delay. Shows error that can not put await in catch.
public async Task<HttpResponseMessage> PostXXX()
{
HttpContent requestContent = Request.Content;
string json = await requestContent.ReadAsStringAsync();
bool success = false;
int retriesMax = 30;
int retries = retriesMax;
while (retries > 0)
{
try
{
// DO ADO.NET EXECUTE THAT MAY FAIL AND NEED RETRY
retries = 0;
}
catch (SqlException exception)
{
// exception is a deadlock
if (exception.Number == 1205)
{
await Task.Delay(1000);
retries--;
}
// exception is not a deadlock
else
{
throw;
}
}
}
return new HttpResponseMessage(HttpStatusCode.OK);
}
* More Added By Peter, Trying Enterprise block, missing class (StorageTransientErrorDetectionStrategy class not found)
public async Task<HttpResponseMessage> Post()
{
HttpContent requestContent = Request.Content;
string json = await requestContent.ReadAsStringAsync();
var retryStrategy = new Incremental(5, TimeSpan.FromSeconds(1),
TimeSpan.FromSeconds(2));
var retryPolicy =
new RetryPolicy<StorageTransientErrorDetectionStrategy>(retryStrategy);
try
{
// Do some work that may result in a transient fault.
retryPolicy.ExecuteAction(
() =>
{
// Call a method that uses Windows Azure storage and which may
// throw a transient exception.
Thread.Sleep(10000);
});
}
catch (Exception)
{
// All the retries failed.
}
*****Code that causes SqlServer to spin out of control with open connections
try
{
await retryPolicy.ExecuteAsync(
async () =>
{
// this opens a SqlServer Connection and Transaction.
// if fails, rolls back and rethrows exception so
// if deadlock, this retry loop will handle correctly
// (caused sqlserver to spin out of control with open
// connections so replacing with simple call and
// letting sendgrid retry non 200 returns)
await InsertBatchWithTransaction(sendGridRecordList);
});
}
catch (Exception)
{
Utils.Log4NetSimple("SendGridController:POST Retries all failed");
}
and the async insert code (with some ...'s)
private static async Task
InsertBatchWithTransaction(List<SendGridRecord> sendGridRecordList)
{
using (
var sqlConnection =
new SqlConnection(...))
{
await sqlConnection.OpenAsync();
const string sqlInsert =
#"INSERT INTO SendGridEvent...
using (SqlTransaction transaction =
sqlConnection.BeginTransaction("SampleTransaction"))
{
using (var sqlCommand = new SqlCommand(sqlInsert, sqlConnection))
{
sqlCommand.Parameters.Add("EventName", SqlDbType.VarChar);
sqlCommand.Transaction = transaction;
try
{
foreach (var sendGridRecord in sendGridRecordList)
{
sqlCommand.Parameters["EventName"].Value =
GetWithMaxLen(sendGridRecord.EventName, 60);
await sqlCommand.ExecuteNonQueryAsync();
}
transaction.Commit();
}
catch (Exception)
{
transaction.Rollback();
throw;
}
}
}
}
}
No. At the very least, you want to replace Thread.Sleep with await Task.Delay. Thread.Sleep will block a thread pool thread in that request context, doing nothing. Using await allows that thread to return to the thread pool to be used for other requests.
You might also want to consider the Transient Fault Handling Application Block.
Update: You can't use await in a catch block; this is a limitation of the C# language in VS2013 (the next version will likely allow this, as I note on my blog). So for now, you have to do something like this:
private async Task RetryAsync(Func<Task> action, int retries = 30)
{
while (retries > 0)
{
try
{
await action();
return;
}
catch (SqlException exception)
{
// exception is not a deadlock
if (exception.Number != 1205)
throw;
}
await Task.Delay(1000);
retries--;
}
throw new Exception("Retry count exceeded");
}
To use the Transient Fault Handling Application Block, first you define what errors are "transient" (should be retried). According to your example code, you only want to retry when there's a SQL deadlock exception:
private sealed class DatabaseDeadlockTransientErrorDetectionStrategy : ITransientErrorDetectionStrategy
{
public bool IsTransient(Exception ex)
{
var sqlException = ex as SqlException;
if (sqlException == null)
return false;
return sqlException.Number == 1205;
}
public static readonly DatabaseDeadlockTransientErrorDetectionStrategy Instance = new DatabaseDeadlockTransientErrorDetectionStrategy();
}
Then you can use it as such:
private static async Task RetryAsync()
{
var retryStrategy = new Incremental(5, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(2));
var retryPolicy = new RetryPolicy(DatabaseDeadlockTransientErrorDetectionStrategy.Instance, retryStrategy);
try
{
// Do some work that may result in a transient fault.
await retryPolicy.ExecuteAsync(
async () =>
{
// TODO: replace with async ADO.NET calls.
await Task.Delay(1000);
});
}
catch (Exception)
{
// All the retries failed.
}
}