Try/Catch in Global.asax while logging errors to db needed? - asp.net

So I handle all exceptions in my project within my Global.asax page. For example, on a random page I could have:
Protected Sub SomeFunction()
Try
'do something here
Catch ex As Exception
Throw ex
End Try
End Sub
So then the exception bubbles up to the Global.asax page where I handle it and log to the database like so:
Sub Application_Error(ByVal sender As Object, ByVal e As EventArgs)
Dim ex As Exception = Server.GetLastError
Dim loggedExceptionID As Integer = 0
Try
loggedExceptionID = New BLL.ExceptionHandler().LogException(ex)
Response.Redirect("~/ErrorPage.aspx?ID=" & loggedExceptionID, False)
Catch loggingError As Exception
'what do I do here?
End Try
End Sub
The BLL.ExceptionHandler().LogException(ex) function just writes the error details to the database.
So my question is in the Application_Error method, do I need the try/catch block when trying to write to the database? I'm thinking I would in case something goes wrong with the connection, etc. - but is this really necessary? Also, what would I do in the catch block if there is an error? At that point I would be catching the error of logging an error which is confusing in its own right.

It's probably a good idea to have a try/catch there, and what I would do is write the details to a text file or xml file so that you can refer back to both errors in the future and try to fix them. That of course means you would probably want another try/catch around writing to the disk, in which case I would just give up and call it a day. =)
On a completely separate note, you really should check out ELMAH: http://code.google.com/p/elmah/. This does probably everything you want and a lot more.

Related

Send Email without waiting for the action to finish execution after Button click ASP.NET

