Working with an Azure Mobile App with Node.js backend. I got everything working, kind of. After setting up Offline Sync it is taking about 8 minutes to sync what should be 30 seconds.
After debugging the app I found everything client side is working great. Reviewing the logs on the website I see the "read script" running repeatedly. It literally runs the read script every second, up until it finally finishes.
Is this behavior expected?
It hangs in SyncAsync() while awaiting LocalCards.PullAsync
Read Script
var table = module.exports = require('azure-mobile-apps').table();
table.read(function (context)
{
console.log('Attempting table read');
return context.execute();
});
App Code
private async Task InitLocalStoreAsync()
{
if (!App.MobileService.SyncContext.IsInitialized)
{
var store = new MobileServiceSQLiteStore("localsync32.db");
store.DefineTable<Nname>();
await App.MobileService.SyncContext.InitializeAsync(store);
}
await SyncAsync();
await RefreshNcards();
}
private async Task SyncAsync()
{
String errorString = null;
string unique = DateTime.Now.Ticks.ToString() + DateTime.UtcNow.TimeOfDay.ToString();
try
{
await LocalCards.PullAsync("Nnames "+unique,LocalCards.CreateQuery()); // first param is query ID, used for incremental sync
}
catch (Exception ex)
{
errorString = "Pull failed: " + ex.Message +
"\n\nIf you are still in an offline scenario, " +
"you can try your Pull again when connected with your Mobile Serice.";
}
if (errorString != null)
{
MessageDialog d = new MessageDialog(errorString);
await d.ShowAsync();
}
}
Apparently the PullAsync() command limits the query results to 50 per trip. So in my code I was only pulling 50 items at a time. To fix I added a pull options Param with a max page size:
private async Task SyncAsync()
{
String errorString = null;
PullOptions pageSize = new PullOptions { MaxPageSize =1000 };
try
{
await LocalCards.PullAsync("Nname",LocalCards.CreateQuery(), pullOptions: pageSize); // first param is query ID, used for incremental sync
}
catch (Exception ex)
{
errorString = "Pull failed: " + ex.Message +
"\n\nIf you are still in an offline scenario, " +
"you can try your Pull again when connected with your Mobile Serice.";
}
if (errorString != null)
{
MessageDialog d = new MessageDialog(errorString);
await d.ShowAsync();
}
}
Related
I am working on a program that gets a list of workitems in the committed state from Azure DevOps for a specific area path and iteration path. My code is based on an example found at the following link: https://learn.microsoft.com/en-us/azure/devops/integrate/quickstarts/work-item-quickstart?view=azure-devops
The issue I am running into is when QueryByWiqlAsync() is called, the program terminates and there are no errors for why it terminated. Below is the code in question. I tried calling QueryByWiqlAsync() with and without the ConfigureAwait(false) and that did not seem to make a difference. Any suggestions on what to try or what to fix are appreciated.
static async void GetWorkItemsToTaskFromADO(string tfs_project, string accessToken)
{
var credentials = new VssBasicCredential(string.Empty, accessToken);
var wiql = new Wiql()
{
Query = #"Select [Id] From WorkItems WHERE [System.TeamProject] = 'SampleADOProject' AND [System.AreaPath] = 'Sample\ADO\AreaPath' AND [System.IterationPath] = 'Sample\ADO\IterationPath' AND [System.State] = 'Committed'"
};
using (var httpClient = new WorkItemTrackingHttpClient(new Uri(tfs_project), credentials))
{
try
{
var result = await httpClient.QueryByWiqlAsync(wiql).ConfigureAwait(false);
var ids = result.WorkItems.Select(item => item.Id).ToArray();
var fields = new[] { "System.Id", "System.Title", "System.State" };
var workItems = await httpClient.GetWorkItemsAsync(ids, fields, result.AsOf).ConfigureAwait(false);
// output results to test what came back...
foreach (var workItem in workItems)
{
Console.WriteLine(
"{0}\t{1}\t{2}",
workItem.Id,
workItem.Fields["System.Title"],
workItem.Fields["System.State"]
);
}
}
catch(Exception ex)
{
Console.WriteLine(ex.Message);
Console.Read();
}
}
}
I'm saving a row to my db (class with teacher/students, time, date, etc), once I have the id I create a message on my Azure service bus where the unique id of the row from my db is used as the message body of the service bus message. I'm creating scheduled messages so I can notify the students before the class and after the class is over so they can rate/review their teacher.
QUESTION - I'd like to know how to roll back or an easy way to remove the db row by not allowing it to fully save if the message to the Azure service bus fails to save?
Currently I'm using a generic repository with UnitOfWork to save to my db and I'm catching the exception from my service bus service if it fails, then deleting the row that was just saved, but it's sloppy looking and I can see it will lead to problems.
Here is what I'm doing now in the controller.
[HttpPost("create")]
public async Task<IActionResult> OnCreating(OnCreatingEventDto onCreatingDto)
{
var userFromRepo = await _userManager.FindByEmailFromClaimsPrinciple(HttpContext.User);
if (userFromRepo == null)
return Unauthorized(new ApiResponse(401));
var newEvent = _mapper.Map<ClassEvent>(onCreatingDto);
_unitOfWork.Repository<ClassEvent>().Add(newEvent);
var success = await _unitOfWork.Complete();
if (success > 0) {
try {
var sequenceNUmber = await _serviceBusProducer.SendMessage(newEvent.Id.ToString(), newEvent.eventTime.addDays(1), queueName);
newEvent.ServiceBusSequenceNumber = sequenceNUmber;
_unitOfWork.Repository<ClassEvent>().Update(newEvent);
var secondSuccess = await _unitOfWork.Complete();
if (secondSuccess > 0) {
return Ok();
}
} catch(Exception ex) {
_logger.LogError("error saving to service bus");
_unitOfWork.Repository<ClassEvent>().Delete(newEvent);
var deleteSuccess = await _unitOfWork.Complete();
if (deleteSuccess > 0) {
}
return BadRequest(new ApiResponse(400, "Problem Creating Event"));
}
}
return BadRequest(new ApiResponse(400, "Problem Creating Event"));
}
Here is the method from my service that creates the message on the queue
public async Task<long> SendMessage(string messageBody, DateTimeOffset scheduledEnqueueTime, string queueName)
{
await using (ServiceBusClient client = new ServiceBusClient(_config["ServiceBus:Connection"]))
{
ServiceBusSender sender = client.CreateSender(_config["ServiceBus:" + queueName]);
ServiceBusMessage message = new ServiceBusMessage(messageBody);
var sequenceNumber =
await sender.ScheduleMessageAsync(message, scheduledEnqueueTime);
return sequenceNumber;
}
}
I am trying to test in app purchases in my application following plugin. Also I have checked this and thousands of other officical/not official articles but there is no result. I do not thins there is a problem in my code because I asked another developer to share me his code and I use it in my app. In his project this code works perfect but I have InAppBillingPurchaseException "Can not connect to Itunes Store". I also logged out of my real accounts before entering sandbox credentials. This is the code but the I do not think the problem is here:
public async Task<bool> PurchaseItemAsync()
{
var billing = CrossInAppBilling.Current;
LastExceptionMessage = null;
try
{
var connected = await billing.ConnectAsync();
if (connected == false)
return false;
var purchase = await billing.PurchaseAsync(_kProductId, ItemType.InAppPurchase, _kPayload);
if (purchase == null)
return false;
else if (purchase.State == PurchaseState.Purchased)
return true;
}
catch (InAppBillingPurchaseException ex)
{
OnPurchaseException(ex);
}
catch (Exception ex)
{
//Dlog.Error("Issue connecting: " + ex);
LastExceptionMessage = ex.Message;
}
finally
{
await billing.DisconnectAsync();
}
return false;
}
In my case, bundleID in my app was not matching with the product id of In App purchase. I had bundle ID com.xam.sample in my app code. But product ID was testiap. So I created IAP with productID com.xam.sample.testiap and I was able to solve that error this way.
I am using following method to sync Azure DB local table with server table but the changes which I made on my local DB are not reflecting to the Azure server,
public async Task PushDataAsync()
{
try
{
await _mobileService.SyncContext.PushAsync();
}
catch (Exception exc)
{
throw exc;
}
}
While using above method I am getting Error :-
Push Operation Fail.
Any Help will appreciated.
you are using right method to sync your offline store with server which is :-
await _mobileService.SyncContext.PushAsync();
I would suggest you to wrote few line of code in catch block which will help you to find out the reasons why the operations are not performed on server side
please use code bellow in catch block:-
public async Task PushDataAsync()
{
try
{
await _mobileService.SyncContext.PushAsync();
}
catch (MobileServicePushFailedException exc)
{
if (exc.PushResult != null)
{
syncErrors = exc.PushResult.Errors;
}
}
// Simple error/conflict handling.
if (syncErrors != null)
{
foreach (var error in syncErrors)
{
if (error.OperationKind == MobileServiceTableOperationKind.Update && error.Result != null || error.OperationKind == MobileServiceTableOperationKind.Insert && error.Result != null || error.OperationKind == MobileServiceTableOperationKind.Delete && error.Result != null)
{
//Update failed, reverting to server's copy.
await error.CancelAndUpdateItemAsync(error.Result);
}
else
{
// Discard local change.
await error.CancelAndDiscardItemAsync();
}
Debug.WriteLine(#"Error executing sync {2} operation. Item: {0} ({1}). Operation discarded.",
error.TableName, error.Item["id"], error.OperationKind);
}
}
}
Remember that PushAsync() pushes ALL changes from your local store to the cloud and that PullAsync first performs a Push. I would get rid of the service variable for each table and just use the service as a singleton class across your app. Here is my initialization. After this method returns, my local db is synced with the cloud and I can start using my tables:
public async Task InitializeStoreAsync()
{
try
{
var sqliteStore = _platform.MobileServiceSqliteStore;
sqliteStore.DefineTable<Memory>();
sqliteStore.DefineTable<User> ();
sqliteStore.DefineTable<Comment> ();
sqliteStore.DefineTable<Status>();
await _zumoClient.SyncContext.InitializeAsync(sqliteStore);
_memoryTable = _zumoClient.GetSyncTable<Memory> ();
_userTable = _zumoClient.GetSyncTable<User> ();
_commentTable = _zumoClient.GetSyncTable<Comment> ();
_statusTable = _zumoClient.GetSyncTable<Status>();
await _userTable.PullAsync ();
await _memoryTable.PullAsync ();
await _commentTable.PullAsync ();
await _statusTable.PullAsync();
}
catch (Exception ex)
{
Debug.WriteLine ("Initialize Store failed: {0}", ex.Message);
}
}
https://adrianhall.github.io/develop-mobile-apps-with-csharp-and-azure/chapter3/client/ search for "Handling Conflict Resolution"
I am developing a quartz.net job which runs every 1 hour. It executes the following method. I am calling a webapi inside a for loop. I want to make sure i return from the GetChangedScripts() method only after all thread is complete? How to do this or have i done it right?
Job
public void Execute(IJobExecutionContext context)
{
try
{
var scripts = _scriptService.GetScripts().GetAwaiter().GetResult();
}
catch (Exception ex)
{
_logProvider.Error("Error while executing Script Changed Notification job : " + ex);
}
}
Service method:
public async Task<IEnumerable<ChangedScriptsByChannel>> GetScripts()
{
var result = new List<ChangedScriptsByChannel>();
var currentTime = _systemClock.CurrentTime;
var channelsToProcess = _lastRunReader.GetChannelsToProcess().ToList();
if (!channelsToProcess.Any()) return result;
foreach (var channel in channelsToProcess)
{
var changedScripts = await _scriptRepository.GetChangedScriptAsync(queryString);
if (changedScriptsList.Any())
{
result.Add(new ChangedScriptsByChannel()
{
ChannelCode = channel.ChannelCode,
ChangedScripts = changedScriptsList
});
}
}
return result;
}
As of 8 days ago there was a formal announcement from the Quartz.NET team stating that the latest version, 3.0 Alpha 1 has full support for async and await. I would suggest upgrading to that if at all possible. This would help your approach in that you'd not have to do the .GetAwaiter().GetResult() -- which is typically a code smell.
How can I use await in a for loop?
Did you mean a foreach loop, if so you're already doing that. If not the change isn't anything earth-shattering.
for (int i = 0; i < channelsToProcess.Count; ++ i)
{
var changedScripts =
await _scriptRepository.GetChangedScriptAsync(queryString);
if (changedScriptsList.Any())
{
var channel = channelsToProcess[i];
result.Add(new ChangedScriptsByChannel()
{
ChannelCode = channel.ChannelCode,
ChangedScripts = changedScriptsList
});
}
}
Doing these in either a for or foreach loop though is doing so in a serialized fashion. Another approach would be to use Linq and .Select to map out the desired tasks -- and then utilize Task.WhenAll.