Share via


Best practices for storing objects in memory?

Question

Wednesday, April 15, 2009 2:25 PM

I'm curious to get some different opinions on best practices for storing application objects in memory in this scenario.

Example, suppose you have multiple pages that edit orders ... an "edit order" page and an "import orders" page.  You want to make some busines-layer code that both of these pages will use to modify orders (to avoid having all of the validation and business logic etc. in 2 different places).  The user needs to be able to preview changes before submitting them to the database on both pages, so the order objects are going to be stored in memory problably (session, cache, or viewstate).

What the "best practices" way to build this part of the business layer?  Would you use viewstate, session, or cache and why?

What I have been doing lately is to make a class that has all of the info I need. Then I save that object in session sort of like this.  OrderInfo class would have various properties that I want to access - a collection of the order objects that are currently being edited, customer object, program object for the orders, etc.

    public static class OrderSessionData
    {
        public static OrderInfo OrderInfo
        {
            get
            {
                return (System.Web.HttpContext.Current.Session["OrderInfo"] as OrderInfo);
            }
            set
            {
                System.Web.HttpContext.Current.Session["OrderInfo"] = value;
            }
        }
        public void Clear() { OrderInfo = null; }
    }
What I like about this is:

1. I like how like is strongly typed - for example in my page I can say OrderSessionInfo.OrderInfo.OrderItems.Add(x) and not have to worry about casting. 

2. I like how all of my storing-stuff-in-memory code is centralized in one place ... I don't have to search in multiple files to see which objects I'm storing in session and what the key names are.

 3. I like how the information persists across pages - I can redirect to a different page and it's still there (unlike ViewState).

What I don't like about it is:

A) I can't have multiple pages open editing different orders because of the way I'm storing it in session - for example if the user opens 2 different tabs and tries to edit separate orders in each tab, then the 2nd tab overwrites the session data from the 1st tab. This leads to some tricky bugs if I'm not careful.

B) Linq objects don't like being created on one page, stored in session across a few different page visits, and then submitted to the database on another page.  I am trying to use Linq more and more because it saves me time but this method of storing info seems incompatible with it.

 

So ... any advice?  What do you do in a situation like this and why?  I am very curious to see how different people handle this scenario ... I feel like my method is not really ideal.

All replies (8)

Thursday, April 16, 2009 9:44 AM ✅Answered

 If you mark you OrderSessionData object as Serializable, then it would persist to SqlServer just fine without requiring much further work on your part.

 [Serializable]

class OrderSessionData

{

 ....

}

The only caveat is that members of this class that are not themselves serializable should be marked as [NonSerializable]

 

If you want to get funky, then you could implement ISerializable and control the serialization of the object yourself, but 9 times out of 10 just having the attribute does the job.

 

 It would be a good idea to make this sort of object Serializable in case you want physically separate your business code from the web front end as Serializable objects can be used over remoting and WCF channels.

 

Viewstate should be avoided, particularly when ViewState is being persisted to the Page, which is default mechanism. You could elect to have ViewState persisted to the session however (see PageStatePersister), in which case storing  the object in the session or the viewstate is not much of a muchness.

 

Before rushing off to start storing state in SqlServer, consider the out of proc ASP.Net state server. You need to make a decision based on anticipated site traffic and anticipated session sizes.

 

Storing this sort of information in the Cache would not be a good idea unless you could generate a session specific key of the cache entry. This is becuase the cache is global to every session. Even then, you would want to make sure that the cache never dumped you stuff but you would need to create some mechanism to clean out unwanted records.

 

So, in answer I would probably mark the object as serializable, store it in the session and then work out what session persistance I want to use.

 

 


Thursday, April 16, 2009 9:58 PM ✅Answered

I would not worry about serialization cost. We're talking web apps and the cost of serialisation is frankly going to be miniscule compared to things like network latency and bandwidth, let alone database connections and the like. Remember objects must be serialisable for use in remoting and WCF scenarios. When you say "serialize the entire object",  we are only talking about fields, not methods.

In the code below,I store a serializable object in either the Session, Viewstate ot Cache (note that objects do not have to be serializable for the Cache option). The point is that you do not have to write any explicit serialization code yourself to make it all work.

You should definitely avoid using the Application dictionary for anything. This is inefficent compared to the Cache or even static members on the base application class. 

If you want thread safe access to global data, write a static method and lock some static object. Or, you make the global itself thread safe, which is easy enought to do (wrap modifers in a  lock using a private static member).

[Serializable]

public class MyObject
{
  //private constructor
  private MyObject(){}

  //factory methods
  public static GetSessionInstance()
  { 
    MyObject obj = Session["myObject"];   
    if(obj == null)
    {
      Session["myObject"] = obj = new MyObject();
    }
    return obj;    
  }

  public static GetViewStateInstance()
  { 
    MyObject obj = ViewState["myObject"];   
    if(obj == null)
    {
      ViewState["myObject"] = obj = new MyObject();
    }
    return obj;    
  }

