Problem with asynchronous webservice response - silverlight

In my WP7 application i'm calling and consuming a webservice with these methods:
In my page .cs file:
public void Page_Loaded(object sender, RoutedEventArgs e)
{
if (NavigationContext.QueryString["val"] == "One")
{
listAgences=JSON.callWSAgence("http://...");
InitializeComponent();
DataContext = this;
}
}
In my json class i have these methods :
public List<Agence> callWSAgence(string url)
{
WebClient webClient = new WebClient();
Uri uri = new Uri(url);
webClient.OpenReadAsync(uri);
webClient.OpenReadCompleted += new OpenReadCompletedEventHandler(OpenReadCompletedTestAgence);
return listAgences;
}
public void OpenReadCompletedTestAgence(object sender, OpenReadCompletedEventArgs e)
{
StreamReader reader = new System.IO.StreamReader(e.Result);
jsonResultString = reader.ReadToEnd().ToString();
addAgencesToList();
reader.Close();
}
public void addAgencesToList()
{
jsonResultString = json.Substring(5, json.Length - 6);
listAgences = JsonConvert.DeserializeObject<List<Agence>>(json);
}
The problem is that the OpenReadCompletedTest method in the json class is not called right after
webClient.OpenReadCompleted += new OpenReadCompletedEventHandler(OpenReadCompletedTestAgence);
So the listAgences returned is empty.
But later OpenReadCompletedTest is called and everything works fined, but my view has already been loaded.
What can i do to have a kind of synchronous call or to reload my view after the webservice response being parsed and my list being filled.

The behaviour (problem) you are seeing is because the web request is made asynchronously.
If you want to have a separate object call the web server this will need to handle a callback to process the response or make appropriate changes itself.
Also:
- the code in the question doesn't show what the variable json is defined as. In Page_Loaded it looks like a custom class but in OpenReadCompletedTestAgence and addAgencesToList it looks like a string.
- the code in Page_Loaded sets the value of listAgences twice.
check out the following question for more information about making asychronous calls synchrously Faking synchronous calls in Silverlight WP7

Related

FiddlerCore Without Registering As A System Proxy

I have a .NET Application that I need to alter some WCF traffic headers on. The Microsoft folks told me there is no way to really inject the headers I need in the request and pick them up out of the response with the current framework. What I would like to do is just add FiddlerCore to the application, and if the header isn't there on the outgoing request from my application, then I would like to add it. (Real simple).
I can get everything to work, however the events only fire if I register the FiddlerApplication as a system proxy. I would like this transparent to the user so that it doesn't screw up their proxy settings in the OS.
private void Form1_Load(object sender, EventArgs e)
{
Fiddler.FiddlerApplication.SetAppDisplayName("FiddlerCoreTester");
Fiddler.FiddlerApplication.RequestHeadersAvailable += this.RequestHeadersAvailable;
Fiddler.FiddlerApplication.BeforeRequest += this.BeforeRequest;
Fiddler.FiddlerApplication.AfterSessionComplete += this.SessionComplete;
FiddlerApplication.OnNotification += this.OnNotification;
Fiddler.FiddlerApplication.ResponseHeadersAvailable += this.ResponseHeadersAvailable;
Fiddler.URLMonInterop.SetProxyInProcess("127.0.0.1:80", "<-loopback>");
Fiddler.FiddlerApplication.Startup(80, false, false);
WebClient wc = new WebClient();
string s = wc.DownloadString("http://www.google.com");
System.Windows.Forms.MessageBox.Show(s);
Fiddler.FiddlerApplication.Shutdown();
}
private void ResponseHeadersAvailable(Session oSession)
{
}
private void OnNotification(object sender, NotificationEventArgs e)
{
}
private void SessionComplete(Session oSession)
{
}
private void RequestHeadersAvailable()
{
}
private void BeforeRequest(Fiddler.Session oSession)
{
if (oSession.RequestHeaders.Exists("TESTHEADER") == false) {
oSession.RequestHeaders.Add("TESTHEADER", "TEST");
}
}
The events never get called in this case, however if I change this over to the below it does:
Fiddler.FiddlerApplication.Startup(80, true, false);
Does anyone know how to get this working?
Thanks so much
You should let your webclient use FiddlerCore as the proxy. You need to do this by setting the proxy of the webclient equal to proxy url that Fiddler listens to. Now your webclient loads the string through http://127.0.0.1:80 and Fiddler can capture your request.
WebClient wc = new WebClient();
wc.Proxy = new WebProxy(new Uri("http://127.0.0.1:80"));
string s = wc.DownloadString("http://www.google.com");
Fiddler.FiddlerApplication.Shutdown();

