using data on the view from async method - c#

I'm using the geolocator plugin to retrieve my current location and add a pin to the page. Here is the service for this:
tasks.cs
public async Task<Plugin.Geolocator.Abstractions.Position> GetDeviceCurrentLocation()
{
try
{
var locator = Plugin.Geolocator.CrossGeolocator.Current;
locator.DesiredAccuracy = 50;
var position = await locator.GetPositionAsync(TimeSpan.FromSeconds(1));
if (position != null)
{
return position;
}
}
catch (Exception ex)
{
Debug.WriteLine("Unable to get location, may need to increase timeout: " + ex);
}
return new Plugin.Geolocator.Abstractions.Position();
}
I'm trying to use it in the view like this :
public MapPage(List<Models.xxx> xxx, Models.yyy yyy )
{
InitializeComponent();
Tasks ts = new Tasks();
var myLocation = ts.GetDeviceCurrentLocation();
var latitudeIm = myLocation.Result.Latitude;
var longitudeIm = myLocation.Result.Longitude;
var pin1 = new Pin
{
Type = PinType.Place,
Position = new Position(latitudeIm, longitudeIm),
Title = "My Location"
};
customMap.Pins.Add(pin1);
}
My application breaks when I try this code var latitudeIm = myLocation.Result.Latitude;
I guess since I have a async task the result must be awaited. Any idea how can I use the public async Task<Plugin.Geolocator.Abstractions.Position> GetDeviceCurrentLocation() data in my view?

You should use await for async method;
var myLocation = await ts.GetDeviceCurrentLocation();
var latitudeIm = myLocation.Latitude;
var longitudeIm = myLocation.Longitude;
You should decorate all methods as async completely. If you don't able to apply it (I wouldn't recommend it), you could use ConfigureAwait to prevent deadlock;
var position = await locator.GetPositionAsync(TimeSpan.FromSeconds(1)).ConfigureAwait(false);
var myLocation = ts.GetDeviceCurrentLocation().Result;//Also don't hit the Result twice
var latitudeIm = myLocation.Latitude;
var longitudeIm = myLocation.Longitude;

Related

AsyncEnumerator library not processing collection

I am using the AsyncEnumerator library by #Serge Semenov, and after getting the orders, creating the ConcurrentBag and setting count to 0, nothing happens - can anyone see what's missing here? It never hits the processedOrders.ToList() line when debugging.
private async Task<List<OrderDto>> ProcessOrderEvents(DateTime cob)
{
var orders = await _client.GetOrderEvents(cob);
var processedOrders = new ConcurrentBag<OrderDto>();
var count = 0;
await orders.ParallelForEachAsync(async order =>
{
var orderDto = new FidessaOrderDto {Cob = cob};
var orderId = order.Ids.PrimaryId;
Debug.WriteLine($"Processing OrderId: {orderId} #= {++count}");
var instrumentDetails = await GetInstrumentDetails(order);
if (instrumentDetails.Results.Any())
{
//...
}
var tradeDetails = await _client.GetTrade(tradeId: fill, cob);
processedOrders.Add(orderDto);
}, maxDegreeOfParallelism:2);
return processedOrders.ToList();
}
Update: Adding Test root.
[TestMethod]
public async Task Test_20_07_2022()
{
var service = new DataService(new ApiClient());
var data = await service.ProcessData(new DateTime(2022, 07, 20), ReportDataType.Orders);
Assert.AreEqual(920, data.Count);
}
public async Task<List<OrderDto>> ProcessData(DateTime cob,
ReportDataType dataType)
{
await PopulateRouteEvents(cob);
var reportData = new List<OrderDto>();
if (dataType == ReportDataType.Orders)
reportData = await ProcessOrderEventsTpl(cob);
return reportData;
}

Aspnet core Parallels operation DbContext problem

