1

After having a chat with a colleague i'm interested to know if anyone has done any research/tests to see the most efficient and recommended ways of storing data in the asp.net session.

I recently stumbled across this question: How to access session variables from any class in ASP.NET? and started to use that technique (creating my own session class to give me the advantage of intellisense and prevent the need for explicit casting when retrieving data from the session).

However, it has been suggested that the data in my object is being serialised into a string and then stored... then de-serialised when being retrieved and this may not be very efficient.

For example... If I want to load the user's current permissions and store them in the session; I might have a property within my session object defined as List<int> user_permissions then on each page I can if (session_class.user_permissions.contains(7))

Alternatively, I could store directly in the session as a string something like; HttpContext.Current.Session["user_permissions"] = "[1][4][8][12]" then on each page I can check HttpContext.Current.Session["user_permissions"].contains("[7]")

Which option would be more efficient and is it possible to quantify?

Lee Tickett
  • 5,847
  • 8
  • 31
  • 55
  • Rather than use a string to represent effectively bitwise values, why not use a flags-based enum, and store the integer value? More efficient, and you can use the enum values instead of strings like `"[7]"` which mean nothing. – Jon Skeet Jul 16 '17 at 16:06
  • I've just had a look and that looks like a really interesting solution but I wonder how well it will work if we have a large number of permissions? – Lee Tickett Jul 16 '17 at 17:52
  • Well how many were you thinking of? You can have an enum with an underlying type of `long`, so you get 64 bits... – Jon Skeet Jul 16 '17 at 19:09
  • I don't think that'll cut it. I can probably think of roughly 50 already and we want to ensure our application is scalable so it seems like a bad way to engineer it from the start. Nice idea though- thanks – Lee Tickett Jul 16 '17 at 20:36
  • 1
    So have two enums and two values. That will cover 128. If there are 128 independent permissions, I think you'll have a scaling problem with *people* understanding your system. – Jon Skeet Jul 16 '17 at 21:33

5 Answers5

1

The first thing is to determine what ASP.NET is capable of serializing out-of-the-box. Since ASP.NET reference source is available, we can drill state serialization down to this class: AltSerialization

It shows that the following types are natively supported:

String
Int32
Boolean
DateTime
Decimal
Byte
Char
Single
Double
SByte
Int16
Int64
UInt16
UInt32
UInt64
TimeSpan
Guid
IntPtr
UIntPtr
Null // not really a type :-)

For all other types, the system uses a BinaryFormatter, which is a public .NET class. BinaryFormatter is very easy to use, it knows many classes by default, so you can do simple benchmarks comparing what BinaryFormatter will produce versus what you can yourself "manually".

In general you will always be able to beat BinaryFormatter's performance because you know your data better, but it means you'll have to write additional serialization/deserialization code. You can also create custom classes and implement ISerializable.

One last note, if you look at AltSerialization source, you'll see this:

internal static void WriteValueToStream(Object value, BinaryWriter writer)
{
    if (known type) { ... }
    else
    {
        ...
        var formatter = new BinaryFormatter();
        if (SessionStateUtility.SerializationSurrogateSelector != null)
        {
            formatter.SurrogateSelector = SessionStateUtility.SerializationSurrogateSelector;
        }

        formatter.Serialize(writer.BaseStream, value);
    }
}

which means that you can implement a global hook to handle specific types in a central place, which can be useful in terms of engeneering, or to do benchmarks (you can enable it or disable it easily). It may be better than create serializable custom classes.

Simon Mourier
  • 132,049
  • 21
  • 248
  • 298
0

Assuming you're only hosting on a single machine (and therefore session state does not need to be serialized to a common repository), you could use a common MemoryCache to host your session data in memory, keyed by a unique session id.

public class SessionData
{
    private static readonly MemoryCache Cache = new MemoryCache("SessionData");
    private static readonly CacheItemPolicy Policy = new CacheItemPolicy {SlidingExpiration = new TimeSpan(0, 30, 0)};

    public static SessionData Get(string sessionId)
    {
        if (Cache.Contains(sessionId))
            return Cache[sessionId] as SessionData;

        var item = new SessionData {SessionId = sessionId};
        Cache.Add(sessionId, item, Policy);
        return item;
    }

    public string SessionId { get; set; }
    public string Foo { get; set; }
    public int Bar { get; set; }
}

Example of use:

var item = SessionData.Get(this.Session.SessionID);
item.Foo = "Hello";
item.Bar = 123;

The session data is held in memory with a sliding expiration of 30 minutes - every time it is accessed the 30 minute timeout is reset. This way you won't suffer memory bloat from expired sessions. Give it a go against your current implementation and see how it stacks up.