  public static GetGlobalInstance()
  { 
    MyObject obj = Cache["myObject"];   
    if(obj == null)
    {
      Cache["myObject"] = obj = new MyObject();
    }
    return obj;    
  }

}

Friday, April 17, 2009 12:48 PM ✅Answered

Make sure you are not suffering premature optmisation[:D]

Static objects will live for the duration of the process, which in the case of ASP.Net is the lifetime of your application pool. Items added in the Application items collection are shared across multiple application instances (remembering there may be multiple applications in any single application pool).

Generally the Application Items collection is not the preferred way of storing information becuase of the sharing overhead -I think it might be hangover from the classic ASP days.

Use the Cache these days instead. It's much more fun.It encompasses features like storage priorities, storage times, callbacks when something gets dumped and the use of data dependencies, such as SqlDataDependancies.

I think static objects would give the most efficient access, and you the strong typedness. The only draw back I can think of off the top of my head is that you have no way of cleaning up after yourself.You also need to take care with what the static object references to help out the GC.

With regard to Struct/Classes I wouldn't worry about it too much.

ViewState doesn;t need to go the page - you can give a Page a PageStatePersister that sends the page state to any medium you want. I am working in a low bandwidth environment at the moment, so it makes more sense for me to persist the viewstate to the session, and then persist the session to sqlserver than to send it over the wire. My pages are typically only 16K in size, total.

 Happy to help!!!

 


Wednesday, April 15, 2009 3:51 PM

 

Having an in-memory object like this does make addressesing elements of your session neat, however if your application ever needs to scale you'll have a problem.

If you should move to SQL Server session storage, then you'd have to be sure to always set the OrderSessionData object directly, as Object.Property = value would cease to work, why? Because it would need to be serialized for storage in the database.

So from a future-proof point of view, I'd not reconmend this.

As for Linq, you can serialize and deserialize Linq entities, and you can attach them up again ready for inserts & updates, but its a fairly complex topic.


Wednesday, April 15, 2009 9:54 PM

Using a strongly typed object for your session is a great way to program.  It's important to remember whenever you have state in your application, that complicates things as you can run into concurrency issues.

It's always a good idea to limit that amount of data that is stored in your session.  Less is more.


Thursday, April 16, 2009 10:57 AM

 Very interesting - I was just wondering the same thing this morning!

 The problem that I'm coming up with is the cost of serialization - It's very ineffcient to do that, every time you want to read the data you deserialize the entire object, and to save the data you have to explicitly reserialize it because Object.data = X won't reserialize Object (i.e. the "set" manager won't be invoked), so you'd need to explicitly myObj = Object; myObj.data = X; Object = myObj; in order to save the data.

 There has to be an easy way to say "look - this object is application wide" without having to dictionary it and serialize/deserialize all the time.

Then you run into concurrency issues when you scale the application up, and I can't find an elegant way to handle this. 

I was thinking about a primitive cache mechanism - i.e. storing a "timestamp" field in the Application dictionary.  So on Session_Start I deserialize the global object, and on every access I check the timestamp.  Do a (Application["myObjectStamp"] as DateTime) > MyObject.Stamp check on every access and reload when necessary, then use a Mutex in order to modify any global data.  I suppose that I could also just use a version number instead of a timestamp as well.  Since the majority (95%+) of access to global object data is read-only it seems like the most efficient method to have syncronization of global data across the application.

I could be missing something really easy and obvious - I'm a relative newb to this, but I can't seem to find a better way to do it.

I'm a big stickler for efficiency as well, which is why I hate the idea of deserializing an entire object to access one field (and still doesn't handle concurrent writes).


Friday, April 17, 2009 9:37 AM

Well yes and no to the cost of serialization.  I have one particular ajax-style call that needs to be able to handle hundreds of requests a second, so I want everything in that to be as efficient as possible.  Generally though yes, the cost is minimal but that still doesn't mean that there isn't a more elegant way to do it.

Out of curiosity: what is the scope and lifetime of static objects (or the Global object for that matter)?  It appears that I can declare anything static and it's application-wide, and anything I put in the global object seems to be application-wide as well.  So why not just store objects there?  If I know by design what objects I need global access to why not just declare them and use them normally instead of sticking them in a dictionary somewhere?  Keep in mind that I am referring to data that I want to be application-wide, not per session.

I didn't know that I could stash stuff in the Cache without needing it to be serializable.

Are structs more efficient for ViewState/Cache than classes are?  I thought that I read that somewhere.

Doesn't the ViewState get transmitted with every page?  I love viewing source on some ASP applications and seeing 40KB of viewstate data.

Sorry for the newb questions, I'm relatively new to C#/ASP.NET (used to be an embedded device programmer).


Wednesday, April 22, 2009 4:53 PM

 Ok, so here's the problem that I was facing with static objects:

 

Session 1 modifies the static object, Session 2 accesses it and does not see the modification.

 

This led me to believe that static objects were per session not per application pool.

 

Maybe I did something really strange, but I load the static object's data in the Application_Start function, so I don't know why it wouldn't reflect the changes between the 2 sessions :S