I've a problem when I use Pararrel function in aspnetcore, in particular when in the cycle i try to save something in database. I get my data from externarl api and deseserialize it in my class.
This is the Parallel code.
Root players = JsonConvert.DeserializeObject<Root>(responseStream);
var bulkhead = Policy.BulkheadAsync(10, Int32.MaxValue);
var tasks = new List<Task>();
foreach (var player in players.players)
{
var t = bulkhead.ExecuteAsync(async () =>
{
int wyId = Convert.ToInt32(player.wyId);
HttpRequestMessage secondRequest = createRequest("https://apirest.com/v2/players/" + wyId + "?details=currentTeam&imageDataURL=true");
var client2 = _clientFactory.CreateClient();
var response2 = await client2.SendAsync(secondRequest);
if (response2.IsSuccessStatusCode)
{
var responseStream2 = await response2.Content.ReadAsStringAsync();
dynamic playerFullDetails = JsonConvert.DeserializeObject<dynamic>(responseStream2);
int wyId2 = Convert.ToInt32(playerFullDetails.wyId);
int marketValue = 0;
HttpRequestMessage tirthRequest = createRequest("https://apirest.com/v2/players/" + wyId2 + "/marketvalue");
var client3 = _clientFactory.CreateClient();
var response3 = await client3.SendAsync(tirthRequest);
if (response3.IsSuccessStatusCode)
{
var responseStream3 = await response3.Content.ReadAsStringAsync();
dynamic marketValueResponse = JsonConvert.DeserializeObject<dynamic>(responseStream3);
if (marketValueResponse.marketValue != 0)
{
marketValue = Convert.ToInt32(marketValueResponse.marketValue);
}
}
DateTime birthday = Convert.ToDateTime(playerFullDetails.birthDate);
int age = DateTime.Now.Year - birthday.Year;
Player finalPlayer = new Player();
finalPlayer.PlayerId = wyId2;
finalPlayer.MarketValue = marketValue;
finalPlayer.Value = Convert.ToDouble(marketValue) / Convert.ToDouble(1000000);
finalPlayer.Firstname = playerFullDetails.firstName;
finalPlayer.Lastname = playerFullDetails.lastName;
finalPlayer.Name = playerFullDetails.shortName;
finalPlayer.Position = playerFullDetails.role.name;
finalPlayer.Height = playerFullDetails.height;
finalPlayer.Foot = playerFullDetails.foot;
finalPlayer.IsLocked = false;
finalPlayer.Team = playerFullDetails.currentTeam != null ? playerFullDetails.currentTeam.name : "";
finalPlayer.TeamId = playerFullDetails.currentTeam != null ? playerFullDetails.currentTeam.wyId : 0;
finalPlayer.CompetitionId = 524;
finalPlayer.UpdatedDay = DateTime.Now;
finalPlayer.League = "Serie A";
finalPlayer.Age = age;
Player playerExist = await _context.Player.Where(x => x.PlayerId == wyId2).SingleOrDefaultAsync();
if (playerExist == null)
{
if (finalPlayer.TeamId != 0)
{
await _context.Player.AddAsync(finalPlayer);
await _context.SaveChangesAsync();
}
}
if (finalPlayer.TeamId != 0)
{
Team teamExist = await _context.Team.Where(x => x.TeamId == finalPlayer.TeamId).SingleOrDefaultAsync();
if (teamExist == null)
{
Team team = new Team();
team.TeamId = finalPlayer.TeamId;
team.TeamName = finalPlayer.Team;
await _context.Team.AddAsync(team);
await _context.SaveChangesAsync();
}
}
}
});
tasks.Add(t);
}
await Task.WhenAll(tasks);
The function isert 50/60 (in total would be 500) element in db and finally i receive this error
A second operation was started on this context before a previous operation completed. This is usually caused by different threads concurrently using the same instance of DbContext. For more information on how to avoid threading issues with DbContext, see https://go.microsoft.com/fwlink/?linkid=2097913.
Thanks
It's best practice to use 1 dbcontext per unit of work, and the dbcontext is not thread safe
So either create a new dbcontext per thread or lock access with someting like a semaphore otherwise you will always get unstable code

