Check if code is executing inside Application_Start - asp.net

Can I check if my exception handler code (registered in Application_Error) is running inside of Application_Start? Tn an ASP.NET app in IIS 7 integrated mode there's RequestContext inside the Application_Start method (see this answer). Right now I'm using a Try ... Catch block to deal with this situation, but if there's a way to check before calling Context.Request it would be nice (Try ... Catch is expensive).
Public Sub Application_Error()
Dim pageName As String
Try
pageName = HttpContext.Current.Request.Path
Catch(ex As Exception)
pageName = "Inside Application_Start, no page name"
End Try
'exception logging co
End Sub

Related

Website - The type initializer for 'Static Class' threw an exception

I have a VB.NET website project. In one of the pages, during a button click, it is supposed to load a telerik RadGrid with data. It works fine on my local machine. However when I deploy it to pre-production on a server, it throws the following error.
The type initializer for 'Utility' threw an exception
Utility is a Static class and while calling any members of the static class (either public static functions or public static variables) I was receiving this error.
Here is the code snippet:
Partial Class Session
Inherits System.Web.UI.Page
Protected Sub RadGrid1_ItemDataBound(sender As Object, e As Telerik.Web.UI.GridItemEventArgs) Handles RadGrid1.ItemDataBound
If TypeOf e.Item Is GridDataItem Then
Dim testString As String = String.Empty
Dim encryptString As String = String.Empty
Try
encryptString = Utility.EncryptString(cellValue.ToString())
testString = Utility.test
Catch ex As Exception
Logger.getInstance().log("Value1" & cellValue.ToString() & "Value2" & testString)
Finally
End Try
End If
End Sub
End Class
In my Utility.vb, this is what I have:
Public Class Utility
Public Shared test As String = "hello"
Public Shared Function EncryptString(ByVal strEncrypted As String) As String
Try
Dim b As Byte() = System.Text.ASCIIEncoding.ASCII.GetBytes(strEncrypted)
Dim encryptedConnectionString As String = Convert.ToBase64String(b)
Return encryptedConnectionString
Catch ex As Exception
Logger.getInstance().log("Error occurred in Utility.vb while executing EncryptString method. \nSOURCE: " & ex.Source.ToString() & "\nMESSAGE: " & ex.Message.ToString() & "\nTARGETSITE: " & ex.TargetSite.ToString() & "\nSTACKTRACE: " & ex.StackTrace.ToString())
Logger.getInstance().log("\nstrEncrypted: " & strEncrypted)
Finally
End Try
End Function
End Class
From my logging statements, I realized that the error is happening right when I call the Utility class because none of the logging statements I added into the EncryptString() function are being shown in my logs.
I tried commenting out the code for EncryptString() function and added just a public static string variable called test in my Utility class, and tried accessing it from my code from Session.aspx.vb. That string value was not being returned as well.
By the way, all of my code is in the same website project. The error is driving me crazy for past 2 days. Like I mentioned, it works fine on my local machine, but fails only on the pre-prod server. The code was written using an older framework (3.5 I believe) and then we upgraded to 4.6.2 and migrating to new servers. We are facing this issue during the migration process on the new server.
OK, so I figured out the issue. At the beginning of the Utility.vb class there were some unsupported cryptographic algorithm variable declarations.
Private Shared DES As New TripleDESCryptoServiceProvider
Private Shared MD5 As New MD5CryptoServiceProvider
FIPS compliance is turned on, on the server and we already knew these algorithms were non-compliant with the latest .net frameworks. But I didn't realize even just these declarations would throw a misleading error, when they were not really being used in my function calls.
When I commented out those parts of the code, it started working fine.
Check the InnerException property of the exception. Any time a class initializer fails the original exception should be set as the InnerException property of the unhandled exception.
If you want a truly static class, you need to make it a module, in VB.Net.

Try/Catch Failing Incorrectly

