Check to see if webbrowser has loaded new page without document complete - c#

I'm writing writing a program to perform some basic tests and I'm trying to get it to automatically login. It seems that the documentComplete event would be overused if I had to use it for every possible scenario.
I saw a number of examples with Browser ReadyState but I'm unable to get them to work.
I have a simple method which finds the username element, enters the user name, invokes the click method, finds the password element, enters the password, and invokes the click method.
If I delay the process with a messagebox then it works because it gives the page time to load.
private void fillInLogin()
{
browser.Document.GetElementById("des_LoginName").InnerText = "myLogon";
browser.Document.GetElementById("btnLogOn").InvokeMember("click");
//*********WHAT CAN I PUT HERE TO CHECK TO SEE IF THOSE ELEMENTS EXIST***********
browser.Document.GetElementById("des_Password").InnerText = "myPassword";
browser.Document.GetElementById("btnSubmit").InvokeMember("click");
}

Have you looked into subscribing to the WebBrowser.DocumentCompleted event? It takes all of the guesswork out of waiting for a page to load, and is actually the proper way to do it. You can't really rely on a server response to be completed within a given wait period. Usually the only guarantee is the timeout period of your client code, but you don't want to force a wait to the timeout maximum. Using a threaded request and an event listening for the callback will allow you to make the request and continue to provide a responsive UI to the user.

Related

Making async call to Controller function (void). Not getting control back to UI

I am working on a MVC 5 based report generating web application (Excel files). On one "GenerateReports" page, on button click, I am calling StartMonthly function. This takes control to a void method "GenerateReportMainMonthly" in the controller. This method calls another void method "GenerateReportMain". In GenerateReportMain, there are 5 other void functions that are being called.
I do not want the control to get stuck at back-end until the report generation is completed. On button click, an alert box should show "Report Generation started." and the control should come back to the "GenerateReports" page.
I have tried ajax but have not been able to get the control back to UI. How can I get the control back to the same page without waiting for the back-end process to complete?
$('#btnStart').on('click', StartMonthly);
function StartMonthly() {
var url = '/GenerateReport/GenerateReportMainMonthly';
window.location.href = url;
}
public void GenerateReportMainMonthly()
{
_isDaily = false;
GenerateReportMain();
}
It seems you are after running background tasks in your controllers. This is generally a bad idea (see this SO answer) as you might find that your server process has been killed mid-way and your client will have to handle it somehow.
If you absolutely must run long-ish processes in your controller and cannot extract it into a background worker of some sort, you can opt for something like this SO answer suggests. Implementation will vary depending on your setup and how fancy you are willing/able to go, but the basics will ramain the same:
you make an instantaneous call to initiate your long action and return back a job id to refer back to
your backend will process the task and update the status accordingly
your client will periodically check for status and do your desired behaviour when the job is reported complete.
If I were to tackle this problem I'd try to avoid periodic polling and rather opt for SignalR updates as describled in this blog post (this is not mine, I just googled it up for example).

ohLibspotify not calling any callback after login