This OperationContextScope is being disposed out of order

I am calling WCF service in ASP.NET Core and everything is working fine, but whenever end of using gets executed, I get an error:
This OperationContextScope is being disposed out of order
I believe I am using wrong pattern to call WCF service using async/await but I am not sure what I am doing wrong.
Below is the code I am using to call a service.
[HttpPost]
public async Task<IActionResult> Runcase(IFormCollection formCollection)
{
if (ModelState.IsValid)
{
var runnumber = formCollection["Run number"];
await CallServiceasync();
return RedirectToAction("", "");
}
else
{
return View(formCollection);
}
}
public async Task CallServiceasync()
{
var product = p1.Value;
var a = product.first;
foreach (int Age in a.age)
{
foreach (int Gender in a.sex)
{
foreach (int Healthclass in a.uclass)
{
RequestData requestData = new RequestData()
{
ProductID = 534,
STATE = "CO",
AGE1 = Age,
SEX1 = Gender,
UND_CLASS1 = Healthclass,
};
RecieveResponseasync(requestData);
}
}
}
}
public async Task RecieveResponseasync(InputValues inputValues)
{
string reqedata = "";
string apikey = "001010iZno7001010L";
QuoteEngineService.MarketingSoftwareClient Service = new QuoteEngineService.MarketingSoftwareClient();
await Service.OpenAsync();
try
{
using (OperationContextScope scope = new OperationContextScope(Service.InnerChannel))
{
HttpRequestMessageProperty httpRequestMessage = new HttpRequestMessageProperty();
httpRequestMessage.Headers.Add("apikey", apikey);
OperationContext.Current.OutgoingMessageProperties[HttpRequestMessageProperty.Name] = httpRequestMessage;
reqedata = inputValues.XmlSerializetoString();
var result = await Service.ProcessRequestAsync(reqedata, "4fa2-ae27");
var outputvalues = new OutputvaluesViewModel();
outputvalues = result.DeserializeToObject();
List<OutputValue> outputs = new List<OutputValue>();
if (outputvalues.InitialPremium != null)
outputs.Add(new OutputValue { Name = "InitialPremium", Result = outputvalues.InitialPremium});
if (outputvalues.TargetPremium != null)
outputs.Add(new OutputValue { Name = "TargetPremium", Result = outputvalues.TargetPremium });
foreach (var output in outputs)
{
await _context.outputValues.AddAsync(output);
await _context.SaveChangesAsync();
}
await Task.Delay(500);
}
}// **At this point I am getting error**
catch (Exception ex)
{
throw;
}
finally
{
if (Service.State == System.ServiceModel.CommunicationState.Opened)
{
await Service.CloseAsync();
}
}
}
From the docs:
Warning
Do not use the asynchronous "await" pattern within a OperationContextScope block. When the continuation occurs, it may run on a different thread and OperationContextScope is thread specific. If you need to call "await" for an async call, use it outside of the OperationContextScope block.

How to call async method from sync in C#?

