views:

202

answers:

5

Hi guys,

I load a list of a database table via Linq in the page load of my site.

Now I must cache the list, because I will use them in a WebMethod again. (I can't load them new, because it can be changes in there).

How to cache a List<> and get it back in a WebMethod?

+2  A: 

I would say the Session would be your best bet.

Example

Storing:

var list = new List<int>() { 1, 2, 3, 4, 5 };
Session["ListOfIntegers"] = list;

Retrieving:

var list = (List<int>)Session["ListOfIntegers"];
James
Easy write in and read out? Thats all?
Kovu
Yup simple as that.
James
Wouldn't an application variable be better than session?
adrianos
@adrianos, it depends on what the list is dependant of the application (i.e. a run once thing) or if it is Session dependant (per user). So this is really for Kovu to answer?
James
Yeah good point, I agree, though I'd have thought a simple list of tables would be global.
adrianos
I tested, works. Please read comment on Guffa
Kovu
To answer: It is user specific, because each user can get his own first-page, depends on the time he joins.
Kovu
Session is defintely the way to go then :)
James
But how to kill them when user leaves the site and don't get hundrets of them for 20 minutes at least?
Kovu
If the user specifically logs out you can easily dispose of the session then and there. However, if you mean detecting if they just leave the site see this question http://stackoverflow.com/questions/147636/best-way-to-detect-when-user-leaves-a-web-page
James
Thank you =) answered.
Kovu
Are you going to be on a web farm? Because if you are then it won't be that simple, you'll need a session database...
Joe R
@joe90 yup good point, however there is no mention of this from the OP so I would imagine it is going to be a single server.
James
A: 

You could make a property which checks if the session field contains data, if not load the data, otherwise return the field. Like so:

List<goof> data {
    get {
        List<goof> data = Session["data"] as List<goof>;
        if (data == null) Session["data"] = LINQstuff;

        return data;
    }
}
Robert Massa
But my site is stateless, isn't it? So I can't have global member variables and save them over time, can I?
Kovu
You're right, didn't understand the question properly. As James points out, you should indeed use the Session variable.
Robert Massa
+1  A: 

I suppose that you want to cache (or rather persist) it separately for the user, as you mention that there can be changes.

Store the list in a Session variable, and enable session state for the web method using the [WebMethod(EnableSession:=True)] attribute.

Consider the amount of memory that you are using, though. Putting the list in a session variable will keep it in memory for a long time. (20 minutes is eons in the scope of a web application, where objects normally survive only a few milliseconds...)

Guffa
Thats exectly my thoughts. Can I react when a user is closing the site and kill these session?
Kovu
@Kovu: Not easily, and not reliably. You can use the unload event in the browser and contact the server when the user leaves, but then you have to keep track of whether the user actually leaves or just reloads the page. Also, it only works if the user is still connected.
Guffa
A: 

I suggest to use the System.Web.Caching.Cache class.

See http://msdn.microsoft.com/en-us/library/system.web.caching.cache.aspx

It doesn't depend on the user session but in your case I think it is better because you are inside a WebMethod.

Davide

Davide Icardi
+2  A: 

I'd do this with the ASP.NET Cache API - here's how.

Imports System.Web

Public Class CacheManager

Private ListKey As String = "MyList"

Public Shared ReadOnly Property TypedList As List(Of Integer)

    Dim cachedObject As Object  
    Dim myList As List (Of Integer)  
    Dim userCacheKey As String = ListKey & HttpContext.Current.User.Identity.Name

    'First check to see if List is in the cache already
    cachedObject = HttpRuntime.Cache.Get(userCacheKey)

    If cachedObject Is Nothing Then
       'If List isn't in the cache already then get it...
       myList = Code to retrieve list members goes here
       ' ...and now we've got it put it in the cache
       HttpRuntime.Cache..Add(key:=userCacheKey, value:=myList, absoluteExpiration:=HttpRuntime.Cache.NoAbsoluteExpiration, slidingExpiration:=New TimeSpan(0,5,0), dependencies:=Nothing, onRemoveCallback:=Nothing, priority:=CacheItemPriority.Default)
    Else
        'List is already in the cache but everything comes out of the cache as System.Object so cast it to List (Of Integer)  
        myList = DirectCast(cachedObject, List (Of Integer))
    End If

    'Now we have List, return it to the caller
    Return myList

End Property

End Class

This gives us a class that will hold an instance of List<> per user that exists in memory for five minutes after the last time it was accessed - you can up this just by changing the length of the TimeSpan object in the slidingExpiration parameter when the List is added to the Cache.

Your usage in the page is then simply:

Public Sub Page_Load (arguments)

Dim myList As List(Of Integer)  
...
myList = CacheManager.TypedList
...
End Sub

<WebMethod()> Public Sub MyEventMethod(arguments)

Dim myList As List(Of Integer)
...
myList = CacheManager.TypedList
...

End Sub

It's not quite clear (to me) from your question whether users can change their individual List or they change the global list. If they change their individual list that's easy to cater for - change the TypedList property like this:

Imports System.Web

Public Class CacheManager

Private ListKey As String = "MyList"

Public Shared Property TypedList As List(Of Integer)
Get
    Dim cachedObject As Object  
    Dim myList As List (Of Integer)  
    Dim userCacheKey As String = ListKey & HttpContext.Current.User.Identity.Name

    'First check to see if List is in the cache already
    cachedObject = HttpRuntime.Cache.Get(userCacheKey)

    If cachedObject Is Nothing Then
       'If List isn't in the cache already then get it...
       myList = Code to retrieve list members goes here
       ' ...and now we've got it put it in the cache
       HttpRuntime.Cache.Add(key:=userCacheKey, value:=myList, absoluteExpiration:=HttpRuntime.Cache.NoAbsoluteExpiration, slidingExpiration:=New TimeSpan(0,5,0), dependencies:=Nothing, onRemoveCallback:=Nothing, priority:=CacheItemPriority.Default)
    Else
        'List is already in the cache but everything comes out of the cache as System.Object so cast it to List (Of Integer)  
        myList = DirectCast(cachedObject, List (Of Integer))
    End If

    'Now we have List, return it to the caller
    Return myList
End Get
Set (ByVal value As List(Of Integer))

    Dim userCacheKey As String = ListKey & HttpContext.Current.User.Identity.Name

    HttpRuntime.Cache.Insert(key:=userCacheKey, value:=value, absoluteExpiration:=HttpRuntime.Cache.NoAbsoluteExpiration, slidingExpiration:=New TimeSpan(0,5,0), dependencies:=Nothing, onRemoveCallback:=Nothing, priority:=CacheItemPriority.Default)

End Set
End Property

End Class

If any user making changes to the list changes it for everybody, then I'd look at using a CacheDependency.

PhilPursglove