I'm trying to use Try/Catch to handle a potential failed connection to a database. There's a Response.Redirect command in the Catch section. Whenever the page loads it redirects as per the Catch section whether the code in the Try section fails or not.
If I comment out the Response.Redirect command in the Catch section the page loads just fine. Similarly, if I replace the Response.Redirect command with code to populate a control on the page with the supposed error being trapped the Try section succeeds. It's something about having Response.Redirect in the Catch section...
Private Sub Page_Load(Sender As Object, e As eventargs) Handles Me.Load
Try
Dim sqlcon As New System.Data.SqlClient.SqlConnection(ConfigurationManager.ConnectionStrings("SarcoidsConnectionString").ConnectionString)
Dim cmd As New System.Data.SqlClient.SqlCommand("SELECT PortalEnabled FROM [tlkSettings]", sqlcon)
sqlcon.Open()
Dim dbReader As System.Data.SqlClient.SqlDataReader = cmd.ExecuteReader()
If dbReader.HasRows Then
While dbReader.Read()
If dbReader("PortalEnabled") = True Then
Response.Redirect("~/SubmitWizard.aspx")
Else
Response.Redirect("~/Maintenance.aspx")
End If
End While
End If
sqlcon.Close()
Catch ex As Exception 'Display Maintenance page if database cannot be connected to
Response.Redirect("~/Maintenance.aspx")
End Try
End Sub
Response.Redirect() without the second parameter of False will generate a ThreadAbortException because .End() is called on the Response object, so the following lines are the issue:
If dbReader("PortalEnabled") = True Then
Response.Redirect("~/SubmitWizard.aspx")
Else
Response.Redirect("~/Maintenance.aspx")
End If
Change it to this:
If dbReader("PortalEnabled") = True Then
Response.Redirect("~/SubmitWizard.aspx", False)
Else
Response.Redirect("~/Maintenance.aspx", False)
End If
The second parameter to Redirect() is the endResponse value, when setting it to False this tells the response to not call .End() and thus does not generate a ThreadAbortException.
Per MSDN documentation:
When you use this method in a page handler to terminate a request for one page and start a new request for another page, set endResponse to false and then call the CompleteRequest method. If you specify true for the endResponse parameter, this method calls the End method for the original request, which throws a ThreadAbortException exception when it completes. This exception has a detrimental effect on Web application performance, which is why passing false for the endResponse parameter is recommended. For more information, see the End method.
Read HttpResponse.Redirect Method documentation for more information.
I think the Problem is related to the other Response.Redirect Statements:
If dbReader("PortalEnabled") = True Then
Response.Redirect("~/SubmitWizard.aspx")
Else
Response.Redirect("~/Maintenance.aspx")
End If
Response.Redirect always throws a ThreadAbortException. This is caught in your exception handler as it catches any exception. Either you substitute the Reponse.Redirect calls or you add a handler the catches ThreadAbortExceptions:
Try
'...
Catch threadAbort As ThreadAbortException
Throw
Catch ex As Exception
'...
End Try
By the way: you should add some Using statements to close the connection and free other objects reliably.

Custom Exception with Entity Framework and Repository Pattern

I am following this Asp .Net tutorial as a guide:
http://www.asp.net/web-forms/tutorials/continuing-with-ef/using-the-entity-framework-and-the-objectdatasource-control-part-2-adding-a-business-logic-layer-and-unit-tests
I have created a custom error to prevent duplicate records on insert and update
Public Class DuplicateAgencyException
Inherits Exception
Public Sub New(ByVal message As String)
MyBase.New(message)
End Sub
End Class
I have created a validation method that checks for duplication on insert or update:
Public Sub ValidateAgencyName(ByVal agency As agency_temp)
If Not IsNothing(agency) Then
Dim duplicateAgency As agency_temp = AgencyRepository.GetAgencyByName(agency.agency_name).FirstOrDefault()
If Not IsNothing(duplicateAgency) AndAlso duplicateAgency.agency_id <> agency.agency_id Then
Throw New DuplicateAgencyException(String.Format("Agency: {0} already exists.", duplicateAgency.agency_name))
End If
End If
End Sub
I run a unit test to insert a record, and purposefully insert a duplicate, and it throws the correct error. Now I need to handle this error with the ObjectDataSource that displays this data, such as OnInserted and OnUpdated, but I can't even get to that point. When I insert a record, I get the error assistant telling me that DuplicateAgencyException was unhandled by user code. Do I need a try...Catch there? (I did try that to no avail).
I believe you need to wrap your exception within the methods referenced by the events you mention in a try catch block as you suggest. You should wrap the existing code like so
MethodName(params)
《
try 《 /// existing code
》
catch (ExceptionType ex) 《
/// error handling code
》
》
If you post your event handling code and the aspx i can be more specific. Please excuse any errots wrote this on my phone.

jQuery/ASP.NET concurrent Ajax calls

I have a webpage where users can look for clients and select them. After selection they can be send to the webserver through an jQuery Ajax call. On the server database operations and another webservice is called, so this can take a while. That is why I wanted to present a progress bar to the user.
This progressbar is also updated by a Ajax call.
The problem seems to be that asp.net doesn't allow concurrent calls and the session state queues all calls. You can solve this in mvc by setteing the attribute [SessionState(SessionStateBehavior.ReadOnly)]
But I don't find to do this in my page-behind webmethods. Anyway, the worker method is using session state (for security, and updating the session variable for the progressbar).
The progress method is only reading and returning the session variable.
Is there a solution for it, or is another approach necessary?
I am using asp.net 4.
You could set the session mode to readonly at the #Page directive in your markup:
<%# Page Title="Home Page" EnableSessionState="ReadOnly" Language="C#" %>
I have found the solution:
1) The WebMethod only needs to receive the data and start a new thread:
<WebMethod()> _
Public Shared Function importContacts(ByVal contactGuids As String, ByVal campaignGuids As String) As String
Dim paramsList As New List(Of Object)
paramsList.Add(contactGuids)
paramsList.Add(campaignGuids)
paramsList.Add(HttpContext.Current.Session)
Dim th As New Threading.Thread(AddressOf processImport)
th.Start(paramsList)
Return ""
End Function
The Ajax call is ended quickly and on the browser you can start polling for progress.
2) The thread function needs to convert the parameters first, then you can use the session state:
Public Shared Sub processImport(params As Object)
Dim paramsList As List(Of Object) = params
Dim contactGuids As String = paramsList(0)
Dim campaignGuids As String = paramsList(1)
Dim _session As HttpSessionState = paramsList(2)
_session("EmailMarketingDatabase_progress") = 0
...
End Sub
3) The progress WebMethod looks like this:
<WebMethod()> _
Public Shared Function getProgressStatus() As Integer
Return HttpContext.Current.Session("EmailMarketingDatabase_progress")
End Function

