Calling multiple WCF service using IAsyncResult and AsyncCallback - iasyncresult

I have one web page MyWebPage.aspx which while loading has to show data from two webservices along with it's own algorithm.
1) WebServiceI.SomeMethod() -> Takes 10 seconds aprx. to respond.
2) WebServiceII.SomeMethod() -> Takes 10 seconds aprx. to respond.
3) My Algorithm -> Takes 5 second aprx to respond.
Now,when I call this synchronously,this will take 10+10+5 = 25 seconds to load.
So,I was suggested "Asynchronous Calling Method",i.e. using IAsyncResult/AsyncCallback.
Now what will(should) happen is that all will be called simultaneously and the page will load in max 10 seconds.
So I call them now in the "Begin/End" way...
public partial class MyWebPage : System.Web.UI.Page
{
WebServiceI WebServiceIObject = new WebServiceI();
WebServiceII WebServiceIIObject = new WebServiceII();
protected void Page_Load(object sender, EventArgs e)
{
//BeginSomeMethod(AsyncCallback callback, object asyncState)[<- Method Signature]
WebServiceIObject.BeginSomeMethod(OnEndGetWebServiceISomeMethodResult, null);
//BeginSomeMethod(AsyncCallback callback, object asyncState)[<- Method Signature]
WebServiceIIObject.BeginSomeMethod(OnEndGetWebServiceIISomeMethodResult, null);
/* My Algorithm 5 seconds*/
DataSet DS = GetDataSetFromSomeWhere();
MyGataGrid.DataSource = DS.tables[0];
MyGataGrid.DataBind();
/* My Algorithm 5 seconds*/
//System.Threading.Thread.Sleep(6000);
}
//Will be called after 10 seconds
void OnEndGetWebServiceISomeMethodResult(IAsyncResult asyncResult)
{
string WebServiceISomeMethodResult = WebServiceIObject.EndSomeMethod(asyncResult);
MyLabelI.Text = WebServiceISomeMethodResult;
//EventLog MyLog = new EventLog("Application"); MyLog.Source = "MySourceI";
//MyLog.WriteEntry(DateTime.Now.ToString());
}
//Will be called after 10 seconds
void OnEndGetWebServiceIISomeMethodResult(IAsyncResult asyncResult)
{
string WebServiceIISomeMethodResult = WebServiceIIObject.EndSomeMethod(asyncResult);
MyLabelII.Text = WebServiceIISomeMethodResult;
//EventLog MyLog = new EventLog("Application"); MyLog.Source = "MySourceII";
//MyLog.WriteEntry(DateTime.Now.ToString());
}
}
Now the issue with the above example is that MyLabelI & MyLabelII Text are never set because the page loads after 5 seconds
and thread is released.Both End Methods are called correctly as checked by writing to EventLog.
How can I resolve this...
something like "All start at once and then all wait till all are complete..."
I understand that if my executing thread waits for 5 seconds more then the code executes as required..
How should I use AsyncWaitHandle...