In short, remaining in the HTTP context, I would like the user, after clicking on an order completion button, not to wait for the mails to be sent before being sent back to a "thak you page".
I saw that HostingEnvironment.QueueBackgroundWorkItem could help me with this but there is always the risk of it being killed by IIS recycling.
What is the best solution to do this?
I know the best would be to develop a separate console solution but it wouldn't be worth it for 3/4 emails, alternatively I could consider speeding it up by making them asynchronous?
Protected Sub btnConcludiOrdine_Click(sender As Object, e As System.EventArgs) Handles btnConcludiOrdine.Click
If IsValidOrder(Me._cart, msg) Then
If Me._cart.SaveOrder(Me._user, Me._orderCode, Me._lang) then
'Update quantity in db
Dim mail As New EmailBLL
mail.SendOrderNotice(Me._cart, Me._lang) '2 Mails
mail.SendProductNotice() '2 Mails
End If
Else
Response.Redirect("*Error URL*")
End If
End Sub
The way you approach this is as suggested – start a task, or so called new processor thread.
So, what you would do is break out the code – this even works if code behind is for a web form.
So, the first step is to move out the “slow” parts, or the parts we want to run separate.
The main issue is that to start/launch/want/desire/achieve a brand new processor thread?
The sub call CAN ONLY PASS ONE “parameter” and the sub can only accept one parameter!!!!!
I note in your case that routine needs two values.
However, that “one parameter” can be a array of “many” values, or even a collection or whatever. In our case we pass the two values.
So just keep in mind that what you call can NOT update or “use” the values of controls on the form – the instance of that form will go out of scope.
But we can of course PASS the values you need. This will allow that routine to run 100% independent of the web form.
I also VERY strong suggest that if you DO place the sub in the same web page code behind? You should/can mark that sub as shared. Doing so will allow the compiler to get mad at you and spit out errors if that routine say tries to use or update a control value on the form.
However, it is MUCH better is to place this sub in a separate standard code module out side of the web forms code behind.
Regardless of above, we can now re-write the code we have as this:
If Me._cart.SaveOrder(Me._user, Me._orderCode, Me._lang) then
Dim myInfo(1) as object
myInfo(0) = me.cart
myInfo(1) = me_._lng
Call MyUpdateQ(myInfo)
End If
' bla bla lba
Shared Sub MyUPdateQ(p() as object)
'Update quantity in db
Dim mail As New EmailBLL
mail.SendOrderNotice(p(0),p(1)
mail.SendProductNotice() '2 Mails
End Sub
Ok, so far, we not achieved much, but we re-writing to accept the ONE array is KEY here.
So, now now make sure the above runs/works and is all happy.
Now, because we moved out the "work load" to that one routine, it is now a simple matter to start a thread.
Now, Our above code becomes this:
Protected Sub btnConcludiOrdine_Click(sender As Object, e As System.EventArgs) Handles btnConcludiOrdine.Click
If IsValidOrder(Me._cart, msg) Then
If Me._cart.SaveOrder(Me._user, Me._orderCode, Me._lang) then
Dim myInfo(1) as object
myInfo(0) = me.cart
myInfo(1) = me_._lng
Dim MyThread As New Thread(New ParameterizedThreadStart(AddressOf MyUpdateQ))
MyThread.Start(myInfo)
End If
Else
Response.Redirect("*Error URL*")
End If
End Sub
Shared Sub MyUPdateQ(p() as object)
'Update quantity in db
Dim mail As New EmailBLL
mail.SendOrderNotice(p(0),p(1)
mail.SendProductNotice() '2 Mails
End Sub
That is it. Now when you click your button it will wait ZERO time, since the long running routine is now going to run 100% as a separate thread. And this will also mean that when the user clicks the button - the page will respond instant and post back to user will be done. So if that thread takes 6 seconds, or even 25 seconds, the user will not notice this delay.
Just push your sending mail logic in Task and if you are not interested in result don't await it. c# syntax
Task.Run(() => SendEmail());

Dealing with a NullReferenceException after user is idle for over a certain amount of time

I have the sub routine below that is used by my game managing web app to refresh the game list.
It works fine except when the user has been idle for more than 20 minutes.
After the user is idle for over 20 minutes, and the user tries to refresh the webpage or navigate within it, this error is always thrown:
[NullReferenceException: Object reference not set to an instance of an object.]
line 15 in refreshGameList() // * line 15 is the "Throw ex" line in the "Catch" statement below *\
How can I prevent this? Either by navigating the user back to the login screen or just "waking up" the app so it doesn't timeout like this?
Thanks
Public Sub refreshGameList(ByVal activePlanetID As Guid)
Dim dbu As New gameUtils.DatabaseUtils
With (Web.HttpContext.Current.Application)
.Lock()
Try
Dim enviromentDataSet As DataSet = CType(Web.HttpContext.Current.Application("enviromentDataSet"), DataSet)
If Not enviromentDataSet Is Nothing And enviromentDataSet.Tables.Contains("gameList") Then
enviromentDataSet.Tables.Remove("gameList")
Dim gameListParams As New ArrayList
gameListParams.Add(New SqlParameter("#planetID", activePlanetID))
dbu.fillDataSet(enviromentDataSet, "gamer_GetGameList", gameListParams, "gameList")
.Item("enviromentDataSet") = enviromentDataSet
End If
Catch ex As Exception
Throw ex
Finally
.UnLock()
End Try
End With
End Sub
In your code, if the cache is empty, the second part of the If statement will still run. Change And with AndAlso
If Not enviromentDataSet Is Nothing AndAlso enviromentDataSet.Tables.Contains("gameList") Then
Caching in asp.net is a bit different than windows form. By default, a web server instance gets removed from memory if there's no activity for a specific amount of time. You need to have steps to rebuild the cache. You can to it in the global.asxa or when you fetch the data, something like.
Public ReadOnly Property EnviromentDataSet As DataSet
Get
If Web.HttpContext.Current.Application("enviromentDataSet") Is Nothing Then
' Load the information
End IF
Return CType(Web.HttpContext.Current.Application("enviromentDataSet"), DataSet)
End Get
End Property

What triggers an System.InvalidCastException in ASP.NET?

I have an issue on my ASP.NET 4.0 application. The error appears when I call one of my subroutines in the App_Code folder. I call the subroutine when a button is clicked on the page.
Protected Sub imgBtnEmailReg_Click(ByVal sender As Object, ByVal e As System.Web.UI.ImageClickEventArgs) Handles imgBtnEmailReg.Click
Dim functions As New Functions
Dim pidm As Integer = Convert.ToInt32(Session("BannerPidm").ToString)
functions.sendStudentAccomLetter(pidm)
End Sub
After the button click the id number is sent to the App_Code/Functions.vb where my subroutine is:
Public Sub sendStudentAccomLetter(ByVal stuPidm As Integer)
...
End Sub
The error I get when calling the above function is:
"System.InvalidCastException: Specified cast is not valid. at Functions.sendStudentAccomLetter(Int32 stuPidm) in D:\www-sec-docs\DisabilityServices\App_Code\Functions.vb:line 259"
Line 259 is the sub signature. I'm not even sure if there is a cast occurring here. Can someone please explain what might be causing this. Thank you.
Your Session("BannerPidm") value may be a different type - the method declaration is not the cause of the cast error. Try implementing some kind of check on the Session("BannerPidm") value such as:
If IsNumeric(Session("BannerPidm")) Then
'Proceed
This link shows a few examples where you would get and error when trying to convert: http://bytes.com/topic/visual-basic-net/answers/514682-difference-between-cint-convert-toint32
Try using CInt() instead and see if you still have problems. If you do, then you may want to start checking what the session variable is and see if there is a better, more standardized way you can store it.
It is most likely the conversion to Int32. Set a breakpoint at that line, and see what value Session("BannerPidm")
is returning. There is definitely a cast occurring, because you are retrieving from Session, casting it to string (.ToString()), then converting that to Int32.

ODBC Connection in Asp.Net (Connect and Dispose)

I Open and Close ODBC connection while the page is up and running. When and where should i put the Connection.Dispose method? I tried on the Page_Disposed but it never makes it there.
Public Class _WepPage1
Inherits System.Web.UI.Page
Dim MyConnection As New Odbc.OdbcConnection
Private Sub Page_Disposed(sender As Object, e As System.EventArgs) Handles Me.Disposed
MyConnection.Dispose()
End Sub
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
'String comes from Web.Config
MyConnection.ConnectionString = ConfigurationManager.ConnectionStrings("MyConnection").ConnectionString
If Not IsPostBack Then
Call LoadSomeData()
Else
'Do some more stuff
End If
End Sub
Private Sub LoadSomeData()
If MyConnection.State = ConnectionState.Closed Then MyConnection.Open()
Dim MyCommand As New Odbc.OdbcCommand("select * from tablename", MyConnection)
Try
Dim dataR As Odbc.OdbcDataReader = MyCommand.ExecuteReader
While dataR.Read
DropDownList1.Items.Add(dataR("column1"))
End While
Catch ex As Exception
End Try
MyCommand.Dispose()
If MyConnection.State = ConnectionState.Open Then MyConnection.Close()
End Sub
I would not use the Page_Load to start the connection. In .NET, the "real" connection object returns to the pool when you Dispose(), so it is there for other work. Even if you open, close and dispose numerous times, there is but a few microseconds of overhead max, so open a connection early and then keeping it open for the entire page duration does not really help you.
In addition, opening connection in Page_Load means you have tightly coupled your data access to your UI, leaving you a lot of rearchitecture and not just rearrangement to divide the work into proper tiers.
What this means is the connection should be created, used and disposed in one method (in your example). This means it is both logically separated and easy to separate into different classes and/or assemblies, should the application grow so you need a full DAL.
I would, at minimum, move all realting to connection to the LoadSomeData() method. If you want more proper separation of concerns, make a GetSomeData() routine and have the LoadSomeData() routine call the GetSomeData() routine and shape the data so it can be easily bound, rather than consume one row at a time (In your example, instead of binding a data object to the drop down, you are pushing one item at a time).
I hope this helps and I am sorry that many of the examples out there are really bad at best practices. And, yes, this includes many samples from people who should know better. ;-)

Error handling using events, checking if an error happened and react accordingly

I have an object, say Order where if an error occurs, it raises the ErrorOccurred event. If I'm running some code in say, the codebehind for a .aspx page with an public Order registered as WithEvents and I want to check to see if an error has occurred before I run more code, how do I do this? I can't simply check theOrder.ErrorOccurred. Do I have to create a local boolean flag that switches over in the event handler (OnErrorOccurred)? Or is there a better way to do this?
Thanks!
Example:
Public WithEvents theOrder As New Order
Public Sub DoStuff()
theOrder.DoSomething()
If theOrder.ErrorOccurred Then
do stuff
End If
End Sub
That seems like a reasonable approach. If there is lots of logic going on with an Order object that depends on knowing about errors, having a Status field would allow easy communication to any consumer what the status of the order was, rather than everyone having to subscribe to the event and track it themselves.
Alternately you could track it internally in the Order and just throw exceptions when critical methods were accessed if the Order was in an error state. This has the disadvantage of making you do more error handling, but would have the advantage of making sure that any Order consumer handled them explicitly.
Why not use structured error handling?
Try
'Code that may raise an error.
Catch
'Code to handle the error.
Finally
'Code to do any final clean up.
End Try
http://support.microsoft.com/kb/315965
This is what it is intended for.
Problems may arize if someone calls DoSomething but thay are unaware that they need to check theOrder.ErrorOccurred. based on what DoSomething is doing, allowing one to call a method and letting it fail quietly can be a problem.
If do something is doing logging sure, let it fail. If it is finalizing an order process..
Brian
Use the Try Catch Block
Try
'try your code here
Catch somevariablenamehere As Exception
'use methods from Exception class to get to know the error better and how to deal with it
Finally
'this is optional, If you want to do something finally, like cleaning up etc. You can do here
End Try
'to end the Try block

Resources