Asynchronous call in web service - c#

I'm using web service for my wp7 app and i have a difficulty in finding out whether the asynchronous call to the service returns the value or not. i have a validation depends on the response(result). The following the code snippet for calling the web service and appropriate changes that should be made to the UI.
private void TLP_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
objevnt.indexChanged(1, lpcountry.SelectedIndex, "depart");
if(objevnt.CName.Count>0)
lpcity.ItemsSource = objevnt.GetCityDetails();
}
public void indexChanged(int Travel,int index,string journey)
{
string slCountry = null;
switch (Travel)
{
case 1:
slCountry = lstCtryDetails.lstCtrylist[index].countryId.ToString();
//= "depart";
travelMode = journey;
break;
case 2:
slCountry = lstCtryDetails.lstCtrylist[index].countryId.ToString();
travelMode = journey;
break;
case 3:
slCountry = lstCtryDetails.lstCtrylist[index].countryId.ToString();
travelMode = journey;
break;
}
GetCities = "http://Solutions/mobileservice/Citylist/countrycode/" + slCountry;
WebClient myClientcity = new WebClient();
myClientcity.DownloadStringAsync(new Uri(GetCities, UriKind.RelativeOrAbsolute));
myClientcity.DownloadStringCompleted += new DownloadStringCompletedEventHandler(myClientcity_DownloadStringCompleted);
}
private void myClientcity_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
string _Countries = null;
if (e.Error == null)
{
_Countries = e.Result;
parseCtry(_Countries);
}
}
private void parseCtry(string xml)
{
XDocument myXdoc = XDocument.Load(new StringReader(xml));
IEnumerable<XElement> lstElm = myXdoc.Descendants("GetCityList");
lstCtryDetails.lstCitylist.Clear();
foreach (XElement ele in lstElm.Elements())
{
if (ele.Name.ToString() != "Message")
{
// Fetch t Details
if (!ele.IsEmpty && ele.Name.ToString() == "City")
{
lstCtryDetails.lstCitylist.Add(new cityList() { CityId = ele.Element("CityCode").Value, CityName = ele.Element("CityName").Value, CityLatitude = ele.Element("Latitude").Value, CityLongitude = ele.Element("Longitude").Value });
CName.Add(ele.Element("CityName").Value);
//Countchk = true;
}
}
}
lsCity = lstCtryDetails.lstCitylist;
//chkloop = true;
}
public List<cityList> GetCityDetails()
{
if (lsCity.Count > 0)
return lsCity;
return null;
}
Now i need to get the list of values from the getcitydetails() method. but that is reurning null due to asynchronous call. how to get the count of list, to do appropriate validations. Thanks in advance.