Well,The answer to this problem is "System.Web.UI.PageAsyncTask" class.It allows Asynchronous calls to tasks and waits for
completion on the same thread.Also multiple tasks can be created and made to run parallel.Please go through the documentation
for further information...Will work in Asp.Net 2.0 & Above Only.
For our problem above...I am putting "My Algorithm" as sync and both other tasks as Async parallel.So my page will take 10+5
= 15 seconds to load.
public partial class MyWebPage : System.Web.UI.Page
{
WebServiceI WebServiceIObject = new WebServiceI();
WebServiceII WebServiceIIObject = new WebServiceII();
protected void Page_Load(object sender, EventArgs e)
{
PageAsyncTask PAT_I = new PageAsyncTask
(BeginGetWebServiceISomeMethodResult, OnEndGetWebServiceISomeMethodResult, null, null, true);
Page.RegisterAsyncTask(PAT_I);
PageAsyncTask PAT_II = new PageAsyncTask
(BeginGetWebServiceIISomeMethodResult, OnEndGetWebServiceIISomeMethodResult, null, null, true);
Page.RegisterAsyncTask(PAT_II);
Page.ExecuteRegisteredAsyncTasks();
/* My Algorithm 5 seconds*/
DataSet DS = GetDataSetFromSomeWhere();
MyGataGrid.DataSource = DS.tables[0];
MyGataGrid.DataBind();
/* My Algorithm 5 seconds*/
}
IAsyncResult BeginGetWebServiceISomeMethodResult
(object Sender, EventArgs EventArgsObject,
AsyncCallback AsyncCallbackObject, object PassAnythingExtraIfRequired)
{
return WebServiceIObject.BeginSomeMethod(AsyncCallbackObject, PassAnythingExtraIfRequired);
}
IAsyncResult BeginGetWebServiceIISomeMethodResult
(object Sender, EventArgs EventArgsObject,
AsyncCallback AsyncCallbackObject, object PassAnythingExtraIfRequired)
{
return WebServiceIIObject.BeginSomeMethod(AsyncCallbackObject, PassAnythingExtraIfRequired);
}
void OnEndGetWebServiceISomeMethodResult(IAsyncResult asyncResult)
{
string WebServiceISomeMethodResult = WebServiceIObject.EndSomeMethod(asyncResult);
MyLabelI.Text = WebServiceISomeMethodResult;
}
void OnEndGetWebServiceIISomeMethodResult(IAsyncResult asyncResult)
{
string WebServiceIISomeMethodResult = WebServiceIIObject.EndSomeMethod(asyncResult);
MyLabelII.Text = WebServiceIISomeMethodResult;
}
}
Works :) The code can be made generic with common refactoring...
Please be careful while planning for this kind of design though...Internally this must also be using threads from threadpool
only...I have not dug in deep..but must be..So if the tasks happen to take moretime than planned and if several such tasks
get raised at the same time,then web server could suffer and users could get time out...
I am still carrying this forward inspite of above flaw as my users will time out anyway if 25 seconds turns out to be 55
seconds...Better to have a situation where some users are able to work rather than none..
If there is some better alternative,please post.

Related

How to run a simulation case using CaseRunner function?