I'm trying to create a project where I use the ohLibspotify .Net libspotify wrapper to login to spotify and stream playlists.
As far as I can see I've set everything up the same way like in the example. First create a session like so:
SpotifySessionConfig sp_config = new SpotifySessionConfig()
{
ApiVersion = 12,
CacheLocation = "cache",
SettingsLocation = "settings",
UserAgent = "player",
ApplicationKey = Properties.Resources.appkey,
Listener = new sp_Listener()
};
sp_session = SpotifySession.Create(sp_config);
Then I call relogin() if that fails than I show the login window to the user because no stored credentials have been found. When the user has supplied me with his account details I call login(username, password, true, null). After that I'm awaiting a call back to the sp_Listener class.
In the sp_Listener class I have overridden the following functions:
SpotifySessionListener.LoggedIn(SpotifySession session, SpotifyError error)
SpotifySessionListener.ConnectionError(SpotifySession session, SpotifyError error)
SpotifySessionListener.LogMessage(SpotifySession session, string data)
The only callback that gets called is the LogMessage callback. I've hooked it up to log4net to read all the output efficiently. This is all of the LogMessageoutput:
2015-02-22 20:58:38,636 [18] DEBUG Namespace.sp_Listener - 19:58:38.634 I [c:/Users/spotify-buildagent/BuildAgent/work/1e0ce8a77adfb2dc/client/core/session/offline_authorizer.cpp:297] Unable to login offline: no such user
2015-02-22 20:58:38,649 [18] DEBUG Namespace.sp_Listener - 19:58:38.649 I [c:/Users/spotify-buildagent/BuildAgent/work/1e0ce8a77adfb2dc/client/core/session/offline_authorizer.cpp:297] Unable to login offline: no such user
2015-02-22 20:58:38,651 [14] DEBUG Namespace.sp_Listener - 19:58:38.649 E [c:/Users/spotify-buildagent/BuildAgent/work/1e0ce8a77adfb2dc/client/core/network/proxy_resolver_win32.cpp:215] WinHttpGetProxyForUrl failed
2015-02-22 20:58:38,664 [19] DEBUG Namespace.sp_Listener - 19:58:38.661 I [ap:1752] Connecting to AP ap.gslb.spotify.com:4070
2015-02-22 20:58:38,713 [19] DEBUG Namespace.sp_Listener - 19:58:38.713 I [ap:1226] Connected to AP: 193.182.7.34:4070
It seems like I must have forgotten something. I've no idea what, maybe one of you guys knows a solution.
I'm the original author of the ohLibSpotify wrapper library. I think you possibly have overlooked the need to call ProcessEvents. ohLibSpotify tries as far as possible to provide only a thin layer over libspotify. Almost everything in the libspotify docs remains relevant when you are using ohLibSpotify, and you should consider those docs your first port-of-call. https://developer.spotify.com/docs/libspotify/12.1.51/index.html
In particular:
The library itself uses multiple threads internally. To allow for synchronization between these threads, you must implement the sp_session_callbacks::notify_main_thread callback. Whenever called (from some internal thread), the application must wake up the main loop so the sp_session_process_events() function can be run.
The API itself is not thread-safe. Thus, you must take care not to call the API functions from more than one of your own threads.
The names are slightly different, but the concepts are the same - you need to implement NotifyMainThread to get notifications that libspotify wants to communicate with you, then you need to make sure that your main thread calls sp_session.ProcessEvents. You also need to make sure that only one thread ever interacts with ohLibSpotify at a time, either by coordinating so that only one thread calls ohLibSpotify, or by using appropriate locks around calls into ohLibSpotify.
(I'm using libspotify names here: the following advice applies equally whether you're using libspotify directory or ohLibSpotify.)
With a few exceptions, libspotify only ever calls your callbacks from inside a call to sp_session_process_events. (The exceptions are notify_main_thread and the callbacks associated with music delivery.) So if you're not set up to call that regularly, you'll find that libspotify doesn't do very much. If your program has an event loop, you should arrange to send yourself events whenever you receive the notify_main_thread callback or when the time specified by your last call to sp_session_process_events has passed, and call sp_session_process_events in the event handler. If you have no event loop, you might want to spawn a thread for this purpose, and make sure to use appropriate locks to stop other threads from calling into libspotify at the same time.

How Implementing a windows service on a server that is hosted with ISP

I am working on an assignment in asp.net to send notification email to users at specific intervals.
But the problem is that since the server is not privately owned i cannot implement a windows service on it.
Any ideas?
There's no reliable way to achieve that. If you cannot install a Windows Service on the host you could write a endpoint (.aspx or .ashx) that will send the email and then purchase on some other site a service which will ping this endpoint at regular intervals by sending it HTTP request. Obviously you should configure this endpoint to be accessible only from the IP address of the provider you purchase the service from, otherwise anyone could send an HTTP request to the endpoint and trigger the process which is probably undesirable.
Further reading: The Dangers of Implementing Recurring Background Tasks In ASP.NET.
There are several ways to get code executing on an interval that don't require a windows service.
One option is to use the Cache class - use one of the Insert overloads that takes a CacheItemRemovedCallback - this will be called when the cache item is removed. You can re-add the cache item with this callback again and again...
Though, the first thing you need to do is contact the hosting company and find out if they already have some sort of solution for you.
You could set up a scheduled task on the server to invoke a program with the desired action.
You can always use a System.Timer and create a call at specific intervals. What you need to be careful is that this must be run one time, eg on application start, but if you have more than one pools, then it may run more times, and you also need to access some database to read the data of your actions.
using System.Timers;
var oTimer = new Timer();
oTimer.Interval = 30000; // 30 second
oTimer.Elapsed += new ElapsedEventHandler(MyThreadFun);
oTimer.Start();
private static void MyThreadFun(object sender, ElapsedEventArgs e)
{
// inside here you read your query from the database
// get the next email that must be send,
// you send them, and mark them as send, log the errors and done.
}
why I select system timer:
http://msdn.microsoft.com/en-us/magazine/cc164015.aspx
more words
I use this in a more complex class and its work fine. What are the points that I have also made.
Signaling the application stop, to wait for the timer to end.
Use mutex and database for synchronize the works.
Easiest solution is to exploit global.asax application events
On application startup event, create a thread (or task) into a static singleton variable in the global class.
The thread/task/workitem will have an endless loop while(true) {...} with your "service like" code inside.
You'll also want to put a Thread.Sleep(60000) in the loop so it doesn't eat unnecessary CPU cycles.
static void FakeService(object obj) {
while(true) {
try {
// - get a list of users to send emails to
// - check the current time and compare it to the interval to send a new email
// - send emails
// - update the last_email_sent time for the users
} catch (Exception ex) {
// - log any exceptions
// - choose to keep the loop (fake service) running or end it (return)
}
Thread.Sleep(60000); //run the code in this loop every ~60 seconds
}
}
EDIT Because your task is more or less a simple timer job any of the ACID type concerns from an app pool reset or other error don't really apply, because it can just start up again and keep trucking along with any data corruption. But you could also use the thread to simply execute a request to an aspx or ashx that would hold your logic.
new WebClient().DownloadString("http://localhost/EmailJob.aspx");