I am adding a new web API call to existing functionality. I want to make this API call async but looks like it is causing deadlock. I have to make a lot more changes if I want to make entire code channel async which is not possible.
Questions I have are:
Is it possible to call async method from regular method?
What am I missing here? OR What is the correct approach here?
Code:
// Exisitng Method
public Tuple<RestaurantDeliveryProvider, DeliveryHubResult, Task<DeliveryManagerQuoteResponse>> CreateDeliveryRequest(OrderContextDTO orderContextDto)
{
var provider = RestaurantBl.GetDeliveryProviderInformationByRestaurantId(orderContextDto.RestaurantId ?? 0);
var deliveryHubResult = RestaurantBl.GetDeliveryHubResult(orderContextDto.OrderId ?? 0);;
// New Call which always comes back with "Not Yet Computed" result
Task<DeliveryManagerQuoteResponse> deliveryManagerQuoteResponse = _deliveryManager.CreateQuoteRequestAsync(orderContextDto, orderInfo);
return Tuple.Create(provider, deliveryHubResult, deliveryManagerQuoteResponse);
}
Async Methods:
public async Task<DeliveryManagerQuoteResponse> CreateQuoteRequestAsync(OrderContextDTO orderContextDto, OrderInfoDTO orderInfo)
{
DeliveryManagerQuoteResponse deliveryManagerQuoteResponse = null;
try
{
var restaurantInfo = RestaurantApi.GetRestaurant(orderInfo.RestaurantId);
var quoteRequest = new DeliveryManagerQuoteRequest
{
DeliveryProvider = null,
Country = orderContextDto.DeliveryEstimateRequestDto.RequestedDeliveryAddress.Country,
Concept = "BK",
StoreName = "BK-TEST-US-4",
OrderId = orderInfo.OrderId.ToString(),
AllowCash = false,
PaymentType = OrderPaymentType.Prepaid_Credit,
Note = orderInfo.DeliveryInstructions,
};
deliveryManagerQuoteResponse = await Quote(quoteRequest);
}
catch (Exception ex)
{
Log.ErrorFormat("Get Delivery Manager Quote failed: Error: {0}, OrderId: {1}", ex.Message, orderContextDto.OrderId);
}
return deliveryManagerQuoteResponse;
}
public async Task<DeliveryManagerQuoteResponse> Quote(DeliveryManagerQuoteRequest quoteRequest)
{
DeliveryManagerQuoteResponse deliveryManagerQuoteResponse;
var client = HttpClientFactory.GetClient();
var content = HttpClientFactory.JsonContentFactory.CreateJsonContent(quoteRequest);
var response = await client.PostAsync("https://myUrl", content);
if (response.IsSuccessStatusCode)
{
var data = await response.Content.ReadAsStringAsync();
deliveryManagerQuoteResponse = JsonConvert.DeserializeObject<DeliveryManagerQuoteResponse>(data);
}
else
{
throw new Exception((int)response.StatusCode + "-" + response.StatusCode);
}
return deliveryManagerQuoteResponse;
}
I tried following as well but same result:
public async Task<DeliveryManagerQuoteResponse> Quote(DeliveryManagerQuoteRequest quoteRequest)
{
DeliveryManagerQuoteResponse deliveryManagerQuoteResponse;
using (var client = new HttpClient())
{
var content = HttpClientFactory.JsonContentFactory.CreateJsonContent(quoteRequest);
var response = await client.PostAsync("https://myUrl", content);
if (response.IsSuccessStatusCode)
{
var data = await response.Content.ReadAsStringAsync();
deliveryManagerQuoteResponse = JsonConvert.DeserializeObject<DeliveryManagerQuoteResponse>(data);
}
else
{
throw new Exception((int)response.StatusCode + "-" + response.StatusCode);
}
}
return deliveryManagerQuoteResponse;
}
Output (sorry for the blurry output, if you click on it, you will see clear result):
don't
don't
Basically, there is no good or workable way to call an async method from a sync method and wait for the answer. There's "sync over async", but that's an anti-pattern and should be aggressively avoided.
So either:
rewrite the caller to be async
implement a synchronous version of the API

Calculate loading time of multiple websites using AsyncTask in Xamarin Android and C#.Net