I'm currently working on a Petrel plug-in in which I need to run a simulation case (through a "For Loop"), I create my case runner, export it and the run it...but after finishing the simulation and closing the console, I check the CaseRunner.IsRunning property and it shows true! This cause that the results have not been loaded to the petrel system.
I tried to load the results manually after finishing the Run of my case (using caserunner and also using a batch file in my code) and I can't see any results in the programming environment.
Does anybody have a solution for this situation?
This is the related part of my code:
Case theCase = arguments.TheCase;
Case Test2 = simroots.CreateCase(theCase, "FinalCase");
CaseRunner cRunners = SimulationSystem.GetCaseRunner(Test2);
cRunners.Export();
cRunners.Run();
bool b = cRunners.IsRunning;
actually I checked when the process finishes; after "cRunners.Run" the code waits for exit the process using:
System.Diagnostics.Process[] parray = System.Diagnostics.Process.GetProcesses();
foreach (System.Diagnostics.Process pr in parray)
{
if (pr.ProcessName == "cmd")
{
pr.WaitForExit();//just wait
}
}
and when the console closes itself, i checked the cRunners.IsRunning term.
However, I'm not so expert... can you show me an example of using CaseRunnerMonitor? both definition of the derived class and its implementation.
All I need is running a simulation case n times via a for loop and
after each Run access to its provided summary results.
I tried some different scenarios to get my desired results, I put here some of them
First I create my CaseRunnerMonitor class:
public class MyMonitor : CaseRunnerMonitor
{
//…
public override void RunCompleted()
{
// define arguments
foreach (Slb.Ocean.Petrel.DomainObject.Simulation.SummaryResult sr in simroot.SummaryResults)
{
IEnumerable ….
List ….
// some codes to change the input arguments according to the current step simulation summary results
}
PetrelLogger.InfoOutputWindow("MyMonitor is completed!");
}
//…
}
And then use it:
private void button1_Click(object sender, EventArgs e)
{
// Some codes that define some arguments…
for (int j = 0; j < 8; j++)
{
// some changes in the arguments
Case MyTest;
MyMonitor monit4 = new MyMonitor();
SimulationRoot simroot = SimulationRoot.Get(PetrelProject.PrimaryProject);
using (ITransaction trans = DataManager.NewTransaction())
{
trans.Lock(simroot);
MyTest = simroot.CreateCase(OriginalCase, MycaseNameFunc());
trans.Commit();
}
CaseRunner cRun = SimulationSystem.GetCaseRunner(MyTest);
cRun.Export();
cRun.Run(monit4);
//Wait(); //waits for current process to close
}
}
But the thing is that MyTest case results part are empty after my run is completed. in this case all the results loaded to the petrel when the 8th (last) simulation completes. If I don’t activate the Wait() function, all 8 runs are almost calling simultaneously…
I changed my scenario, my callback after each run is read the simulation results, change something and call next run so
I create my CaseRunnerMonitor class:
public class MyMonitor2 : CaseRunnerMonitor
{
//…
public override void RunCompleted()
{
// define arguments
index++;
if (index <=8)
{
foreach (Slb.Ocean.Petrel.DomainObject.Simulation.SummaryResult sr in simroot.SummaryResults)
{
IEnumerable ….
List ….
// some codes to change the input arguments according to the current step simulation summary results
}
Case MyTest;
MyMonitor monit4 = new MyMonitor();
SimulationRoot simroot = SimulationRoot.Get(PetrelProject.PrimaryProject);
using (ITransaction trans = DataManager.NewTransaction())
{
trans.Lock(simroot);
MyTest = simroot.CreateCase(OriginalCase, MycaseNameFunc());
trans.Commit();
}
CaseRunner cRun = SimulationSystem.GetCaseRunner(MyTest);
cRun.Export();
cRun.Run(monit4);
}
PetrelLogger.InfoOutputWindow("MyMonitor2 is completed!");
}
//…
}
And then use it:
private void button1_Click(object sender, EventArgs e)
{
Index=0;
// Some codes that define some arguments…
// some changes in the arguments
Case MyTest;
MyMonitor monit5 = new MyMonitor();
SimulationRoot simroot = SimulationRoot.Get(PetrelProject.PrimaryProject);
using (ITransaction trans = DataManager.NewTransaction())
{
trans.Lock(simroot);
MyTest = simroot.CreateCase(OriginalCase, MycaseNameFunc());
trans.Commit();
}
CaseRunner cRun = SimulationSystem.GetCaseRunner(MyTest);
cRun.Export();
cRun.Run(monit5);
}
in this situation no need to wait() function is required. But the problem is that I access to MyTest case results in one level before the current run completes. i.e, I can view the step 5 results via MyTest.Results when the run 6 is completed while step 6 results are empty despite of completion of its run.
I check the CaseRunner.IsRunning property and it shows true
This is because Caserunner.Run() is non-blocking; that is, it starts another thread to launch the run. Control flow then passes immediately to your cRunners.IsRunning check which is true as simulation is in progress.
cRunners.Run(); //non-blocking
bool b = cRunners.IsRunning;
You should look at CaseRunnerMonitor if you want a call-back when the simulation is complete.
Edit:
can you show me an example of using CaseRunnerMonitor? both definition of the derived class and its implementation.
Create your monitor class:
public class CustomCaseRunnerMonitor : CaseRunnerMonitor
{
//...
public override void RunCompleted()
{
//This is probably the callback you want
}
}
Use it:
Case myCase = WellKnownSimulators.ECLIPSE100.CreateSimulationCase(...);
CaseRunner runner = SimulationSystem.GetCaseRunner(myCase);
var myMonitor = new CustomCaseRunnerMonitor(...);
runner.Run(myMonitor);
//Your callbacks defined in your CustomCaseRunnerMonitor will now be called
See also "Running and monitoring a Simulation" in SimulationSystem API documentation.
Ah, OK. I didn't realise you were trying to load results with the CaseMonitor.
I'm afraid the short answer is "No, you can't know when Petrel has loaded results".
The long answer is Petrel will automatically load results if the option is set in the Case arguments. (Define Simulation Case -> Advance -> Automatically load results).
In API:
EclipseFormatSimulator.Arguments args = EclipseFormatSimulator.GetEclipseFormatSimulatorArguments(myCase);
EclipseFormatSimulator.Arguments.RuntimeArguments runtimeArgs = args.Runtime;
runtimeArgs.AutoLoadResults = true;
runtimeArgs.AutoLoadResultsInterval = 120; //How frequently in seconds Petrel polls sim dir.
You will have to poll SimulationRoot.SummaryResults (using the same API you are already using) after case has finished.
You should use the CaseRunnerMonitor we discussed to determine when to start doing this, rather than the System.Diagnostics.Process[] parray = System.Diagnostics.Process.GetProcesses(); code you currently have.

Silveright Prism: How to wait for two events

