BackgroundWorker – Waiting for it to finish or a timeout elapses - asp.net

I have a web page which is made up of a form for data entry and a panel for displaying the results. This page is written in VB.Net and the website hosting it is in ASP.Net.
Normal usage of the page is as follows:
The user inputs the form with some data/filters
The user presses the button "Search"
A BackgroundWorker starts finding the solutions
The BackgroundWorker instance is stored in a static variable, as I don't care about multi-user scenarios, but I'm not tied to this choice and I can change this. Also, the search process is asynchronous, but I really don't need to display anything while it's in progess.
The BackgroundWorker stores the result in a SolutionStorage object.
My goal is the following.
When the BackgroundWorker ends, the solutions found must be shown on the page. However, if after a fixed amount of time (currently, three minutes) it's still running, I want to terminate it and display the solutions present in the SolutionStorage in that moment.
The code goes as follows.
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
If Not IsNothing(worker) Then
worker.CancelAsync()
End If
worker = New BackgroundWorker()
worker.WorkerReportsProgress = False
worker.WorkerSupportsCancellation = True
AddHandler worker.DoWork, AddressOf workerDo
AddHandler worker.RunWorkerCompleted, AddressOf workerComplete
End Sub
Protected Sub search(ByVal sender As Object, ByVal e As EventArgs) Handles submitButton.Click
worker.RunWorkerAsync()
Thread.Sleep(180 * 1000)
If worker.IsBusy Then
worker.CancelAsync()
Dim solutions = repository.getSolutions()
'' Display solutions
If (solutions.Count > 0) Then
SolutionsRepeater.DataBind()
End If
End If
End Sub
Protected Sub workerDo()
' Collect data from the form
' Build the SolutionStorage
' Start the search
End Sub
Protected Sub workerComplete(sender As Object, e As RunWorkerCompletedEventArgs)
'' Display solutions
If (solutions.Count > 0) Then
SolutionsRepeater.DataBind()
End If
End sub
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
If Not IsNothing(worker) Then
worker.CancelAsync()
End If
worker = New BackgroundWorker()
worker.WorkerReportsProgress = False
worker.WorkerSupportsCancellation = True
AddHandler worker.DoWork, AddressOf workerDo
AddHandler worker.RunWorkerCompleted, AddressOf workerComplete
End Sub
Is this a correct way of doing things? Is this the better way of doing things?

Locking down an asp .net thread with a sleep call for 3 minutes is not a very good solution.
I would recommend you to build a background system that could do search in the background so your asp .net pages returns immediately. Then you could refresh your page every 10 seconds to
see if the search is still running, finish or failed.
Here is a little trick you might be able to use, but it has some limitations and warnings:
https://blog.stackoverflow.com/2008/07/easy-background-tasks-in-aspnet/
Read the comments to see the warnings and limitations.
But since you don't care about multi user support, and I assume the number of visits to your page is very limited, I can't see a direct problem with your current solution. But as soon as you expand it, it might cause you some problems.

Related

How can I place a pause in an ASP.NET Web Application during page rendering to wait for a sub containing a SQL query to complete execution?

I have an ASP.NET application that works fine on the deployed server, but when I run it from a remote development computer connected to the production database, it displays the error: "Object reference not set to an instance of an object," which I assume is due to the SQL query used to populate some combo boxes, or perhaps the main sub populating the grid, not returning in time for the entire page to finish rendering.
These are the subs with SQL queries: FillGridOnLoad(), FillAseguradoraDropdownList() and FillDoctorDropdownList()
I'm guessing that introducing a pause in the page rendering to wait for all those subs with SQL queries to complete might solve the problem. Does that make sense? In that case, where is the wait to be inserted?
Partial Class all orders
Inherits System.Web.UI.Page
Protected Sub gridOrdenes_PageIndexChanged(sender As Object, e As EventArgs) Handles gridOrdenes.PageIndexChanged
gridOrdenes.AllowPaging = True
End Sub
Protected Sub gridOrdenes_PageIndexChanging(sender As Object, e As GridViewPageEventArgs) Handles gridOrdenes.PageIndexChanging
gridOrdenes.PageIndex = e.NewPageIndex
FillGridOnLoad()
End Sub
Protected Sub Page_Load(sender As Object, e As EventArgs) Handles Me.Load
'...some code for authentication, etc... and then the following is the last part of the code:
If Not IsPostBack Then
FillGridOnLoad()
'Se llena el dropdown de laboratorios
Dim laboratorios As New laboratorios
dropLaboratorio.DataSource = laboratorios.fetch_all()
dropLaboratorio.DataTextField = "laboratorio"
dropLaboratorio.DataValueField = "idLaboratorio"
dropLaboratorio.DataBind()
dropLaboratorio.Items.Insert(0, "")
FillAseguradoraDropdownList()
FillDoctorDropdownList()
End If
End Sub