I have a string array which contains addresses of websites:
string[] arr = new string[]
{
"https://www/google.com",
"https://www.yahoo.com",
"https://www.microsoft.com"
};
I have to send these URLs as argument to the asynctask method so that I will be able to calculate the loading time of each website. I don't have to show the website pages, so I am not using webview.
I can use stopwatch or httprequest to calculate the loading time and my ultimate goal is that all the websites need to start loading at the same time asynchronously, and output has to look like the following
Loading time
google - 00:00:04:092345 (hr:min:sec:millisec) yahoo - 00:00:06:028458
How can I send an array to asynctask and how I can generate loading time without using await?
Here is a brief solution of what you could do.
This is not complete nor perfect. It will will give you the loading time of one URL. Also there is a suggestion of how you could extend this to multiple URLs.
You will need a WebView, either in code or from UI.
Load the URL into the WebView using webview.LoadUrl("https://www/google.com");.
Create a new class by extending it from WebViewClient as follows:
public class myWebViewClient : WebViewClient
{
public override void OnPageFinished(WebView view, string url)
{
base.OnPageFinished(view, url);
Console.WriteLine("OnPageFinished for url : " + url + " at : " + DateTime.Now);
}
}
In your OnCreate() method add the following line of code :
webview.SetWebViewClient(new myWebViewClient());
So from here what you have to do is, Create a Dictionary with URL as key and Loading time as value. Set all the loading time to 0 initially. Update the value corresponding to each URL in the OnPageFinished(). Create an async Task function which would return you the populated dictionary.
public async Task<Dictionary<string, double>> myAsyncFunction()
{
await Task.Delay(5); //to make it async
//Wait till all the OnPageFinished events have fired.
while (myDictionary.Any(x=>x.Value == 0) == true)
{
//there are still websites which have not fully loaded.
await Task.Delay(1); //wait a millisecond before checking again
}
return myDictionary;
}
You can call myAsyncFunction() in a seprate thread than your UI and implement the ContinueWith() or just let it run in a separate thread and write that output into somewhere that you can check when required.
eg : Task.Run(async () => await myAsyncFunction());
UPDATE : based on OP's comments
In the UI thread :
var myClassList = new List<myClass>
{
new myClass{URL = "https://www/google.com", TimeTaken = null},
new myClass{URL = "https://www.yahoo.com", TimeTaken = null},
new myClass{URL = "https://www.microsoft.com", TimeTaken = null}
};
Console.WriteLine("Started at : " + DateTime.Now.ToShortTimeString());
var business = new BusinessLogic();
var loadtimetask = business.GetLoadTimeTakenAsync(myClassList);
await loadtimetask;
Console.WriteLine("Completed at : " + DateTime.Now.ToShortTimeString());
And implementation class :
public async Task<List<myClass>> GetLoadTimeTakenAsync(List<myClass> myClassList)
{
Parallel.ForEach(myClassList, myClassObj =>
{
using (var client = new HttpClient())
{
myClassObj.StartTime = DateTime.Now;
var stream = client.GetStreamAsync(myClassObj.URL)
.ContinueWith((s) =>
{
if (s.IsCompleted)
{
var myClassObjCompleted = myClassList.Where(x => x.URL == myClassObj.URL).First();
myClassObjCompleted.EndTime = DateTime.Now;
myClassObjCompleted.TimeTaken = myClassObj.EndTime - myClassObj.StartTime;
}
});
Task.Run(async () => await stream);
}
});
while (myClassList.Any(x => x.TimeTaken == null))
{
await Task.Delay(1);
}
return myClassList;
}
//Create TextView to display status of Wifi
TextView wifitext = FindViewById<TextView>(Resource.Id.WifiTextView);
//Configuring Wifi connection
var connectivityManager = (ConnectivityManager)GetSystemService(ConnectivityService);
var activeConnection = connectivityManager.ActiveNetworkInfo;
if (activeConnection != null && activeConnection.IsConnected)
{
wifitext.Text = "WIFI AVAILABLE";
string[] urladdress = new string[] { "https://www.google.com/", "https://www.yahoo.com/"};
for (int i = 0; i < urladdress.Length; i++)
{
string url = urladdress[i];
//Call async method
Task returnedTask = Task_MethodAsync(url);
}
}
else
wifitext.Text = "WIFI UNAVAILABLE";
}
public async Task Task_MethodAsync(string url)
{
LinearLayout ll = FindViewById<LinearLayout>(Resource.Id.linearLayout1);
WebClient client = new WebClient();
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
Stream listurl = client.OpenRead(url);
StreamReader reader = new StreamReader(listurl);
stopwatch.Stop();
// listurl.Close();
var time = Convert.ToString(stopwatch.Elapsed);

Categories

Resources