How to ensure wcf service client finishs his works in silverlight?

I use wcf service client to submit changes of data for a silverlight project. The correlative codes like this:
public class DispatcherCollection : UpdatableCollection<DocumentDispatcher>
{
public override void SubmitChanges()
{
DocumentServiceClient client = new DocumentServiceClient();
client.NewDocumentCompleted += (s, e) =>
{
// (s as DocumentServiceClient).CloseAsync();
// do something
};
client.UpdateColumnCompleted += (s, e) =>
{
// (s as DocumentServiceClient).CloseAsync();
// do something
};
client.RemoveDocumentCompleted += (s, e) =>
{
// (s as DocumentServiceClient).CloseAsync();
// do something
};
foreach (DocumentDispatcher d in this)
{
if (d.IsNew)
{
// d=>object[] data
client.NewDocumentAsync(data);
d.IsNew=false;
}
else
{
foreach (string propertyName in d.modifiedProperties)
{
client.UpdateColumnAsync(d.ID, GetPropertyValue(propertyName));
}
dd.ClearModifications();
}
}
foreach (DocumentDispatcher dd in removedItems)
{
client.RemoveDocumentAsync(dd.ID);
}
removedItems.Clear();
}
}
Class UpdatableCollection derives from ObserableCollection, and I implemtent logics in class DocumentDispatcher and UpdatableCollection to buffer the changes of data such as new created, property modified and removed. I use SubmitChanges method to submit all changes to server.
Now I am stuck:
1. I am at a loss when to close the client after a bunlde fo async calls. I don't know which callback is the last one.
2. What will happen when a user closes the IE immediately right after clicking the save button (it seems to be done because it runs async but in fact the updating threads are industriously running.)?
You can keep a counter or use an isbusy function to monitor the callbacks from your Async calls - to make sure they all finished.
If the user fires off a request to the WCF service, the WCF service will complete but there will be no call back - as the application will be closed.
I think that there is no wait handle for silverlight asynchornized call brings inconvenience. Here is my experence. I want to check and submit modifications of data which are not expicitly submitted when browser is closing. I have implemented codes in App_Exit like this:
private void Application_Exit(object sender, EventArgs e)
{
Document doc = EDPViewModel.CurrentViewModel.Document;
if (doc != null) new ServiceClient().SubmitChangesAsync(doc);
}
provided that in the SubmitChangesAsync method, not submitted modifications of doc are found out and submitted. Therefore, because of the asynchronized running features, while the service invoking is being sent, the application is yet immediately closed. And that will dispose related resouces of the application, including Service Invoking Tasks. So the codes above work not. I hope so eagerly that somewhere exists a mechanism, which can export a wait handle from silverlight asynchronized call, so that I can update the above codes whith this:
private void Application_Exit(object sender, EventArgs e)
{
Document doc = EDPViewModel.CurrentViewModel.Document;
if (doc != null)
{
Task t = new TaskFactory().StartNew(() => new ServiceClient().SubmitChangesAsync(doc));
t.Wait();
}
}
With wait operation I can really be sure that all modifications are really definitely submitted. So is there any similar pattern that can be used in silverlight?
It's for me a good news, as you put it, that calls could work like the mode "requesting and forgetting". So I needn' to worry too much about data losing during submitting.
To ensure all service calls are sent out before application is closed, I think, counter is a simple and effient idea. I will try to implement it in my project.
Thank you for your help!

How to make Entity methods in RIA DomainContext work asynchronously

