Why is my asp.net caching throwing an exception? - asp.net

I have a bunch of simple lookup tables cached in my asp.net application since the source data is on a seperate server from our main web architecture and it changes infrequently. I've been following answers here and various documentation and I have my initial load function call the following:
HttpContext.Current.Cache.Insert("CheckLocations", GetAllCheckLocations(), _
Nothing, DateAdd(DateInterval.Day, 1, Now()), _
System.Web.Caching.Cache.NoSlidingExpiration, _
CacheItemPriority.Normal, _
New CacheItemRemovedCallback(AddressOf CheckLocationsExpired))
For my cache expired callback, I have the following code.
Public Shared Sub CheckLocationsExpired(ByVal key As String, ByVal value As Object, ByVal reason As CacheItemRemovedReason)
Dim dtCheckLocation As New ReferenceSchema.CheckLocationDataTable
dtCheckLocation = GetAllCheckLocations()
HttpContext.Current.Cache.Insert("CheckLocations", dtCheckLocation, Nothing, _
DateAdd(DateInterval.Day, 1, Now()), _
System.Web.Caching.Cache.NoSlidingExpiration, _
CacheItemPriority.Normal, _
New CacheItemRemovedCallback(AddressOf CheckLocationsExpired))
End Sub
For the record, the GetAllCheckLocations method simply calls a web service and parses the results into the data table being stored.
Now when I recompile the application for local testing, everything still functions fine, but I find the following exception message in my log file:
System.NullReferenceException: Object reference not set to an instance of an
object. at EAF.CacheMethods.CheckLocationsExpired(String key, Object value,
CacheItemRemovedReason reason) in
C:\Projects\HR\EAF 2.0\DAL\CacheMethods.vb:line 434 at
System.Web.Caching.CacheEntry.CallCacheItemRemovedCallback(CacheItemRemovedCallback
callback, CacheItemRemovedReason reason)
I verify that the data is indeed there and up to date, and nothing in the command arguments seems out of place when I step through the debugger.
Does anybody know what I'm missing here? Is this another one of those "nuances" like the Reponse.Redirect issue where terminating the processing technically throws a thread abort exception?

You may want to use HttpRuntime.Cache instead. It's possible that HttpContext.Current is null if you are calling it from a unit test or such.

Does it still exception out when you don't give it a callback function? Seems more like the delegated function is having issues with null objects.

My initial thought is that GetAllCheckLocations is throwing the exception or returning null.

Maybe you call the Method with AJAXPro or something.

Related

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.

asp.net global synclock object

is there a way in asp.net to make sure that a certain threaded sub is not run twice concurrently, no matter what?
the code i have now is
Public Class CheckClass
ReadOnly Property CheckSessionsLock As Object
Get
If HttpRuntime.Cache("CheckSessionsLock") Is Nothing Then HttpRuntime.Cache("CheckSessionsLock") = New Object
Return HttpRuntime.Cache("CheckSessionsLock")
End Get
End Property
Sub TryThreads()
Dim thread = New Thread(AddressOf TryLock)
thread.Priority = ThreadPriority.Lowest
thread.Start()
End Sub
Sub TryLock()
SyncLock CheckSessionsLock
DoTrace("entered locker")
For x = 0 To 10000
Next
DoTrace("exiting locker")
End SyncLock
DoTrace("exited locker")
End Sub
End Class
if i run this code on every page then several times the code overlaps. the DoTrace function in the code simply writes the message to a table.
the messages in the table should appear in order (entered,exiting,exited) again and again, but in reality, they don't. i get like entered, exiting,entered,exited,exiting...
this means that the synclock is not complete. is that true?
if so, how can we implement a complete synclock on a block of code, across requests and across sessions?
EDIT: i need this lock, as the real code will be sending emails, according to a list of mailing types in a db. after each mailing type is sent, its marked, then it continues with the next mailing. i cant have in middle of processing, another thread should see this mailing as unprocessed.
please advise
Rather than using the HttpRuntime Cache have you considered using a static variable?
Just as a note (it might be helpful to explain why you want this functionality) your website is not going to be very scalable if this can only be run once at a time.
In C# (sorry, don't know VB syntax) I use this:
private static readonly object Padlock = new object();
It's a field, not a property,
It's static (in VB, that's "shared" if I'm not mistaken) so it's the same throughout the entire application
It's initialised once as soon as you use this class, not when you explicitly use the field.
With your property/cache version, you could have two threads trying to get the lock-object and each creating a different one:
Thread 1 checks the cache and doesn't find the object
Thread 1 is parked
Thread 2 checks the cache, doesn't find the object
Thread 2 creates the object and caches it, retrieves it again and returns from the property
Thread 1 resumes
Thread 1 creates a new object and caches it, retrieves it again and returns a different lock object than thread 2 uses
Any further threads will use the lock object of thread 1

