1

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?

PassionateDeveloper
  • 14,558
  • 34
  • 107
  • 176
  • Are you going to be on a web farm? – Joe Ratzer Nov 27 '09 at 11:59
  • Check out - http://en.wikipedia.org/wiki/Server_farm Basically, are you going to put your web site on more than one server? In which case the use of Session requires a bit more work... – Joe Ratzer Nov 27 '09 at 19:42

6 Answers6

4

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
  • 12,511
  • 5
  • 46
  • 68
3

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
  • 80,725
  • 18
  • 167
  • 237
  • Wouldn't an application variable be better than session? – adrianos Nov 27 '09 at 11:10
  • @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 Nov 27 '09 at 11:12
  • Yeah good point, I agree, though I'd have thought a simple list of tables would be global. – adrianos Nov 27 '09 at 11:13
  • To answer: It is user specific, because each user can get his own first-page, depends on the time he joins. – PassionateDeveloper Nov 27 '09 at 11:16
  • Session is defintely the way to go then :) – James Nov 27 '09 at 11:18
  • But how to kill them when user leaves the site and don't get hundrets of them for 20 minutes at least? – PassionateDeveloper Nov 27 '09 at 11:21
  • 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 Nov 27 '09 at 11:26
  • 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 Ratzer Nov 27 '09 at 11:44
  • @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 Nov 27 '09 at 12:00
1

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
  • 687,336
  • 108
  • 737
  • 1,005
  • Thats exectly my thoughts. Can I react when a user is closing the site and kill these session? – PassionateDeveloper Nov 27 '09 at 11:12
  • @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 Nov 27 '09 at 13:06
0

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
  • 4,345
  • 1
  • 32
  • 44
0

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
  • 11,919
  • 8
  • 56
  • 77
0
[WebMethod(EnableSession = true)]
public void SetCache()
{
    LoginData userList = new LoginData
    {
        Status = "Success",
        LastLogin = DateTime.Now.ToString("yyyyMMdd-HHMMss"),
        UserName = "Some User"
    };
    HttpRuntime.Cache["loggedIn"] = userList;
}
public class LoginData
{
    public string Status { get; set; }
    public string UserName { get; set; }
    public string LastLogin { get; set; }
}

There is a big difference between a cached list and a session list and different roles they play.

Kevin
  • 101
  • 1
  • 3