I try to develop a mobile app in xamarin forms.
I have news list and i am using SQLite for store that.
When my main page open, before i get the news from SQLite and then i try to get new news from web API. I can save the new news in SQLite but my UI hasn't changed. How can i fix that?
public MainPage()
{
InitializeComponent();
database = new Helper.xxxDatabase(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "xxx.db3"));
SetLocalDBMainNews();
Task.Run(async () => { await GetNewsAsync(); });
}
void SetLocalDBMainNews()
{
if(database.TableExists("News"))
{
List<News> newsList = database.GetNewsData();
if (newsList.Count > 0)
{
MaiNewsList.ItemsSource = newsList.Where(x => x.Type.Equals((int)NewsType.MainNews)).OrderBy(x => x.Sort).ToList();
}
}
else
database.CreateNewsTable();
}
private async Task GetNewsAsync()
{
try
{
var register = database.GetRegisterData();
string url = "http://xxx.azurewebsites.net/api/News/GetPublishedNews";
var response = Tools.Send(url, "GET", register.Token);
var result = JsonConvert.DeserializeObject<ApiResponse<List<News>>>(response);
if (result.HasError == false)
{
var newsList = result.Value;
database.DeleteAllNews();
foreach(var n in newsList)
{
database.Insert(n);
}
MaiNewsList.ItemsSource = newsList.Where(x => x.Type.Equals((int)NewsType.MainNews)).OrderBy(x => x.Sort).ToList();
}
}
catch{}
}
The code works for saving new news to SQLite but i couldn't bind the news to UI in async.
Edit :
After jason's answer, here is the my code latest version
Device.BeginInvokeOnMainThread solve the my problem.
private async Task GetNewsAsync()
{
try
{
var register = database.GetRegisterData();
string url = "http://xxx.azurewebsites.net/api/News/GetPublishedNews";
var response = Tools.Send(url, "GET", register.Token);
var result = JsonConvert.DeserializeObject<ApiResponse<List<News>>>(response);
if (result.HasError == false)
{
var newsList = result.Value;
database.DeleteAllNews();
foreach(var n in newsList)
{
database.Insert(n);
}
Device.BeginInvokeOnMainThread(() =>
{
MaiNewsList.ItemsSource = newsList.Where(x => x.Type.Equals((int)NewsType.MainNews)).OrderBy(x => x.Sort).ToList();
});
}
}
catch
{
}
}
UI changes need to happen on the UI thread using BeginInvokeOnMainThread
Device.BeginInvokeOnMainThread( () =>
{
MaiNewsList.ItemsSource = newsList.Where(x => x.Type.Equals((int)NewsType.MainNews)).OrderBy(x => x.Sort).ToList();
});
Related
I was using the Microsoft Graph API 1.0 but have updated to the Beta in order to use CustomSecurityAttributeValue support.
I've managed to port most of the code but I can't see any way to process multiple results pages.
Previously you would just do something like
if (membersPage.NextPageRequest != null)
membersPage = await membersPage.NextPageRequest.GetAsync();
But NextPageRequest no longer exists, the only available information is OdataNextLink which is a string with no obvious way to request the next page or create a raw request using the url.
Code I have so far:
public async Task<IEnumerable<Microsoft.Graph.Beta.Models.User>> GetGraphUsersInGroups(IEnumerable<string> groupIds, string? searchText = null)
{
Dictionary<String, Microsoft.Graph.Beta.Models.User> users = new Dictionary<String, Microsoft.Graph.Beta.Models.User>();
foreach (var groupId in groupIds)
{
try
{
var membersPage = await GraphClient.Groups[groupId].Members
.GetAsync((memberRequest) => {
memberRequest.Headers.Add(new KeyValuePair<string, string>("$count", "true"));
memberRequest.Headers.Add(new KeyValuePair<string, string>("ConsistencyLevel", "eventual"));
memberRequest.QueryParameters.Count = true;
memberRequest.QueryParameters.Orderby = new[] { "displayName" };
if (searchText != null)
memberRequest.QueryParameters.Search = $"\"displayName:{searchText}\"";
});
while (membersPage != null)
{
foreach (var member in membersPage.Value.OfType<Microsoft.Graph.Beta.Models.User>())
{
users[member.Id] = member;
}
if (membersPage.OdataNextLink != null)
{
// How to use membersPage.OdataNextLink???
}
else
break;
}
}
catch(Exception ex)
{
Console.WriteLine(ex.Message);
Console.WriteLine(ex.StackTrace);
}
}
return users.Values;
}
You should use the PageIterator, see an example below:
var users = new List<User>();
var userResponse = await serviceClient.Users.GetAsync((builder) => {
// builder.SomeStuff
});
// Added the namespace here, just for some clarity :-)
var pageIterator = Microsoft.Graph.PageIterator<User,UserCollectionResponse>
.CreatePageIterator(serviceClient, userResponse, (user) =>
{
users.Add(user.Id);
return true; });
I am trying to run a SQL stored procedure in an async way in C#. I have found two options and I would like to understand the differences between the two:
I have to await before getting out of the using statement else the context would be disposed:
private async Task<List<Currency>> Test1Async()
{
using (var dc = new LandmarkEntities())
{
return await Task.Run(() =>
{
return dc.get_currencies()
.Select(x => new Currency
{
ExchangeRate = x.exchange_rate,
Mnemonic = x.mnemonic,
})
.ToList();
});
}
}
Or I return a running async task containing the entity framework context which will be awaited somewhere else:
private Task<List<Currency>> Test2Async()
{
return Task.Run(() =>
{
using (var dc = new LandmarkEntities())
{
return dc
.get_currencies()
.Select(x => new Currency
{
ExchangeRate = x.exchange_rate,
Mnemonic = x.mnemonic,
})
.ToList();
}
});
}
As get_currencies() is a stored procedure .ToListAsync(); cannot be used.
You could use .ToListAsync();
private async Task<List<Currency>> Test2Async()
{
using (var dc = new LandmarkEntities())
{
return await dc
.get_currencies()
.Select(x => new Currency
{
ExchangeRate = x.exchange_rate,
Mnemonic = x.mnemonic,
})
.ToListAsync();
}
}
I have an issue related to updating an object by reference and not sure exactly what is going on.
I need to delete a list of rules and for logging I need to load another field
ruleItem.ReferencedItemValue = eventEntity.Title;
When
builder.LogDelete(showRule, showRuleResource.ToAuditLog(), "Logic Rule");
is called the ReferencedItemValue is not populated.
Any ideas, suggestions, alternatives?
Thanks
CODE:
public void DeleteCustomLogicRule(int[] ruleIds){
var rules = uow.Context.ShowRules.Where(sr => ruleIds.Contains(sr.Id)).ToList();
if (rules.Any())
{
var showId = rules.FirstOrDefault().ShowId;
var builder = AuditBuilder.FromShowId(showId);
rules.ForEach(showRule =>
{
var showRuleResource = ToShowRuleResource(showRule);
FillReferenceValue(showRuleResource);
builder.LogDelete(showRule, showRuleResource.ToAuditLog(), "Logic Rule");
});
uow.Context.SaveChanges();
builder.ToDatabase();
}
}
private void FillReferenceValue(ShowRuleResource showRuleResource)
{
foreach (var ruleItem in showRuleResource.ItemsPredicateAppliesTo.ToList())
{
FillRuleItem(ruleItem);
}
}
private void FillRuleItem(RuleItemResource ruleItem)
{
var eventEntity = uow.Context.Events.FirstOrDefault(e => e.Id == ruleItem.ReferencedItemId.Value);
if (eventEntity != null)
ruleItem.ReferencedItemValue = eventEntity.Title;
}
try this one:
rules.ForEach(showRule =>
{
var item = showRule;
var showRuleResource = ToShowRuleResource(item);
FillReferenceValue(showRuleResource);
builder.LogDelete(item, showRuleResource.ToAuditLog(), "Logic Rule");
});
Start to dig from this function
private void FillRuleItem(RuleItemResource ruleItem)
It seems like this exspressiion
var eventEntity = uow.Context.Events.FirstOrDefault(e => e.Id == ruleItem.ReferencedItemId.Value);
is null.
Yesterday I've found out how to create several async http requests without async/await. But today I need to do it in a loop: if some of responses don't satisfy some condition - I need to change a request for them and send these requests again. It may be repeated several times.
I've tried this code:
do
{
var loadingCoordinatesTasks = new List<Task<Terminal>>();
var totalCountOfTerminals = terminalPresetNode.ChildNodes.Count;
var uiTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext();
foreach (var terminal in terminals.Except(_terminalsWithCoordinates))
{
var address = terminal.GetNextAddress();
var webRequest = (HttpWebRequest)WebRequest.Create(GeoCoder.GeoCodeUrl + address);
var webRequestTask = Task.Factory.FromAsync<WebResponse>(webRequest.BeginGetResponse,
webRequest.EndGetResponse,
terminal);
var parsingTask = webRequestTask.ContinueWith(antecedent =>
{
// Parse the response
});
loadingCoordinatesTasks.Add(parsingTask);
}
Task.Factory.ContinueWhenAll(loadingCoordinatesTasks.ToArray(), antecedents =>
{
foreach (var antecedent in antecedents)
{
var terminalWithCoordinates = antecedent.Result;
if (antecedent.Status == TaskStatus.RanToCompletion &&
!terminalWithCoordinates.Coordinates.AreUnknown)
{
_terminalsWithCoordinates.Add(terminalWithCoordinates);
_countOfProcessedTerminals++;
}
}
});
} while (_countOfProcessedTerminals < totalCountOfTerminals);
but is it possible to check the condition in while just after every single set of requests executed?
You can perform the check after increasing the count:
_countOfProcessedTerminals++;
if (_countOfProcessedTerminals >= totalCountOfTerminals)
{
break;
}
Is _countOfProcessedTerminals thread-safe though?
I manage to do it using recursion:
public void RunAgainFailedTasks(IEnumerable<Task<Terminal>> tasks)
{
Task.Factory.ContinueWhenAll(tasks.ToArray(), antecedents =>
{
var failedTasks = new List<Task<Terminal>>();
foreach (var antecedent in antecedents)
{
var terminal = antecedent.Result;
// Previous request was failed
if (terminal.Coordinates.AreUnknown)
{
string address;
try
{
address = terminal.GetNextAddress();
}
catch (FormatException) // No versions more
{
continue;
}
var getCoordinatesTask = CreateGetCoordinatesTask(terminal, address);
failedTasks.Add(getCoordinatesTask);
}
else
{
_terminalsWithCoordinates.Add(terminal);
}
}
if (failedTasks.Any())
{
RunAgainFailedTasks(failedTasks);
}
else
{
// Display a map
}
}, CancellationToken.None,
TaskContinuationOptions.None,
TaskScheduler.FromCurrentSynchronizationContext());
}
private Task<Terminal> CreateGetCoordinatesTask(Terminal terminal, string address)
{
var webRequest = (HttpWebRequest)WebRequest.Create(GeoCoder.GeoCodeUrl + address);
webRequest.KeepAlive = false;
webRequest.ProtocolVersion = HttpVersion.Version10;
var webRequestTask = Task.Factory.FromAsync<WebResponse>(webRequest.BeginGetResponse,
webRequest.EndGetResponse,
terminal);
var parsingTask = webRequestTask.ContinueWith(webReqTask =>
{
// Parse the response
});
return parsingTask;
}
I Created several Task in the way below. But it seems WaitAll is not working. It is sending response without wait. Anything goes wrong here?
private void GetItemsPrice(IEnumerable<Item> items, int customerNumber)
{
try
{
var tasks = new List<Task>();
for (var i = 0; i < items.Count(); i += 50)
{
var newTask = DoGetItemsPrice(items.Skip(i).Take(50), customerNumber);
tasks.Add(newTask);
}
Task.WaitAll(tasks.ToArray());
}
catch (Exception ex)
{
ErrorLog.WriteLog(GetType().Name, "GetItemsPrice", string.Format("customerNumber={0}", customerNumber), ex.Message);
}
}
private static Task DoGetItemsPrice(IEnumerable<Item> items, int customerNumber)
{
return Task.Factory.StartNew(() =>
{
var sxApiObj = new SxApiService();
var request = new OEPricingMultipleRequest();
request.customerNumber = customerNumber;
request.arrayProduct =
items.Select(
itemCode =>
new OEPricingMultipleinputProduct
{
productCode = itemCode.ItmNum,
quantity = itemCode.Quantity,
warehouse = ConfigurationVariables.DefaultWareHouse
}).ToArray();
var response = sxApiObj.OEPricingMultiple(ConfigurationVariables.SfAppServer,
ConfigurationVariables.SfUserId,
ConfigurationVariables.SfPassword,
request);
if (response.arrayPrice != null)
{
foreach (var priceData in response.arrayPrice)
{
var productCode = priceData.productCode;
var item = items.FirstOrDefault(itm => itm.ItmNum == productCode);
if (item == null) continue;
item.ItmListPrice1 = priceData.price.ToString("c", ConfigurationVariables.UsCulture);
item.ItmListPrice2 = priceData.discountAmount.ToString("c", ConfigurationVariables.UsCulture);
item.ItmListPrice3 = priceData.extendedAmount.ToString("c", ConfigurationVariables.UsCulture);
item.Quantity = priceData.netAvailable;
}
}
});
}
There is nothing wrong with my question. WaitAll works fine and the code also correct.