Silverlight Webservice Problem - silverlight

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

Related

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

WPF + Tasks + WCF = No SynchronizationContext?

I have a WPF application that is using System.Threading.Tasks to call a WCF service in the background. I'm using Task.ContinueWith to return the results of the service call to the WPF UI thread. My issue is that, although the continuation does run on the UI thread, when it does SynchronizationContext.Current is null. I can run the same code, commenting out the WCF call in the initial Task, and the continuation is on the UI thread, with a DispatcherSynchronizationContext as expected.
The WCF proxy is generated using ChannelFactory, and uses wsHttpBinding. There is no callback contract. The relevant code is shown below:
private TaskScheduler _uiScheduler;
public MainWindow()
{
InitializeComponent();
_uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
var serviceTask = new Task<Int32>(ServiceCallWrapper,
CancellationToken.None,
TaskCreationOptions.None);
var continueTask = serviceTask.ContinueWith(result => ServiceContinuation(result.Result),
CancellationToken.None,
TaskContinuationOptions.OnlyOnRanToCompletion,
_uiScheduler);
serviceTask.Start();
}
private Int32 ServiceCallWrapper()
{
Int32 result = 0;
var service = {elided - initializes service using ChannelFactory };
result = service.TheServiceMethod();
service.Close();
return result;
}
private void ServiceContinuation(Int32 result)
{ elided }
If I run this code as is, the ServiceContinuation is called on the correct thread (verified using ManagedThreadID), but SynchronizationContext.Current is null. If I comment out the single line that makes the service call (result = service.TheServiceMethod();), then ServiceContinuation is correctly called with a DispatcherSynchronizationContext.
One note - the SynchronizationContext is not permanently lost - if I Click on the button again, the button click handler does have the correct SynchronizationContext.
I've captured stack traces for the two cases; they have a few differences. I've left out all of the bits that are identical, and only included the top of the stacks where they differ, plus a few frames for reference:
Fails - Calls WCF Service
WpfContinuationsTest.MainWindow.ServiceContinuation
WpfContinuationsTest.MainWindow.<Button_Click>b__0
System.Threading.Tasks.Task`1+<>c__DisplayClass17.<ContinueWith>b__16
System.Threading.Tasks.Task.InnerInvoke
System.Threading.Tasks.Task.Execute
System.Threading.Tasks.Task.ExecutionContextCallback
System.Threading.ExecutionContext.runTryCode
System.Runtime.CompilerServices.RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup
System.Threading.ExecutionContext.RunInternal
System.Threading.ExecutionContext.Run
System.Threading.Tasks.Task.ExecuteWithThreadLocal
System.Threading.Tasks.Task.ExecuteEntry
System.Threading.Tasks.SynchronizationContextTaskScheduler.PostCallback
Succeeds - No Call To WCF Service
WpfContinuationsTest.MainWindow.ServiceContinuation
WpfContinuationsTest.MainWindow.<Button_Click>b__0
System.Threading.Tasks.Task`1+<>c__DisplayClass17.<ContinueWith>b__16
System.Threading.Tasks.Task.InnerInvoke
System.Threading.Tasks.Task.Execute
System.Threading.Tasks.Task.ExecutionContextCallback
System.Threading.ExecutionContext.Run
System.Threading.Tasks.Task.ExecuteWithThreadLocal
System.Threading.Tasks.Task.ExecuteEntry
System.Threading.Tasks.SynchronizationContextTaskScheduler.PostCallback
Does anyone know why, when the only difference is a WCF client service call (with no callback contract), in one case the continuation on the main thread would have a SynchronizationContext, and in the other case it wouldn't?
According to Microsoft, this is a known bug with the TPL:
http://social.msdn.microsoft.com/Forums/en-US/wcf/thread/629d5524-c8db-466f-bc27-0ced11b441ba

Can Silverlight store data retrieved from a WCF service locally?

Is it possible to save some result from wcf service in variable (inside my silverlight app)? For example : (why myvariable is null) ? thx for help
public partial class MainPage : UserControl
{
private int myvariable;
....
public void s_DoWorkCompleted(object o, ServiceReference1.DoWorkCompletedEventArgs e)
{
myvariable = e.Result;
}
}
Yes, this is totally possible and quite routine. Just remember that what you get back from the server is a copy and not the original variable itself. You can't pull and object back, modify it on the client, and expect the server to get the update. You have to send the object back to the server to do that.
If you are storing null into myvariable, you need to set a breakpoint in your s_DoWorkCompleted to make sure 1) it is getting reached and 2) e.Result isn't null itself. If e.Result is null, you need to go back to your service to ensure it is working.

Problem with asynchronous webservice response

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

Resources