Intro:
I have a RIA service on Silverlight application that generates the code from the .Web app.
On a server side, I am using EF4 and a DomainService based on a EF4 model.
First example:
If I extend the DomainService with my own methods implementing IEnumerable or IQueryable the RIA generates the appropriate methods on its DomainContext class. Something like this:
public partial class SymbolicDataService
{
public IQueryable<Chemical> GetWeightedChemicals(int min, int max)
{
// ... some EF query here
}
}
RIA generates the method, so I can do something like this on Silverlight side:
private void btnLoad_Click(object sender, RoutedEventArgs e)
{
SymbolicDataContext db = new SymbolicDataContext();
var chemicals = db.Load(db.GetWeightedChemicalsQuery(10,24), onChemicalsLoaded, false);
}
and then I respond to the loading in a onChemicalsLoaded callback function.
Second example:
If I want a method that does not return IEnumerable or IQueryable, but is a void method, I mark the DomainService's method with [Invoke] attribute:
[Invoke]
public void FlushChemical(Chemical chemical)
{
// some code that does what it does (with EF)
}
Now I can do something like:
private void btnLoad_Click(object sender, RoutedEventArgs e)
{
SymbolicDataContext db = new SymbolicDataContext();
var chemical = db.GetWeightedChemicals(10,24).FirstOrDefault();
db.FlushChemical(chemical);
}
Third Example:
If I do:
public void ShakeChemical(Chemical chem, int timeShaking)
{
// Shake the chemical until it drops
}
RIA will create an Entity method on client side that enables me to do this:
private void btnShake_Click(object sender, RoutedEventArgs e)
{
Chemical chem = (ListBox)sender.SelectedItem as Chemical;
chem.ShakeChemical(22);
db.SaveChanges();
}
Question:
My question here is how to make the last two examples work asynchronously like the LoadOperation? In the first example, I can use callback on Load method to respond to the operation completion, but I have no idea how to make the other two functions asynchronous and I don't want my UI to block during the calls.
EDIT:
I see now that the second example's method has an overload with Action argument so I do have a callback for the second example. However, the question remains for the third example.
I'm assuming that the third scenario is updating a Chemical object in some way?? If this is the case then just look into "Named Update" methods for RIA Services. Hope this helps

Silverlight Webservice Problem

I have a webservice that calls a method and returns a generic list. The webservice completed method looks like this (note: names and e.Result are both of the same type of List):
void SetNames()
{
ServiceReference1.ServiceClient webservice = new ServiceReference1.ServiceClient();
webservice.GetNameCompleted += new EventHandler<GetNameCompletedEventArgs>(webservice_GetNameCompleted);
webservice.GetNameAsync();
}
private void webservice_GetNameCompleted(object sender, ServiceReference1.GetNameCompletedEventArgs e)
{
names = e.Result;
}
The problem I'm having is that I can only retrieve the items in the names list in the webservice method. Whenever I try to access the items in the names list anywhere outside of that method it is empty. For example (this displays nothing in the textbox),
List<string> names = new List<string>();
public MainPage()
{
InitializeComponent();
SetNames();
foreach (string name in names)
textBox1.Text += name;
}
But this will display the correct thing:
private void webservice_GetNameCompleted(object sender, ServiceReference1.GetNameCompletedEventArgs e)
{
names = e.Result;
foreach (string name in names)
textBox1.Text += name;
}
I'm new to Silverlight and webservies, and I'm probably over looking something. I've been working on this for a while and I'm at the point where I feel I need to ask for help. Any help would be greatly appreciated!
In Silverlight all calls to web-services are asynchronous (unlike WPF which can also use synchronous call).
It means that the code after the call to the web-service will be invoked before the service has sent a response to the Silverlight client.
So, in the MainPage constructor, the foreach loop is iterating over the collection BEFORE the service has returned, and then iterate over an empty collection.
The right way to proceed is the second one : initializing the collection after the service has responded, in the callback method dedicated to this task : webservice_GetNameCompleted.
You have to wait for the Web Servicec call back to complete.
By defualt all Silverlight WCF web service calls are asynchronous.
you are sending a request to the webservice and unlike .asmx with WCF and Silverlight the application continues to run instead of waiting for the webservice to return a result.
So when you make a call like:
public MainPage()
{
InitializeComponent();
SetNames();
foreach (string name in names)
textBox1.Text += name;
}
The application does not stop and wait for SetNames to Return a value it just carries on and since the webservice hasn't returned a result yet you have a blank or null list still when you call your foreach.
Cheers

synchronizing webClient download (silverlight)

so I have this function which gets called multiple times during my program.
//global variable
BitmapImage img;
private void LoadImageFile(string ImageName)
{
WebClient ImageClient = new WebClient();
ImageClient.DownloadStringCompleted += new DownloadStringCompletedEventHandler(ImageFileLoaded);
xmlClient.DownloadStringAsync(new Uri("/images/"+ImageName, UriKind.RelativeOrAbsolute));
}
void ImageFileLoaded(object sender, DownloadStringCompletedEventArgs e)
{
if (e.Error == null)
{
img.set = e.Result;
}
}
the following code uses the new value of "img" so I want it to start only after img has been assigned the new source but it seems that it runs before that happens
You want to use WebClient.OpenReadAsync() instead of WebClient.DownloadStringAsync() because you want to read a binary image, not a string.
Then when you get the stream, you call BitmapImage.SetSource() using that stream.
I would check out this blog by Jeremy Likness.
It uses corountines to help organise async requests. I have used this approach and have dealt with similar issues where I want actions to occur after several async tasks.

Resources