views:

191

answers:

5

Session[Constant] vs Session["String Literal"] Performance

I'm retrieving user-specific data like ViewData["CartItems"] = Session["CartItems"]; with a string literal for keys on every request. Should I be using constants for this?

If yes, how should I go about implementing frequently used string literals and will it significantly affect performance on a high-traffic site?


Related question does not address ASP.NET MVC or Session.

+2  A: 

Use the constant because the compiler will give you an error if you mistype it, whereas mistyped strings will just give you wonky bugs.

The performance difference will likely be so small it would be very difficult to measure.

ScottS
Actually the performance difference won't be small, it will be inexistent : the compiler replaces constants by their values, so you end up with exactly the same IL
Thomas Levesque
@ScottS: Great, succinct answer, but Sudit nailed it with a focus on maintainbility and a disconnect between Session key and ViewData key with an example.
FreshCode
@Thomas: I wasn't aware that C# replaced in-code string literals with constants. Nice to know.
FreshCode
@Fresh: Since C# strings are immutable, it typically combines identical literals in memory. You might also want to look up string interning, which is related to this. http://msdn.microsoft.com/en-us/library/system.string.intern.aspx
Steven Sudit
@Thomas L, I left the performance difference a bit fuzzy because I am not sure how perfectly strings will be interned. There may be tiny differences due to extra executable size or different memory location of the strings. Very small differences if at all.
ScottS
+2  A: 

The reason to use constants has to do with maintainability, not performance. Performance is about the same either way.

With a string literal, you can never tell whether it's the same as another string literal intentionally or by coincidence, so when it's time to change one, you don't know which other ones to change. But if you have the values in constants, you just make the change in one place.

Bad:

ViewData["username"] = Session["username"];

Good:

const UserNameSessionKey = "username";
const UserNameViewDataKey = "username";

ViewData[UserNameViewDataKey] = Session[UserNameSessionkey];

Now imagine changing the value of the session key to "userName" without wanting to change it for any viewdata key...

Steven Sudit
Nice. Why not go further and have const UserNameKey = "username"; where you then have the freedom of differentiating the two keys or keeping them the same. ie. const UserNameSessionKey = UserNameKey; and const UserNameViewDataKey = "SomethingElse";
FreshCode
@Fresh: I would do that if I wanted to show that it's not a coincidence that the two keys are the same. In the code above, I'm emphasizing their independence. Also, see my answer to ScottS.
Steven Sudit
+1  A: 

Lets go further with maintainability. I'll quote my other answer about using Session:

Lets say we want to store shopping cart is Session of our ASP.NET MVC application. It will be stored in Session["ShoppingCart"], but we want easy, strongly typed access and high testability:

First we define interface:

public interface ISessionWrapper
{
    List<CartItem> ShoppingCart { get; set; }
}

Then we make HttpContext implementation:

public class HttpContextSessionWrapper : ISessionWrapper
{
    private T GetFromSession<T>(string key)
    {
        return (T) HttpContext.Current.Session[key];
    }

    private void SetInSession(string key, object value)
    {
        HttpContext.Current.Session[key] = value;
    }

    public List<CartItem> ShoppingCart
    {
        get { return GetFromSession<List<CartItem>>("ShoppingCart"); }
        set { SetInSession("ShoppingCart", value); }
    }
}

GetFromSession and SetInSession are helper method that make taking and setting data in Session easier. They can be easily reused to access other fields in Session.

Then we define our base controller (applicable to ASP.NET MVC):

public class BaseController : Controller
{
    public ISessionWrapper SessionWrapper { get; set; }

    public BaseController()
    {
        SessionWrapper = new HttpContextSessionWrapper();
    }
}

If you want to use Session outside controller, you just create or inject new HttpContextSessionWrapper().

You can replace SessionWrapper with ISessionWrapper mock in Controller tests, so it is not dependent on HttpContext anymore. Session is also more easy to use, because instead of calling (List<CartItem>)Session["ShoppingCart"], you call SessionWrapper.ShoppingCart. It looks nicer, isn't it?

If you don't use model class for view and I think using model class is better, you can do the same with ViewData:

public interface IViewDataWrapper
{
    List<CartItem> ShoppingCart { get; set; }
}

public class ViewDataWrapper : IViewDataWrapper
{
}

public class BaseController : Controller
{
    public IViewDataWrapper ViewDataWrapper { get; set; }

    public BaseController()
    {
        IViewDataWrapper = new ViewDataWrapper();
    }
}

And then simply in controller:

ViewDataWrapper.ShoppingCart = SessionWrapper.ShoppingCart 

or if you decide not to use ViewData and specific model:

Model.ShoppingCart = SessionWrapper.ShoppingCart

And simply in view (if you define base class for view and introduce this interface):

<%= ViewDataWrapper.ShoppingCart %>

or

<%= Model.ShoppingCart %>

No mistyped strings, strongly typed, nice looking.

LukLed
Maintainable and fairly elegant. If only the .NET MVC Framework did not warrant such a wrapper. Do you always use this model in your applications?
FreshCode
Incorporate an introduction and start with a simple example to make your answer more attractive to a wider audience.
FreshCode
While I generally like the notion of such a framework, I'd still suggest using a constant for the key, to increase maintainability.
Steven Sudit
@Steven Sudit: Why do You want to use constants? "username" is used only in Get and Set (in the same place), so there is no need to create additional classes with constants.
LukLed
@LukeLed: Because you may have the string "username" elsewhere in the codebase, and without constants, you don't know if it's just a coincidence that they have the same value.
Steven Sudit
A: 

Just a quick note but a lot of the better examples have a class of SessionKeys containing string constants. This also aids Unit Testing as you can call the constants in your Unit Tests where necessary.

E.g. (just one key but obviously you can add more

public class SessionKeys
{
    public const string UserDto = "UserDto";
}

used as such (I use SessionStateWrapper)

UserDto userDto = _sessionStateWrapper.GetItem(SessionKeys.UserDto) as UserDto;
ArtificialGold
Can you please detail SessionStateWrapper or state if you are referencing LukLed's answer in this regard?
FreshCode
I was deliberately avoiding referencing SessionStateWrapper as it can be even quite tough to get your head around (at least it was for me coming from Web Forms). The point is that you can just use any getter from a collection by using a constant in another class which your whole project can consistently access.I am tempted to blog about SessionStateWrapper and so forth but not 100% I have it right yet, the varying solutions on the internet have lead me down several paths and not all feel right.
ArtificialGold
A: 

According to this benchmark on the length of dictionary keys, shorter keys are faster. Quoted here:

Dictionary string key length benchmark in C#

Are shorter lookup keys significantly faster? As keys get shorter, lookup time gets faster:

  • Key A - 20 characters: 4436 ms [slowest]
  • Key B - 10 characters: 2010 ms
  • Key C - 5 characters: 1749 ms
  • Key D - 2 characters: 1575 ms [fastest]

where:

  • Key A = "01234567890123456789";
  • Key B = "0123456789";
  • Key C = "01234";
  • Key D = "01";
FreshCode
If you call it 100000000 times there will be about 3000ms difference. If you call it once, there will be 0,00003 ms difference. Hmmmm... Is it worth even mentioning?
LukLed
In this context, no.
FreshCode
Both the hashing and string comparison algorithms are linear, so this is not a big deal.
Steven Sudit