Is this code thread safe?

''' <summary>
''' Returns true if a submission by the same IP address has not been submitted in the past n minutes.
'' </summary>
Protected Function EnforceMinTimeBetweenSubmissions(ByVal minTimeBetweenRequestsMinutes as Integer) As Boolean
If minTimeBetweenRequestsMinutes = 0 Then
Return True
End If
If Cache("submitted-requests") Is Nothing Then
Cache("submitted-requests") = New Dictionary(Of String, Date)
End If
' Remove old requests. '
Dim submittedRequests As Dictionary(Of String, Date) = CType(Cache("submitted-requests"), Dictionary(Of String, Date))
Dim itemsToRemove = submittedRequests.Where(Function(s) s.Value < Now).Select(Function(s) s.Key).ToList
For Each key As String In itemsToRemove
submittedRequests.Remove(key)
Next
If submittedRequests.ContainsKey(Request.UserHostAddress) Then
' User has submitted a request in the past n minutes. '
Return False
Else
submittedRequests.Add(Request.UserHostAddress, Now.AddMinutes(minTimeBetweenRequestsMinutes))
End If
Return True
End Function
No. The ASP.NET Cache is not inherently thread-safe and it looks like you are creating objects in the Cache depending on whether they exist or not.
You need to lock the Cache when writing to it.
Let me word things a little differently. The code is, in fact, thread safe. The way you currently have it coded though could cause performance issues in multi-threaded situations.
In this case, multiple users would be running the same code simultaneously, theoretically accessing and modifying the same cache objects at the same time. As that scenario scales up, performance suffers.
Creating a lock will improve performance under heavy load (while imposing a slight overhead under light load) because you won't be fetching data neadlessly due to Caching issues.
The System.Web.Caching.Cache class is thread-safe according to the MSDN documenation. However, the documenation also shows an example where a read and a write are performed on the cache without locking. That cannot possibily be thread-safe since the write is dependent on the read. The code you posted basically looks like the example. I definitely recommend putting a lock around the entire method.

InsertOnSubmit not triggering a database insert on SubmitChanges

I'm experiencing an odd scenario and I'm looking for ways to figure out what's going wrong. I've got a piece of code that inserts a row into a table - the kind of thing I've done in dozens of other apps - but the end result is nothing happens on the database end, and no errors are generated. How do I find out what's going wrong?
Here's my code:
Partial Class MyDatabaseDataContext
Public Sub CreateEnrollee(subId, depId)
dim newEnrollee = New enrolee With {.subId = subId, .depId = depId}
Me.enrollees.InsertOnSubmit(newEnrollee)
Me.SubmitChanges()
dim test = NewEnrollee.id '<-- auto-incrementing key'
End Sub
End Class
After SubmitChanges is called, no new row is created, and "test" is zero. No errors are generated. I have no idea why it's not trying to insert the row. Any ideas on how to debug this?
You could enable logging:
Me.Log = Console.Out;
You could check the ChangeSet for your object.
FOUND IT! Part of the debugging I did for some other issues included adding some logging to some of the extensibility methods:
Partial Private Sub InsertEnrollee(instance As Enrollee)
End Sub
I thought "InsertEnrollee" existed so I could perform actions after the Enrollee was inserted, so I added logging code here and that's when the trouble started. Now I'm guessing this is how you would override the Enrollee insert and do it yourself if you so desired. Since I was essentially overriding with logging code, that's why nothing was happening (from a database perspective).

Resources