What's the correct way of posting this data asynchronously, and canceling/queuing new requests?

I am attempting to improve the stability of the web dashboard I have created. I have noticed that the main cause of getting into a bad state is when the user moves too quickly and requests get cut short.
For instance, I have the following javascript:
//When a resize occurs a lot of handling needs to happen to convert the user's action into
//a visibly-pleasing result. In addition, the server has to be spoken with in order to save the controls.
function OnClientResizing(pane, eventArgs) {
eventArgs.set_cancel(true);
var parameters = new Array();
parameters.push("Resize");
parameters.push(pane.get_id());
parameters.push(eventArgs.get_delta());
__doPostBack(pane.get_splitter()._uniqueID, parameters);
}
This function passes the hard work back to the server, so that it can calculate the appropriate ways to resize the controls on the page during resizes. This takes X seconds. If the user then resizes the page again before X seconds has elapsed -- I enter into a bad state. Either the old request gets cut off prematurely, or the new one runs at the same time. Either way, controls become mishapen on the page.
As such, I would like to queue future resizes, or play around with canceling current requests. I read that the best way to do something like this is to simply set a flag outside the scope of this function. I can do that, but I am not sure how to detect the end of a doPostBack. Am I supposed to change the javascript variable from the server-side somehow in PageRequestManager - EndRequest?
Cheers
First off, don't let your server participate in UI resize algorithms. Do that entirely client side. You can send resulting data to the server at any time, but don't make a real-time UI positioning depend upon a server response. That should be handled client-side with CSS or javascript logic.
Second off, if your code can't handle two ajax calls in flight at the same time, then your options are as follows:
Fix your code so it can handle sequential ajax responses in flight at the same time.
Cancel/ignore the first ajax response the moment you send a second one so that you ignore the response from the first and wait for the response from the second.
Prevent a second ajax request until the first one completes. I wouldn't suggest queueing them because that's just going to lead to an even worse user experience.
The details of how to do 1, 2 or 3 depend upon how your code works which you have not yet shared.
The easiest is option 3). That can be done with just a global flag. Just define a global variable, set it to true when you start an ajax call and clear it when the ajax call completes (in a completion function):
var ajaxInFlight = false; // global declaration
function OnClientResizing(pane, eventArgs) {
if (ajaxInFlight) return; // do nothing if ajax call already in flight
ajaxInFlight = true;
eventArgs.set_cancel(true);
var parameters = new Array();
parameters.push("Resize");
parameters.push(pane.get_id());
parameters.push(eventArgs.get_delta());
__doPostBack(pane.get_splitter()._uniqueID, parameters);
}
function postBackCompletionHandler(id, parms) {
ajaxInFlight = false; // clear global flag, ajax call done
// ... rest of your function here
}
You will also have to make sure that error conditions are handled so that the global flag is reset if the ajax call fails for any reason.

Calling a webservice async