You're setting the ItemsSource potentially before your WebClient has returned it's result, nor had chance to call parseCtry.
If you move:
lpcity.ItemsSource = objevnt.GetCityDetails();
to the end of parseCtry (i.e. where the comment //chkloop = true; is) then it will evaluate to have the correct count - but you may run into cross-thread UI access issues, but you should be able to check with something like (at the start of DownloadStringCompleted):
if (Dispatcher.CheckAccess())
Dispatcher.BeginInvoke(new DownloadStringCompletedEventHandler(myClientcity_DownloadStringCompleted), xml) // put us back on the UI thread if required
You may be better off changing the type of lsCity to be an ObservableCollection<cityList> and binding to it.

Have you tried adding the callback before performing the webrequest?
myClientcity.DownloadStringCompleted += new DownloadStringCompletedEventHandler(myClientcity_DownloadStringCompleted);
myClientcity.DownloadStringAsync(new Uri(GetCities, UriKind.RelativeOrAbsolute));
=== Updated ===
From your comments I think I understand that problem. What you want is to data bind an ObservableCollection<T> to your UI element lpCity. If for instance if lpCity was data bound to lsCity (implementing ObservableCollection<T>), once lpCity is updated via a asynchronous web request, the UI element will update automatically.
I would write out a code skeleton for you, I'm away from my Windows PC.

Related

Xamarin form :How to search in listview

I have listview containing data from web API. I want to search in the listview with character wise. The problem I am facing is when I start searching, it works fine but it gets very very slow. I need some solution to fix it. Here is my code:
private async void Entry_TextChanged(object sender, TextChangedEventArgs e)
{
var httpClient = new HttpClient();
var json = await httpClient.GetStringAsync(" http://172.16.4.212:51583/api/GetItems");
var admtPatients = JsonConvert.DeserializeObject<List<tblItem>>(json);
ObservableCollection<tblItem> trends = new ObservableCollection<tblItem>(admtPatients);
if (string.IsNullOrEmpty(medicine.Text))
{
MyListView.ItemsSource = trends;
}
else
{
MyListView.ItemsSource = trends
.Where(x =>
x.strItemName.ToLowerInvariant().Contains(e.NewTextValue.ToLowerInvariant()) ||
x.strItemName.ToUpperInvariant().Contains(e.NewTextValue.ToUpperInvariant()));
}
//await ((MainViewModel)this.BindingContext).LoadCountNotificationAsync();
}
Each time Entry_TextChanged is triggered, the call to GetStringAsync is done, which is very time consuming. This means that whenever the user presses a key a call to the API is made. This is why it is so slow.
You are better off calling GetStringAsync in the page's OnAppearing (for example), and saving the result globally:
private List<tblItem> listOfTableItems = new List<tblItem>();
protected override void OnAppearing()
{
var json = await httpClient.GetStringAsync("http://172.16.4.212:51583/api/GetItems");
listOfTableItems = JsonConvert.DeserializeObject<List<tblItem>>(json);
}
Then, in your Entry_TextChanged you reference listOfTableItems from the examples above:
if (String.IsNullOrEmpty(e.NewTextValue))
{
MyListView.ItemsSource = new ObservableCollection<tblItem>(listOfTableItems);
}
else
{
MyListView.ItemsSource = new ObservableCollection<tblItem>(listOfTableItems
.Where(x => x.strItemName.ToLowerInvariant().Contains(e.NewTextValue.ToLowerInvariant())));
}

SendMessageAsync throws exception randomly when communicating between Win32 and UWP

In my UWP App I need to continuously send data to a UWP app from a WinForms (Win32) component and vice versa. However, I have a weird bug in my WinForms component. Sometimes, upon launching the WinForm, I get a System.InvalidOperationException when calling await connection.SendMessageAsync(message) saying: A method was called at an unexpected time Other times, it works perfectly.
My Code:
private async void SendToUWPVoidAsync(object content)
{
ValueSet message = new ValueSet();
if (content != "request") message.Add("content", content);
else message.Add(content as string, "");
#region SendToUWP
// if connection isn't inited
if (connection == null)
{
// init
connection = new AppServiceConnection();
connection.PackageFamilyName = Package.Current.Id.FamilyName;
connection.AppServiceName = "NotifyIconsUWP";
connection.ServiceClosed += Connection_ServiceClosed;
// attempt connection
AppServiceConnectionStatus connectionStatus = await connection.OpenAsync();
}
AppServiceResponse serviceResponse = await connection.SendMessageAsync(message);
// get response
if (serviceResponse.Message.ContainsKey("content"))
{
object newMessage = null;
serviceResponse.Message.TryGetValue("content", out newMessage);
// if message is an int[]
if (newMessage is int[])
{
// init field vars
int indexInArray = 0;
foreach (int trueorfalse in (int[])newMessage)
{
// set bool state based on index
switch (indexInArray)
{
case 0:
notifyIcon1.Visible = Convert.ToBoolean(trueorfalse);
break;
case 1:
notifyIcon2.Visible = Convert.ToBoolean(trueorfalse);
break;
case 2:
notifyIcon3.Visible = Convert.ToBoolean(trueorfalse);
break;
default:
break;
}
indexInArray++;
}
}
}
#endregion
}
The method is called like this:
private void TCheckLockedKeys_Tick(object sender, EventArgs e)
{
...
if (statusesChanged)
{
// update all bools
bool1 = ...;
bool2 = ...;
bool3 = ...;
// build int[] from bool values
int[] statuses = new int[] { Convert.ToInt32(bool1), Convert.ToInt32(bool2), Convert.ToInt32(bool3) };
// update UWP sibling
SendToUWPVoidAsync(statuses);
}
// ask for new settings
SendToUWPVoidAsync("request");
}
TCheckLockedKeys_Tick.Interval is set to 250 milliseconds.
Is there any way to prevent or to correctly handle this exception without the WinForm Component exiting but still establishing the vital communication path?
Any ideas?
Thanks
Okay, I have found a solution. One might actually call it a workaround.
In my WinForm, I changed the code as follows:
AppServiceResponse serviceResponse = await connection.SendMessageAsync(message);
to:
AppServiceResponse serviceResponse = null;
try
{
// send message
serviceResponse = await connection.SendMessageAsync(message);
}
catch (Exception)
{
// exit
capsLockStatusNI.Visible = false;
numLockStatusNI.Visible = false;
scrollLockStatusNI.Visible = false;
Application.Exit();
}
I have also changed code in my App.xaml.cs file:
private async void OnTaskCanceled(IBackgroundTaskInstance sender, BackgroundTaskCancellationReason reason)
{
if (this.appServiceDeferral != null)
{
// Complete the service deferral.
this.appServiceDeferral.Complete();
}
}
to:
private async void OnTaskCanceled(IBackgroundTaskInstance sender, BackgroundTaskCancellationReason reason)
{
if (reason == BackgroundTaskCancellationReason.SystemPolicy)
{
// WinForm called Application.Exit()
await FullTrustProcessLauncher.LaunchFullTrustProcessForCurrentAppAsync();
}
if (this.appServiceDeferral != null)
{
// Complete the service deferral.
this.appServiceDeferral.Complete();
}
}
I know all I'm doing is, technically, relaunching the Form till it succeeds which is not entirely the correct way of solving it. But, it works.
Some advice based on
Reference Async/Await - Best Practices in Asynchronous Programming
Avoid using async void except with event handlers. Prefer async Task methods over async void methods.
async void methods a fire an forget, which can cause the issues being encountered as exceptions are not being thrown in the correct context.
Async void methods have different error-handling semantics. When an exception is thrown out of an async Task or async Task method, that exception is captured and placed on the Task object. With async void methods, there is no Task object, so any exceptions thrown out of an async void method will be raised directly on the SynchronizationContext that was active when the async void method started.
Given that the method is being called within an event handler. then refactor the method to use an async Task
private async Task SendToUWPVoidAsync(object content) {
//...
}
and update the event handler to be async
private async void TCheckLockedKeys_Tick(object sender, EventArgs e) {
try {
//...
if (statusesChanged) {
// update all bools
bool1 = ...;
bool2 = ...;
bool3 = ...;
// build int[] from bool values
int[] statuses = new int[] { Convert.ToInt32(bool1), Convert.ToInt32(bool2), Convert.ToInt32(bool3) };
// update UWP sibling
await SendToUWPVoidAsync(statuses);
}
// ask for new settings
await SendToUWPVoidAsync("request");
}catch {
//...handle error appropriately
}
}
which should also allow for any exception to be caught as shown in the example above.

HttpApplication.Context null in Global.asax

Referencing this answer regarding regenerating new SessionID
I created this code in my Global.asax.cs:
protected void Application_Start(object sender, EventArgs e)
{
Bootstrapper.Initialized += new EventHandler<ExecutedEventArgs>(Bootstrapper_Initialized);
}
void Bootstrapper_Initialized(object sender, Telerik.Sitefinity.Data.ExecutedEventArgs e)
{
if (e.CommandName == "Bootstrapped")
{
EventHub.Subscribe<ILoginCompletedEvent>(LoginCompletedEventVerification);
}
}
private void LoginCompletedEventVerification(ILoginCompletedEvent evt)
{
if (evt.LoginResult == UserLoggingReason.Success)
{
var manager = new SessionIDManager();
var oldId = manager.GetSessionID(Context);
var newId = manager.CreateSessionID(Context);
bool isAdd = false, isRedir = false;
manager.SaveSessionID(Context, newId, out isRedir, out isAdd);
var ctx = HttpContext.Current.ApplicationInstance;
var mods = ctx.Modules;
var ssm = (SessionStateModule)mods.Get("Session");
var fields = ssm.GetType().GetFields(BindingFlags.NonPublic | BindingFlags.Instance);
SessionStateStoreData rqItem = null;
SessionStateStoreProviderBase store = null;
FieldInfo rqIdField = null, rqLockIdField = null, rqStateNotFoundField = null;
foreach (var field in fields)
{
if (field.Name.Equals("_store")) store = (SessionStateStoreProviderBase)field.GetValue(ssm);
if (field.Name.Equals("_rqId")) rqIdField = field;
if (field.Name.Equals("_rqLockId")) rqLockIdField = field;
if (field.Name.Equals("_rqSessionStateNotFound")) rqStateNotFoundField = field;
if ((field.Name.Equals("_rqItem")))
{
rqItem = (SessionStateStoreData)field.GetValue(ssm);
}
}
var lockId = rqLockIdField.GetValue(ssm);
if ((lockId != null) && (oldId != null))
{
store.ReleaseItemExclusive(Context, oldId, lockId);
store.RemoveItem(Context, oldId, lockId, rqItem);
}
rqStateNotFoundField.SetValue(ssm, true);
rqIdField.SetValue(ssm, newId);
}
}
Please keep in mind that I am developing in a Sitefinity web application.
Every time my application hits LoginCompletedEventVerification during a successful login, Context comes up as null. Now, I initially wanted to add this snippet to the Sitefinity LoginWidget, but making that happen is a whole other story.
I did not include it in the code sample, but I do have Session_Start firing to create my application's "shopping cart." I am just trying to create a new SessionID for the cart after authentication.
Is there a reason I cannot get a value for Context during this event?
Thanks in advance. I appreciate any suggestions or criticism!
EDIT: Sitefinity knowledge base article where I got my Bootstrapper_Initialized code
I did not include it in the code sample, but I do have Session_Start
firing to create my application's "shopping cart." I am just trying to
create a new SessionID for the cart after authentication.
Nooooo. Forget about accessing HttpContext in the Application_Start event.
Alternatively you could do that in Application_BeginRequest:
private static object syncRoot = new object();
private static bool initialized = false;
public void Application_BeginRequest(object sender, EventArgs e)
{
if (!initialized)
{
lock (syncRoot)
{
if (!initialized)
{
// Do your stuff here with HttpContext
initialized = true;
}
}
}
}
Another thing you should be aware of is that HttpContext will not be available in any background threads that you might have spawned and in which the HTTP request has already finished executing. So you should be extremely careful where you are trying to access this HttpContext.
The LoginCompletedEvent event is synchronous - it is not fired in a background thread, it is rather part of the authentication request. You can access the current context by either directly calling HttpContect.Current or since you are in the context of a Sitefinity application you can use the Sitefinity wrapper for the current context:
var currentContext = SystemManager.CurrentHttpContext;

Multithreading design-pattern

While keeping in mind that:
I am using a blocking queue that waits for ever until something is added to it
I might get a FileSystemWatcher event twice
The updated code:
{
FileProcessingManager processingManager = new FileProcessingManager();
processingManager.RegisterProcessor(new ExcelFileProcessor());
processingManager.RegisterProcessor(new PdfFileProcessor());
processingManager.Completed += new ProcessingCompletedHandler(ProcessingCompletedHandler);
processingManager.Completed += new ProcessingCompletedHandler(LogFileStatus);
while (true)
{
try
{
var jobData = (JobData)fileMonitor.FileQueue.Dequeue();
if (jobData == null)
break;
_pool.WaitOne();
Application.Log(String.Format("{0}:{1}", DateTime.Now.ToString(CultureInfo.InvariantCulture), "Thread launched"));
Task.Factory.StartNew(() => processingManager.Process(jobData));
}
catch (Exception e)
{
Application.Log(String.Format("{0}:{1}", DateTime.Now.ToString(CultureInfo.InvariantCulture), e.Message));
}
}
}
What are are you suggestions on making the code multi-threaded while taking into consideration the possibility that two identical string paths may be added into the blocking queue? I have left the possibility that this might happen and in this case.. the file would be processed twice, the thing is that sometimes I get it twice, sometimes not, it is really awkward, if you have suggestions on this, please tell.
The null checking is for exiting the loop, I intentionally add a null from outside the threaded loop to determine it to stop.
For multi-threading this... I would probably add a "Completed" event to your FileProcessingManager and register for it. One argument of that event will be the "bool" return value you currently have. Then in that event handler, I would do the checking of the bool and re-queueing of the file. Note that you will have to keep a reference to the FileMonitorManager. So, I would have this ThreadProc method be in a class where you keep the FileMonitorManager and FileProcessingManager instances in a property.
To deduplicate, in ThreadProc, I would create a List outside of the while loop. Then inside the while loop, before you process a file, lock that list, check to see if the string is already in there, if not, add the string to the list and process the file, if it is, then skip processing.
Obviously, this is based on little information surrounding your method but my 2 cents anyway.
Rough code, from Notepad:
private static FileMonitorManager fileMon = null;
private static FileProcessingManager processingManager = new FileProcessingManager();
private static void ThreadProc(object param)
{
processingManager.RegisterProcessor(new ExcelFileProcessor());
processingManager.RegisterProcessor(new PdfFileProcessor());
processingManager.Completed += ProcessingCompletedHandler;
var procList = new List<string>();
while (true)
{
try
{
var path = (string)fileMon.FileQueue.Dequeue();
if (path == null)
break;
bool processThis = false;
lock(procList)
{
if(!procList.Contains(path))
{
processThis = true;
procList.Add(path);
}
}
if(processThis)
{
Thread t = new Thread (new ParameterizedThreadStart(processingManager.Process));
t.Start (path);
}
}
catch (System.Exception e)
{
Console.WriteLine(e.Message);
}
}
}
private static void ProcessingCompletedHandler(bool status, string path)
{
if (!status)
{
fileMon.FileQueue.Enqueue(path);
Console.WriteLine("\n\nError on file: " + path);
}
else
Console.WriteLine("\n\nSucces on file: " + path);
}

How can I call a function that requires a return value that calls WebClient?

Here is the function I have now that obviously doesn't work. The reason it doesn't work is because WebClient is asynchronous and data is empty before it gets filled by WebClient and crashes on the XML reader. How can I call WebClient within this function and still allow it to return ServerResult as required with or without needing an external event handler?
static public ServerResult isBarcodeCorrectOnServer(string barcode)
{
Dictionary<string, IPropertyListItem> dict = configDictionary();
string urlString = (string.Format("http://www.myurl.com/app/getbarcodetype.php?realbarcode={0}&type={1}", barcode, dict["type"]));
WebClient wc = new WebClient();
string data = "";
wc.DownloadStringCompleted += (sender, e) =>
{
if (e.Error == null)
{
//Process the result...
data = e.Result;
}
};
wc.DownloadStringAsync(new Uri(urlString));
StringReader stream = new StringReader(data);
var reader = XmlReader.Create(stream);
var document = XDocument.Load(reader);
var username = document.Descendants("item");
var theDict = username.Elements().ToDictionary(ev => ev.Name.LocalName, ev => ev.Value);
if (theDict.ContainsKey("type") == true && theDict["type"].ToString() == dict["type"].ToString())
{
return ServerResult.kOnServer;
}
else if (theDict.ContainsKey("type") == true)
{
return ServerResult.kWrongType;
}
else
{
return ServerResult.kNotOnServer;
}
}
You can't without "hacks" and you shouldn't - embrace asynchrony and pass in a delegate that you want to be executed once the download is completed:
static public void isBarcodeCorrectOnServer(string barcode, Action<string> completed)
{
//..
wc.DownloadStringCompleted += (sender, e) =>
{
if (e.Error == null)
{
//Process the result...
data = e.Result;
completed(data);
}
};
//..
}
You can move all your processing code now into a separate method which you call with the download result.
You can't, basically. Or at the very least you shouldn't. You've got a method which is designed to be synchronous, on a platform which is designed for asynchronous IO.
Fundamentally, you should design your code to work with the platform. Accept that it will be asynchronous, and make the calling code deal with that.
Note that when C# 5 comes out and when it's supported by Windows Phone, all of this will be a lot simpler using async. You'd return a Task<ServerResult> from the method, and await the result of the WebClient. If you're only developing for fun (so don't mind working with a CTP which has some bugs, and may not be valid to ship for marketplace apps) you can do that today.

Categories

Resources