Log4Net, ThreadContext, and Global.asax

I am working on a Log4Net configuration that will log all unhandled exceptions. I need certain properties, based on user, to be added to each log entry. I have set this up successfully in the following manner in my Application_Error event. Here is my complete global.asax
Imports log4net
Imports log4net.Config
Public Class Global_asax
Inherits System.Web.HttpApplication
'Define a static logger variable
Private Shared log As ILog = LogManager.GetLogger(GetType(Global_asax))
Sub Application_Start(ByVal sender As Object, ByVal e As EventArgs)
' Fires when the application is started
ConfigureLogging()
End Sub
Sub Application_Error(ByVal sender As Object, ByVal e As EventArgs)
' Code that runs when an unhandled error occurs
Dim ex As Exception = Server.GetLastError()
ThreadContext.Properties("user") = User.Identity.Name
ThreadContext.Properties("appbrowser") = String.Concat(Request.Browser.Browser, " ", Request.Browser.Version)
If TypeOf ex Is HttpUnhandledException AndAlso ex.InnerException IsNot Nothing Then
ex = ex.InnerException
End If
log.Error(ex)
ThreadContext.Properties.Clear()
End Sub
Private Sub ConfigureLogging()
Dim logFile As String = Server.MapPath("~/Log4Net.config")
log4net.Config.XmlConfigurator.ConfigureAndWatch(New System.IO.FileInfo(logFile))
log4net.GlobalContext.Properties("appname") = System.Reflection.Assembly.GetExecutingAssembly.GetName.Name
End Sub
End Class
This appears to be working fine. However, I have some questions that I am unable to answer.
Is the way that I am adding the user specific properties, via the threadcontext, correct? Will this always log the correct information, even under load? When would you use threadlogicalcontext? Is there a better way to do this?
Thanks
It is not safe to load request-specific values into ThreadContext like that. The reason is that ASP.NET shared threads to service requests. It does this quite often, in fact.
You could instead use LogicalThreadContext, however that simply stores the values in Call Context, which is used for Remoting.
AFAIK there is no HttpContext specific context storage, so what you can do is instead assign a "value provider" instance as your thread context, and at runtime it will call .ToString() on this class to get the value.
public class HttpContextUserProvider
{
public override string ToString()
{
return HttpContext.Current.User.Identity.Name;
}
}
It's less than ideal, but it works.
Ben's answer is right on.
However, like some of the other users, I was still a bit lost on how to proceed. This log4net Context problems with ASP.Net thread agility post and especially this Marek Stój's Blog - log4net Contextual Properties and ASP.NET one give some more context for the problem with some excellent code examples.
I highly recommend Marek Stój's implementation, although the ThreadContext.Properties["UserName"] needed to be replaced with ThreadContext.Properties["User"] in my case.
I added a BeginRequest method to my Logger class which I call from Application_AuthenticateRequest which loads all the relevant log4net properties.
protected void Application_AuthenticateRequest(object sender, EventArgs e)
{
Logger.BeginRequest(Request);
}
And the method code:
public static void BeginRequest(System.Web.HttpRequest request)
{
if (request == null) return;
ThreadContext.Properties["ip_address"] = AdaptivePropertyProvider.Create("ip_address", IPNetworking.GetMachineNameAndIP4Address());
ThreadContext.Properties["rawUrl"] = AdaptivePropertyProvider.Create("rawUrl", request.RawUrl);
if (request.Browser != null && request.Browser.Capabilities != null)
ThreadContext.Properties["browser"] = AdaptivePropertyProvider.Create("browser", request.Browser.Capabilities[""].ToString());
if (request.IsAuthenticated && HttpContext.Current.User != null)
ThreadContext.Properties["User"] = AdaptivePropertyProvider.Create("user", HttpContext.Current.User.Identity.Name);
}
I found I had to pass in the Request object instead of using HttpContext.Current.Request within the method. Otherwise I would loose the user and authentication information. Note that the IPNetworking class is my own so you will need to provide your own method of obtaining the client IP. The AdaptivePropertyProvider class is directly from Marek Stój.

Resources