I have been struggling to figure out how to use Rx. Most of the examples are out of date, reference Begin/End or are long and complex.
I have a simple WCF service method that takes an int and returns a JobMaster object.
Here is how I call it at the moment:
public static void GetJob(int jobId)
{
KernServiceClient.GetJobCompleted += GetJobCompleted;
KernServiceClient.GetJobAsync(jobId);
}
private static void GetJobCompleted(object sender, GetJobCompletedEventArgs e)
{
// JobMaster available in e.Result
}
How do I change this to use Rx?
EDIT
Thanks to Paul's help I have got most of the way there. This is what it looks like now. Only problem is the Subscribe never fires. Any ideas?
public static JobMaster GetJob(int jobId)
{
JobMaster retval = null;
IKernService kernServiceInterface = KernServiceClient;
var getJobFunc = Observable.FromAsyncPattern<int, Server.KernMobileWcfService.JobMaster>(
kernServiceInterface.BeginGetJob, kernServiceInterface.EndGetJob);
var result = getJobFunc(jobId);
result
.Subscribe
(
onNext: x => retval = ConvertJobMaster(x),
onError: ex => ShowError(ex.Message)
);
return retval;
}
http://blog.paulbetts.org/index.php/2010/09/26/calling-web-services-in-silverlight-using-reactivexaml/ // Ignore the ReactiveXaml part
Summary: Cast KernServiceClient to the Interface it implements to get back the Begin/End methods, use FromAsyncPattern.
Seems you're returning "retval" even though you're computing it asynchronously and assigning it from your OnNext handler. Your logic upon receiving the value from the service should be moved into the OnNext handler, or you should return the IObservable to the caller.
what are you doing with retval when you return it?
if you need to do more processing when on next completes do it in the onCompleted event of the subscription
Related
I have a WCF data service [WebGet] method returning an int. (no of products in products table)
How can I read it's result?
The method works i checked with the browser.
public void ProductsCount()
{
ctx.BeginExecute<int>(new Uri(uriBase + "/GetNoProducts"), GetProductsCountCompleted, ctx);
}
public void GetProductsCountCompleted(IAsyncResult result)
{
inventory_db_bigEntities context=result.AsyncState as inventory_db_bigEntities;
var x = context.EndExecute<int>(result);
//how do i read the int out of the x variable
}
UPDATE
Maybe BeginExecute and EndExecute is not the way to go for it.
In the browser window the webget method returns:
"<GetNoProducts p1:type="Edm.Int32">223863</GetNoProducts>"
The MSDN page for IAsyncResult uses BeginInvoke rather than BeginExecute and then calls EndInvoke to get the value back:
// The asynchronous method puts the thread id here.
int threadId;
// Create an instance of the test class.
AsyncDemo ad = new AsyncDemo();
// Create the delegate.
AsyncMethodCaller caller = new AsyncMethodCaller(ad.TestMethod);
// Initiate the asychronous call.
IAsyncResult result = caller.BeginInvoke(3000, out threadId, null, null);
...
// Wait for the WaitHandle to become signaled.
result.AsyncWaitHandle.WaitOne();
// Perform additional processing here.
// Call EndInvoke to retrieve the results.
string returnValue = caller.EndInvoke(out threadId, result);
What's wrong with your example? EndExecute should do the trick...just call FirstOrDefault() to get the primitive value. Alternatively, if you're not using SL, you can do it synchronously:
http://msdn.microsoft.com/en-us/library/hh230677.aspx#ExecutePrimitiveValue
The question pretty much sums it up. I have a WCF service, and I want to wait until it finished to do something else, but it has to be until it finishes. My code looks something like this. Thanks!
private void RequestGeoCoordinateFromAddress(string address)
{
GeocodeRequest geocodeRequest = new GeocodeRequest();
GeocodeServiceClient geocodeService = new GeocodeServiceClient("BasicHttpBinding_IGeocodeService");
geocodeService.GeocodeCompleted += new EventHandler<GeocodeCompletedEventArgs>(geocodeService_GeocodeCompleted);
// Make the geocode request
geocodeService.GeocodeAsync(geocodeRequest);
//if (geocodeResponse.Results.Length > 0)
// results = String.Format("Latitude: {0}\nLongitude: {1}",
// geocodeResponse.Results[0].Locations[0].Latitude,
// geocodeResponse.Results[0].Locations[0].Longitude);
//else
// results = "No Results Found";
// wait for the request to finish here, so I can do something else
// DoSomethingElse();
}
private void geocodeService_GeocodeCompleted(object sender, GeocodeCompletedEventArgs e)
{
bool isErrorNull = e.Error == null;
Exception error = e.Error;
try
{
double altitude = e.Result.Results[0].Locations[0].Latitude;
double longitude = e.Result.Results[0].Locations[0].Longitude;
SetMapLocation(new GeoCoordinate(altitude, longitude));
}
catch (Exception ex)
{
// TODO: Remove reason later
MessageBox.Show("Unable to find address. Reason: " + ex.Message);
}
}
There is a pattern, supported by WCF, for a call to have an asynchronous begin call, and a corresponding end call.
In this case, the asynchronous methods would be in the client's interface as so:
[ServiceContract]
interface GeocodeService
{
// Synchronous Operations
[OperationContract(AsyncPattern = false, Action="tempuri://Geocode", ReplyAction="GeocodeReply")]
GeocodeResults Geocode(GeocodeRequestType geocodeRequest);
// Asynchronous operations
[OperationContract(AsyncPattern = true, Action="tempuri://Geocode", ReplyAction="GeocodeReply")]
IAsyncResult BeginGeocode(GeocodeRequestType geocodeRequest, object asyncState);
GeocodeResults EndGeocode(IAsyncResult result);
}
If you generate the client interface using svcutil with the asynchronous calls option, you will get all of this automatically. You can also hand-create the client interface if you aren't using automatically generating the client proxies.
The End call would block until the call is complete.
IAsyncResult asyncResult = geocodeService.BeginGeocode(geocodeRequest, null);
//
// Do something else with your CPU cycles here, if you want to
//
var geocodeResponse = geocodeService.EndGeocode(asyncResult);
I don't know what you've done with your interface declarations to get the GeocodeAsync function, but if you can wrangle it back into this pattern your job would be easier.
You could use a ManualResetEvent:
private ManualResetEvent _wait = new ManualResetEvent(false);
private void RequestGeoCoordinateFromAddress(string address)
{
...
_wait = new ManualResetEvent(false);
geocodeService.GeocodeAsync(geocodeRequest);
// wait for maximum 2 minutes
_wait.WaitOne(TimeSpan.FromMinutes(2));
// at that point the web service returned
}
private void geocodeService_GeocodeCompleted(object sender, GeocodeCompletedEventArgs e)
{
...
_wait.Set();
}
Obviously doing this makes absolutely no sense, so the question here is: why do you need to do this? Why using async call if you are going to block the main thread? Why not use a direct call instead?
Generally when using async web service calls you shouldn't block the main thread but do all the work of handling the results in the async callback. Depending of the type of application (WinForms, WPF) you shouldn't forget that GUI controls can only be updated on the main thread so if you intend to modify the GUI in the callback you should use the appropriate technique (InvokeRequired, ...).
Don't use this code with Silverlight:
private ManualResetEvent _wait = new ManualResetEvent(false);
private void RequestGeoCoordinateFromAddress(string address)
{
...
_wait = new ManualResetEvent(false);
geocodeService.GeocodeAsync(geocodeRequest);
// wait for maximum 2 minutes
_wait.WaitOne(TimeSpan.FromMinutes(2));
// at that point the web service returned
}
private void geocodeService_GeocodeCompleted(object sender, GeocodeCompletedEventArgs e)
{
...
_wait.Set();
}
When we call _wait.WaitOne(TimeSpan.FromMinutes(2)), we are blocking the UI thread, which means the service call never takes place. In the background, the call to geocodeService.GeocodeAsync is actually placed in a message queue, and will only be actioned when the thread is not executing user code. If we block the thread, the service call never takes place.
Synchronous Web Service Calls with Silverlight: Dispelling the async-only myth
The Visual Studio 11 Beta inludes C# 5 with async-await.
See Async CTP - How can I use async/await to call a wcf service?
It makes it possible to write async clients in a 'synchronous style'.
I saw one guy did use ManualReset and waitAll, but he had to wrap all code inside of ThreadPool..
It is very bad idea...thought it works
The following code contains a few nested async calls within some foreach loops. I know the silverlight/wcf calls are called asyncrously -but how can I ensure that my wcfPhotographers, wcfCategories and wcfCategories objects are ready before the foreach loop start? I'm sure I am going about this all the wrong way -and would appreciate an help you could give.
private void PopulateControl()
{
List<CustomPhotographer> PhotographerList = new List<CustomPhotographer>();
proxy.GetPhotographerNamesCompleted += proxy_GetPhotographerNamesCompleted;
proxy.GetPhotographerNamesAsync();
//for each photographer
foreach (var eachPhotographer in wcfPhotographers)
{
CustomPhotographer thisPhotographer = new CustomPhotographer();
thisPhotographer.PhotographerName = eachPhotographer.ContactName;
thisPhotographer.PhotographerId = eachPhotographer.PhotographerID;
thisPhotographer.Categories = new List<CustomCategory>();
proxy.GetCategoryNamesFilteredByPhotographerCompleted += proxy_GetCategoryNamesFilteredByPhotographerCompleted;
proxy.GetCategoryNamesFilteredByPhotographerAsync(thisPhotographer.PhotographerId);
// for each category
foreach (var eachCatergory in wcfCategories)
{
CustomCategory thisCategory = new CustomCategory();
thisCategory.CategoryName = eachCatergory.CategoryName;
thisCategory.CategoryId = eachCatergory.CategoryID;
thisCategory.SubCategories = new List<CustomSubCategory>();
proxy.GetSubCategoryNamesFilteredByCategoryCompleted += proxy_GetSubCategoryNamesFilteredByCategoryCompleted;
proxy.GetSubCategoryNamesFilteredByCategoryAsync(thisPhotographer.PhotographerId,thisCategory.CategoryId);
// for each subcategory
foreach(var eachSubCatergory in wcfSubCategories)
{
CustomSubCategory thisSubCatergory = new CustomSubCategory();
thisSubCatergory.SubCategoryName = eachSubCatergory.SubCategoryName;
thisSubCatergory.SubCategoryId = eachSubCatergory.SubCategoryID;
}
thisPhotographer.Categories.Add(thisCategory);
}
PhotographerList.Add(thisPhotographer);
}
PhotographerNames.ItemsSource = PhotographerList;
}
void proxy_GetPhotographerNamesCompleted(object sender, GetPhotographerNamesCompletedEventArgs e)
{
wcfPhotographers = e.Result.ToList();
}
void proxy_GetCategoryNamesFilteredByPhotographerCompleted(object sender, GetCategoryNamesFilteredByPhotographerCompletedEventArgs e)
{
wcfCategories = e.Result.ToList();
}
void proxy_GetSubCategoryNamesFilteredByCategoryCompleted(object sender, GetSubCategoryNamesFilteredByCategoryCompletedEventArgs e)
{
wcfSubCategories = e.Result.ToList();
}
Yes, before you can proceed with the next step of the algorithm, you need to have gotten the result of the previous step, which can be hard when you have to use the async methods.
If this is not happening on the UI thread, then you could just block and wait for the response. For example, have each "completed" method signal (using whatever synchronization primitives are available in Silverlight; I don't know offhand e.g. if ManualResetEvent is there, if so, have the completed callback call .Set()), and then have your main PopulateControl method invoke the FooAsync() call and then block until the ManualResetEvent signals (by calling .Wait()).
If this is on the UI thread and you really need to write a non-blocking solution, then it is much, much harder to code this up correctly in C#. You might consider using F# instead, where asyncs provide a nice programming model for non-blocking calls.
EDIT:
Pseudo-code example to block for results:
// class-level
ManualResetEvent mre = new ManualResetEvent(false);
// some method that needs to make WCF call and use results
void Blah() {
// yadda yadda
proxy.FooCompleted += (o,ea) => { ... mre.Set(); };
proxy.FooAsync(...);
mre.WaitOne(); // block until FooCompleted
// use results from FooCompleted now that they're here
// mre.Reset() if you intend to use it again later
}
I used a lambda for FooCompleted, but using a separate method like you have is fine too.
Alternatively, for each async method you are using to populate the collection you can create a helper method that would return IObservable and then use Linq query to group the result.
E.g.:
private IObservable<Photographer> GetPhotographerNames()
{
var photographers = Observable
.FromEvent<GetPhotographerNamesCompletedEventArgs>(proxy, "GetPhotographerNamesCompleted")
.Prune()
.SelectMany(e => e.EventArgs.Result.ToObservable());
proxy.GetPhotographerNamesAsync();
return photographers;
}
And similarly:
private IObservable<Category> GetCategoryNamesFilteredByPhotographer(int photographerId) { ... }
private IObservable<SubCategory> GetSubCategoryNamesFilteredByCategory(int photographerId, int categoryId) { ... }
Now you can write a Linq query:
var pcs = from p in GetPhotographerNames()
from c in GetCategoryNamesFilteredByPhotographer(p.PhotographerId)
from s in GetSubCategoryNamesFilteredByCategory(p.PhotographerId, c.CategoryId)
select new {p, c, s};
This query will return you a list of triplets (Photographer, Category, SubCategory) Now all you have to do is to Subscribe to it and aggregate it to the objects you use on the client which should be pretty straightforward.
I have the following situation: My WCF service allows a client to register to wait for some event. The waiting is asynchronous on the service side, that is, the waiter is registered and when the process is finished, the waiter is notified. At the moment, it's simply a ManualResetEvent.
Now I want to expose this method via WCF. I tried to use AsyncPattern=true and created two methods, BeginWait which bundles the event into a IAsyncResult, and EndWait which calls AsyncWaitHandle.WaitOne(). However, if I call BeginWait, EndWait from the client, the server side EndWait is not executed. I'm using a manually implemented wrapper (my proxy class is derived from ChannelBase<IWaitService>, IWaitService), which basically calls Channel.EndWait(), and this function is indeed called; but on the server side, the call never arrives.
What am I doing wrong here? Follow-up question: If the asynchronous call is working, is there an easy way to make that synchronous on the client side?
The line
var task = Task.Factory.StartNew(() => IsPrime(a));
uses overload
TaskFactory.StartNew(Action)
which results in
((IAsyncResult)task).AsyncState == null
the call to callback(task) results in an ArgumentException, complaining that the state object is different from the state object that was passed to the BeginXxx method. The line must be modified to
var task = Task.Factory.StartNew((actionState) => IsPrime(a), state);
using overload
TaskFactory.StartNew(Action<object>, object)
such that the state object passed by WCF ends up in the task:
((IAsyncResult)task).AsyncState.GetType().FullName == System.ServiceModel.Dispatcher.MessageRpc+Wrapper
The synchronous/asynchronous decision can be made independently on the server or client side. Calling EndWait on the client does not translate into an EndWait call on the server. I would recommend testing an async service with a sync client just to keep things simple and avoid confusion.
I would further recommend that you not call WaitOne inside of the EndWait method. The contract is that this method will only be called after the IAsyncResult tells the framework that it is done. This is done in one of three ways:
CompletedSynchronously returns true
The AsyncCallback is invoked
The AsyncWaitHandle is signalled
CompletedSynchronously should only return true if BeginWait had enough information to complete the request before it returned. This is probably not the case. You can satisfy the other two conditions with your ManualResetEvent as follows:
class EventBasedAsyncResult : IAsyncResult
{
private readonly ManualResetEvent _manualResetEvent;
private readonly AsyncCallback _asyncCallback;
private readonly object _asyncState;
public EventBasedAsyncResult(AsyncCallback callback, object asyncState)
{
_manualResetEvent = new ManualResetEvent(false);
_asyncState = asyncState;
_asyncCallback = callback;
}
public void WaitCompleted()
{
_manualResetEvent.Set();
_asyncCallback(this);
}
public object AsyncState
{
get { return _asyncState; }
}
public WaitHandle AsyncWaitHandle
{
get { return _manualResetEvent; }
}
public bool CompletedSynchronously
{
get { return false; }
}
public bool IsCompleted
{
get { return _manualResetEvent.WaitOne(0); }
}
}
I think once you do this you'll find that EndWait is called, even if the client is synchronous.
In classic ASP.NET I’d persist data extracted from a web service in base class property as follows:
private string m_stringData;
public string _stringData
{ get {
if (m_stringData==null)
{
//fetch data from my web service
m_stringData = ws.FetchData()
}
return m_stringData;
}
}
This way I could simply make reference to _stringData and know that I’d always get the data I was after (maybe sometimes I’d use Session state as a store instead of a private member variable).
In Silverlight with a WCF I might choose to use Isolated Storage as my persistance mechanism, but the service call can't be done like this, because a WCF service has to be called asynchronously.
How can I both invoke the service call and retrieve the response in one method?
Thanks,
Mark
In your method, invoke the service call asynchronously and register a callback that sets a flag. After you have invoked the method, enter a busy/wait loop checking the flag periodically until the flag is set indicating that the data has been returned. The callback should set the backing field for your method and you should be able to return it as soon as you detect the flag has been set indicating success. You'll also need to be concerned about failure. If it's possible to get multiple calls to your method from different threads, you'll also need to use some locking to make your code thread-safe.
EDIT
Actually, the busy/wait loop is probably not the way to go if the web service supports BeginGetData/EndGetData semantics. I had a look at some of my code where I do something similar and I use WaitOne to simply wait on the async result and then retrieve it. If your web service doesn't support this then throw a Thread.Sleep -- say for 50-100ms -- in your wait loop to give time for other processes to execute.
Example from my code:
IAsyncResult asyncResult = null;
try
{
asyncResult = _webService.BeginGetData( searchCriteria, null, null );
if (asyncResult.AsyncWaitHandle.WaitOne( _timeOut, false ))
{
result = _webService.EndGetData( asyncResult );
}
}
catch (WebException e)
{
...log the error, clean up...
}
Thanks for your help tvanfosson. I followed your code and have also found a pseudo similar solution that meets my needs exactly using a lambda expression:
private string m_stringData;
public string _stringData{
get
{
//if we don't have a list of departments, fetch from WCF
if (m_stringData == null)
{
StringServiceClient client = new StringServiceClient();
client.GetStringCompleted +=
(sender, e) =>
{
m_stringData = e.Result;
};
client.GetStringAsync();
}
return m_stringData;
}
}
EDIT
Oops... actually this doesn't work either :-(
I ended up making the calls Asynchronously and altering my programming logic to use MVVM pattern and more binding.