I am using Prism for my application and
I got 3 modules called A, B, C.
A fires an event x, B fires an event y.
C will be listened these two events x and y.
In case of event x => event handler called XEventHandler => Which will generate object A.
In case of event y => event handler called YEventHandler
In YEventHandler, Object A will be used. So I need some way to handle this so that:
If Object A is created => YEventHanlder will be executed if module C catch y Event
If Object A is not created yet (x event haven't been fired yet or the process of creating object A hasn't finished yet) => YEventHanlder need to wait until object A is ready and continue.
How can I solve this issue?
Thanks for such a cool community.
This seems to be a quite common task. Eventhandler Y has to wait for a WaitHandle, e.g. AutoResetEvent. This ResetEvent is set when object A is created.
private AutoResetEvent objectACreated = new AutoResetEvent(false);
private A a = null;
void HandlerY(object sender, EventArgs args)
{
if(objectACreated.WaitOne())
{
a.DoSomething();
}
}
void HandlerX(object sender, EventArgs args)
{
a = new A();
objectACreated.Set();
}

How can I wait for an async WCF service to complete?

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

How can I use nested Async (WCF) calls within foreach loops in Silverlight?

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.

Asynchronous callback - gwt

I am using gwt and postgres for my project. On the front end i have few widgets whose data i am trying to save on to tables at the back-end when i click on "save project" button(this also takes the name for the created project).
In the asynchronous callback part i am setting more than one table. But it is not sending the data properly. I am getting the following error:
org.postgresql.util.PSQLException: ERROR: insert or update on table "entitytype" violates foreign key constraint "entitytype_pname_fkey"
Detail: Key (pname)=(Project Name) is not present in table "project".
But when i do the select statement on project table i can see that the project name is present.
Here is how the callback part looks like:
oksave.addClickHandler(new ClickHandler(){
#Override
public void onClick(ClickEvent event) {
if(erasync == null)
erasync = GWT.create(EntityRelationService.class);
AsyncCallback<Void> callback = new AsyncCallback<Void>(){
#Override
public void onFailure(Throwable caught) {
}
#Override
public void onSuccess(Void result){ }
};
erasync.setProjects(projectname, callback);
for(int i = 0; i < boundaryPanel.getWidgetCount(); i++){
top = new Integer(boundaryPanel.getWidget(i).getAbsoluteTop()).toString();
left = new Integer(boundaryPanel.getWidget(i).getAbsoluteLeft()).toString();
if(widgetTitle.startsWith("ATTR")){
type = "regular";
erasync.setEntityAttribute(name1, name, type, top, left, projectname, callback);
} else{
erasync.setEntityType(name, top, left, projectname, callback);
}
}
}
Question:
Is it wrong to set more than one in the asynchronous callback where all the other tables are dependent on a particular table?
when i say setProjects in the above code isn't it first completed and then moved on to the next one?
Please any input will be greatly appreciated.
Thank you.
With that foreign key constraint, you must make sure the erasync.setProjects(...) has completed before you insert the rest of the stuff.
I suggest doing the erasync.setEntityAttribute(...) magic in (or from) an onsuccess callback instead of jumping right to it.
You're firing several request in which (guessing from the error message) really should be called in sequence.
Any time you call more than one rpc call; try to think that you should be able to rearrange them in any order (because that's allmost what actually happens because they're asynchronous)... If running them in reverse order does not make sense; you cannot fire them sequentially!
Two ways to fix your problem:
Nesting:
service.callFirst(someData, new AsyncCallback<Void> callback = new AsyncCallback<Void>(){
#Override
public void onFailure(Throwable caught) {/*Handle errors*/}
#Override
public void onSuccess(Void result){
service.callSecond(someOtherData, new AsyncCallback<Void> callback = new AsyncCallback<Void>(){
/* onSuccess and onFailure for second callback here */
});
}
});
Or creating one service call that does both (Recommended):
service.callFirstAndSecond(someData, someOtherData, new AsyncCallback<Void> callback = new AsyncCallback<Void>(){
#Override
public void onFailure(Throwable caught) {/*Handle errors*/}
#Override
public void onSuccess(Void result){
/* Handle success */
}
});
The second option is most likely going to be much less messy, as several nested asynch callbacks quickly grows quite wide and confusing, also you make just one request.
Because of nature of Async, don't assume setProjects(...) method will be called on the server before setEntityAttribute or setEntityType.
Personally, I prefer to have a Project class which contains all necessary info, for example:
public class Project{
private String projectName;
private List attributes = new ArrayList();
.. other properties
// Getter & Setter methods
}
Then send to the server in one round trip:
Project project = new Project();
project.setProjectName(..);
// Set other properties
erasync.saveProjects(project, callback);