page refresh detection code not working

I found a nice article on how to detect page refresh in this article: Detecting browser 'Refresh' from Code behind in C#, I tried to follow the code and use it in a web form application written in vb.net.
My problem is that when I refresh the page using the browser refresh button, the boolean IsPageRefresh, always remains false. So the code that I am trying to prevent from executing on page refresh, keeps getting executed.
Here is an example if my vb.net code including the converted code from the article:
Dim IsPageRefresh As Boolean = False
Protected Sub Account_reset_Load(sender As Object, e As EventArgs) Handles Me.Load
If Not IsPostBack Then
ViewState("ViewStateId") = System.Guid.NewGuid().ToString()
Session("SessionId") = ViewState("ViewStateId").ToString()
Else
If ViewState("ViewStateId").ToString() <> Session("SessionId").ToString() Then
IsPageRefresh = True
End If
Session("SessionId") = System.Guid.NewGuid().ToString()
ViewState("ViewStateId") = Session("SessionId").ToString()
End If
End Sub
Protected Sub Account_reset_LoadComplete(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.LoadComplete
If (Not Page.IsPostBack) Then
If Not IsPageRefresh Then
If Not String.IsNullOrEmpty(Request("key")) Then
If Not (Customer.SignInByResetKey(Request("key"))) Then
Customer.Messages.Add("Invalid password reset key", MessageType.Error)
FormsAuthentication.SignOut()
Functions.Redirect(SiteLink.ResetPassword)
Else
' Do Nothing
End If
Else
Functions.Redirect(SiteLink.ResetPassword)
End If
Else
'Do Nothing
End If
End If
End Sub
I would like to ask for help to try to identify where I possibly am making the mistake, or if there is a better method.
I saw many articles that show how to catch page refresh in code behind, but they use the PreRender method, and I cannot implement that solution due to how to website was built.
I hope I can get some positive feedback.
Many thanks.

client side script for treeview events

I have a Treeview with "populateOnDemand" set to true. I want this treeview to keep its state (nodes expanded/collapsed) from one page to another. Viewstate doesn't work because the treeview is different for each page. Here is my code at the moment.
ASPX page :
<asp:TreeView
ID="arbre"
EnableViewState="true"
PopulateNodesFromClient="true"
runat="server" />
Code behind :
Protected Sub arbre_TreeNodeCollapsed(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.TreeNodeEventArgs) Handles arbre.TreeNodeCollapsed
CType(Session("listeNoeudsOuverts"), Hashtable)(e.Node.Value) = True
End Sub
Protected Sub arbre_TreeNodeExpanded(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.TreeNodeEventArgs) Handles arbre.TreeNodeExpanded
CType(Session("listeNoeudsOuverts"), Hashtable)(e.Node.Value) = False
End Sub
Protected Sub arbre_TreeNodePopulate(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.TreeNodeEventArgs) Handles arbre.TreeNodePopulate
// code to populate nodes
CType(Session("listeNoeudsOuverts"), Hashtable)(e.Node.Value) = True
End Sub
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
If Session("listeNoeudsOuverts") Is Nothing Then
Session("listeNoeudsOuverts") = New Hashtable()
End If
// other stuff
End Sub
This works well, buf I wish I could avoid the postback everytime the user expands or collapses a node. I know I can set EnebleClientScript to true and manage the events client side in Javascript but I won't be able to use my session variable anymore. Is there a way to achieve this ?
Thank you
I will answer myself to a certain extent.
I've done some tests and research, EnableClientScript is true by default, and indeed means that expand and collapse actions are processed client side, by Javascript code that is automaticalt generated by the .Net framework. You can't edit it.
Apparently, if you need to add custom actions when a user expands or collapses anode, you have to use TreeNodeExpanded and TreeNodeCollapsed events, like i did above, and can't avoid postbacks because they are triggered server-side.

problem using winforms WebBrowser in asp.net

i am using the WebBrowser control in asp.net page. here is the simple code:
Public Class _Default
Inherits System.Web.UI.Page
Private WithEvents browser As WebBrowser
Dim th As New Threading.Thread(AddressOf ThreadStart)
Sub ThreadStart()
browser = New WebBrowser
AddHandler browser.DocumentCompleted, AddressOf browser_DocumentCompleted
browser.Navigate("http://www.someurl.com/")
End Sub
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
th.SetApartmentState(Threading.ApartmentState.STA)
th.Start()
th.Join()
End Sub
Private Sub browser_DocumentCompleted(ByVal sender As Object, ByVal e As System.Windows.Forms.WebBrowserDocumentCompletedEventArgs)
If browser.Document IsNot Nothing Then
Dim textbox As HtmlElement = browser.Document.GetElementById("txt1")
textbox.InnerText = "some text"
Dim button As HtmlElement = browser.Document.GetElementById("btn1")
button.InvokeMember("click")
End If
End Sub
End Class
the problem is that the webbrowser's DocumentCompleted event is not being handled. It looks like the page request finishes before anything else could happen.
what's the solution to this problem?
I really recommend reading this article(He won a price for it..)
Using the WebBrowser Control in ASP.NET
http://www.codeproject.com/KB/aspnet/WebBrowser.aspx
His solution is to create 3 threads for it to work..
I'm not sure but I have some concerns about the way you wrote your code.
You are creating and initializing your thread as soon as your class instance is created. This is before the form has been loaded.
I can't say for sure this couldn't work but I would definitely recommend creating the thread in your Load event handler, just before you use it.
I wrote some similar code in C# to generate a website thumbnail. Although that code does not use the DocumentCompleted event, I played with that event when I wrote it and it seemed to work okay. You can compare my code to yours.
Also, I should mention I have one hosting account where the code doesn't work. It seems to simply die when I call Thread.Join. However, it doesn't appear that's the issue you're running into.

Possible - cancel user control rendering if not loaded in x seconds?

This is probably a lark, but for the recaptcha control as it sometimes takes a long time to render, is this possible?
If it takes more than say 5 seconds to render, I'd like to stop the rendering of the object and display my own captcha.
I'd start a timer on page load and if 5 seconds has elapsed, in some event in the recaptcha control (prerender?), I'd cancel the render or make it invisible or something to that effect. It is a 3rd party user control, so I don't have the source.
Update:
I tried the code below after I posted. It sort of works in that if the user control can't connect its server, ie - I turn disconnect my internet connection, but it doesn't sense when there is a really long pause when the control has waiting for the server to get back to it. Even if I change the millisecond interval to 1, the control renders.
<MTAThread()> _
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
If Not IsPostBack Then
Dim ucChk As New UCExistenceChecker(recaptcha, Me)
Dim doFindUC As System.Threading.TimerCallback = AddressOf ucChk.FindUC
Dim stateTimer As System.Threading.Timer = New System.Threading.Timer(doFindUC, Nothing, 0, 5000)
End If
End Sub
Public Class UCExistenceChecker
Dim _r As Recaptcha.RecaptchaControl
Dim _pg As Page
Sub New(ByVal r As Recaptcha.RecaptchaControl, ByVal pg As Page)
_r = r
_pg = pg
End Sub
Sub FindUC(ByVal stateInfo As Object)
If _pg.FindControl("recaptcha") Is Nothing Then
_r.SkipRecaptcha = True 'This "unrenders" the control, sort of.
End If
End Sub
End Class
You may be able to use an iframe to contain the captcha block and subscribe to the either the readystatechanged or layoutcomplete events. You could then use setTimeout() to schedule some JavaScript to run after the maximum time you care to wait, and if neither of these events has fired, remove the iframe from the DOM and replace it with your own.

Resources