Pete
  • 1,807
  • 1
  • 16
  • 32
  • very interested i've seen system.web.caching.cache mentioned a few times now. so can you confirm... data stored in the session is serialised when setting and deserialised when getting? and this is inefficient? but data stored in system.web.caching.cache isn't and hence more performant? – Lee Tickett Jul 18 '17 at 20:32
  • The MemoryCache certainly doesn't serialize data - it doesn't need to because the data is always memory resident and not garbage collected until such stage as it is either expired and released from the cache or the cache itself is garbage collected. One potential issue with this approach, however, is when the IIS app pool recycles every 29 hours - I've a feeling that static objects will not be persisted, so you may well be better off using the built-in session object. – Pete Jul 19 '17 at 08:15
  • thanks pete, i now beleive the session isn't serialised when using inproc mode so what advantages does MemoryCache offer? (apologies i've probably missed something obvious) – Lee Tickett Jul 20 '17 at 20:34
  • 1
    Hi Lee - whist the MemoryCache is high-performance and automatically tidies up session data that's no longer required / expires, it is probably no more performant than the built-in session object - I merely offered it as an alternative that I know doesn't serialize. On the understanding that the session, when used in InProc mode, does not serialize ether I'd recommend sticking with it since the contents of the MemoryCache will likely be lost when the app pool recycles. – Pete Jul 21 '17 at 07:47
0

There are two questions i find out in your post.

1. Storing different datatypes in the asp.net session

You may know, there are three modes to keep Session in ASP.NET, as follows: InProc, StateServer and SqlServer.

In InProc mode, you can set any object for a session's value without serialize, because session and the web run in a same process.

But for other ways, the session's value must to be serialize when the value is not a struct. SateServer and SqlServer running on independent process(maybe on independent servers), So session's value will be serialize and keep on another process when you set it.

2. Which option would be more efficient and is it possible to quantify in List.contains() and String.contains()?

If you don't got large data in you case, they don't make much difference.There are using loop for each. If you want to get good efficient in contains, you can try HashSet.

Johan Shen
  • 158
  • 6
0

The question is actually how important is the performance of your method versus how important is it that you have a maintainable and readable codebase?

It'd make a difference on which framework you were using to serialise and deserialise your data also, might be worth doing some rudimentary speed testing on it. If you can process a quadrillion requests in a second, that's probably OK.

You question explicitly asks "Which option would be more efficient and is it possible to quantify?" Yes. Absolutely. Just set off a StopWatch (see System.Diagnostics I believe it is) and call the method a bunch of times using some testing data, then check the check the stopwatch's time.

p.s. Your question indicates a discussion regarding exactly how performant this Session class needs to be... In my experience, in a web-app, (de)serialisation isn't really of primary concern. It's the database bottleneck and other long running processes that slow the server down. If this question has been asked because of the argument "the server is already slow, so this is just extra..." then tell them to fix the other problems, this is like blaming the straw for breaking the camel's back when it's the 2 tonned of bricks that really did it. Just a hunch that's the argument :P

Dan Rayson
  • 1,315
  • 1
  • 14
  • 37
  • Hi Dan, the app is actually incredibly performant at the moment (we are only a few months into the build) but we want to try and make every decision as informed as possible. There is also the consideration of course if we need to scale to multiple servers and move the session out of inproc... – Lee Tickett Jul 20 '17 at 20:31
-1

I think we have come up with what seems to be a valid reason to determine that storing objects (especially using the custom class wrapper technique How to access session variables from any class in ASP.NET?) is not the best way to go. From what we understand this would store all properties (fields) of the class in memory even those which are empty.

public class my_session {
 public int iUserID;
 public string sUserName;
 public List<int> lsUSerPermissions;
 public List<TestStatus> lsTestStatuses;
 public int iSomethingElse;
 public bool bSomething;
}

Even though we won't populate/set all properties for all sessions, every session will contain all properties (and consume the memory associated). Initially I though this odd, but now I understand it makes sense so if/when you do set each property the memory is available and the object doesn't have to be destroyed and recreated somewhere else.

If we assign directly we only need to create session variables for those properties which need storing.

So, now that we've decided we will store these individually;

Session["iUserID"] = 1;
Session["sUserName"] = "Lee";

How do we tackle the objects we "must" store? It seems like each object needs to be looked at individually. Does string sUserPermissions = "[ViewUser][CreateUser][EditUser]" take up less memory then List<string> lsUserPermissions = new List<string>() { "[ViewUser]", "[CreateUser]", "[EditUser]" } and is it quicker to serialise/de-serialise if needed?

Still a fair amount of research needed.

Lee Tickett
  • 5,847
  • 8
  • 31
  • 55