Long post.. sorry
I've been reading up on this and tried back and forth with different solutions for a couple of days now but I can't find the most obvious choice for my predicament.
About my situation; I am presenting to the user a page that will contain a couple of different repeaters showing some info based on the result from a couple of webservice calls. I'd like to have the data brought in with an updatepanel (that would be querying the result table once per every two or three seconds until it found results) so I'd actually like to render the page and then when the data is "ready" it gets shown.
The page asks a controller for the info to render and the controller checks in a result table to see if there's anything to be found. If the specific data is not found it calls a method GetData() in WebServiceName.cs. GetData does not return anything but is supposed to start an async operation that gets the data from the webservice. The controller returns null and UpdatePanel waits for the next query.
When that operation is complete it'll store the data in it's relevant place in the db where the controller will find it the next time the page asks for it.
The solution I have in place now is to fire up another thread. I will host the page on a shared webserver and I don't know if this will cause any problems..
So the current code which resides on page.aspx:
Thread t = new Thread(new ThreadStart(CreateService));
t.Start();
}
void CreateService()
{
ServiceName serviceName = new ServiceName(user, "12345", "MOVING", "Apartment", "5100", "0", "72", "Bill", "rate_total", "1", "103", "serviceHost", "password");
}
At first I thought the solution was to use Begin[Method] and End[Method] but these don't seem to have been generated. I thought this seemed like a good solution so I was a little frustrated when they didn't show up.. is there a chance I might have missed a checkbox or something when adding the web references?
I do not want to use the [Method]Async since this stops the page from rendering until [Method]AsyncCompleted gets called from what I've understood.
The call I'm going to do is not CPU-intensive, I'm just waiting on a webService sitting on a slow server, so what I understood from this article: http://msdn.microsoft.com/en-us/magazine/cc164128.aspx making the threadpool bigger is not a choice as this will actually impair the performance instead (since I can't throw in a mountain of hardware).
What do you think is the best solution for my current situation? I don't really like the current one (only by gut feeling but anyway)
Thanks for reading this awfully long post..
Interesting. Until your question, I wasn't aware that VS changed from using Begin/End to Async/Completed when adding web references. I assumed that they would also include Begin/End, but apparently they did not.
You state "GetData does not return anything but is supposed to start an async operation that gets the data from the webservice," so I'm assuming that GetData actually blocks until the "async operation" completes. Otherwise, you could just call it synchronously.
Anyway, there are easy ways to get this working (asynchronous delegates, etc), but they consume a thread for each async operation, which doesn't scale.
You are correct that Async/Completed will block an asynchronous page. (side note: I believe that they will not block a synchronous page - but I've never tried that - so if you're using a non-async page, then you could try that). The method by which they "block" the asynchronous page is wrapped up in SynchronizationContext; in particular, each asynchronous page has a pending operation count which is incremented by Async and decremented after Completed.
You should be able to fake out this count (note: I haven't tried this either ;) ). Just substitute the default SynchronizationContext, which ignores the count:
var oldSyncContext = SynchronizationContext.Current;
try
{
SynchronizationContext.SetSynchronizationContext(new SynchronizationContext());
var serviceName = new ServiceName(..);
// Note: MyMethodCompleted will be invoked in a ThreadPool thread
// but WITHOUT an associated ASP.NET page, so some global state
// might be missing. Be careful with what code goes in there...
serviceName.MethodCompleted += MyMethodCompleted;
serviceName.MethodAsync(..);
}
finally
{
SynchronizationContext.SetSynchronizationContext(oldSyncContext);
}
I wrote a class that handles the temporary replacement of SynchronizationContext.Current as part of the Nito.Async library. Using that class simplifies the code to:
using (new ScopedSynchronizationContext(new SynchronizationContext()))
{
var serviceName = new ServiceName(..);
// Note: MyMethodCompleted will be invoked in a ThreadPool thread
// but WITHOUT an associated ASP.NET page, so some global state
// might be missing. Be careful with what code goes in there...
serviceName.MethodCompleted += MyMethodCompleted;
serviceName.MethodAsync(..);
}
This solution does not consume a thread that just waits for the operation to complete. It just registers a callback and keeps the connection open until the response arrives.
You can do this:
var action = new Action(CreateService);
action.BeginInvoke(action.EndInvoke, action);
or use ThreadPool.QueueUserWorkItem.
If using a Thread, make sure to set IsBackground=true.
There's a great post about fire and forget threads at http://consultingblogs.emc.com/jonathangeorge/archive/2009/09/10/make-methods-fire-and-forget-with-postsharp.aspx
try using below settings
[WebMethod]
[SoapDocumentMethod(OneWay = true)]
void MyAsyncMethod(parameters)
{
}
in your web service
but be careful if you use impersonation, we had problems on our side.
I'd encourage a different approach - one that doesn't use update panels. Update panels require an entire page to be loaded, and transferred over the wire - you only want the contents for a single control.
Consider doing a slightly more customized & optimized approach, using the MVC platform. Your data flow could look like:
Have the original request to your web page spawn a thread that goes out and warms your data.
Have a "skeleton" page returned to your client
In said page, have a javascript thread that calls your server asking for the data.
Using MVC, have a controller action that returns a partial view, which is limited to just the control you're interested in.
This will reduce your server load (can have a backoff algorithm), reduce the amount of info sent over the wire, and still give a great experience to the client.

Categories